// ==UserScript==
// @name Confluence Plus
// @namespace https://docs.scriptcat.org/
// @version 0.6.7
// @description Confluence helpers using the current browser session
// @author Shu Su
// @homepage https://scriptcat.org/en/script-show-page/6271
// @homepageURL https://scriptcat.org/en/script-show-page/6271
// @match https://*.atlassian.net/wiki/*
// @connect openai.azure.com
// @connect *.openai.azure.com
// @connect cognitiveservices.azure.com
// @connect *.cognitiveservices.azure.com
// @icon data:image/x-icon;base64,AAABAAQAQEAAAAEAIAAoQAAARgAAACAgAAABACAAKBAAAG5AAAAYGAAAAQAgACgJAACWUAAAEBAAAAEAIAAoBAAAvlkAACgAAABAAAAAgAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD9/f3e/f397v39/e79/f3u/f397v39/e79/f3u/f397v39/e7/////+u7p7v///////////////9xxJu7aZxnu2mYW7tpmFu7aZhbu2mYW7tpmFu7aZhbu2mYW7tpmFu7aZhbu2mYW7tpmFu7aZhbu2mYW7tpmFu7aZhbu2mYW7tpmFu7aZhbu2mYW7tpmFu7aZhbu2mYW7tpmFu7aZhbu2mYW7tpmFu7aZhbu2mYW7tpmFu7aZhbu2mYW7tpmFu7aZxnu3HEm7t+AP+7mmWPu8cGi7vru6e79/f3u/f397v39/e79/f3u//////39/e79/f3u/f397v39/e79/f3e/f399P//////////////////////////////////////6t3//////+N0Jv///////////+BmEf/gaRX/4WoY/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WoY/+BpFf/fZhH/32MM/99jDf/jdCX/76Vy///r3f///////////////////////////////////////f399P39/e7///////////////////////////76+P//////////////////////22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/2mQS/9hbBP/aZhP/66p9//77+f////////////////////////////39/e79/f3u////////////////////////////////////////////////22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBn/2mUU/9daAf/hgT7////////////////////////////9/f3u/f397v//////////////////////////////////////////22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBn/2F0G///////44NH//////////////////f397v39/e7////////////////cbiP/2V8K///////baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9tpGf//////3W8j//////////////////39/e79/f3u//////76+P/hgj7/2F0G/9tpGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj////////////////////////////9/f3u/f397v/////rrID/11kB/9toGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP///////////////////////f397v39/e797OD/2mUS/9plFP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj///////////////////////39/e79/f3u8Kd0/9hbA//baBn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBn/////////////////+vLq7uN1J//aZBL/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY//////////////////HFo+7fYwz/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/////////////////nmmPu32MM/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj////////////baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY////////////34A67uBmEf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY////////////22gY/9toGP//////22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP///////////9xxJ+7gaRb/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP///////////9toGP///////////9toGP//////22cX/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj////////////aahru4WoY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj////////////baBj//////////////////////9lgDP/aZBL/22kZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gZ/9tnFv/ZYAz/2mMR/9tpGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/+FqGP//////2mYW7uFrGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY///////////////////////////////////////11L3/3nMo/9piD//baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBn/22YV///////aZBH/4ohJ/952LP/aYxD/22gZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/haxn/2mYW7tpmFu7haxn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP////////////////////////////////////////////LMsf/YXAb/22cX/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGf/aZRT/2FwF///////////////////////////////////////////////////////////////////////baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/4WsZ/9pmFu7aZhbu4WsZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBn/////////////////////////////////////////////////5phg/9haAf/baRn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gZ/9pkEv/YWwP/22kZ/+ifbf//////////////////////////////////////////////////////////////////////22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/+FrGf/aZhbu2mYW7uFrGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baRn/2WEN///////poXD///////////////////////////////////////zz7P/feTT/2FoD/9toGP/baBj/22gY/9toGP/bZxb/2WEN/9hbA//cbiD/6qd5//no3P/////////////////////////////////////////////////////////////////baBj//////9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/haxn/2mYW7tpmFu7haxn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/2mMR/997M///////////////////////////////////////////////////////+uvi/+KGRf/YWwT/2FsF/9hcBf/YXAX/2mUS/+B/O//ttY3/+/Dn/////////////////////////////////////////////////////////////////////////////////9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/4WsZ/9pmFu7aZhbu4WsZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9hbA//wvp3/////////////////////////////////////////////////////////////////9NC3/+2yjf/ssov/8cSl//jh0v///v7////////////////////////////////////////////////////////////////////////////////////////////baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/+FrGf/aZhbu2mYW7uFrGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/ZYQ3/4YRE//78+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/haxn/2mYW7tpmFu7haxn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gZ/9hbA//ts4z//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/4WsZ/9pmFu7aZhbu4WsZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/aZRP/2mQS//jg0f///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////994Mf/aYxD/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/+FrGf/aZhbu2mYW7uFrGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gZ/9lgCv/fezP//fbz//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////fdy//dbyH/2mUT/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/haxn/2mYW7tpmFu7haxn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baRn/2FwF/+WQVf///v3/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////997N/+ecZf/bZhL/2mQR/9toGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/4WsZ/9pmFu7aZhbu4WsZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/YWwP/55lj///+/f//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////5ZNb/9pjEP/YXAX/2mYV/9toGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/+FrGf/aZhbu2mYW7uFrGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9hbBP/kjlL//PDp/////////////////////////////////////////////////////////////////////////////////////////////////////////////////9hdB//bZhb/22gZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/haxn/2mYW7tpmFu7haxn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/2V0G/950Kv/yx6n////////////////////////////////////////////////////////////////////////////////////////////////////////////baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/4WsZ/9pmFu7aZhbu4WsZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9tpGf/ZYg//2V4H/+KFQ//wwJ7/+uzj///////////////////////////////////////++ff/9+DR/+65lf/jik3/2mYV/9hbBP/aYg//22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/+FrGf/aZhbu2mYW7uFrGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/ZYAv/2F0G/9xsH//ig0P/5phh/+qneP/qqXz/6KJw/+WSWP/hgT7/3Gwd/9dYAP/VTwD/1lQA/9leCP/aZRT/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/haxn/2mYW7tpmFu7haxn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/aZRT/2F0H/9ZTAP/WUgD/2WAJ/952Lv/kjE//6KBu/+usgP/tsor/7LGH/+ifbP/jiEn/3W0g/9hdBv/ZYAz/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/4WsZ/9pmFu7aZhbu4WsZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9tnF//ZXwv/2FwF/91wJP/nmWD/8ser//rr4v////////////////////////////////////////////vt5f/wwqH/4oVD/9ldBv/aYxD/22kZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/+FrGf/aZhbu2mYW7uFrGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBn/2mYU/9hcBv//////4odH//DAn//88ev////////////////////////////////////////////////////////////////////////////wwaH/3G4h/9leCP/baRn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/haxn/2mYW7tpmFu7haxn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGf/aZBL/2FsE//////////////////////////////////////////////////////////////////////////////////////////////////////////////////rq3v/hg0L/2FsF/9toGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/4WsZ/9pmFu7aZhbu4WsZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gZ/9pjEf/YWwP/3G0g/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////ff0/+OLUP/YXAX/22kZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/+FrGf/aZhbu2mYW7uFrGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gZ/9piD//dcST/7K+F///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9+Pb/4YRB/9ldBv/baRn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/haxn/2mYW7tpmFu7haxn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9liD//gfjr//PHq//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////rt4v/cbyH/2mIO/9toGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/4WsZ/9pmFu7aZhbu4WsZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/ZYAv/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9NG3/9heCP/bZxb/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/+FrGf/aZhbu2mYW7uFrGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22cX///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////poXD/2FwE/9toGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/haxn/2mYW7tpmFu7haxn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////fXw/993MP/aYxD/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/4WsZ/9pmFu7aZhbu4WsZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/////////////////////////////////////////////////////////////////////////////////9dXB///////////////////////////////////////////////////////////////////////////////////////ponP/2F0G/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/+FrGf/aZhbu2mYW7uFrGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY//////////////////////////////////////////////////////////////////ffzf///////////9lgDP/YXAX/2FwG/9hbBP//////////////////////////////////////////////////////////////////////3G0f/9plFP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/haxn/2mYW7tpmFu7haxn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/////////////////67OL/////////////////////////////////////////////////2FsE/9pjEf/baBf/22gY/9toGP/baBj/22gY/9dZAf/jiEj///////////////////////////////////////jh0////////////9pkE//baRn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/4WsZ/9pmFu7aZhbu4WsZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj////////////////////////////////////////////00Lf//////9lhDP/YXQb/22YW/9toGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/11kA/+uqfv/////////////////////////////////////////////////baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/+FrGf/aZhbu2mYW7uFrGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY///////baBj////////////77ub//vz7//LIq////////////9leCP/bZxf/22gZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9pmFf/ZYAz/9tvJ/////////////////////////////////////////////////9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/haxn/2mYW7tpmFu7haxn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP///////////9tpGf/aYxD/33Yt/+B9N//ZXwj/2V8K/9toF//baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBn/2WAM/+B8Nv/zzbL/6J5q////////////////////////////////////////////22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/4WsZ/9pmFu7aZhbu4WsZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj////////////baBj/22gZ/9pjEf/aYg//22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9tpGf/aYxH/2V8J/9lfCf/bZxf/22gY////////////22gY/////////////////9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/+FqGP/aahnu228j7uBpFv/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj////////////////////////////baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/gaBX/3HIn7t9+N+7gZhL/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj////////////baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/32YR/9+APe7mmGLu32MM/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/99iC///////8cCg7t9iC//baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/fYwv///////ru6e7jdCX/2mUT/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/aZBL/43Un///////9/f3u/////9hbBP/baBn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBn/2FsD//Codv///////f397v3r3///////2mUU/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/2mUU/9tmFP/+7eH///////39/e7////////////////baBn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gZ/9dZAP/rrYD////////////9/f3u//////759v/gfzr//////9tpGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22kZ/9hdBv/hgj7//vr3/////////////f397v//////////+efb////////////22kZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22kZ/9lfCv/dcCT/+urf/////////////f397v39/e7////////////////33s/////////////baBn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gZ/9hdBv/dbyT/+OHT//////////////////39/e79/f3u//////////////////////nn2//hgD3/2FkB///////baBn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBn/2mUU/9dZAP/hgT7/+ejc///////////////////////9/f3u/f397v///////////////////////////vr4/+uqfP/aZhP//////9pkEv/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/aZBL/2FsE/////////////////////////////////////////////f397v39/fT//////////////////////////////////////+ve/++lcf///////////99jDP/gZhL/4GkW/+FqGP/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FqGP/gaRX/32YR/99jDP/fYw3///////////////////////////////////////////////////////39/fT9/f3e/f397v39/e79/f3u/f397v39/e79/f3u/f397v39/e79/f3u+vHp7vHBou7//////////9xxJu7aZxju2mYW7tpmFu7aZhbu2mYW7tpmFu7aZhbu2mYW7tpmFu7aZhbu2mYW7tpmFu7aZhbu2mYW7tpmFu7aZhbu2mYW7tpmFu7aZhbu2mYW7tpmFu7aZhbu2mYW7tpmFu7aZhbu2mYW7tpmFu7aZhbu2mYW7tpmFu7aZhbu2mYW7tpmFu7aahvu3HEm7t+APu7////////////////9/f3u/f397v39/e79/f3u/f397v39/e79/f3u/f397v39/e79/f3eKAAAACAAAABAAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP39/dz9/f3t/f397f39/e322sft55lm7d12Lu3baRnt22cW7dtoFu3baBbt22gW7dtoFu3baBbt22gW7dtoFu3baBbt22gW7dtoFu3baBbt22gW7dtoFu3baBbt22cW7dtpGe3ddi7t55xm7fbbyO39/f3t/f397f39/e39/f3c/f398///////////8Kl4/+BoFP/fYQr/4GgU/+FqGP/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WoY/+BoFP/fYQr/4GgU//Cqef////////////39/fP9/f3t//38/+SOUv/XWAD/22YV/9toGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGf/bZhX/11gA/+SOUv///fz//f397f39/e3xrH3/11gA/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/11cA//Gsff/9/f3t99vM7eBnE//bZhX/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/bZhX/4GgU//fdzu3nnWjt32EK/9toGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGf/fYQr/551o7d13Lu3gaBT/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22cW/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/+BoFP/ddy7t22kZ7eFqGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gX/9lfCv/aZBH/2mQS/9tpGf/baBj/22gY/9toGP/baBj/22gY/9tnFv/ZXgn/2FwG/9tnF//baBn/22gY/9toGP/baBj/22gY/9toGP/baBj/4WsY/9tpGe3baBbt4WsZ/9toGP/baBj/22gY/9toGP/baBj/22kZ/9lhDv/ZXAb/5pVd//fcyf/ihkb/2F4I/9tpGf/baBj/22gZ/9pmFf/YXAb/2mIP/+aXX//utpD/22cW/9tnFv/baBj/22gY/9toGP/baBj/22gY/9toGP/haxn/22gW7dtoFu3haxn/22gY/9toGP/baBj/22gY/9toGf/ZYg//3nUs//LIq/////////////jh0v/aZRP/2FoD/9leCf/YWgP/22YV/+eZYv/33Mr////////////lklf/2F0H/9toGP/baBj/22gY/9toGP/baBj/22gY/+FrGf/baBbt22gW7eFrGf/baBj/22gY/9toGP/baBj/2mEN/+GBP//++fb///////////////////////bayP/lkVf/5I9V/+ywhv/55df///////////////////////XXwv/ZXwn/22gX/9toGP/baBj/22gY/9toGP/baBj/4WsZ/9toFu3baBbt4WsZ/9toGP/baBj/22gY/9toGP/aZBL/3XEm//vv5////////////////////////////////////////////////////////////////////////////+SNUP/ZYAv/22gY/9toGP/baBj/22gY/9toGP/haxn/22gW7dtoFu3haxn/22gY/9toGP/baBj/22gY/9tpGf/YXQf/44tO////////////////////////////////////////////////////////////////////////////662C/9hcBv/baBj/22gY/9toGP/baBj/22gY/+FrGf/baBbt22gW7eFrGf/baBj/22gY/9toGP/baBj/22gY/9toGP/YWwT/6aNy////////////////////////////////////////////////////////////9dfC/+WTWf/bZxf/22cX/9toGP/baBj/22gY/9toGP/baBj/4WsZ/9toFu3baBbt4WsZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9tnF//YXAX/5pdg//vt5P/////////////////////////////////89O//8can/+OISf/ZYQ3/2F0H/9tnFv/baBn/22gY/9toGP/baBj/22gY/9toGP/haxn/22gW7dtoFu3haxn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/YXQf/3W8i/+mhb//wv57/8seq//HEpf/uto//5ZRb/9toGP/WUQD/2FwF/9toF//baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/+FrGf/baBbt22gW7eFrGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gZ/9tnFv/YWgP/1lMA/9xuIP/nmmT/77uX//LJrP/zzLL/8cSk/+micf/cbiH/2F0H/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/4WsZ/9toFu3baBbt4WsZ/9toGP/baBj/22gY/9toGP/baBj/22gZ/9tmFf/YXAX/2mQR/+WRVv/zz7X//vr3//////////////////////////////////vs4v/lkln/2FsF/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/haxn/22gW7dtoFu3haxn/22gY/9toGP/baBj/22gY/9toGP/bZhb/3Gsc/+idaP/3387////////////////////////////////////////////////////////////nnGj/2FsE/9toGf/baBj/22gY/9toGP/baBj/22gY/+FrGf/baBbt22gW7eFrGf/baBj/22gY/9toGP/baBj/22gY/9hcBv/vupf////////////////////////////////////////////////////////////////////////9/P/ihEL/2V8J/9toGf/baBj/22gY/9toGP/baBj/4WsZ/9toFu3baBbt4WsZ/9toGP/baBj/22gY/9toGP/baBj/2V8K/+aVXP////////////////////////////////////////////////////////////////////////////rq3//cbB//2mUT/9toGP/baBj/22gY/9toGP/haxn/22gW7dtoFu3haxn/22gY/9toGP/baBj/22gY/9toGP/bZxb/2WIO//fgz///////////////////////993L/+qoev/jik7/5JBV//ffzf///////////////////////PHq/994Mf/aYxD/22gY/9toGP/baBj/22gY/+FrGf/baBbt22gW7eFrGf/baBj/22gY/9toGP/baBj/22gY/9toGP/YXAX/55tl////////////9dO8/+SQVP/aYg//2FsD/9lfCv/YWgL/22oa//ro3P///////////++9mv/cbiH/2mMQ/9toGf/baBj/22gY/9toGP/baBj/4WsZ/9toFu3baBbt4WsZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9tmFf/cahz/7bSM/+SOUf/ZXwr/2F4I/9tnFv/baBj/22gY/9tpGf/YXQf/5I1R//XWwP/ji03/2FsE/9pjEf/baRn/22gY/9toGP/baBj/22gY/9toGP/haxn/22cW7dtpF+3haxn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gZ/9tnFv/YXAX/2WAL/9tnF//baBj/22gY/9toGP/baBj/22gY/9tpGf/aZBH/2mIO/9lgC//baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/+FqGP/baxnt3XYt7eBoFP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/bZxf/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/4GgU/913L+3nmWbt32IK/9toGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGf/fYQr/551o7ffby+3gZxP/22YV/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22YV/+BoFP/33c7t/f397fCre//XWAD/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/XVwD/8a19//39/e39/f3t//37/+SNUf/XWAD/22YV/9toGf/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGf/bZhX/11gA/+SOU////vz//f397f39/fP///////////CpeP/gaBT/32EK/+BoFP/hahj/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FqGP/gaBT/32EK/+BoFP/wqnn////////////9/f3z/f393P39/e39/f3t/f397fbbyO3nnGbt3XYu7dtpGO3bZxbt22gW7dtoFu3baBbt22gW7dtoFu3baBbt22gW7dtoFu3baBbt22gW7dtoFu3baBbt22gW7dtoFu3bZxbt22sZ7d13Lu3nnGft9tvI7f39/e39/f3t/f397f39/dwoAAAAGAAAADAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/f393P39/e38+fft67KJ7d56NO3baRjt2mcW7dtoFu3baBbt22gW7dtoFu3baBbt22gW7dtoFu3baBbt22gW7dtoFu3aZxbt22kY7d56NO3rsort/Pn37f39/e39/f3c/f398//48P/oiEb/3l8H/+BmEv/hahj/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WoY/+BmEv/eXwf/6IlG///48P/9/f3z/Pf27eiKSP/YWgP/22gZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBn/2FoD/+iKSP/89/ft7LSP7d5fBv/baBn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gZ/95fBv/stZDt3ns17eBmEv/baBj/22gY/9toGP/baBj/22gY/9toGP/bZxb/22gX/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/+BmEv/eezXt22kY7eFrGP/baBj/22gY/9toGP/baBj/22gY/9lfCv/aZBH/2mQS/9toGf/baBj/22gY/9toGP/aYxD/2F0H/9lhDv/baRn/22gY/9toGP/baBj/22gY/+FrGP/baRjt22cW7eFrGf/baBj/22gY/9tpGf/aZRT/2FsE/+SNUf/22sf/44xP/9hZAv/aZhT/2WAM/9hbBP/edSz/7rmU/+idaf/ZXgn/22gZ/9toGP/baBj/22gY/+FrGf/bZxbt22gW7eFrGf/baBj/22gY/9plE//dbyL/8cSl////////////+une/+B9OP/baRv/4YI//++8mf/99vL///////rs4v/aZhX/2mYV/9toGP/baBj/22gY/+FrGf/baBbt22gW7eFrGf/baBj/22gY/9pjEf/edi3///////////////////////79+//77ub////////////////////////////po3P/2F0H/9toGP/baBj/22gY/+FrGf/baBbt22gW7eFrGf/baBj/22gY/9toGf/YXQf/55li/////////////////////////////////////////////////////v/ssIb/2V4J/9toGP/baBj/22gY/+FrGf/baBbt22gW7eFrGf/baBj/22gY/9toGP/baBj/2FwG/+qldf///fz////////////////////////////99fD/8MGf/+GAPf/aYg7/22gY/9toGP/baBj/22gY/+FrGf/baBbt22gW7eFrGf/baBj/22gY/9toGP/baBj/22cX/9hdB//hgj//7riS//PNs//00bn/8cWm/+ecaP/baBj/1lUA/9lhDf/baBj/22gY/9toGP/baBj/22gY/+FrGf/baBbt22gW7eFrGf/baBj/22gY/9toGP/baBj/22gX/9lgC//XVgD/3G0f/+ihb//yyKv/9dS9//TQt//uuZX/4YA+/9hcBv/baBj/22gY/9toGP/baBj/22gY/+FrGf/baBbt22gW7eFrGf/baBj/22gY/9toGP/bZxf/2mMQ/+KGRv/yx6r//vn2/////////////////////////////vz6/+igbv/YXAb/22gY/9toGP/baBj/22gY/+FrGf/baBbt22gW7eFrGf/baBj/22gY/9toGP/ZXwr/77qW///////////////////////////////////////////////////////lk1n/2F4I/9toGf/baBj/22gY/+FrGf/baBbt22gW7eFrGf/baBj/22gY/9toGP/YXQf/66p9/////////////////////////v7/+uvi//79/P/////////////////++/n/3XAl/9pkEv/baBj/22gY/+FrGf/baBbt22gW7eFrGf/baBj/22gY/9toGP/aZRT/22oa//zy6////////PLr/+21jf/gfTj/22YY/+B/O//77uX////////+/v/vu5j/3Gsd/9pmFP/baBj/22gY/+FrGf/baBbt22cW7eFrGf/baBj/22gY/9toGP/baBn/2V4J/+igbv/ts4v/3XEl/9hbA//ZYQ3/22YV/9dZAf/lklj/9dbA/+KGRf/YWwP/22YV/9toGf/baBj/22gY/+FrGf/bZxbt22gX7eFrGP/baBj/22gY/9toGP/baBj/22kZ/9lhDf/YXQf/2mQR/9toGf/baBj/22gY/9toGf/aZBL/2mIP/9lgDP/baBj/22gY/9toGP/baBj/22gY/+FrGP/baRjt3no07eBmEv/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBf/22cX/9toGP/baBj/22gY/9toGP/baBj/22gY/+BmEf/eezXt7LSP7d5fBv/baBn/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gZ/95fBv/stZDt/Pf27eiJR//YWgP/22gZ/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBj/22gY/9toGP/baBn/2FoD/+iKSP/89/bt/f398//47//oiEb/3l8H/+BmEv/hahj/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WoY/+BmEv/eXwf/6IlG///58P/9/f3z/f393P39/e38+fft67KK7d56NO3baRjt2mcW7dtoFu3baBbt22gW7dtoFu3baBbt22gW7dtoFu3baBbt22gW7dtoFu3aZxbt22kZ7d57Ne3rsort/Pr37f39/e39/f3cKAAAABAAAAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP39/dz659nt5IpN7dtpGO3aZxbt22gW7dtoFu3baBbt22gW7dtoFu3baBbt2mcW7dtpGO3kik3t+ufZ7f39/dz349fz6nos/95hCf/hahj/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/hahj/3mEJ/+p6Lf/35Nfz5ItN7d5gCf/baRn/22gY/9toGP/bZxb/22gX/9toGP/baBj/22gY/9toF//baBj/22gY/9tpGf/eYAn/5ItN7dtpGO3hahj/22gY/9toGf/ZYAv/2mMR/9pkEv/baBj/22cW/9leCf/ZXwr/2mYV/9tpGf/baBj/4WoY/9tpGO3bZxbt4WsZ/9toF//aZRT/44hJ//bYw//mlVz/1lQA/9pjEf/mll7/882y/992Lv/aYxH/22gY/+FrGf/bZxbt22gW7eFrGf/aZBL/3XEl//78+v///////ffy//HFp//44dH////////////vu5f/2FwG/9toGP/haxn/22gW7dtoFu3haxn/22gY/9lfC//qpnf////////////////////////////88uv/66t+/9lhDv/baBj/4WsZ/9toFu3baBbt4WsZ/9toGP/bZxf/2V4J/+WUW//zy7D/997M//XUvf/on2z/22cW/9leCf/baBn/22gY/+FrGf/baBbt22gW7eFrGf/baBj/22gY/9leCP/bahv/6aNy//XWwP/3387/88yy/+WTWf/ZXgn/22gX/9toGP/haxn/22gW7dtoFu3haxn/22gX/9piD//ssYj//fXw////////////////////////////6aNy/9lfC//baBj/4WsZ/9toFu3baBbt4WsZ/9toGP/YXAb/8MCf////////////993L//HEpf/9+PT///////338//cbSD/2mUT/+FrGf/baBbt22cW7eFrGf/baBj/2mMQ/995Mv/yyq7/5ZBW/9piDv/WVAD/55li//XVvv/ihEL/2mUU/9toGP/haxn/22cW7dtpGO3hahj/22gY/9tpGf/aZhT/2V8J/9lfCv/bZxb/22gZ/9pkEv/aYg//2WAM/9toGf/baBj/4WoY/9tpGO3kik3t3mAJ/9tpGf/baBj/22gY/9toF//baBj/22gY/9toGP/baBf/22cW/9toGP/baBj/22kZ/95gCf/ki03t9+PX8+p6LP/eYQn/4WoY/+FrGf/haxn/4WsZ/+FrGf/haxn/4WsZ/+FrGf/haxn/4WoY/95gCf/qey3/9+TX8/39/dz659nt5IpN7dtpGO3aZxbt22gW7dtoFu3baBbt22gW7dtoFu3baBbt2mcW7dtpGO3kik3t+ujZ7f39/dwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @grant GM_info
// @run-at document-idle
// @license MIT
// ==/UserScript==
(function () {
'use strict';
/********************************************************
* CONFIG
********************************************************/
const Config = {
storageKey: 'confluence_plus_state',
api: {
v1Path: '/wiki/rest/api',
v2Path: '/wiki/api/v2',
},
request: {
timeout: 15000,
},
ai: {
defaultApiVersion: '2025-03-01-preview',
defaultModelsApiVersion: '2024-10-21',
},
ui: {
defaultWidth: 420,
defaultHeight: 700,
minWidth: 320,
minHeight: 400,
},
};
/********************************************************
* UTILS
********************************************************/
const Utils = {
extractPageId(url = location.href) {
const source = String(url || '');
const match = source.match(/\/pages\/(\d+)/i) ||
source.match(/\/page\/(\d+)/i) ||
source.match(/[?]pageId=(\d+)/i) ||
source.match(/[?]homepageId=(\d+)/i);
if (match) {
return match[1];
}
try {
const queryPageId = new URL(source, location.href).searchParams.get(
'homepageId'
);
if (/^\d+$/.test(queryPageId || '')) {
return queryPageId;
}
} catch {
// Fall back to the metadata lookup below.
}
// Confluence also exposes the current content id in page metadata on
// some routes where the URL itself does not contain /pages/{id}.
const meta = document.querySelector(
'meta[name="ajs-page-id"], meta[name="ajs-content-id"]'
);
const dataElement = document.querySelector(
'[data-page-id], [data-content-id]'
);
const fallback = meta?.content ||
dataElement?.getAttribute('data-page-id') ||
dataElement?.getAttribute('data-content-id') ||
'';
return /^\d+$/.test(fallback) ? fallback : '';
},
isValidPageId(pageId) {
return /^\d+$/.test(pageId);
},
debounce(fn, delay = 300) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
},
};
/********************************************************
* LOGGER
********************************************************/
const Logger = {
info(event, data) {
if (typeof data === 'undefined') {
console.info(`[Confluence Plus] ${event}`);
return;
}
console.info(`[Confluence Plus] ${event}`, data);
},
warn(event, data) {
console.warn(`[Confluence Plus] ${event}`, data);
},
error(event, error) {
console.error(`[Confluence Plus] ${event}`, error);
},
};
/********************************************************
* STORAGE
********************************************************/
const Storage = {
loadData() {
try {
const raw = GM_getValue(Config.storageKey, '{}');
const data = JSON.parse(raw);
// Remove credentials written by pre-session versions.
if (data.auth) {
delete data.auth;
GM_setValue(
Config.storageKey,
JSON.stringify(data)
);
}
return data;
} catch (error) {
Logger.error('storage.load-failed', error);
return {};
}
},
loadUi() {
const ui = this.loadData().ui || {};
// Only the panel size is persistent. Menu state and position
// intentionally reset for every page visit.
const result = {};
if (typeof ui.width === 'number') {
result.width = ui.width;
}
if (typeof ui.height === 'number') {
result.height = ui.height;
}
return result;
},
saveUi(ui) {
const data = this.loadData();
data.ui = {
width: ui.width,
height: ui.height,
};
GM_setValue(
Config.storageKey,
JSON.stringify(data)
);
},
loadAi() {
return this.loadData().ai || {};
},
saveAi(ai) {
const data = this.loadData();
data.ai = ai;
GM_setValue(
Config.storageKey,
JSON.stringify(data)
);
},
clearAi() {
const data = this.loadData();
delete data.ai;
GM_setValue(
Config.storageKey,
JSON.stringify(data)
);
},
};
/********************************************************
* AI CONFIGURATION
********************************************************/
const AIConfig = (() => {
const defaults = {
endpoint: '',
apiKey: '',
apiVersion: Config.ai.defaultApiVersion,
modelsApiVersion: Config.ai.defaultModelsApiVersion,
deployment: '',
models: [],
lastCheck: null,
};
let config = {
...defaults,
...Storage.loadAi(),
};
function normalizeEndpoint(endpoint) {
const value = endpoint.trim().replace(/\/+$/, '');
const url = new URL(value);
if (url.protocol !== 'https:') {
throw new Error('Azure OpenAI Endpoint 必须使用 HTTPS');
}
if (url.username || url.password || url.search || url.hash) {
throw new Error('Endpoint 不应包含账号、查询参数或 hash');
}
return url.origin + url.pathname.replace(/\/+$/, '');
}
function validate(candidate) {
const next = {
...config,
...candidate,
};
if (!next.endpoint) {
throw new Error('请输入 Azure OpenAI Endpoint');
}
if (!next.apiKey) {
throw new Error('请输入 Azure OpenAI API Key');
}
next.endpoint = normalizeEndpoint(next.endpoint);
next.apiVersion = next.apiVersion.trim();
next.modelsApiVersion = next.modelsApiVersion.trim();
next.deployment = next.deployment.trim();
if (!next.apiVersion || !next.modelsApiVersion) {
throw new Error('API Version 不能为空');
}
return next;
}
return {
get() {
return { ...config, models: [...config.models] };
},
validate,
save(candidate) {
const previous = config;
const next = validate(candidate);
const changed =
previous.endpoint !== next.endpoint ||
previous.apiKey !== next.apiKey ||
previous.deployment !== next.deployment;
const hasExplicitHealth =
Object.prototype.hasOwnProperty.call(
candidate,
'lastCheck'
);
config = {
...next,
lastCheck: hasExplicitHealth
? next.lastCheck
: changed
? null
: next.lastCheck || null,
};
Storage.saveAi(config);
return this.get();
},
recordHealth(ok, message) {
config = {
...config,
lastCheck: {
ok,
message,
endpoint: config.endpoint,
checkedAt: Date.now(),
},
};
Storage.saveAi(config);
},
getStatus() {
if (!config.endpoint || !config.apiKey || !config.deployment) {
return {
key: 'not-configured',
label: 'Not configured',
};
}
if (!config.lastCheck ||
config.lastCheck.endpoint !== config.endpoint) {
return {
key: 'unverified',
label: 'Configured, not verified',
};
}
return config.lastCheck.ok
? {
key: 'available',
label: 'Available',
}
: {
key: 'unavailable',
label: 'Unavailable',
};
},
clear() {
config = { ...defaults };
Storage.clearAi();
},
isConfigured() {
return Boolean(config.endpoint && config.apiKey);
},
};
})();
/********************************************************
* STORE
********************************************************/
const Store = (() => {
const state = {
context: {
pageId: '',
url: location.href,
},
ui: {
x: 120,
y: 120,
width: Config.ui.defaultWidth,
height: Config.ui.defaultHeight,
minimized: true,
docked: true,
dockRight: 80,
dockBottom: 12,
maximized: false,
},
};
const listeners = [];
const persistUi = Utils.debounce(() => {
Storage.saveUi(state.ui);
});
function notify(type, value) {
listeners.forEach((listener) => {
listener(type, value, state);
});
}
return {
init() {
Object.assign(state.ui, Storage.loadUi());
},
getContext() {
return state.context;
},
getUi() {
return state.ui;
},
setContext(context) {
state.context = {
...state.context,
...context,
};
notify('context', state.context);
},
updateUi(patch) {
Object.assign(state.ui, patch);
persistUi();
notify('ui', state.ui);
},
resetPanelSession() {
Object.assign(state.ui, {
x: 120,
y: 120,
minimized: true,
docked: true,
dockRight: 80,
dockBottom: 12,
maximized: false,
});
notify('ui', state.ui);
},
subscribe(listener) {
listeners.push(listener);
return () => {
const index = listeners.indexOf(listener);
if (index >= 0) {
listeners.splice(index, 1);
}
};
},
};
})();
/********************************************************
* ROUTER / PAGE CONTEXT
********************************************************/
const Router = (() => {
let lastUrl = '';
function getContext() {
const url = location.href;
return {
url,
pageId: Utils.extractPageId(url),
};
}
function emitIfChanged() {
const context = getContext();
if (context.url === lastUrl) {
return;
}
lastUrl = context.url;
// Reset before publishing the new route so the panel cannot keep
// displaying tree data loaded for the previous Space.
Store.resetPanelSession();
Store.setContext(context);
Logger.info('route.changed', context);
}
function init() {
const rawPushState = history.pushState;
const rawReplaceState = history.replaceState;
history.pushState = function (...args) {
rawPushState.apply(this, args);
setTimeout(emitIfChanged, 50);
};
history.replaceState = function (...args) {
rawReplaceState.apply(this, args);
setTimeout(emitIfChanged, 50);
};
window.addEventListener('popstate', emitIfChanged);
emitIfChanged();
}
return {
init,
getContext,
};
})();
/********************************************************
* HTTP CLIENT
********************************************************/
const Http = {
requestViaManager(url, options = {}) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: options.method || 'GET',
url,
data: options.body,
headers: {
Accept: 'application/json',
...(options.headers || {}),
},
timeout: Config.request.timeout,
onload(response) {
if (response.status < 200 || response.status >= 300) {
const detail = (response.responseText || '')
.replace(/\s+/g, ' ')
.slice(0, 500);
reject(new Error(
`HTTP ${response.status}: ${detail}`
));
return;
}
if (response.status === 204) {
resolve(null);
return;
}
try {
resolve(JSON.parse(response.responseText));
} catch {
reject(new Error(
'Azure OpenAI 返回了非 JSON 响应'
));
}
},
onerror() {
reject(new Error('Azure OpenAI 跨域请求失败'));
},
ontimeout() {
reject(new Error('Azure OpenAI API 请求超时'));
},
});
});
},
async request(url, options = {}) {
if (
options.crossOrigin &&
typeof GM_xmlhttpRequest === 'function'
) {
return this.requestViaManager(url, options);
}
const controller = new AbortController();
const timeoutId = setTimeout(() => {
controller.abort();
}, Config.request.timeout);
try {
const response = await fetch(url, {
...options,
credentials: options.credentials || 'same-origin',
signal: controller.signal,
headers: {
Accept: 'application/json',
...(options.headers || {}),
},
});
if (!response.ok) {
const text = await response.text();
const detail = text.replace(/\s+/g, ' ').slice(0, 500);
throw new Error(
`HTTP ${response.status}: ${detail}`
);
}
if (response.status === 204) {
return null;
}
const contentType =
response.headers.get('content-type') || '';
if (!contentType.includes('application/json')) {
throw new Error(
'Confluence 返回了非 JSON 响应,可能需要重新登录'
);
}
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
throw new Error('Confluence API 请求超时');
}
throw error;
} finally {
clearTimeout(timeoutId);
}
},
get(url, options = {}) {
return this.request(url, {
method: 'GET',
...options,
});
},
post(url, body, options = {}) {
return this.request(url, {
...options,
method: 'POST',
body: JSON.stringify(body),
headers: {
'Content-Type': 'application/json',
...(options.headers || {}),
},
});
},
};
/********************************************************
* AZURE OPENAI API SERVICES
********************************************************/
const AzureOpenAI = {
isV1Endpoint(endpoint) {
return /\/openai\/v1$/i.test(endpoint);
},
getUrl(endpoint, path, apiVersion) {
if (this.isV1Endpoint(endpoint)) {
return `${endpoint}${path}`;
}
const query = new URLSearchParams({
'api-version': apiVersion,
});
return `${endpoint}${path}?${query}`;
},
getHeaders(apiKey) {
return {
'api-key': apiKey,
Accept: 'application/json',
};
},
async listModels(candidate) {
const config = AIConfig.validate(candidate);
const modelsPath = this.isV1Endpoint(config.endpoint)
? '/models'
: '/openai/models';
const url = this.getUrl(
config.endpoint,
modelsPath,
config.modelsApiVersion
);
const data = await Http.get(url, {
crossOrigin: true,
credentials: 'omit',
headers: this.getHeaders(config.apiKey),
});
const rawModels = Array.isArray(data)
? data
: data.data || data.models || data.value || [];
const models = rawModels
.map((item) => {
if (typeof item === 'string') {
return {
id: item,
label: item,
};
}
const id = item.id ||
item.name ||
item.model ||
item.deploymentName;
return id
? {
id: String(id),
label: String(
item.displayName || id
),
}
: null;
})
.filter(Boolean);
return models;
},
async testConnection(candidate) {
const models = await this.listModels(candidate);
return {
models,
message: models.length
? `连接成功,找到 ${models.length} 个模型`
: '连接成功,但没有返回模型列表',
};
},
getResponsesUrl(config) {
const path = this.isV1Endpoint(config.endpoint)
? '/responses'
: '/openai/responses';
return this.getUrl(
config.endpoint,
path,
config.apiVersion
);
},
extractOutputText(response) {
if (typeof response.output_text === 'string') {
return response.output_text;
}
const messages = response.output || [];
const texts = [];
messages.forEach((item) => {
(item.content || []).forEach((content) => {
if (content.type === 'output_text' && content.text) {
texts.push(content.text);
}
});
});
return texts.join('\n');
},
async suggestLabels({ title, content, existingLabels }) {
const config = AIConfig.get();
if (!AIConfig.isConfigured() || !config.deployment) {
throw new Error('请先配置并保存可用的 AI Configuration');
}
const prompt = [
'Recommend 3 to 10 concise Confluence labels for this page.',
'Return JSON only in the shape {"labels":["label-a"]}.',
'Use lowercase letters, numbers, and hyphens only.',
'Do not include existing labels or explanations.',
`Existing labels: ${(existingLabels || []).join(', ') || '(none)'}`,
`Title: ${title || '(untitled)'}`,
'Page content:',
content.slice(0, 24000),
].join('\n\n');
const response = await Http.post(
this.getResponsesUrl(config),
{
model: config.deployment,
input: prompt,
},
{
crossOrigin: true,
credentials: 'omit',
headers: {
...this.getHeaders(config.apiKey),
},
}
);
const output = this.extractOutputText(response);
const match = output.match(/\{[\s\S]*\}/);
if (!match) {
throw new Error('AI 未返回可识别的 Label 建议');
}
let parsed;
try {
parsed = JSON.parse(match[0]);
} catch {
throw new Error('AI 返回的 Label 建议不是有效 JSON');
}
return Array.isArray(parsed.labels) ? parsed.labels : [];
},
};
/********************************************************
* CONFLUENCE API SERVICES
********************************************************/
const ConfluenceApi = {
getV1Url(path) {
return `${location.origin}${Config.api.v1Path}${path}`;
},
getV2Url(path) {
return `${location.origin}${Config.api.v2Path}${path}`;
},
folders: {
get(folderId) {
return Http.get(
ConfluenceApi.getV2Url(
`/folders/${encodeURIComponent(folderId)}`
)
);
},
},
spaces: {
get(spaceId) {
return Http.get(
ConfluenceApi.getV2Url(
`/spaces/${encodeURIComponent(spaceId)}`
)
);
},
async getByKey(spaceKey) {
const query = new URLSearchParams({
keys: spaceKey,
limit: '1',
});
const data = await Http.get(
`${ConfluenceApi.getV2Url('/spaces')}?${query}`
);
return Array.isArray(data.results) ? data.results[0] : null;
},
},
pages: {
get(pageId) {
return Http.get(
ConfluenceApi.getV2Url(`/pages/${pageId}`)
);
},
getForAi(pageId) {
const query = new URLSearchParams({
expand: 'body.storage,metadata.labels',
});
return Http.get(
`${ConfluenceApi.getV1Url(`/content/${pageId}`)}?${query}`
);
},
addLabels(pageId, labels) {
return Http.post(
ConfluenceApi.getV1Url(`/content/${pageId}/label`),
labels.map((name) => ({
prefix: 'global',
name,
}))
);
},
getChildren(pageId) {
return Http.get(
ConfluenceApi.getV2Url(
`/pages/${pageId}/children`
)
);
},
getSpacePages(spaceId, cursor = '') {
const query = new URLSearchParams({
limit: '250',
});
if (cursor) {
query.set('cursor', cursor);
}
return Http.get(
`${ConfluenceApi.getV2Url(
`/spaces/${encodeURIComponent(spaceId)}/pages`
)}?${query}`
);
},
},
pageStates: {
get(pageId) {
return Http.get(
ConfluenceApi.getV1Url(
`/content/${pageId}/state`
)
);
},
},
};
/********************************************************
* AI LABEL SERVICE
********************************************************/
const AILabelService = {
normalize(label) {
return String(label || '')
.trim()
.toLowerCase()
.replace(/[^a-z0-9-]+/g, '-')
.replace(/^-+|-+$/g, '')
.slice(0, 80);
},
getExistingLabels(page) {
return (page.metadata?.labels?.results || [])
.map((item) => item.name)
.filter(Boolean);
},
async generate(pageId) {
const page = await ConfluenceApi.pages.getForAi(pageId);
const existingLabels = this.getExistingLabels(page);
const body = page.body?.storage?.value || '';
const suggestions = await AzureOpenAI.suggestLabels({
title: page.title,
content: body,
existingLabels,
});
const existing = new Set(
existingLabels.map((label) => this.normalize(label))
);
const unique = [];
suggestions.forEach((label) => {
const normalized = this.normalize(label);
if (
normalized &&
!existing.has(normalized) &&
!unique.includes(normalized)
) {
unique.push(normalized);
}
});
return {
page,
existingLabels,
suggestions: unique.slice(0, 10),
};
},
};
/********************************************************
* SPACE TREE SERVICE
********************************************************/
const SpaceTreeService = {
getNextCursor(data) {
const directCursor =
data.meta?.cursor || data.cursor || '';
if (directCursor) {
return directCursor;
}
const next = data._links?.next || data.links?.next;
if (!next) {
return '';
}
try {
return new URL(next, location.origin)
.searchParams.get('cursor') || '';
} catch {
return '';
}
},
loadFromSidebar(currentPageId) {
const containers = [
...document.querySelectorAll(
'[role="tree"], nav, aside, [data-testid*="sidebar" i], [data-testid*="tree" i]'
),
];
let best = null;
let bestScore = 0;
containers.forEach((container) => {
const links = [...container.querySelectorAll(
'a[href*="/pages/"]'
)];
const pageLinks = links.filter((link) => {
const id = Utils.extractPageId(link.href);
return id && link.getClientRects().length;
});
const containsCurrent = pageLinks.some(
(link) => Utils.extractPageId(link.href) === String(currentPageId)
);
const score = pageLinks.length + (containsCurrent ? 1000 : 0);
if (score > bestScore) {
best = {
container,
links: pageLinks,
};
bestScore = score;
}
});
if (!best || !best.links.length) {
return [];
}
const items = [];
const seen = new Set();
const stack = [];
best.links.forEach((link) => {
const id = Utils.extractPageId(link.href);
if (!id || seen.has(id)) return;
seen.add(id);
const treeItem = link.closest('[role="treeitem"]');
const ariaLevel = treeItem?.getAttribute('aria-level');
let depth = ariaLevel ? Number(ariaLevel) - 1 : NaN;
if (!Number.isFinite(depth)) {
depth = 0;
let listItem = link.closest('li');
while (listItem && best.container.contains(listItem)) {
depth += 1;
listItem = listItem.parentElement?.closest('li');
}
depth = Math.max(0, depth - 1);
}
depth = Math.max(0, depth);
stack.length = Math.min(stack.length, depth);
const title = (
link.getAttribute('data-item-title') ||
link.getAttribute('aria-label') ||
link.textContent ||
'(Untitled)'
).trim();
const parent = depth > 0 ? stack[depth - 1] : null;
const item = {
id,
title,
parentId: parent ? parent.id : null,
sidebarDepth: depth,
};
items.push(item);
stack[depth] = item;
stack.length = depth + 1;
});
return items;
},
async load(spaceId) {
const pages = [];
let cursor = '';
do {
const data = await ConfluenceApi.pages.getSpacePages(
spaceId,
cursor
);
const results = Array.isArray(data.results)
? data.results
: [];
pages.push(...results);
cursor = this.getNextCursor(data);
} while (cursor);
// The pages endpoint does not include folder entities. Pages whose
// parentType is folder still expose the folder id, so load those
// folder records separately and merge them into the tree.
const folders = new Map();
const pendingFolderIds = new Set(
pages
.filter((page) => page.parentType === 'folder' && page.parentId)
.map((page) => String(page.parentId))
);
while (pendingFolderIds.size) {
const folderId = pendingFolderIds.values().next().value;
pendingFolderIds.delete(folderId);
if (folders.has(folderId)) continue;
try {
const folder = await ConfluenceApi.folders.get(folderId);
if (!folder?.id) continue;
const normalizedFolder = {
...folder,
type: 'folder',
id: String(folder.id),
parentId: folder.parentId || null,
parentType: folder.parentType || 'page',
};
folders.set(normalizedFolder.id, normalizedFolder);
if (
normalizedFolder.parentType === 'folder' &&
normalizedFolder.parentId
) {
pendingFolderIds.add(String(normalizedFolder.parentId));
}
} catch (error) {
Logger.warn('space-tree.folder-load-failed', {
folderId,
error,
});
}
}
return [...pages, ...folders.values()];
},
buildTree(pages, currentPageId, spaceRootTitle) {
const normalizedSpaceName = (spaceRootTitle || '').trim();
const spaceRoot = pages.find((page) => (
(page.title || '').trim() === normalizedSpaceName
));
const allowedIds = new Set();
if (spaceRoot) {
allowedIds.add(String(spaceRoot.id));
let changed = true;
while (changed) {
changed = false;
pages.forEach((page) => {
const parentId = page.parentId || page.parent?.id;
if (
parentId &&
allowedIds.has(String(parentId)) &&
!allowedIds.has(String(page.id))
) {
allowedIds.add(String(page.id));
changed = true;
}
});
}
}
const nodes = new Map();
pages.forEach((page, index) => {
const id = String(page.id);
if (!allowedIds.has(id)) return;
const parentId = page.id === spaceRoot?.id
? null
: page.parentId || page.parent?.id || null;
const rawPosition = page.position ??
page.positionInParent ??
page.order ??
page.ordinal ??
page.sortOrder;
const position = Number(rawPosition);
nodes.set(id, {
id,
title: page.title || '(Untitled)',
parentId: parentId ? String(parentId) : null,
children: [],
page,
position: Number.isFinite(position) ? position : null,
sourceIndex: index,
});
});
const roots = [];
nodes.forEach((node) => {
const parent = node.parentId
? nodes.get(node.parentId)
: null;
if (parent && parent.id !== node.id) {
parent.children.push(node);
} else {
roots.push(node);
}
});
const sortNodes = (items) => {
items.sort((a, b) => {
if (a.position !== null && b.position !== null) {
return a.position - b.position || a.sourceIndex - b.sourceIndex;
}
if (a.position !== null) return -1;
if (b.position !== null) return 1;
return a.sourceIndex - b.sourceIndex;
});
items.forEach((item) => sortNodes(item.children));
};
sortNodes(roots);
const expanded = new Set();
let current = nodes.get(String(currentPageId));
while (current) {
expanded.add(current.id);
current = current.parentId
? nodes.get(current.parentId)
: null;
}
return {
roots,
nodes,
expanded,
filteredCount: nodes.size,
};
},
};
/********************************************************
* UI PANEL
********************************************************/
const UI = (() => {
let root;
let unsubscribe;
let overlay;
let persistentFab;
let contextMenu;
let panelView = 'features';
let lastViewport = {
width: window.innerWidth,
height: window.innerHeight,
};
function applyLayout() {
if (!root) return;
const ui = Store.getUi();
root.classList.toggle('cft-minimized', ui.minimized);
root.classList.toggle('cft-expanded', !ui.minimized);
root.style.position = 'fixed';
root.style.zIndex = '999999';
if (ui.minimized) {
root.style.left = 'auto';
root.style.top = 'auto';
root.style.right = `${ui.dockRight}px`;
root.style.bottom = `${ui.dockBottom}px`;
root.style.width = '48px';
root.style.height = '48px';
return;
}
if (ui.maximized) {
// Use an explicit border-box size so the resize observer can tell
// the true fullscreen geometry from a user resize.
root.style.left = '0px';
root.style.top = '0px';
root.style.right = 'auto';
root.style.bottom = 'auto';
root.style.width = `${window.innerWidth}px`;
root.style.height = `${window.innerHeight}px`;
return;
}
root.style.width = `${ui.width}px`;
root.style.height = `${ui.height}px`;
if (ui.docked) {
root.style.left = 'auto';
root.style.top = 'auto';
// Keep the popup 48px icon width + 8px gap away from the right edge.
root.style.right = `${ui.dockRight + 48 + 8}px`;
root.style.bottom = `${ui.dockBottom}px`;
} else {
root.style.right = 'auto';
root.style.bottom = 'auto';
root.style.left = `${ui.x}px`;
root.style.top = `${ui.y}px`;
}
}
function centerPanel() {
const ui = Store.getUi();
const width = Math.min(
ui.width,
Math.max(Config.ui.minWidth, window.innerWidth - 24)
);
const height = Math.min(
ui.height,
Math.max(Config.ui.minHeight, window.innerHeight - 24)
);
const x = Math.max(0, Math.round((window.innerWidth - width) / 2));
const y = Math.max(0, Math.round((window.innerHeight - height) / 2));
Store.updateUi({
x,
y,
width,
height,
docked: false,
});
}
function setMinimized(minimized) {
Store.updateUi({ minimized });
render();
}
function toggleMaximized() {
const ui = Store.getUi();
if (ui.maximized) {
Store.updateUi({
maximized: false,
width: ui._normalWidth || ui.width,
height: ui._normalHeight || ui.height,
x: ui._normalX ?? ui.x,
y: ui._normalY ?? ui.y,
});
} else {
Store.updateUi({
maximized: true,
_normalWidth: ui.width,
_normalHeight: ui.height,
_normalX: ui.x,
_normalY: ui.y,
});
}
render();
}
function showMenuOverlay() {
if (!overlay) {
overlay = document.createElement('div');
overlay.id = 'cft-menu-overlay';
overlay.addEventListener('click', () => {
setMinimized(true);
});
document.body.appendChild(overlay);
}
overlay.style.display = 'block';
}
function hideMenuOverlay() {
if (overlay) {
overlay.style.display = 'none';
}
}
function getIconMarkup() {
const icon = typeof GM_info !== 'undefined'
? GM_info.script?.icon
: '';
return icon
? ``
: 'C+';
}
function createPersistentFab() {
if (persistentFab) return;
persistentFab = document.createElement('button');
persistentFab.id = 'cft-fab-persistent';
persistentFab.className = 'cft-fab-persistent';
persistentFab.title = 'Click to close the menu';
persistentFab.innerHTML = getIconMarkup();
persistentFab.addEventListener('click', () => {
setMinimized(true);
});
document.body.appendChild(persistentFab);
}
function positionPersistentFab() {
if (!persistentFab) return;
const ui = Store.getUi();
persistentFab.style.right = `${ui.dockRight}px`;
persistentFab.style.bottom = `${ui.dockBottom}px`;
}
function removePersistentFab() {
persistentFab?.remove();
persistentFab = null;
}
function renderSpaceTreePage() {
let pageId = Store.getContext().pageId;
root.innerHTML = `