User:Mikevoir/betterCodeMirror.js

mw.loader.using('mediawiki.api').then(function {	var lib = this._LIB;	var settings = this.betterCodeMirror || {};	var $textbox = $( '#wpTextbox1' );	var betterCodeMirror = {		config: mw.config.get(['wgAction']),		row: document.querySelector('.group-insert'),		init: function {			if (this.config.wgAction && ['edit', 'submit'].includes(this.config.wgAction)) {				if (!settings.disable || !Array.isArray(settings.disable) || !settings.disable.includes('quickOL'))				{lib.waitFor('.CodeMirror', this.quickOL);}				if (!settings.disable || !Array.isArray(settings.disable) || !settings.disable.includes('templateDataShow'))				{lib.waitFor('.CodeMirror', this.templateDataShow);}			}		},		addButton: function(query, content, handler) {			var addTo = document.querySelector(query);			if (addTo) {				var container = document.createElement('span');				container.classList.add('tool', 'oo-ui-widget', 'oo-ui-widget-enabled', 'oo-ui-buttonElement', 'oo-ui-buttonElement-frameless', 'oo-ui-iconElement', 'oo-ui-buttonWidget'); var button = document.createElement('a'); button.classList.add('oo-ui-buttonElement-button'); button.innerHTML = content; button.role = 'button'; button.addEventListener('mouseup', handler); container.appendChild(button); addTo.insertBefore(container, addTo.children[0]); }		},		quickOL: function { // Add buttons betterCodeMirror.addButton('.group-insert', 'OL_tl', addOL); betterCodeMirror.addButton('.group-insert', 'OL', addOL); // Add call when clicked function addOL(event) { // OL call without TL params if (event.target.innerHTML == 'OL'){ $textbox.textSelection(						'replaceSelection',						''); // OL call with TL params }else if (event.target.innerHTML == 'OL_tl'){ $textbox.textSelection(						'replaceSelection',						''); }			}		},		templateDataShow: function { var $textbox = $textbox || $( '#wpTextbox1' ); var api = api || new mw.Api; var cachedPage; var template_data; var change = {}; // Load VE-only styling importArticles({			   type: 'css',			    articles: ['u:community:User:Mikevoir/betterCodeMirror.css']			});

var TDSmethods = { init: function(event) { var checkTemplate = /^[\s\S]*)?$/; // check for template start with name					var uptoCaret = $textbox.textSelection('getSelection');					var customSelect = true;					if (uptoCaret.length==0){						uptoCaret = $textbox.textSelection('getContents').substring(0, $textbox.textSelection('getCaretPosition'));						customSelect = false;					}					if (customSelect || (uptoCaret && checkTemplate.test(uptoCaret))) {						var template = (customSelect && uptoCaret) || uptoCaret.replace(checkTemplate, '$1').trim;						var FROM = $textbox.textSelection('getCaretPosition') -							( (customSelect && (uptoCaret.length + 2)) || (uptoCaret.replace(checkTemplate, '$1$2').length + 2) );						var toCheck = TDSmethods.getEncased($textbox.textSelection('getContents').substring(FROM), '{', '}');						cachedPage = null;						template_data = null;						change = {};						if (toCheck.length>0){							var waitForCache = function {								if (cachedPage && cachedPage.TEMPLATE == template) { template_data = TDSmethods.parseTemplate(toCheck, FROM); }								else { setTimeout(waitForCache, 1000); }							};							waitForCache; // Start the loop						}						this.getTD(template, this.renderTD);					}				},				getTD: function(template, callBack) {					var tdRegex = / ([\s\S]+)<\/templatedata>/g;					api.get({ action: 'parse', page: 'Template:'+template+'/doc', prop: 'wikitext|categories|sections' }).then(function(data){ if (							data &&							data.parse &&							data.parse.wikitext &&							data.parse.wikitext['*'] &&							tdRegex.test(data.parse.wikitext['*'])						){ var raw_templateData = data.parse.wikitext['*'].match(tdRegex)[0].replace(' ', ).replace(' ', ); var _templateData = JSON.parse(raw_templateData); if (_templateData) { _templateData.TEMPLATE = template; TDSmethods.proofTD(_templateData, callBack); } else { alert('"Template:'+template+'" does not have templatedata on "Template:'+template+'/doc".'); }						}					}, function{alert('"Template:'+template+'" is not a valid target.');});				},				proofTD: function(data, callBack) {					if (!data.paramOrder && data.params) {						data.paramOrder = [];						Object.keys(data.params).forEach(function(key) { data.paramOrder.push(key); });					}					if (!data.format) {data.format='inline';}					callBack(data);				},				renderTD: function(data) {					if (!data.NORECORD) {cachedPage = data;}					var frame = 						TDSmethods.nestElements(document.querySelector('body'), [								{classNames: [ 'td-popup' ]}, {classNames: [ 'td-popup-wrapper' ]} ]						);					// Head container					TDSmethods.addElement({ addTo: frame, classNames: [ 'td-popup-head-wrapper' ], content: TDSmethods.listElements(null, 								[									{										node: 'div',										classNames: [ 'td-popup-head-label' ],										content: 'Template Data: ' + data.TEMPLATE									},									{										node: 'a',										classNames: [ 'td-popup-head-close-button' ],										attributes: {											role: 'button',											tabindex: '0',											rel: 'nofollow',											title: 'Close popup'										},										content: 'X'									},									{										node: 'span',										classNames: [ 'td-popup-head-apply-button', 'wds-button' ],										attributes: {											role: 'button',											tabindex: '0',											rel: 'nofollow',											title: 'Apply changes'										},										content: 'Apply changes'									}								]							) });					// Window body					var body = TDSmethods.addElement({ addTo: frame, classNames: [ 'td-popup-body-wrapper' ] });					// Body title					var body_title = TDSmethods.addElement({ addTo: body, classNames: [ 'td-popup-body-header' ], content: TDSmethods.listElements(								null,								[									{ 										classNames: [ 'td-popup-template' ],										content: data.TEMPLATE,									},									{ 										classNames: [ 'td-popup-template-description' ],										content: data.description || 'No template description.'									},									{ 										classNames: [ 'td-popup-template-format' ],										content: 											TDSmethods.listElements(null, [													{classNames: ['td-popup-format-text'], content:'Format: ' + (data.format.replace('\n', '\\n') || 'inline')}, {node: 'input', classNames: ['td-popup-checkbox', 'td-popup-format-button'], attributes: {type: 'checkbox', id: 'template-format'}}, {content:'Apply format', node: 'label', classNames: ['td-popup-checkbox', 'td-popup-format-button-label'], attributes: {type: 'checkbox', 'for': 'template-format'}}, ]											)									},									{ 										classNames: [ 'td-popup-template-filter' ],										content:											TDSmethods.listElements( null, [													{ 														classNames: [ 'td-popup-template-filter-label' ], content: 'Filter params: ', },													{ 														classNames: [ 'td-popup-template-filter-input' ], attributes: {id: 'td-popup-template-filter'}, node: 'input', content: data.description || 'No template description.' },												])									},								]							)					});					var waitForData = function {						if (template_data) {							data.paramOrder.forEach(function(param){ TDSmethods.addElement({									addTo: body,									classNames: [ 'td-popup-param' ],									attributes: {id: param},									content: TDSmethods.parseParam(data.params[param], param)								}); });							document.addEventListener('mouseup', function(event){ if (event.target.classList.contains('td-popup-head-apply-button')) { var deploy = false; if (document.getElementById('template-format') && document.getElementById('template-format').checked) {deploy = true;} else if ( Object.keys(change).length>0 ) { Object.keys(change).forEach(function(key){											if (change[key] == true) {deploy = true;}										}); }									if (deploy) { TDSmethods.orderTemplate; }								}							});							document.addEventListener('change', function(event){ if (event.target.id && event.target.id.match(/^param-toggle-/)) { var param = event.target.id.replace(/^param-toggle-/, ''); if (event.target && event.target.checked) { template_data.params[param] = {NEW: true}; change['__PARAM-TOGGLE__'+param] = true; event.target.setAttribute('checked', 'true'); }									else { template_data.params[param].NEW = false; change['__PARAM-TOGGLE__'+param] = false; event.target.setAttribute('checked', 'false'); }								}							});						}						else { setTimeout(waitForData, 1000); }					};					waitForData; // Start the loop					// Close popup					var listenTo = ['mouseup', 'keyup'];					var closePopup = function(event) {						if ( (								event.target.classList.contains('td-popup-head-close-button') &&								(event.type == 'mouseup')							)|| (								event.type == 'keyup' &&								event.key == 'Escape'							) ){							// Close popup and remove listeners							var el = document.querySelector('.td-popup');							if (el){								el.remove;								listenTo.forEach(function(type){document.removeEventListener(type, closePopup);});							}						}					};					listenTo.forEach(function(type){document.addEventListener(type, closePopup);});					document.addEventListener('change', TDSmethods.textUpdates);				},				addElement: function(options) {					if (!options.node){options.node = 'div';}					var newNode;					if (options.node !== 'text') {						newNode = document.createElement(options.node);						if (options.classNames) {							options.classNames.forEach(function(className){ if ('string' == typeof className && className.length > 0) { newNode.classList.add(className); }							});						}						if (options.styles) {							Object.keys(options.styles).forEach(function(key) { newNode.style[key] = options.styles[key]; });						}						if (options.attributes) {							Object.keys(options.attributes).forEach(function(key) { newNode.setAttribute(key, options.attributes[key]); });						}						if (options.content) {							if (Array.isArray(options.content) && options.content.length>0) {								options.content.forEach(function(content){ if ('string' == typeof content) { newNode.appendChild(document.createTextNode(content)); }else if ('object' == typeof content && content.ownerDocument) { newNode.appendChild(content); }								});							} else if ('string' == typeof options.content) {								newNode.appendChild(document.createTextNode(options.content));							}else if ('object' == typeof options.content && options.content.ownerDocument) {								newNode.appendChild(options.content);							}						}					} else { newNode = document.createTextNode(options.content); }					if (options.addTo) {						options.addTo.appendChild(newNode);					}					if (!options.noreturn) {						return newNode;					}				},				nestElements: function(startNode, optionsArray) {					var oldNode = startNode;					optionsArray.forEach(function(options){ options.addTo = oldNode; oldNode = TDSmethods.addElement(options); });					return oldNode;				},				listElements: function(parentNode, optionsArray) {					var newNodes = [];					optionsArray.forEach(function(options){ if (parentNode) {options.addTo = parentNode;} var newNode = TDSmethods.addElement(options); if (!parentNode && newNode) {newNodes.push(newNode);} });					return parentNode || newNodes;				},				parseParam: function(data, param) {					var checkbox_attr = {						type: 'checkbox',						id: 'param-toggle-'+param					};					if (template_data.params[param] && (template_data.params[param].value||template_data.params[param].NEW)) {checkbox_attr.checked = 'true';}					var all = [						{							node: 'input',							classNames: ['td-popup-checkbox', 'td-popup-param-toggle-button'],							attributes: checkbox_attr						},						{							content: data.label || param,							node: 'label',							classNames: ['td-popup-checkbox', 'td-popup-param-toggle-button-label', 'td-popup-param-label'],							attributes: {type: 'checkbox', 'for': 'param-toggle-'+param}						}					];					var param_names = [{ content: param, node: 'code', classNames: ['td-popup-param-list-item'] }];					var param_desc  = [];					var hr = false;					if (data.aliases && data.aliases.length>0) { data.aliases.forEach(function(alias){							param_names.push({ content: alias, node: 'code', classNames: ['td-popup-param-list-item'] });						});					}					all.push({ classNames: [ 'td-popup-param-list' ], content: TDSmethods.listElements(null,param_names) }); if (data.description) { if (!hr){param_desc.push({node: 'hr'}); hr=true;} param_desc.push({							content: data.description,							node: 'p'						}); }					if (data.default) { if (!hr){param_desc.push({node: 'hr'}); hr=true;} param_desc.push({							content: 'Default: ' + data.default,							classNames: ['vetd-popup-param-default'],							node: 'p'						});} if (data.example) { if (!hr){param_desc.push({node: 'hr'}); hr=true;} param_desc.push({							content: 'Example: ' + data.example,							classNames: ['td-popup-param-example'],							node: 'p'						}); }					all.push({ classNames: [ 'td-popup-param-description' ], content: TDSmethods.listElements(null,param_desc) }); all.push({						classNames: [ 'td-popup-param-value' ],						node: 'textarea',						content: template_data.params[param] && template_data.params[param].value || null,						attributes: {id: 'ParamVal'+param}					}); return TDSmethods.listElements(null, all); },				parseTemplate: function(str, FROM) { var unnamedCount = 0; var order = 0; var nest = 0; var link, INTEMPLATE, PARAMS, sub; var template = { name: null, params: {}, };					var tempParam = { name: '', value: '', start: 0, raw: '' };					var S = { braceL: '{', braceR: '}', bracketL: '[', bracketR: ']', tag: '<', pipe: '|', equal: '=', excl: '!', };					var i = 0; while (i < str.length) { var char = str.charAt(i); var Nchar = str.charAt(i+1); if (!INTEMPLATE) { if (char == S.braceL && Nchar == S.braceL) { INTEMPLATE = true; i++; i++; } } else if (INTEMPLATE) { if (!template.name) { if (![S.braceL, S.pipe].includes(char) && ![S.braceL, S.pipe].includes(Nchar)) { tempParam.name = tempParam.name + char; i++; } else if (![S.braceL, S.pipe].includes(char) && [S.braceL, S.pipe].includes(Nchar)) { tempParam.name = tempParam.name + char; template.name = tempParam.name.trim; tempParam.name = ''; i++; }							} else { // End template if (char == S.braceR && Nchar == S.braceR) { closeParam(i); template.FROM = FROM; template.STR = str; template._echo = JSON.parse(JSON.stringify(template)); return template; // Template param start } else if (char == S.pipe) { tempParam.start = i;									closeParam(i); i++; } else { sub = handleNest(str.substring(i)); if (sub && sub == '_//_//_SKIP_//_//_') { tempParam.raw = tempParam.raw + char; i++; }									else { if (sub && sub.length>0) { tempParam.value = tempParam.value + sub; tempParam.raw = tempParam.raw + sub; i = i + sub.length; } else { tempParam.value = tempParam.value + char; tempParam.raw = tempParam.raw + char; i++; }									}								}							}						}					}					return null; function handleNest(newS) { var one = newS.charAt(0); var two = newS.charAt(1); // Handle named param if (one == S.equal && tempParam.value.length>0 && tempParam.name.length==0) { tempParam.name = tempParam.value; tempParam.value = ''; return '_//_//_SKIP_//_//_'; }						// Handle nested template else if (one == S.braceL && two == S.braceL) { return TDSmethods.getEncased(newS, S.braceL, S.braceR); }						// Handle link else if (one == S.bracketL) { return TDSmethods.getEncased(newS, S.bracketL, S.bracketR); }						// Handle comments else if (one == S.tag && two == S.excl) { return TDSmethods.getEncased(newS, ''); }						// Handle tags else if (one == S.tag && newS.match(/^<[a-z][^>/]*>/g)) { return TDSmethods.getEncased(newS, '/]*\/>/g)) { return TDSmethods.getEncased(newS, '', true); }						return ''; }					function closeParam(end){ if (tempParam.name.length==0) { unnamedCount++; tempParam.name = tempParam.name+unnamedCount; }						template.params[tempParam.name.trim] = { value: tempParam.value.trim || '', start: tempParam.start, raw: tempParam.raw, order: order, end: end, };						tempParam = { name: '', value: '', start: 0, raw: '' };						order++; }				},				getEncased: function(str, startC, endC, REGEX) { var nest = null; var newStr = ''; var caret = 0; while (caret < str.length) { var sub = str.substring(caret); if ((REGEX && sub.match('^'+startC)) || sub.indexOf(startC) == 0) { nest = nest || 0; sub = REGEX && sub.match(startC)[0] || startC; nest++; }						else if ((REGEX && sub.match('^'+endC)) || sub.indexOf(endC) == 0) { nest = nest || 0; sub = sub.match(endC)[0] || endC; nest--; }						else { sub = str.charAt(caret); }						newStr = newStr + sub; caret = caret + sub.length; if (nest < 1) { return newStr; }					}					return ''; },				orderTemplate: function { var format = TDSmethods.handleFormat(cachedPage.format); var param_echo = JSON.parse(JSON.stringify(template_data.params)); var newT = [''; $textbox.textSelection('setSelection', {start: template_data.FROM, end: template_data.FROM+template_data.STR.length}); $textbox.textSelection('replaceSelection', newT); document.querySelector('.td-popup').remove; function buildParam(param) { var param_str = '|'; if (template_data.params[param].NEW == true) { var input = document.getElementById('ParamVal'+param).value || ''; if (!isNaN(param)) { param_str = param_str + input; } else { param_str = param_str + param + ' '.repeat(Math.max(0, (format.maxlen - param.length))) + ' = '+								input; }						}						else if (template_data.params[param] && change[param]) { if (!isNaN(param)) { param_str = param_str + template_data.params[param].value; } else if (!updateFormat && template_data.params[param].raw) { param_str = param_str + template_data.params[param].raw.replace(/^(.*?=)[\s\S]*$/g, '$1 ' + lib.escapeReplace(template_data.params[param].value)).trim; }							else if (updateFormat || !template_data.params[param].raw) { param_str = param_str + param + ' '.repeat(Math.max(0, (format.maxlen - param.length))) + ' = '+								template_data.params[param].value; } else { param_str = param_str + template_data.params[param].value; }						} else if (template_data._echo.params[param] && !updateFormat) { param_str = param_str + template_data._echo.params[param].raw.trim; } else if (document.getElementById('param-toggle-'+param) && document.getElementById('param-toggle-'+param).checked) { if (!isNaN(param)) { param_str = param_str + template_data.params[param].value; } else { param_str = param_str + param + ' '.repeat(Math.max(0, (format.maxlen - param.length))) + ' = '+								template_data.params[param].value; }						}						newT.push(param_str); param_echo[param] = null; }				},				handleFormat: function(format){ var sett = { preall: '', maxlen: 0, afterall: '' };					if (format == 'inline') {} else if (format == 'block') { sett.preall = '\n'; } else { var touse = /^[\s\S]*$/g.exec(format.trim); if (touse) { sett = { preall:  touse[1], maxlen:  touse[2].length, afterall: touse[3] };						}else{alert('Template with non-standard "format," defaulting to inline.');} }					return sett; },				textUpdates: function(event) { if (event.target.id == 'td-popup-template-filter') { var filter = document.getElementById('td-popup-template-filter').value; document.querySelectorAll('.td-popup-body-wrapper .td-popup-param').forEach(function(el) {						   var match = el.id.search(new RegExp('^'+filter, 'i')); //check if param starts with						    if (match < 0) { el.classList.add('td-popup-param-filterout'); }						    else { el.classList.remove('td-popup-param-filterout'); }						}); } else if (event.target.classList.contains('td-popup-param-value')) { var newVal = event.target.value; var param = event.target.id.replace(/^ParamVal(.+)$/, '$1'); if (param) { template_data.params[param] = { value: newVal.trim };							if (!template_data._echo.params[param] || (template_data.params[param].value !== template_data._echo.params[param].value)){ change[param] = true; } else { change[param] = false; }						}					}				}			};			var fireTD = function(event){ // Run on: left click when (alt key + ctrl + shift) if (event.ctrlKey && event.altKey){ event.preventDefault; TDSmethods.init(event); }			};			document.addEventListener('keydown', fireTD); },	};

betterCodeMirror.init; });