(function($, mw) {
	/*=======================================
	* 跟[[Module:Special wikitext]]保持一致的段落。
	* =======================================*/
	
	var wikiTextKey = "_addText";
	
	function _check(input_string, content_model){
		//使用頁面內容模型來判斷格式
		var contentModel = (content_model || mw.config.get('wgPageContentModel')).toString().toLowerCase();
		//根據文檔格式選用適當的解析模式
		switch(contentModel){
			case 'json':
				return _getJSONwikitext(input_string);
			case 'js':
			case 'javascript':
				return _getJSwikitext(input_string);
			case 'css':
			case 'sanitized-css':
				return _getCSSwikitext(input_string);
			default:
	        //若不是json、js、css則返回空字串
				return '';
		}
	}
	//合併多個wikitext字串,並以換行分隔
	function _addText(input_str,new_str,_escape){
		if (new_str !== ''){ 
			if(input_str !== '') input_str = input_str + '\n';
			var text = new_str;
			if(_escape){ 
				var escape_str = JSON.stringify(new_str.toString());
				escape_str = escape_str.substr(1,escape_str.length - 2);
				text = escape_str;
			}
			input_str += text;
		}
		return input_str;
	}
	//讀取wikitext字串,並忽略註解尾部
	function _getString(str){
		var test_str = /[^\n]*\*\//.exec(str);
		if(test_str) {
			test_str = test_str[0] || '';
			test_str = test_str.substr(0,test_str.length - 2);
		} else test_str = str;
		var trim_check = test_str.trim();
		var first_char = trim_check.charAt(0);
		if (first_char === trim_check.charAt(trim_check.length - 1) && (first_char === "'" || first_char === '"'))
			return trim_check.substr(1,trim_check.length - 2);
		else
			return test_str;
	}
	//讀取CSS之 _addText  { content:"XXX" } 模式的字串
	function _getContentText(str){
		var wikitext = '';
		try{
			str.replace(new RegExp(wikiTextKey + "\\s*\\{[^c\\}]*content\\s*:\\s*[^\n]*",'g'),function(text){
				var temp_text = (/content\s*:\s*[^\n]*/.exec(text) || ['content:'])[0].
					replace(/^[\s\uFEFF\xA0\t\r\n\f ;}]+|[\s\uFEFF\xA0\t\r\n\f ;}]+$/g,'').
					replace(/\s*content\s*:\s*/,'');
				if (wikitext !== '' ) wikitext += '\n';
				wikitext += _getString(temp_text);
				return text;
			});
		}catch(ex){ return ''; }
		return wikitext;
	}
	//讀取物件定義模式為 _addText=XXX 或 _addText:XXX 模式的字串 (註解全形避免被抓取)
	function _getObjText(str){
		var wikitext = '';
		try{
			str.replace(new RegExp(wikiTextKey + "\\s*[\\=:]\\s*[^\n]*",'g'),function(text){
				var temp_text = text.replace(/^[\s\uFEFF\xA0\t\r\n\f ;}]+|[\s\uFEFF\xA0\t\r\n\f ;}]+$/g,'').
					replace(new RegExp(wikiTextKey + "\\s*[\\=:]\\s*"),'');
				if (wikitext !== '' ) wikitext += '\n';
				wikitext += _getString(temp_text);
				return text;
			});
		}catch(ex){ return ''; }
		return wikitext;
	}
	//分析CSS中符合條件的wikitext
	function _getCSSwikitext(input_string){
		var wikitext = '';
		var css_text = input_string || $("#wpTextbox1").val() || '';
		if(css_text.trim() === '')return '';
		//匹配 _addText { content:"XXX" } 模式
		wikitext = _addText(wikitext, _getContentText(css_text), true);
		//同時亦匹配 /* _addText:XXX */ 模式
		wikitext = _addText(wikitext, _getObjText(css_text));
		return wikitext;
	}
	//分析JavaScript中符合條件的wikitext
	function _getJSwikitext(input_string){
		var wikitext = '';
		var js_text = input_string || $("#wpTextbox1").val() || '';
		if(js_text.trim() === '')return '';
		wikitext = _addText(wikitext, _getObjText(js_text));
		return wikitext;
	}
	//分析JSON中符合條件的wikitext
	function _getJSONwikitext(input_string){
		var wikitext = '';
		var json_text = input_string || $("#wpTextbox1").val() || '';
		if(json_text.trim() === '')return '';
		try{
			var json_data = JSON.parse(json_text);
			Object.keys(json_data).forEach(function(key){
				var k = key, v = json_data[key];
				if(new RegExp(wikiTextKey).exec(k) && typeof(v) === typeof(''))	{
					wikitext = _addText(wikitext, v);
				}
				//如果是陣列物件會多包一層
				if(typeof(v) !== typeof('')){
					for (var prop in v) {
						if (Object.hasOwnProperty.call(v, prop)) {
							var testArr_k = prop, testArr_v = v[prop];
							if(new RegExp(wikiTextKey).exec(testArr_k) && typeof(testArr_v) === typeof(''))	{
								wikitext = _addText(wikitext, testArr_v);
							}
						}
					}
				}
			});
		}catch(ex){ return ''; }
		return wikitext;
	}
	//本行以上的算法請跟[[Module:Special wikitext]]保持一致。
	
	/*=======================================
	* 程式主要部分
	* =======================================*/
	function previewTool() {
		//各類提示文字
		var _notice_addText = "((Twinkle standard installation))((ambox| type  = notice| sect  = 腳本| issue = 為[[Module:Special wikitext]]的[[JavaScript]]實現,功能為在頁面預覽時運作[[Module:Special wikitext]]的效果。| fix   = 修改本腳本時請確定相關算法與[[Module:Special wikitext]]一致。))";
		var _data_addText = "((ambox| type  = notice| sect  = 腳本| issue = 主要功能是以[[AJAX]]實現未能以介面完成文字插入的頁面模板放置。))";
		//((Quote box |quote  = -{zh-hans:预览加载中;zh-hant:預覽載入中;}-... |width  = 50% |align  = center))
		var _notice_loading = '<div id="mw-_addText-preview-loading"><div class="quotebox" style="margin: auto; width: 50%; padding: 6px; border: 1px solid #aaa; font-size: 88%; background-color: #F9F9F9;"><div id="mw-_addText-preview-loading-content" style="background-color: #F9F9F9; color: black; text-align: center; font-size: larger;"><img src="//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif" decoding="async" data-file-width="32" data-file-height="32" width="32" height="32"> ' + wgULS('预览加载中...','預覽載入中...') + ' </div></div></div>';
		//[[File:Gnome-dialog-warning2.svg|32px]] -{zh-hans:预览加载失败;zh-hant:預覽載入失敗;}-
		var _notice_fail = '<img src="//upload.wikimedia.org/wikipedia/commons/thumb/e/e9/Gnome-dialog-warning2.svg/32px-Gnome-dialog-warning2.svg.png" decoding="async" srcset="//upload.wikimedia.org/wikipedia/commons/thumb/e/e9/Gnome-dialog-warning2.svg/48px-Gnome-dialog-warning2.svg.png 1.5x, //upload.wikimedia.org/wikipedia/commons/thumb/e/e9/Gnome-dialog-warning2.svg/64px-Gnome-dialog-warning2.svg.png 2x" data-file-width="48" data-file-height="48" width="32" height="32">' + wgULS('预览加载失败','預覽載入失敗');
		//檢查對應selector的網頁物件是否存在
		function _elementExist(selectors){
			var selector_array = Array.isArray(selectors) ? selectors : (selectors ? [selectors] : []);
			var ele_count = 0;
			for(var i in selector_array){ 
				if (Object.hasOwnProperty.call(selector_array, i)) 
					ele_count += $(selector_array[i]).length;
			}
			return ele_count > 0;
		}
		//檢查mediaWiki的設置
		function _checkMwConfig(checkTarget, mwConfigs){
			var mwConfigData = mw.config.get(checkTarget);
			if(!mwConfigData || (mwConfigData.toString().trim() === ''))return false;
			mwConfigData = mwConfigData.toString().toLowerCase();
			var mwConfig_array = Array.isArray(mwConfigs) ? mwConfigs : (mwConfigs ? [mwConfigs] : []);
			return mwConfig_array.indexOf(mwConfigData) > -1;
		}
		//將解析後的wikitext加入頁面中
		function _addParsedWikitext(parsedWikitext){
			var _html_obj = $(parsedWikitext);
			if (_elementExist('#mw-_addText-preview-loading'))$('#mw-_addText-preview-loading').html(parsedWikitext);
			else if(_elementExist('.diff-currentversion-title'))_html_obj.insertAfter( '.diff-currentversion-title' );
			else if(_elementExist('.previewnote'))_html_obj.insertAfter( '.previewnote' );
			else if(_elementExist('#mw-undelete-revision'))_html_obj.insertAfter( '#mw-undelete-revision' );
			else if(_elementExist('#mw-content-text'))_html_obj.insertBefore( '#mw-content-text' );
		}
		//如果網頁物件存在,則改動其html內容
		function _setHtml(selector,html_content){
			if (_elementExist(selector))$(selector).html(html_content);
		}
		//加入[載入中]的提示
		function _addLoadingNotice(){
			if((_notice_addText || _data_addText) && _notice_loading)_addParsedWikitext(_notice_loading);
		}
		//載入錯誤的提示
		function _loadingFailNotice(){
			_setHtml('#mw-_addText-preview-loading-content',_notice_fail);
		}
		//移除[載入中]的提示
		function _removeLoadingNotice(){
			_setHtml('#mw-_addText-preview-loading','');
		}
		//檢查是否有預覽的必要性
		function _needPreview(){
			return document.body.innerHTML.search("_addText") > -1;
		}
		//加入預覽內容
		function _addWikiText(wikiText, pagename, is_preview){
			if(wikiText.toString().trim() !== ''){
				var params = {
					action: 'parse',
					//避免內容重複
					title: pagename + '/temp',
					text: wikiText,
					contentmodel: "wikitext",
					prop: "text",
					format: 'json'
				};
				if(is_preview){
					params.preview = 1;
					params.disableeditsection = 1;
				}
				new mw.Api().get(params).done(function(data){
					var parsed_wiki = (data.parse.text['*'] || '').toString().trim();
					if(parsed_wiki !== ''){
						_addParsedWikitext(parsed_wiki);
					}else _removeLoadingNotice();
				}).fail(function () { _loadingFailNotice(); });
			}else _removeLoadingNotice();
		}
		//加入預覽的Lua內容
		function _addLuaText(wikiText, pagename, is_preview, call_back){
			var temp_module_name = "AddText/Temp/Module/Data.lua";
			var module_call = {
				wikitext:"#invoke:",//分開來,避免被分到[[:Category:有脚本错误的页面]]
				pagename:"Module:"
			};
			if(wikiText.toString().trim() !== ''){
				var params = {
					action: "parse",
					format: "json",
					title: pagename,
					text: "((" + module_call.wikitext + temp_module_name + "|main))",
					prop: "text",
					contentmodel: "wikitext",
					templatesandboxtitle: module_call.pagename + temp_module_name,
					//產生臨時Lua Module
					templatesandboxtext: "return {main=function()\nxpcall(function()\n" + wikiText + "\nend,function()end)\nlocal moduleWikitext = package.loaded[\"Module:Module wikitext\"]\nif moduleWikitext then\nlocal wikitext = moduleWikitext.main()\nif mw.text.trim(wikitext)~=''then\nreturn mw.getCurrentFrame():preprocess(moduleWikitext.main())\nend\nend\nreturn ''\nend}",
					templatesandboxcontentmodel: "Scribunto",
					templatesandboxcontentformat: "text/plain"
				};
				if(is_preview){
					params.preview = 1;
					params.disableeditsection = 1;
				}
				new mw.Api().get(params).done(function(data){
					var parsed_wiki = (data.parse.text['*'] || '').toString().trim();
					if(parsed_wiki !== ''){
						//若出錯在這個臨時模組中則取消
						if($(parsed_wiki).find(".scribunto-error").text().search(temp_module_name) < 0){
							if(typeof(call_back)===typeof($.noop))call_back(parsed_wiki);
							else _addParsedWikitext(parsed_wiki);
						}else _removeLoadingNotice();
					}else _removeLoadingNotice();
				}).fail(function () { _loadingFailNotice(); });
			}else _removeLoadingNotice();
		}
		//從頁面當前歷史版本取出 _addText
		function _applyRevision(revisionId, current_page_name){
			new mw.Api().get({
				action: 'parse',//get the original wikitext content of a page
				oldid: mw.config.get('wgRevisionId'),
				prop: 'wikitext',
				format: 'json'
			}).done(function(data)//若取得 _addText 則顯示預覽
			{
				var page_content = _check((data.parse.wikitext['*'] || '').toString().trim());
				page_content = (_elementExist('#mw-clearyourcache') ? ("((int:clearyourcache))" + 
						(page_content.toString().trim() === '' ? '' : '\n') //換行問題
					) : "") + page_content.toString();
				if(page_content.toString().trim() !== '') _addWikiText(page_content, current_page_name);
				else _removeLoadingNotice();
			}).fail(function () { _removeLoadingNotice(); });
		}
		//加入編輯提示 (如果存在)
		function _applyNotice(current_page_name){
			new mw.Api().get({
				action: 'parse',//get the original wikitext content of a page
				title: current_page_name,
				text: '((#ifexist:Template:Editnotices/Page/' + current_page_name + '|((Editnotices/Page/' + current_page_name + ')) ))',
				prop: 'text',
				format: 'json'
			}).done(function(data)
			{
				var html = data.parse.text['*'];
				if($(html.toString()).text().trim() !== '')_addParsedWikitext(html);
			});
		}
		
		/*=======================================
		* 測試樣例
		* =======================================*/
		//本腳本的Testcase模式
		function wikitextPreviewTestcase(is_preview){
			if(!_needPreview())return; //沒有可預覽元素,退出。
			var testcase_list = $('.special-wikitext-preview-testcase');
			//若頁面中沒有Testcase,退出。
			if(testcase_list.length < 0)return;
			//收集位於頁面中的Testcase預覽元素
			var testcase_data_list = [], i, testcase_it;
			for(i=0; i<testcase_list.length; ++i){
				testcase_it = testcase_list[i];
				var code_it = $(testcase_it).find('.mw-highlight');
				if(code_it.length > 0){
					var code_id = (/(?:mw-highlight-lang-)([^\s]+)/.exec($(code_it[0]).attr("class")) || [])[1];
					var load_index = testcase_data_list.length;
					$(testcase_it).attr("preview-id",load_index);
					testcase_data_list.push({
						element:testcase_it,
						lang:code_id,
						code:code_it.text().toString()
					});
				}
			}
			//整理頁面中的Testcase預覽元素,並放置[載入中]訊息
			var package_wikitext = '';
			for(i in testcase_data_list){
				if (Object.hasOwnProperty.call(testcase_data_list, i)) {
					testcase_it = testcase_data_list[i];
					if(testcase_it.code.trim() !== ''){
						if(['javascript','js','css','json'].indexOf(testcase_it.lang.toLowerCase()) > -1){
							var addWiki = _check(testcase_it.code, testcase_it.lang);
							if(addWiki.toString().trim() !== '')//如果解析結果非空才放置預覽
							{
								$(testcase_it.element).prepend(_notice_loading);
								package_wikitext+='<div class="special-wikitext-preview-testcase-' + i + '">' + addWiki + '</div>';
							}
						}else if(['lua','scribunto'].indexOf(testcase_it.lang.toLowerCase()) > -1){
							_addLuaText(testcase_it.code, mw.config.get("wgPageName"), is_preview, (function(index){
								return function(wikitext){
									$(testcase_data_list[index].element).prepend(wikitext);
								};
							})(i));
						}
					}
				}
			}
			//將整理完的Testcase預覽元素統一發送API請求,並將返回結果分發到各Testcase
			if(package_wikitext.trim() !== ''){
				package_wikitext = '<div class="special-wikitext-preview-testcase-undefined">' + package_wikitext + '</div>';
				var params = {
					action: 'parse',
					text: package_wikitext,
					contentmodel: "wikitext",
					prop: "text",
					format: 'json'
				};
				if(is_preview){
					params.preview = 1;
					params.disableeditsection = 1;
				}
				new mw.Api().get(params).done(function(data){
					var parsed_wiki = (data.parse.text['*'] || '').toString().trim();
					if(parsed_wiki !== ''){
						var parsed_element = $(parsed_wiki);
						for(var i in testcase_data_list){
							if (Object.hasOwnProperty.call(testcase_data_list, i)) {
								var testcase_it = testcase_data_list[i];
								if(['javascript','js','css','json'].indexOf(testcase_it.lang.toLowerCase()) > -1){
									var check_parse_result = parsed_element.find(".special-wikitext-preview-testcase-undefined > .special-wikitext-preview-testcase-"+i);
									if(check_parse_result.length > 0){
										$(testcase_it.element).find('#mw-_addText-preview-loading').html(check_parse_result);
									}
								}
							}
						}
					}
				});
			}
		}
		
		/*=======================================
		* 程式進入點
		* =======================================*/
		//給頁面添加預覽
		function _addPreview(){
			var current_page_name = mw.config.get("wgPageName");
			//預覽模式只適用於以下頁面內容模型
			if(_checkMwConfig('wgPageContentModel',['javascript', 'js', 'json', 'css', 'sanitized-css']))
			{
				//模式1 : 頁面預覽
				if(_elementExist('.previewnote'))//檢查是否為預覽模式
				{
					//預覽有可能是在預覽其他條目
					var preview_selector = $('.previewnote .warningbox > p > b a');
					if(preview_selector.length > 0){
						var path_path = decodeURI(preview_selector.attr('href') || ('/wiki/' + current_page_name)).replace(/^\/?wiki\//,'');
						//如果預覽的頁面並非本身,則不顯示預覽
						if(path_path !== current_page_name)return;
					}
					var addWiki = _check();
					if(addWiki.toString().trim() !== '')//如果解析結果非空才放置預覽
					{
						_addLoadingNotice();//放置提示,提示使用者等待AJAX
						_addWikiText(addWiki, current_page_name, true);//若取得 _addText 則顯示預覽
					}
				}
				//模式2 : 不支援顯示的特殊頁面
				else if(!_elementExist('#mw-clearyourcache') && _checkMwConfig('wgAction','view'))
				{//經查,不止是模板樣式,所有未嵌入'#mw-clearyourcache'的頁面皆無法正常顯示
					if(!_needPreview())return; //沒有預覽必要時,直接停止程式,不繼續判斷,以節省效能
					if(!_elementExist("#wpTextbox1"))//非編輯模式才執行 (預覽使用上方的if區塊)
					{
						_addLoadingNotice();//放置提示,提示使用者等待AJAX
						_applyRevision(mw.config.get('wgRevisionId'), current_page_name);//為了讓歷史版本正常顯示,使用wgRevisionId取得內容
					}
				}
				//模式3 : 頁面歷史版本檢視 : 如需複查的項目為頁面歷史版本,本工具提供頁面歷史版本內容顯示支援
				else if(_elementExist('#mw-revision-info') && _checkMwConfig('wgAction','view'))
				{//有嵌入'#mw-clearyourcache'的頁面的歷史版本會只能顯示最新版的 _addText 因此執行修正
					if(!_elementExist("#wpTextbox1"))//非編輯模式才執行 (預覽使用上方的if區塊)
					{
						$('#mw-clearyourcache').html(_notice_loading);//差異模式(含檢閱修訂版本刪除)的插入點不同
						_applyRevision(mw.config.get('wgRevisionId'), current_page_name);//為了讓特定版本正常顯示,使用wgRevisionId取得內容
					}
				}
				else _removeLoadingNotice();
			}
			//模組預覽功能  https://t.me/wikipedia_zh_n/1367097
			else if(_checkMwConfig('wgPageContentModel',['Scribunto', 'scribunto', 'lua']))
			{
				if(!_needPreview())return; //沒有預覽必要時,直接停止程式,不繼續判斷,以節省效能
				if(_elementExist("#wpTextbox1") && _elementExist("table.diff") && !_elementExist('.previewnote') && !_checkMwConfig('wgAction','view')){
					$(_notice_loading).insertAfter( '#wikiDiff' );
					_addLuaText($("#wpTextbox1").val(), current_page_name, true);
				}
			}
			//模式4 : 已刪頁面預覽
			else if(_elementExist('#mw-undelete-revision'))
			{//已刪內容頁面是特殊頁面,無法用常規方式判斷頁面內容模型
				if(!_needPreview())return; //沒有預覽必要時,直接停止程式,不繼續判斷,以節省效能
				if(_elementExist(['.mw-highlight','pre','.mw-json']))//確認正在預覽已刪內容
				{
					var tryGetWiki = $('textarea').val();//嘗試取得已刪內容原始碼
					var tryAddWiki = _getJSONwikitext(tryGetWiki);
					if(tryAddWiki.trim() === '')tryAddWiki = _getCSSwikitext(tryGetWiki);
					if(tryAddWiki.trim() !== '')//若取得 _addText 則顯示預覽
					{
						_addLoadingNotice();
						_addWikiText(tryAddWiki, mw.config.get("wgRelevantPageName"), true);
					}//嘗試Lua解析
					else if(/Module[_ ]wikitext.*_addText/i.exec($('.mw-parser-output').text()))
					{
						//本功能目前測試正常運作
						//若哪天預覽又失效,請取消註解下方那行
						//_addLuaText(tryGetWiki, mw.config.get("wgRelevantPageName"), true);
					}
				}
			}
			//如果特殊頁面缺乏編輯提示,則補上編輯提示 (如果存在)
			else if(!_elementExist('.mw-editnotice') && _checkMwConfig('wgCanonicalNamespace', 'special')){
				var pagename = mw.config.get('wgCanonicalSpecialPageName');
				if(pagename !== false && pagename !== null && pagename.toString().trim() !== ''){
					var fullpagename = mw.config.get('wgCanonicalNamespace') + ':' + pagename;
					_applyNotice(fullpagename);
				}
			}
			//都不是的情況則不顯示預覽
			else _removeLoadingNotice();
		}
		if(mw.config.get("wgIsSpecialWikitextPreview") !== true)//一頁只跑一次預覽
		{//避免小工具重複安裝冒出一大堆預覽
			//標記預覽已跑過
			mw.config.set("wgIsSpecialWikitextPreview",true);
			//執行預覽
			_addPreview();
			//檢查測試樣例
			wikitextPreviewTestcase(true);
		}
	}
	//為了避免wgULS未定義,因此在site-lib載入完畢後執行
	mw.loader.using('ext.gadget.site-lib').done(previewTool).fail(function(){
		//正式佈署前應拿掉本行。(測試期間為了讓沒有相關函數的站點可以使用)
		if( typeof wgULS !== typeof($.noop) )wgULS = function(a){return a;};
		previewTool();
	});
})(jQuery, mw);