Greasy Fork

Greasy Fork is available in English.

PttChrome and term.ptt.cc Enhanced Add-on

add features of "PttChrome Long Change" into "PttChrome+term.ptt.cc Add-on"

当前为 2019-03-05 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         PttChrome and term.ptt.cc Enhanced Add-on
// @namespace    http://greasyfork.icu/zh-TW/scripts/377781-pttchrome-and-term-ptt-cc-enhanced-add-on
// @supportURL   https://github.com/alan23273850/PttChrome-and-term.ptt.cc-Enhanced-Add-on
// @description  add features of "PttChrome Long Change" into "PttChrome+term.ptt.cc Add-on"
// @version      1.1
// @author       alan23273850
// @compatible   firefox
// @compatible   chrome (or chromium-based)
// @match        https://iamchucky.github.io/PttChrome/*
// @match        https://term.ptt.cc/
// @require      https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/tippy.js/2.5.4/tippy.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/lz-string/1.4.4/lz-string.min.js
// @require      https://openuserjs.org/src/libs/sizzle/GM_config.js
// @require      http://greasyfork.icu/scripts/372760-gm-config-lz-string/code/GM_config_lz-string.js?version=634230
// @require      http://greasyfork.icu/scripts/372675-flags-css/code/Flags-CSS.js?version=632757
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        unsafeWindow
// ==/UserScript==
"use strict";
//===================================
const isTerm = window.location.href.match(/term.ptt.cc/);
let configStatus = false, configBlackStatus = false, flagMap = {};
let fields = { // Fields object
	'isAddFloorNum': {
		'label': '是否顯示推文樓層', // Appears next to field
		'type': 'checkbox', // Makes this setting a checkbox input
		'default': true // Default value if user doesn't change it
	},
	'isMouseBrowsingFriendly': {
		'label': '是否啟用滑鼠瀏覽友善模式', // Appears next to field
		'type': 'checkbox', // Makes this setting a checkbox input
		'default': false // Default value if user doesn't change it
	},
	'isShowFlags': {
		'label': '看板內若有IP(ex.Gossiping),是否依IP顯示國旗', // Appears next to field
		'type': 'checkbox', // Makes this setting a checkbox input
		'default': true // Default value if user doesn't change it
	},
	'whenShowFlagsIgnoreSpecificCountrys': {
		'label': '指定國家不顯示 ex.「tw;jp」(ISO 3166-1 alpha-2)', // Appears next to field
		'type': 'text', // Makes this setting a text input
		'size': 35, // Limit length of input (default is 25)
		'default': '' // Default value if user doesn't change it
	},
	'isShowDebug': {
		'label': '是否顯示DeBug紀錄', // Appears next to field
		'type': 'checkbox', // Makes this setting a checkbox input
		'default': false // Default value if user doesn't change it
	},
};
if (isTerm) {
	fields = Object.assign({
		'isAutoLogin': {
			'label': '是否自動登入', // Appears next to field
			'type': 'checkbox', // Makes this setting a checkbox input
			'default': false // Default value if user doesn't change it
		},
		'autoUser': {
			'label': '帳號', // Appears next to field
			'type': 'text', // Makes this setting a text input
			'size': 25, // Limit length of input (default is 25)
			'default': '' // Default value if user doesn't change it
		},
		'autoPassWord': {
			'label': '密碼', // Appears next to field
			'type': 'password', // Makes this setting a text input
			'size': 25, // Limit length of input (default is 25)
			'default': '' // Default value if user doesn't change it
		},
		'isAutoSkipInfo1': {
			'label': '是否自動跳過登入後歡迎畫面', // Appears next to field
			'type': 'checkbox', // Makes this setting a checkbox input
			'default': false // Default value if user doesn't change it
		},
		'isAutoToFavorite': {
			'label': '是否自動進入 Favorite 我的最愛', // Appears next to field
			'type': 'checkbox', // Makes this setting a checkbox input
			'default': false // Default value if user doesn't change it
		},
		'isEnableDeleteDupLogin': {
			'label': '當被問到是否刪除其他重複登入的連線,回答:', // Appears next to field
			'type': 'select', // Makes this setting a dropdown
			'options': ['N/A', 'Y', 'N'], // Possible choices
			'default': 'N/A' // Default value if user doesn't change it
		},
		'Button': {
			'label': '編輯黑名單', // Appears on the button
			'type': 'button', // Makes this setting a button input
			'size': 100, // Control the size of the button (default is 25)
			'click': function() { // Function to call when button is clicked
				if (configBlackStatus) gmcBlack.close();
				else if (!configBlackStatus) gmcBlack.open();
			}
		},
		'isHideViewImg': {
			'label': '是否隱藏黑名單圖片預覽', // Appears next to field
			'type': 'checkbox', // Makes this setting a checkbox input
			'default': true // Default value if user doesn't change it
		},
		'isHideViewVideo': {
			'label': '是否隱藏黑名單影片預覽', // Appears next to field
			'type': 'checkbox', // Makes this setting a checkbox input
			'default': true // Default value if user doesn't change it
		},
		/*
		'isHideAll': {
			'label': '是否隱藏黑名單推文', // Appears next to field
			'type': 'checkbox', // Makes this setting a checkbox input
			'default': false // Default value if user doesn't change it
		},
		'whenHideAllShowInfo': {
			'label': '當隱藏黑名單推文顯示提示訊息', // Appears next to field
			'type': 'text', // Makes this setting a text input
			'size': 35, // Limit length of input (default is 25)
			'default': '<本文作者已被列黑名單>' // Default value if user doesn't change it
		},
		'whenHideAllShowInfoColor': {
			'label': '上述提示訊息之顏色', // Appears next to field
			'type': 'text', // Makes this setting a text input
			'class':'jscolor',
			'data-jscolor': '{hash:true}',
			'size': 10, // Limit length of input (default is 25)
			'default': '#c0c0c0' // Default value if user doesn't change it
		},
		'isReduceHeight': {
			'label': '是否調降黑名單推文高度', // Appears next to field
			'type': 'checkbox', // Makes this setting a checkbox input
			'default': true // Default value if user doesn't change it
		},
		'reduceHeight': {
			'label': '設定高度值(單位em)', // Appears next to field
			'type': 'float', // Makes this setting a text input
			'min': 0, // Optional lower range limit
			'max': 10, // Optional upper range limit
			'size': 10, // Limit length of input (default is 25)
			'default': 0.4 // Default value if user doesn't change it
		},
		'isReduceOpacity': {
			'label': '是否調降黑名單推文透明值', // Appears next to field
			'type': 'checkbox', // Makes this setting a checkbox input
			'default': false // Default value if user doesn't change it
		},
		'reduceOpacity': {
			'label': '設定透明值', // Appears next to field
			'type': 'float', // Makes this setting a text input
			'min': 0, // Optional lower range limit
			'max': 1, // Optional upper range limit
			'size': 10, // Limit length of input (default is 25)
			'default': 0.05 // Default value if user doesn't change it
		},
		'isDisableClosePrompt': {
			'label': '是否停用關閉頁面提示', // Appears next to field
			'type': 'checkbox', // Makes this setting a checkbox input
			'default': true // Default value if user doesn't change it
		},
		*/
	}, fields);
} else {
	fields = Object.assign({
		'isHideAll': {
			'label': '是否隱藏黑名單推文', // Appears next to field
			'type': 'checkbox', // Makes this setting a checkbox input
			'default': false // Default value if user doesn't change it
		},
		'whenHideAllShowInfo': {
			'label': '當隱藏黑名單推文顯示提示訊息', // Appears next to field
			'type': 'text', // Makes this setting a text input
			'size': 35, // Limit length of input (default is 25)
			'default': '<本文作者已被列黑名單>' // Default value if user doesn't change it
		},
		'whenHideAllShowInfoColor': {
			'label': '上述提示訊息之顏色', // Appears next to field
			'type': 'text', // Makes this setting a text input
			'class':'jscolor',
			'data-jscolor': '{hash:true}',
			'size': 10, // Limit length of input (default is 25)
			'default': '#c0c0c0' // Default value if user doesn't change it
		},
		'isHideViewImg': {
			'label': '是否隱藏黑名單圖片預覽', // Appears next to field
			'type': 'checkbox', // Makes this setting a checkbox input
			'default': true // Default value if user doesn't change it
		},
		'isHideViewVideo': {
			'label': '是否隱藏黑名單影片預覽', // Appears next to field
			'type': 'checkbox', // Makes this setting a checkbox input
			'default': true // Default value if user doesn't change it
		},
		'isReduceHeight': {
			'label': '是否調降黑名單推文高度', // Appears next to field
			'type': 'checkbox', // Makes this setting a checkbox input
			'default': true // Default value if user doesn't change it
		},
		'reduceHeight': {
			'label': '設定高度值(單位em)', // Appears next to field
			'type': 'float', // Makes this setting a text input
			'min': 0, // Optional lower range limit
			'max': 10, // Optional upper range limit
			'size': 10, // Limit length of input (default is 25)
			'default': 0.4 // Default value if user doesn't change it
		},
		'isReduceOpacity': {
			'label': '是否調降黑名單推文透明值', // Appears next to field
			'type': 'checkbox', // Makes this setting a checkbox input
			'default': false // Default value if user doesn't change it
		},
		'reduceOpacity': {
			'label': '設定透明值', // Appears next to field
			'type': 'float', // Makes this setting a text input
			'min': 0, // Optional lower range limit
			'max': 1, // Optional upper range limit
			'size': 10, // Limit length of input (default is 25)
			'default': 0.05 // Default value if user doesn't change it
		},
	}, fields);
};
const queryConfigEl = (configSelectors, selectors, callback) => {
	let configEl = document.querySelector(configSelectors);
	if (!configEl) {
		setTimeout(queryConfigEl.bind(null, configSelectors, selectors, callback), 1000);
		return;
	}
	configEl = configEl.contentWindow.document.querySelector(selectors);
	if (!configEl) {
		setTimeout(queryConfigEl.bind(null, configSelectors, selectors, callback), 1000);
		return;
	}
	callback(configEl);
};

const addCssLink = (id, cssStr) => {
	let checkEl = document.querySelector(`#${id}`);
	if (checkEl) {
		checkEl.remove();
	}
	const cssLinkEl = document.createElement('link');
	cssLinkEl.setAttribute('rel', 'stylesheet');
	cssLinkEl.setAttribute('id', id);
	cssLinkEl.setAttribute('type', 'text/css');
	cssLinkEl.setAttribute('href', 'data:text/css;charset=UTF-8,' + encodeURIComponent(cssStr));
	document.head.appendChild(cssLinkEl);
};
const gmc = new ConfigLzString({
	'id': 'PttChromeAddOnConfig', // The id used for this instance of GM_config
	'title': 'PttChrome Add-on Settings', // Panel Title
	'fields': fields,
	'events': { // Callback functions object
		'open': function() {
			this.frame.setAttribute('style', "border: 1px solid #AAA;color: #999;background-color: #111; width: 23em; height: 35em; position: fixed; top: 2.5em; right: 0.5em; z-index: 900;");

			configStatus = true;
		},
		'close': () => { configStatus = false;},
	},
	'css': `#PttChromeAddOnConfig * { color: #999 !important;background-color: #111 !important; } body#PttChromeAddOnConfig { background-color: #111}`,
	'src':`https://cdnjs.cloudflare.com/ajax/libs/jscolor/2.0.4/jscolor.js`,
});
const gmcDebug = new ConfigLzString({
	'id': 'PttChromeAddOnConfigDebug', // The id used for this instance of GM_config
	'title': 'PttChrome Add-on DeBugLog', // Panel Title
	'fields': { // Fields object
		'showLog': {
			'label': 'Show log of debug text',
			'type': 'textarea',
			'default': ''
		},
	},
	'events': { // Callback functions object
		'open': () => {
			gmcDebug.frame.setAttribute('style', "border: 1px solid #AAA;color: #999;background-color: #111; width: 26em; height: 35em; position: fixed; top: 2.5em; left: 0.5em; z-index: 900;");
		},
	},
	'css': `#PttChromeAddOnConfigDebug * { color: #999 !important;background-color: #111 !important; } body#PttChromeAddOnConfigDebug { background-color: #111} #PttChromeAddOnConfigDebug_field_showLog { width:26em; height: 24em;}`
});
const addBlackStyle = (blackList) => {
	if (blackList && blackList.trim().length === 0) return;
	blackList = blackList.replace(/\n$/g, '').replace(/\n\n/g, '\n');

	let opacityStyle = blackList.replace(/([^\n]+)/g, '.blu_$1').replace(/\n/g, ',');
	addCssLink('opacityStyle', `${opacityStyle} {opacity: 0.2;}`);

	if (gmc.get('isHideViewImg')) {
		let imgStyle = blackList.replace(/([^\n]+)/g, '.blu_$1 + div > .easyReadingImg').replace(/\n/g, ',');
		addCssLink('imgStyle', `${imgStyle} {display: none;}`);
	}
	if (gmc.get('isHideViewVideo')) {
		let videoStyle = blackList.replace(/([^\n]+)/g, '.blu_$1 + div > .easyReadingVideo').replace(/\n/g, ',');
		addCssLink('videoStyle', `${videoStyle} {display: none;}`);
	}
}
const gmcBlack = new ConfigLzString({
	'id': 'PttChromeAddOnConfigBlack', // The id used for this instance of GM_config
	'title': 'PttChrome Add-on Black List', // Panel Title
	'fields': { // Fields object
		'blackList': {
			'label': 'Black List',
			'type': 'textarea',
			'default': ''
		},
	},
	'events': { // Callback functions object
		'init': function() {
			addBlackStyle(this.get('blackList'));
		},
		'open': function() {
			gmcBlack.frame.setAttribute('style', "border: 1px solid #AAA;color: #999;background-color: #111; width: 26em; height: 35em; position: fixed; top: 2.5em; left: 0.5em; z-index: 900;");
			configBlackStatus = true;
		},
		'save': function() {
			addBlackStyle(this.get('blackList'));
		},
		'close': function() { configBlackStatus = false;},
	},
	'css': `#PttChromeAddOnConfigBlack * { color: #999 !important;background-color: #111 !important; } body#PttChromeAddOnConfigBlack { background-color: #111} #PttChromeAddOnConfigBlack_field_blackList { width:26em; height: 24em;}`
});
const HOST = 'https://osk2.me:9977',
	  ipValidation = /(\d{1,3}\.){3}\d{1,3}/,
	  timerArray = [];

let timestamp = Math.floor(Date.now() / 1000);
const execInterval = () => {
	if (timerArray.length === 0) {
		timerArray.push(setInterval(excute, 200)); // shrink interval to make floor-reading faster
	}
}
const stopInterval = () => {
	while (timerArray.length > 0) {
		clearInterval(timerArray .shift());
	}
}
let currentNum, currentPage, pageData, currentPush, currentShu, currentArrow, arrayMap = {};
const excute = async () => {
	//console.log("do excute");
	let authorName = $("span:contains('作者')").first().text().trim().split(' ')[2];
	let board = $("span:contains('看板')").first().text().trim().split(" ").pop();
	const css = (elements, styles) => {
		elements = elements.length ? elements : [elements];
		elements.forEach(element => {
			for (var property in styles) {
				element.style[property] = styles[property];
			}
		});
	}
	const findAll = (elements, selectors) => {
		let rtnElements = [];
		elements = elements.length ? elements : [elements];
		elements.forEach(element => rtnElements.push.apply(rtnElements, element.querySelectorAll(selectors)));
		return rtnElements;
	}
	const innerHTMLAll = (elements) => {
		let rtn = "";
		elements = elements.length ? elements : [elements];
		elements.forEach(element => {element.innerHTML ? rtn += element.innerHTML : ""});
		return rtn;
	}
	const show = (elements, specifiedDisplay = 'block') => {
		elements = elements.length ? elements : [elements];
		elements.forEach(element => {
			if (!element.style) return;
			element.style.display = specifiedDisplay;
		});
	}
	const hide = (elements) => {
		elements = elements.length ? elements : [elements];
		elements.forEach(element => {
			if (!element.style) return;
			element.style.display = 'none';
		});
	}
	const generateImageHTML = (ip, flag) => {
		if (!flag) return;
		flag.countryCode = flag.countryCode ? flag.countryCode : "unknown";
		const ignoreCountrys = gmc.get('whenShowFlagsIgnoreSpecificCountrys').match(new RegExp(flag.countryCode, 'i'));
		if (ignoreCountrys && ignoreCountrys.length > 0) return;
		const imageTitile = `${flag.locationName || 'N/A'}<br><a href='https://www.google.com/search?q=${ip}' target='_blank'>${ip}</a>`;

		return `<div data-flag title="${imageTitile}" class="flag-${flag.countryCode}" style="background-repeat:no-repeat;background-position:left;float:right;height:0.8em;width:0.8em;cursor:pointer !important;"></div>`;
	}
	const chkBlackSpan = (isListPage) => {
		if (isTerm && isListPage) {
			let allNode = document.querySelectorAll('span[data-type="bbsline"]');
			if (allNode && allNode.length > 0) {
				allNode = [].filter.call(allNode, (element, index) => {
					if (element.dataset.type === 'bbsline') { //for term.ptt.cc
						let user = element.querySelectorAll('span[class^="q7"]')
						if (user && user.length > 1 && user[1].innerHTML.length > 10) {
							user = user[1].innerHTML.replace(/ +/g, ' ').split(' ');
							user = user && user.length > 3 ? user[1].toLowerCase() : "";
							user && user.match(/^[^\d][^ ]+$/) ? element.classList.add(`blu_${user}`) : null;
						}
						user = element.querySelector('span[class^="q15"]')
						if (user && user.innerHTML.trim().match(/^[^\d][^ ]+$/)) {
							user = user ? user.innerText.trim().toLowerCase() : "";
							user ? element.classList.add(`blu_${user}`) : null;
						}
					}
				});
			}
		}
		let blackSpan = document.querySelectorAll('span[style="opacity:0.2"]');
		let whenHideAllShowInfoCss = document.querySelector('#whenHideAllShowInfo');
		if (blackSpan.length > 0) {
			writeDebugLog(`黑名單筆數:${blackSpan.length}`);
			if (whenHideAllShowInfoCss) whenHideAllShowInfoCss.remove();
			gmc.get('isHideViewImg') && hide(findAll(blackSpan, 'img:not([style*="display: none"])'));
			gmc.get('isHideViewVideo') && hide(findAll(blackSpan, '.easyReadingVideo:not([style*="display: none"])'));
			if (gmc.get('isHideAll')) {
				if (gmc.get('whenHideAllShowInfo').length > 0 || isListPage) {
					addCssLink('whenHideAllShowInfo', `
span[type="bbsrow"][style="opacity:0.2"] {opacity:1 !important;visibility: hidden;}
span[type="bbsrow"][style="opacity:0.2"]:before {
visibility: visible;color: ${gmc.get('whenHideAllShowInfoColor')};
content: '                 -            ${gmc.get('whenHideAllShowInfo')}';
}`);
				} else {
					hide(blackSpan);
				}
			} else {
				!isListPage && gmc.get('isReduceHeight') && css(blackSpan, {
					'height': gmc.get('reduceHeight') + 'em',
					'font-size': (gmc.get('reduceHeight')/2) + 'em',
					'line-height': gmc.get('reduceHeight') + 'em'
				});
				gmc.get('isReduceOpacity') && css(blackSpan, {'opacity': gmc.get('reduceOpacity')});
			}
		}
	}
	const findPrevious = (element, selectors) => {
		if (!element) return;
		if (element.dataset.type === 'bbsline') { //for term.ptt.cc
			element = element.closest('span[type="bbsrow"]');
			element = element.parentElement;
		}
		element = element.previousElementSibling;
		if (!element) return;
		let rtnElement = element.querySelectorAll(selectors)
		if (rtnElement && rtnElement.length > 0) {
			return rtnElement;
		} else {
			return findPrevious(element, selectors);
		}
	}
	const firstEl = (element) => {
		if (!element) return;
		if (element.dataset.type === 'bbsline') { //for term.ptt.cc
			element = element.closest('span[type="bbsrow"]');
			element = element.parentElement;
		}
		element = element.nextElementSibling;
		if (!element) return;
		let e = element.querySelector('span[data-type="bbsline"]');
		let user = e ? e.querySelector('span[class^="q11"]') : null;
		let name = user ? user.innerHTML.match(/^([^ ]+)[ ]*$/) : null;
		if (name && name.length > 0) { //for term.ptt.cc
			return e;
		} else if (element.classList.toString().match(/blu_[^ ]+/)) {
			return element;
		} else {
			return firstEl(element);
		}
	}
	const queryPage = (node) => {
		let rtnPage;
		if (node && node.length > 0) {
			rtnPage = node[node.length -1].querySelector('span');
			if (!rtnPage) return;
			rtnPage = rtnPage.innerText.match(/瀏覽[^\d]+(\d+)\/(\d+)/);
			if (rtnPage && rtnPage.length === 3) {
				rtnPage = rtnPage[1];
				writeDebugLog(`警告:未啟用文章好讀模式,結果會不正確`);
				return rtnPage;
			}
		}
	}
	const currentTS = Math.floor(Date.now() / 1000);

	if ((currentTS - timestamp) > 2) {
		stopInterval();
	}

	//const checkNode = document.querySelector('span.q2');
	const checkNodes = document.querySelectorAll('span.q2');
	let allShort = true;
	for (let i in checkNodes) {
		if (checkNodes[i].innerHTML.length > 10) {
			allShort = false;
			break;
		}
	}
	// if (!checkNode || (checkNode && checkNode.innerHTML.length <= 10)) {
	if (!checkNodes || (checkNodes && allShort)) {
		chkBlackSpan(true);
		return;
		/* I don't know whether this return statement is related to the blacklist or not... */
	} else {
		chkBlackSpan();
	}

	let firstNode, isHasFirst, allNode = document.querySelectorAll('span[type="bbsrow"]'), bbsline = document.querySelectorAll('span[data-type="bbsline"]');
	bbsline && bbsline.length > 0 ? allNode = bbsline : null;

	currentPage = queryPage(allNode);

	let count = {author:0, comment:0, authorCnt:0, commentCnt:0, authorIp:0, commentIp:0, completed: 0};
	allNode = [].filter.call(allNode, (element, index) => {
		if (element.dataset.type === 'bbsline') { //for term.ptt.cc
			let user = element.querySelector('span[class^="q11"]'); //ex.1.<span class="q11 b0">USERNAME</span> 2.<span class="q11 b0">USERNAME   </span>
			let name = user ? user.innerHTML.match(/^([^ ]+)[ ]*$/) : "";
			name && name.length > 0 ? element.classList.add(`blu_${name[1]}`) : null;
		}
		let node = element.innerHTML.match('※ 文章網址:');
		node = node && node.length > 0 ? node : element.innerHTML.match('※ 發信站:');
		if (node && node.length > 0) {
			isHasFirst = true;
			firstNode = firstEl(element);
			if (firstNode && !firstNode.innerHTML.match(/data-floor/)) {
				pageData = [];
				arrayMap = {};
				let currentFloors = document.getElementById("currentFloors");
				if (currentFloors == null) {
					currentFloors = document.createElement("p");
					currentFloors.setAttribute("type", "hidden");
					currentFloors.setAttribute("id", "currentFloors");
					document.getElementById("BBSWindow").appendChild(currentFloors);
				}
				currentFloors.setAttribute("value", "");
				currentNum = -1;
				currentPush = 0;
				currentShu = 0;
				currentArrow = 0;
				let mouseDownTimer = document.getElementById("mouseDownTimer");
				if (mouseDownTimer == null) {
					mouseDownTimer = document.createElement("p");
					mouseDownTimer.setAttribute("type", "hidden");
					mouseDownTimer.setAttribute("id", "mouseDownTimer");
					document.getElementById("BBSWindow").appendChild(mouseDownTimer);
				}
				mouseDownTimer.setAttribute("value", "");
			}
		}
		if (innerHTMLAll(findAll(element, "span.q2")).match(ipValidation)) {
			count.author++;
			return true;
		}
		if (element.classList && element.classList.toString().match(/blu_[^ ]+/)) {
			count.comment++;
			return true;
		}
	});
	writeDebugLog(`偵測 作者筆數:${count.author}、留言筆數:${count.comment}`);
	let allIpList = allNode.map(c => {
		const ip = c.innerHTML.match(ipValidation);
		if (ip && !flagMap[ip[0]]) return ip[0];
	});
	allIpList = new Set(allIpList);
	allIpList.delete(undefined);
	allIpList.delete(null);
	allIpList = Array.from(allIpList);
	if (allIpList && allIpList.length > 0 && allIpList[0]) {
		try {
			const flagsResponse = await axios.post(`${HOST}/ip`, { ip: allIpList}, {headers: {'Content-Type': 'application/json',}}),
				  flags = flagsResponse.data;
			if (flags && flags.length > 0) {
				flags.forEach((flag, index) => {
					const ip = allIpList[index];
					if (!flag) {
						flag = [];
					} else if (flag.imagePath) {
						flag.countryCode = flag.imagePath.toLowerCase().replace('assets/','').replace('.png','');;
					}
					flag.ip = ip;
					flagMap[ip] = flag;
				});
			}
		} catch (ex) {
			writeDebugLog(`查詢IP失敗...${ex}`);
			console.log(ex);
		}
	}

	allNode.some((comment, index) => {
		const test = comment.innerHTML.match(/^[ \t]*\d+/);
		if (test && test.length > 0) return true;

		if (gmc.get('isAddFloorNum') && comment.classList && comment.classList.toString().match(/blu_[^ ]+/) && !comment.innerHTML.match(/data-floor/)) {
			let upstairs = null;
			if (currentNum > 0) {
				upstairs = findPrevious(allNode[index], 'div[data-floor]');
				if (upstairs && upstairs.length > 0) {
					let upstairsNum = Number(upstairs[0].innerHTML);
					if (upstairsNum) {
						currentNum = Number(upstairs[0].innerHTML) + 1;
						let pushNode = upstairs[0].nextSibling;
						if (isTerm) pushNode = pushNode.firstChild; // format modification for term.ptt.cc
						if (pushNode.innerHTML == '推 ') {
							currentPush++;
							pushNode.setAttribute("onmouseover", `document.getElementById('PUSH_${currentNum}').innerHTML = '${String(currentPush).padStart(2,0)} 推 '`);
							pushNode.setAttribute("onmouseout", `document.getElementById('PUSH_${currentNum}').innerHTML = '推 '`);
						}
						else if (pushNode.innerHTML == '噓 ') {
							currentShu++;
							pushNode.setAttribute("onmouseover", `document.getElementById('PUSH_${currentNum}').innerHTML = '${String(currentShu).padStart(2,0)} 噓 '`);
							pushNode.setAttribute("onmouseout", `document.getElementById('PUSH_${currentNum}').innerHTML = '噓 '`);
						}
						else {
							currentArrow++;
							pushNode.setAttribute("onmouseover", `document.getElementById('PUSH_${currentNum}').innerHTML = '${String(currentArrow).padStart(2,0)} → '`);
							pushNode.setAttribute("onmouseout", `document.getElementById('PUSH_${currentNum}').innerHTML = '→ '`);
						}
						pushNode.setAttribute("id", `PUSH_${currentNum}`);
						addValueToKey(pushNode.nextSibling.innerHTML, currentNum); // highlight all floors of the same pusher
						if ( gmc.get('isMouseBrowsingFriendly') ) {
							pushNode.nextSibling.setAttribute('onmouseup', `
								document.getElementById("mouseDownTimer").setAttribute("value", new Date().getTime() - parseInt(document.getElementById("mouseDownTimer").getAttribute("value")));
								if ( parseInt(document.getElementById("mouseDownTimer").getAttribute("value")) > 200) {
									${onClickForDropDownMenu(board)};
								}`
							);
						}
						else
							pushNode.nextSibling.setAttribute("onclick", `${onClickForDropDownMenu(board)}`); // set the drop down menu for each pusher
						if (pushNode.nextSibling.innerHTML.trim() == authorName) // author's highlighting
							pushNode.nextSibling.setAttribute("style", "background-color:blue");

						// deal with the currently last floor
						if (isTerm) pushNode = pushNode.parentElement.parentElement.parentElement.parentElement; // format modification for term.ptt.cc
						let lastNode = null;
						let traversalNode = pushNode.parentElement.nextSibling;
						while (traversalNode) {
							if (isTerm && traversalNode.firstChild.firstChild.firstChild.className.startsWith('blu_') || !isTerm && traversalNode.className.startsWith('blu_')) {
								if (lastNode) {
									lastNode = null;
									break;
								}
								else
									lastNode = traversalNode;
							}
							traversalNode = traversalNode.nextSibling;
						}
						if (lastNode) {
							pushNode = lastNode.firstChild;
							if (isTerm) pushNode = pushNode.firstChild.firstChild.firstChild.firstChild; // format modification for term.ptt.cc
							currentNum++;
							if (pushNode.innerHTML == '推 ') {
								currentPush++;
								pushNode.setAttribute("onmouseover", `document.getElementById('PUSH_${currentNum}').innerHTML = '${String(currentPush).padStart(2,0)} 推 '`);
								pushNode.setAttribute("onmouseout", `document.getElementById('PUSH_${currentNum}').innerHTML = '推 '`);
								currentPush--;
							}
							else if (pushNode.innerHTML == '噓 ') {
								currentShu++;
								pushNode.setAttribute("onmouseover", `document.getElementById('PUSH_${currentNum}').innerHTML = '${String(currentShu).padStart(2,0)} 噓 '`);
								pushNode.setAttribute("onmouseout", `document.getElementById('PUSH_${currentNum}').innerHTML = '噓 '`);
								currentShu--;
							}
							else {
								currentArrow++;
								pushNode.setAttribute("onmouseover", `document.getElementById('PUSH_${currentNum}').innerHTML = '${String(currentArrow).padStart(2,0)} → '`);
								pushNode.setAttribute("onmouseout", `document.getElementById('PUSH_${currentNum}').innerHTML = '→ '`);
								currentArrow--;
							}
							pushNode.setAttribute("id", `PUSH_${currentNum}`);
							addValueToKey(pushNode.nextSibling.innerHTML, currentNum); // highlight all floors of the same pusher
							if ( gmc.get('isMouseBrowsingFriendly') ) {
								pushNode.nextSibling.setAttribute('onmouseup', `
									document.getElementById("mouseDownTimer").setAttribute("value", new Date().getTime() - parseInt(document.getElementById("mouseDownTimer").getAttribute("value")));
									if ( parseInt(document.getElementById("mouseDownTimer").getAttribute("value")) > 200) {
										${onClickForDropDownMenu(board)};
									}`
								);
							}
							else
								pushNode.nextSibling.setAttribute("onclick", `${onClickForDropDownMenu(board)}`); // set the drop down menu for each pusher
							currentNum--;
							if (pushNode.nextSibling.innerHTML.trim() == authorName) // author's highlighting
								pushNode.nextSibling.setAttribute("style", "background-color:blue");
						}
					}
				} else if (currentPage) { //非好讀模式才有頁數
					if (!pageData[currentPage]) pageData[currentPage] = currentNum;
					currentNum = pageData[currentPage];
				} else {
					currentNum = 1;
				}
			} else if (isHasFirst && comment === firstNode) {
				currentNum = 1;
			} else if (!isHasFirst) {
				currentNum = 1;
			}
			if (currentNum > 0) {
				count.commentCnt++
				const divCnt = `<div data-floor style="float:left;margin-left: 2.2%;height: 0em;width: 1.5em;font-size: 0.4em;font-weight:bold;text-align: right;">${currentNum}</div>`;
				comment.innerHTML = divCnt + comment.innerHTML.trim();
			} else {
				const divCnt = `<div data-floor></div>`;
				comment.innerHTML = divCnt + comment.innerHTML.trim();
			}
		} else if ((gmc.get('isAddFloorNum') && comment.classList && !comment.querySelector('.q2') && !comment.classList.toString().match(/blu_[^ ]+/))) {
			writeDebugLog(`警告 推文資料格式錯誤:${comment.innerHTML}`);
		} else if (comment.innerHTML.match(/data-floor/)) {
			count.completed++;
		}

		if (!gmc.get('isShowFlags')) return;

		const ip = comment.innerHTML.match(ipValidation);

		if (!ip) return;
		if (comment.innerHTML.match(/data-flag/)) return;
		const imageHTML = generateImageHTML(ip[0], flagMap[ip[0]]);
		if (!imageHTML) return;

		const authorNode = comment.querySelector("span.q2");
		if (authorNode) {
			count.authorIp++;
			authorNode.innerHTML = imageHTML + authorNode.innerHTML.trim()
		} else {
			count.commentIp++;
			comment.innerHTML = imageHTML + comment.innerHTML.trim();
		}
		timestamp = Math.floor(Date.now() / 1000);
	});

	if (count.comment !== count.completed) {
		writeDebugLog(`寫入 作者IP數:${count.authorIp}、留言樓層:${count.commentCnt}、留言IP數:${count.commentIp}`);

		// highlight all floors of the same pusher (perform only at the end)
		for (let key in arrayMap) {
			let array = arrayMap[key];
			for (let i in array) {
				let str = 'PUSH_' + array[i];
				if (document.getElementById(str)!=null) {
					if (gmc.get('isMouseBrowsingFriendly'))
						document.getElementById(str).nextSibling.addEventListener('click', function (e) {
							e.stopPropagation(); // this line is required to show the "menu" but i don't know why!!!
							if (!(parseInt(document.getElementById("mouseDownTimer").getAttribute("value")) > 200))
								onClickForHighlighting(e, array, str);
						});
					else
						document.getElementById(str).parentNode.addEventListener('click', function (e) {onClickForHighlighting(e, array, str)});
				}
			}
		}
	}

	tippy('[data-flag]', {
		arrow: true,
		size: 'large',
		placement: 'left',
		interactive: true
	});
}
const chkBeforeunloadEvents = () => {
	if (gmc.get('isDisableClosePrompt')) {
		window.addEventListener("beforeunload", function f() {
			window.removeEventListener("beforeunload", f, true);
		}, true);
		unsafeWindow.addEventListener("beforeunload", function beforeunload() {
			unsafeWindow.removeEventListener("beforeunload", beforeunload, true);
		}, true);
 		if (window.getEventListeners) {
			window.getEventListeners(window).beforeunload.forEach((e) => {
				window.removeEventListener('beforeunload', e.listener, true);
			})
		} else if (unsafeWindow.getEventListeners) {
			unsafeWindow.getEventListeners(unsafeWindow).beforeunload.forEach((e) => {
				unsafeWindow.removeEventListener('beforeunload', e.listener, true);
			})
		} else {
			setTimeout(chkBeforeunloadEvents, 2000);
		}
	}
}

const CreateMutationObserver = () => {
	const container = document.querySelector('#mainContainer');
	if (!container) {
		setTimeout(CreateMutationObserver, 2000);
		return;
	}

	if (isTerm) {
		autoLogin(container);
		const reactAlert = document.querySelector('#reactAlert');
		const observerTerm = new MutationObserver(mutations => {
			mutations.forEach(mutation => {
				if (reactAlert.querySelector('p button')) {
					reactAlert.querySelector('p button').addEventListener("click", function(event) {
						autoLogin(container);
					});
				}
			});
		})
		observerTerm.observe(reactAlert, {childList: true,});
	}
	const observer = new MutationObserver(mutations => {
		mutations.forEach(mutation => execInterval());
	})
	observer.observe(container, {childList: true,});

	//chkBeforeunloadEvents();
}

const writeDebugLog = (log) => {
	if (gmc.get('isShowDebug')) {
		queryConfigEl('#PttChromeAddOnConfigDebug', 'textarea', el => {
			el.value = `${log}\n` + el.value;
		});
	}
}
const sleep = msec => new Promise(resolve => setTimeout(resolve, msec));
const autoLogin = async (container) => {
	const checkAndWait = async (container, keyword) => {
		if (container && container.innerText.match(keyword)) {
			await sleep(1000);
			return checkAndWait(container, keyword);
		}
	}
	const pasteInputArea = async (str) => {
		let inputArea = document.querySelector('#t');
		if (!inputArea) {
			await sleep(1000);
			return pasteInputArea(str);
		}

		const pasteE = new CustomEvent('paste');
		pasteE.clipboardData = { getData: () => str };
		inputArea.dispatchEvent(pasteE);
	}
	const autoSkip = async (node, regexp, pasteKey, isReCheck) => {
		if (node.innerText.match(regexp)) {
			await pasteInputArea(pasteKey);
			await checkAndWait(node, regexp);
		} else if (isReCheck) {
			await sleep(1000);
			return autoSkip(node, regexp, pasteKey, isReCheck)
		}
	}
	if (gmc.get('isAutoLogin')) {
		if (container.innerText.trim().length < 10) {
			await sleep(1000);
			return autoLogin(container);
		}
		const list = [];
		if (gmc.get('autoUser') && gmc.get('autoPassWord')) {
			list.push({regexp: /請輸入代號,或以/, pasteKey: `${gmc.get('autoUser')}\n${gmc.get('autoPassWord')}\n`, isReCheck: true});
		}

		if (gmc.get('isEnableDeleteDupLogin') !== "N/A") {
			list.push({regexp: /您有其它連線已登入此帳號/, pasteKey: `${gmc.get('isEnableDeleteDupLogin')}\n`, isReCheck: true});
		}

		if (gmc.get('isAutoSkipInfo1')) {
			list.push(
				{regexp: /正在更新與同步線上使用者及好友名單,系統負荷量大時會需時較久.../, pasteKey: '\n'},
				{regexp: /歡迎您再度拜訪,上次您是從/, pasteKey: '\n'},
				{regexp: /─+名次─+範本─+次數/, pasteKey: 'q'},
				{regexp: /發表次數排行榜/, pasteKey: 'q'},
				{regexp: /大富翁 排行榜/, pasteKey: 'q'},
				{regexp: /本日十大熱門話題/, pasteKey: 'q'},
				{regexp: /本週五十大熱門話題/, pasteKey: 'q'},
				{regexp: /每小時上站人次統計/, pasteKey: 'qq'},
				{regexp: /程式開始啟用/, pasteKey: 'q'},
				{regexp: /排名 +看 *板 +目錄數/, pasteKey: 'q'},
			);

		}
		if (gmc.get('isAutoToFavorite')) {
			list.push({regexp: /【主功能表】 +批踢踢實業坊/, pasteKey: `f\n`, isReCheck: true});
		}
		let isMatch = false;
		for (let idx=0;idx < list.length; idx++) {
			if (container.innerText.match(list[idx].regexp)) {
				isMatch = true;
				await autoSkip(container, list[idx].regexp, list[idx].pasteKey, list[idx].isReCheck);
			}
			if (idx == list.length-1 && !isMatch) {
				idx = 0;
				await sleep(1000);
			}
		}
	}
}

try {
	window.addEventListener("load", function(event) {
		CreateMutationObserver();
	});
} catch (ex) {
	writeDebugLog(`出現錯誤...${ex}`);
	console.error(ex);
}

const _button = document.createElement("div");
_button.innerHTML = 'Settings';
_button.onclick = event => {
	event.preventDefault();
	event.stopPropagation();
	if (!configStatus) {
		configStatus = true;
		if (gmc) gmc.open();
		if (gmc.get('isShowDebug') && gmcDebug) gmcDebug.open();
	} else if (configStatus) {
		configStatus = false;
		if (gmc.isOpen) gmc.close();
		if (gmcDebug.isOpen) gmcDebug.close();
		if (gmcBlack.isOpen) gmcBlack.close();
	}
}
_button.style = "border: 1px solid #AAA;color: #999;background-color: #111;position: fixed; top: 0.5em; right: 0.5em; z-index: 900;cursor:pointer !important;"

document.body.appendChild(_button)

const el = document.createElement('link');
el.rel = 'stylesheet';
el.type = 'text/css';
el.href = "https://cdnjs.cloudflare.com/ajax/libs/tippy.js/2.5.4/tippy.css";
document.head.appendChild(el);

function addValueToKey(key, value) {
    arrayMap[key] = arrayMap[key] || [];
    arrayMap[key].push(value);
}

// Disable the pusher highlighting and close the dropdown menu if the user clicks outside of it
window.onclick = function(event) {
	if (event.which != 3) { // we must enforce the menu to be closed due to stopPropagation() before.
		if (isTerm)
			document.getElementsByClassName("dropdown-menu DropdownMenu--reset")[0].parentNode.parentNode.style.display = 'none';
		else
			document.getElementsByClassName("dropdown-menu")[0].style.display = 'none';
	}
	let hasDisabledSomething = false;
	if (!event.target.getAttribute('link')) {
		if (isTerm) {
			if (event.target.parentNode.parentNode.parentNode.parentNode.parentNode.getAttribute("style")!="background-color:navy") {
				let array = document.getElementById('currentFloors').getAttribute('value');
				if (array != "") {
					array = array.split(',');
					for (let i in array)
						document.getElementById('PUSH_' + array[i]).parentNode.parentNode.parentNode.parentNode.parentNode.setAttribute("style", "background-color:black");
					hasDisabledSomething = true;
				}
				document.getElementById('currentFloors').setAttribute('value', '');
			}
		} else {
			if (event.target.parentNode.getAttribute("style")!="background-color:navy") {
				let array = document.getElementById('currentFloors').getAttribute('value');
				if (array != "") {
					array = array.split(',');
					for (let i in array)
						document.getElementById('PUSH_' + array[i]).parentNode.setAttribute("style", "background-color:black");
					hasDisabledSomething = true;
				}
				document.getElementById('currentFloors').setAttribute('value', '');
			}
		}
		let dropDownMenu = document.getElementById("dropDownMenu");
		if (!event.target.id.startsWith('dropDownMenu') && !event.target.id.startsWith('PUSH_') && dropDownMenu != null) {
			dropDownMenu.parentNode.removeChild(dropDownMenu);
			event.stopPropagation(); // prevent duplication with pusher highlighting
			hasDisabledSomething = true;
		}
	}
	if (gmc.get('isMouseBrowsingFriendly') && hasDisabledSomething)
		event.stopImmediatePropagation(); // disable mouse browsing temporarily in case the screen runs when something is closed.
}

window.onmousedown = function(event) {
	// Start the timer for the use of triggering menu
	document.getElementById("mouseDownTimer").setAttribute("value", new Date().getTime());

	// Mouse click may have been disabled due to stop propagation from mouse browsing.
	// So we must close the menu manually in the onclick event before.
	// After that, the menu still must be re-shown if the user right click on the screen.
	if (event.which == 3) {
		if (isTerm) {
			document.getElementsByClassName("dropdown-menu DropdownMenu--reset")[0].style.top = event.clientY.toString() + 'px'; // for firefox's lag response
			document.getElementsByClassName("dropdown-menu DropdownMenu--reset")[0].style.left = event.clientX.toString() + 'px'; // for firefox's lag response
			document.getElementsByClassName("dropdown-menu DropdownMenu--reset")[0].parentNode.parentNode.style.display = '';
		}
		else
			document.getElementsByClassName("dropdown-menu")[0].style.display = 'block';
	}
}

function onClickForHighlighting(event, array, str) {
	event.stopPropagation(); // To disable some functions of 滑鼠瀏覽 to avoid collision
	if (isTerm) {
		document.getElementsByClassName("dropdown-menu DropdownMenu--reset")[0].parentNode.parentNode.style.display = 'none';
		if (!event.target.getAttribute('link')) {
			if (document.getElementById(str).parentNode.parentNode.parentNode.parentNode.parentNode.getAttribute("style")=="background-color:navy") {
				for (let i in array)
					document.getElementById('PUSH_' + array[i]).parentNode.parentNode.parentNode.parentNode.parentNode.setAttribute("style", "background-color:black");
				document.getElementById('currentFloors').setAttribute('value', '');
			}
			else {
				let array2 = document.getElementById('currentFloors').getAttribute('value');
				if (array2 != "") {
					array2 = array2.split(',');
					for (let i in array2)
						document.getElementById('PUSH_' + array2[i]).parentNode.parentNode.parentNode.parentNode.parentNode.setAttribute("style", "background-color:black");
				}
				for (let i in array)
					document.getElementById('PUSH_' + array[i]).parentNode.parentNode.parentNode.parentNode.parentNode.setAttribute("style", "background-color:navy");
				document.getElementById('currentFloors').setAttribute('value', array);
				// make highlighting and menu mutually exclusive
				let dropDownMenu = document.getElementById("dropDownMenu");
				if (dropDownMenu != null)
					dropDownMenu.parentNode.removeChild(dropDownMenu);
			}
		}
	}
	else {
		document.getElementsByClassName("dropdown-menu")[0].style.display = 'none';
		if (!event.target.getAttribute('link')) {
			if (document.getElementById(str).parentNode.getAttribute("style")=="background-color:navy") {
				for (let i in array)
					document.getElementById('PUSH_' + array[i]).parentNode.setAttribute("style", "background-color:black");
				document.getElementById('currentFloors').setAttribute('value', '');
			}
			else {
				let array2 = document.getElementById('currentFloors').getAttribute('value');
				if (array2 != "") {
					array2 = array2.split(',');
					for (let i in array2)
						document.getElementById('PUSH_' + array2[i]).parentNode.setAttribute("style", "background-color:black");
				}
				for (let i in array)
					document.getElementById('PUSH_' + array[i]).parentNode.setAttribute("style", "background-color:navy");
				document.getElementById('currentFloors').setAttribute('value', array);
				// make highlighting and menu mutually exclusive
				let dropDownMenu = document.getElementById("dropDownMenu");
				if (dropDownMenu != null)
					dropDownMenu.parentNode.removeChild(dropDownMenu);
			}
		}
	}
}

function onClickForDropDownMenu(board) {
	return `
		event.stopPropagation(); // prevent duplication with pusher highlighting
		let pusherNode = event.target;
		if ('${isTerm}' != 'null')
			document.getElementsByClassName("dropdown-menu DropdownMenu--reset")[0].parentNode.parentNode.style.display = 'none';
		else
			document.getElementsByClassName("dropdown-menu")[0].style.display = 'none';
		let dropDownMenu = document.getElementById("dropDownMenu");
		let hasPressDifferentPlace = (pusherNode.parentNode.lastChild != dropDownMenu);
		if (dropDownMenu != null)
			dropDownMenu.parentNode.removeChild(dropDownMenu);
		if (hasPressDifferentPlace) {
			let pusherName = pusherNode.innerHTML.trim();
			let tmp = document.createElement("div");
			tmp.setAttribute("id", "dropDownMenu");
			tmp.setAttribute("class", "dropdown-content");
				let tmp1 = document.createElement("a");
				tmp1.setAttribute("id", "dropDownMenu1");
				tmp1.setAttribute("target", "_blank");
				tmp1.setAttribute("href", "https://www.ptt.cc/bbs/${board}/search?q=author:" + pusherName);
				tmp1.innerHTML = "Search 此板 " + pusherName + " 的文章";
				let tmp2 = document.createElement("a");
				tmp2.setAttribute("id", "dropDownMenu2");
				tmp2.setAttribute("target", "_blank");
				tmp2.setAttribute("href", "https://www.ptt.cc/bbs/ALLPOST/search?q=author:" + pusherName);
				tmp2.innerHTML = "Search ALLPOST 板" + pusherName + " 的文章";
				let tmp3 = document.createElement("a");
				tmp3.setAttribute("id", "dropDownMenu3");
				tmp3.setAttribute("target", "_blank");
				tmp3.setAttribute("href", "https://www.google.com/search?q=site%3Aptt.cc%20" + pusherName);
				tmp3.innerHTML = "Google PTT " + pusherName;
				let tmp4 = document.createElement("a");
				tmp4.setAttribute("id", "dropDownMenu4");
				tmp4.setAttribute("target", "_blank");
				tmp4.setAttribute("href", "https://www.google.com/search?q=" + pusherName);
				tmp4.innerHTML = "Google " + pusherName;
			tmp.appendChild(tmp1);
			tmp.appendChild(document.createElement("br"));
			tmp.appendChild(tmp2);
			tmp.appendChild(document.createElement("br"));
			tmp.appendChild(tmp3);
			tmp.appendChild(document.createElement("br"));
			tmp.appendChild(tmp4);
			pusherNode.parentNode.appendChild(tmp);
			// console.log(floorNode.getBoundingClientRect().bottom);
			// console.log(tmp.getBoundingClientRect().height);
			// console.log(document.getElementById("mainContainer").parentNode.getBoundingClientRect().bottom);
			// console.log(tmp1.getBoundingClientRect().height);
			if ('${isTerm}' != 'null') {
				let floorNode = pusherNode;
				if (floorNode.getBoundingClientRect().bottom + tmp.getBoundingClientRect().height > document.getElementById("mainContainer").parentNode.getBoundingClientRect().bottom - tmp1.getBoundingClientRect().height)
					document.getElementById("mainContainer").parentNode.scrollTop += floorNode.getBoundingClientRect().bottom + tmp.getBoundingClientRect().height + tmp1.getBoundingClientRect().height - document.getElementById("mainContainer").parentNode.getBoundingClientRect().bottom; // manually scroll down because in the last floor case the webpage doesn't do so.
				// make highlighting and menu mutually exclusive
				let array = document.getElementById('currentFloors').getAttribute('value');
				if (array != "") {
					array = array.split(',');
					for (let i in array)
						document.getElementById('PUSH_' + array[i]).parentNode.parentNode.parentNode.parentNode.parentNode.setAttribute("style", "background-color:black");
				}
				document.getElementById('currentFloors').setAttribute('value', '');
			} else {
				let floorNode = pusherNode.parentNode;
				if (floorNode.getBoundingClientRect().bottom + tmp.getBoundingClientRect().height > document.getElementById("mainContainer").parentNode.getBoundingClientRect().bottom + 3 * tmp1.getBoundingClientRect().height)
					document.getElementById("mainContainer").parentNode.scrollTop += floorNode.getBoundingClientRect().bottom + tmp.getBoundingClientRect().height - 3 * tmp1.getBoundingClientRect().height - document.getElementById("mainContainer").parentNode.getBoundingClientRect().bottom; // manually scroll down because in the last floor case the webpage doesn't do so.
				// make highlighting and menu mutually exclusive
				let array = document.getElementById('currentFloors').getAttribute('value');
				if (array != "") {
					array = array.split(',');
					for (let i in array)
						document.getElementById('PUSH_' + array[i]).parentNode.setAttribute("style", "background-color:black");
				}
				document.getElementById('currentFloors').setAttribute('value', '');
            }
		}
	`;
}