XenForo Post Style

Allows a XenForo user to add a custom BBCode template to their posts. Optional automatic formatting and keyboard shortcut included.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==UserScript==
// @name	XenForo Post Style
// @namespace	Makaze
// @include	*
// @grant	none
// @version	5.0.4
// @description Allows a XenForo user to add a custom BBCode template to their posts. Optional automatic formatting and keyboard shortcut included.
// ==/UserScript==

/*
 XENFORO POST TEMPLATE SCRIPT
 
 PRESS Alt+T TO APPLY ON ANY PAGE

 AUTOMATIC FORMATTING OPTIONAL
*/

function getPosition(element) {
	var xPosition = 0,
	yPosition = 0;

	while (element) {
		xPosition += (element.offsetLeft
			+ element.clientLeft);
		yPosition += (element.offsetTop
			+ element.clientTop);
		element = element.offsetParent;
	}
	return {x: xPosition, y: yPosition};
}

Math.easeInOutQuad = function (time, start, change, duration) {
	time /= duration / 2;
	if (time < 1) {
		return change / 2 * time * time + start;
	}
	time--;
	return -change / 2 * (time * (time - 2) - 1) + start;
};

function scrollTo(element, to, duration) {
	var start = element.scrollTop,
	change = to - start,
	currentTime = 0,
	increment = 1;

	var animateScroll = function() {        
		var val = Math.easeInOutQuad(currentTime, start, change, duration);                        
		element.scrollTop = val; 
		currentTime += increment;
		if (currentTime < duration) {
			setTimeout(animateScroll, increment);
		}
	};

	animateScroll();
}

function selectRange(elem, start, end) {
	var range;

	if (elem.setSelectionRange) {
		elem.focus();
		elem.setSelectionRange(start, end);
	} else if (elem.createTextRange) {
		range = elem.createTextRange();
		range.collapse(true);
		range.moveEnd('character', end);
		range.moveStart('character', start);
		range.select();
	}
}

function cursor(elem, position) {
	selectRange(elem, position, position);
}

function applyTemplate(elem) {
	var formContext = elem,
	opts = (localStorage.getItem('MakazeScriptOptions')) ? JSON.parse(localStorage.getItem('MakazeScriptOptions')) : {},
	htmlPrefix = (opts.hasOwnProperty('xf_template_htmlPrefix')) ? opts.xf_template_htmlPrefix : '',
	htmlSuffix = (opts.hasOwnProperty('xf_template_htmlSuffix')) ? opts.xf_template_htmlSuffix : '',
	bbPrefix = (opts.hasOwnProperty('xf_template_bbPrefix')) ? opts.xf_template_bbPrefix : '',
	bbSuffix = (opts.hasOwnProperty('xf_template_bbSuffix')) ? opts.xf_template_bbSuffix : '',
	thisList,
	thisLink,
	instance,
	postForm,
	plainText,
	i = 0;

	var applyTemplateEvent = function(event) {
		var parent = event.target;
		applyToChildren(parent);
		event.target.removeEventListener('keydown', applyTemplateEvent, false);
	};

	var applyToChildren = function(parent) {
		var thisChild,
		applyTo,
		i = 0;

		for (i = 0; i < parent.childNodes.length; i++) {
			thisChild = parent.childNodes[i];
			if (thisChild.innerHTML) {
				applyTo = thisChild.innerHTML.replace(
					/\[quote([^]+)\[\/quote\]/gi, htmlSuffix + '[quote$1[/quote]' + htmlPrefix
				);

				thisChild.innerHTML = htmlPrefix + applyTo + htmlSuffix;
			}
		}
	};

	while (formContext.getElementsByClassName('redactor_box')[0] == null && formContext.parentNode) {
		formContext = formContext.parentNode;
	}

	if (opts.hasOwnProperty('xf_template_auto')) {
		for (i = 0; i < formContext.getElementsByClassName('redactor_MessageEditor').length; i++) {
			instance = formContext.getElementsByClassName('redactor_MessageEditor')[i];
			postForm = instance.contentWindow.document;
			if (!postForm.body.textContent.length) {
				postForm.body.addEventListener('keydown', applyTemplateEvent, false);
			} else {
				applyToChildren(postForm.body);
			}
		}
		for (i = 0; i < formContext.getElementsByClassName('bbCodeEditorContainer').length; i++) {
			plainText = formContext.getElementsByClassName('bbCodeEditorContainer')[i].getElementsByTagName('textarea')[0];
			plainText.value = bbPrefix + plainText.value + bbSuffix;
			cursor(plainText, plainText.value.length - bbSuffix.length);
		}
	} else {
		thisList = document.getElementById('AccountMenu').getElementsByClassName('blockLinksList')[0];
		for (i = 0; i < thisList.getElementsByTagName('a').length; i++) {
			thisLink = thisList.getElementsByTagName('a')[i];
			if (thisLink.href.match(/account\/personal\-details/gi) && thisLink.href.substr(window.location.href.length - 14, 14) !== '#Post_Template') {
				thisLink.href = thisLink.href + '#Post_Template';
				thisLink.click();
				break;
			}
		}
	}
}

function xenForoMessage(msg, success) {
	if (success) {
		$('#templateMessage .content').html(msg);
		console.log(msg);
	} else {
		$('#templateMessage .content').html('<strong>Error:</strong> ' + msg);
		console.log('Error:', msg);
	}
	$('#templateMessage').slideDown('medium');
	$('#templateMessage .content').animate({
		'opacity': 1
	}, 'fast');
	setTimeout(function() {
		$('#templateMessage').slideUp('medium');
		$('#templateMessage .content').animate({
			'opacity': 0
		}, 'fast');
	}, 1500);
}

function runInGlobal(code) {
	var scripts = document.createElement('script');
	scripts.type = 'text/javascript';
	scripts.id = 'runInGlobal';
	scripts.appendChild(document.createTextNode(
		code +
		'\n\n' +
		'document.getElementById(\'runInGlobal\').remove();'
	));

	(document.head || document.body || document.documentElement).appendChild(scripts);
}

function saveTemplateSettings() {
	if (!document.getElementById('htmlPrefixField').value.length) {
		xenForoMessage('HTML prefix required.', false);
		return false;
	}

	if (!document.getElementById('htmlSuffixField').value.length) {
		xenForoMessage('HTML suffix required.', false);
		return false;
	}

	if (!document.getElementById('bbPrefixField').value.length) {
		xenForoMessage('BBCode prefix required.', false);
		return false;
	}

	if (!document.getElementById('bbSuffixField').value.length) {
		xenForoMessage('BBCode suffix required.', false);
		return false;
	}
	
	var opts = (localStorage.getItem('MakazeScriptOptions')) ? JSON.parse(localStorage.getItem('MakazeScriptOptions')) : {};

	opts.xf_template_auto = (document.getElementById('autoApplyField').options[document.getElementById('autoApplyField').selectedIndex].value === 'true');
	opts.xf_template_htmlPrefix = document.getElementById('htmlPrefixField').value;
	opts.xf_template_htmlSuffix = document.getElementById('htmlSuffixField').value;
	opts.xf_template_bbPrefix = document.getElementById('bbPrefixField').value;
	opts.xf_template_bbSuffix = document.getElementById('bbSuffixField').value;
	localStorage.setItem('MakazeScriptOptions', JSON.stringify(opts));

	xenForoMessage('Your settings have been saved.', true);
}

var applyHandler = function() {
	applyTemplate(this);
};

if (document.documentElement.id === "XenForo") {
	var opts = (localStorage.getItem('MakazeScriptOptions')) ? JSON.parse(localStorage.getItem('MakazeScriptOptions')) : {},
	autoApply = (opts.hasOwnProperty('xf_template_auto')) ? opts.xf_template_auto : false,
	htmlPrefix = (opts.hasOwnProperty('xf_template_htmlPrefix')) ? opts.xf_template_htmlPrefix : '',
	htmlSuffix = (opts.hasOwnProperty('xf_template_htmlSuffix')) ? opts.xf_template_htmlSuffix : '',
	bbPrefix = (opts.hasOwnProperty('xf_template_bbPrefix')) ? opts.xf_template_bbPrefix : '',
	bbSuffix = (opts.hasOwnProperty('xf_template_bbSuffix')) ? opts.xf_template_bbSuffix : '',
	instance,
	buttonsContext,
	richInstance,
	richDoc,
	field,
	i = 0;

	// Button creation and auto-application

	if (document.getElementsByClassName('MessageEditor')[0] != null) {
		for (i = 0; i < document.getElementsByClassName('MessageEditor').length; i++) {
			instance = document.getElementsByClassName('MessageEditor')[i];
			buttonsContext = instance;

			while (buttonsContext.getElementsByClassName('submitUnit')[0] == null && buttonsContext.parentNode) {
				buttonsContext = buttonsContext.parentNode;
			}

			buttonsContext = buttonsContext.getElementsByClassName('submitUnit')[0].getElementsByClassName('button primary')[0].parentNode;

			var applyButton = document.createElement('input'),
			applyButtonSpacer = document.createTextNode(String.fromCharCode(160));
			applyButton.type = 'button';
			applyButton.value = 'Apply Style';
			applyButton.className = 'button JsOnly applyButton';
			applyButton.onclick = applyHandler;

			buttonsContext.appendChild(applyButtonSpacer);
			buttonsContext.appendChild(applyButton);

			if (autoApply.toString() === 'true') {
				applyTemplate(instance);
			}
		}

		document.addEventListener('keydown', function(e) {
			var i = 0,
			keyInstance;

			if (e.keyCode == 84 && e.altKey) {
				for (i = 0; i < document.getElementsByClassName('MessageEditor').length; i++) {
					keyInstance = document.getElementsByClassName('MessageEditor')[i];
					applyTemplate(keyInstance);
				}
			}
		}, false);

		var applyToRichHandler = function(e) {
			if (e.keyCode == 84 && e.altKey) {
				applyTemplate(richInstance);
			}
		};

		if (document.getElementsByClassName('redactor_MessageEditor')[0] != null) {
			for (i = 0; i < document.getElementsByClassName('redactor_MessageEditor').length; i++) {
				richInstance = document.getElementsByClassName('redactor_MessageEditor')[i];
				richDoc = richInstance.contentWindow.document;
				richDoc.addEventListener('keydown', applyToRichHandler, false);
			}
		}
	}
	
	if (window.location.href.match(/account\/personal\-details/gi)) {
		// Define xenForoMessage and saveTemplateSettings

		runInGlobal(
			xenForoMessage.toString() +
			saveTemplateSettings.toString()
		);

		// Settings creation

		var optionsContainer = document.createElement('fieldset'),

		header = document.createElement('dl'),
		headerDT = document.createElement('dt'),
		headerDD = document.createElement('dd'),
		headerDD_Text = document.createTextNode('Post Style'),

		autoApplyField = document.createElement('dl'),
		autoApplyFieldDT = document.createElement('dt'),
		autoApplyFieldDT_Text = document.createTextNode('Auto-apply:'),
		autoApplyFieldDD = document.createElement('dd'),
		autoApplyFieldDD_Select = document.createElement('select'),
		autoApplyFieldDD_Select_true = document.createElement('option'),
		autoApplyFieldDD_Select_true_Text = document.createTextNode('True'),
		autoApplyFieldDD_Select_false = document.createElement('option'),
		autoApplyFieldDD_Select_false_Text = document.createTextNode('False'),

		htmlPrefixField = document.createElement('dl'),
		htmlPrefixFieldDT = document.createElement('dt'),
		htmlPrefixFieldDT_Text = document.createTextNode('HTML Prefix:'),
		htmlPrefixFieldDD = document.createElement('dd'),
		htmlPrefixFieldDD_input = document.createElement('input'),

		htmlSuffixField = document.createElement('dl'),
		htmlSuffixFieldDT = document.createElement('dt'),
		htmlSuffixFieldDT_Text = document.createTextNode('HTML Suffix:'),
		htmlSuffixFieldDD = document.createElement('dd'),
		htmlSuffixFieldDD_input = document.createElement('input'),

		bbPrefixField = document.createElement('dl'),
		bbPrefixFieldDT = document.createElement('dt'),
		bbPrefixFieldDT_Text = document.createTextNode('BBCode Prefix:'),
		bbPrefixFieldDD = document.createElement('dd'),
		bbPrefixFieldDD_input = document.createElement('input'),

		bbSuffixField = document.createElement('dl'),
		bbSuffixFieldDT = document.createElement('dt'),
		bbSuffixFieldDT_Text = document.createTextNode('BBCode Suffix:'),
		bbSuffixFieldDD = document.createElement('dd'),
		bbSuffixFieldDD_input = document.createElement('input'),

		submitField = document.createElement('dl'),
		submitFieldDT = document.createElement('dt'),
		submitFieldDD = document.createElement('dd'),
		submitFieldDD_input = document.createElement('input'),

		templateMessage = document.createElement('div'),
		templateMessage_content = document.createElement('div'),
		templateMessage_content_Text = document.createTextNode('Your settings have been saved.');

		// Load input settings

		htmlPrefixFieldDD_input.value = htmlPrefix;
		htmlSuffixFieldDD_input.value = htmlSuffix;
		bbPrefixFieldDD_input.value = bbPrefix;
		bbSuffixFieldDD_input.value = bbSuffix;

		// Header

		headerDD.setAttribute('style', 'font-weight: bolder; font-size: 130%; width: 40%; text-decoration: underline;');
		headerDD.appendChild(headerDD_Text);

		header.className = 'ctrlUnit';
		header.appendChild(headerDT);
		header.appendChild(headerDD);

		// Auto apply field

		autoApplyFieldDT.appendChild(autoApplyFieldDT_Text);

		autoApplyFieldDD_Select_true.value = true;
		autoApplyFieldDD_Select_true.appendChild(autoApplyFieldDD_Select_true_Text);

		autoApplyFieldDD_Select_false.value = false;
		autoApplyFieldDD_Select_false.appendChild(autoApplyFieldDD_Select_false_Text);

		autoApplyFieldDD_Select.id = 'autoApplyField';
		autoApplyFieldDD_Select.className = 'textCtrl';
		autoApplyFieldDD_Select.appendChild(autoApplyFieldDD_Select_true);
		autoApplyFieldDD_Select.appendChild(autoApplyFieldDD_Select_false);

		autoApplyFieldDD.appendChild(autoApplyFieldDD_Select);

		autoApplyField.className = 'ctrlUnit';
		autoApplyField.appendChild(autoApplyFieldDT);
		autoApplyField.appendChild(autoApplyFieldDD);

		// HTML prefix field

		htmlPrefixFieldDT.appendChild(htmlPrefixFieldDT_Text);

		htmlPrefixFieldDD_input.type = 'text';
		htmlPrefixFieldDD_input.id = 'htmlPrefixField';
		htmlPrefixFieldDD_input.className = 'textCtrl OptOut';

		htmlPrefixFieldDD.appendChild(htmlPrefixFieldDD_input);

		htmlPrefixField.className = 'ctrlUnit';
		htmlPrefixField.appendChild(htmlPrefixFieldDT);
		htmlPrefixField.appendChild(htmlPrefixFieldDD);

		// HTML suffix field

		htmlSuffixFieldDT.appendChild(htmlSuffixFieldDT_Text);

		htmlSuffixFieldDD_input.type = 'text';
		htmlSuffixFieldDD_input.id = 'htmlSuffixField';
		htmlSuffixFieldDD_input.className = 'textCtrl OptOut';

		htmlSuffixFieldDD.appendChild(htmlSuffixFieldDD_input);

		htmlSuffixField.className = 'ctrlUnit';
		htmlSuffixField.appendChild(htmlSuffixFieldDT);
		htmlSuffixField.appendChild(htmlSuffixFieldDD);

		// BBCode prefix field

		bbPrefixFieldDT.appendChild(bbPrefixFieldDT_Text);

		bbPrefixFieldDD_input.type = 'text';
		bbPrefixFieldDD_input.id = 'bbPrefixField';
		bbPrefixFieldDD_input.className = 'textCtrl OptOut';

		bbPrefixFieldDD.appendChild(bbPrefixFieldDD_input);

		bbPrefixField.className = 'ctrlUnit';
		bbPrefixField.appendChild(bbPrefixFieldDT);
		bbPrefixField.appendChild(bbPrefixFieldDD);

		// BBCode suffix field

		bbSuffixFieldDT.appendChild(bbSuffixFieldDT_Text);

		bbSuffixFieldDD_input.type = 'text';
		bbSuffixFieldDD_input.id = 'bbSuffixField';
		bbSuffixFieldDD_input.className = 'textCtrl OptOut';

		bbSuffixFieldDD.appendChild(bbSuffixFieldDD_input);

		bbSuffixField.className = 'ctrlUnit';
		bbSuffixField.appendChild(bbSuffixFieldDT);
		bbSuffixField.appendChild(bbSuffixFieldDD);

		// Submit field

		submitFieldDD_input.type = 'button';
		submitFieldDD_input.id = 'submitTemplate';
		submitFieldDD_input.className = 'button';
		submitFieldDD_input.value = 'Save';
		submitFieldDD_input.setAttribute('onClick', 'saveTemplateSettings();');

		submitFieldDD.appendChild(submitFieldDD_input);

		submitField.className = 'ctrlUnit';
		submitField.appendChild(submitFieldDT);
		submitField.appendChild(submitFieldDD);

		// Template message

		templateMessage_content.className = 'content baseHtml';
		templateMessage_content.style.opacity = 0;
		templateMessage_content.appendChild(templateMessage_content_Text);

		templateMessage.id = 'templateMessage';
		templateMessage.className = 'xenOverlay timedMessage';
		templateMessage.setAttribute('style', 'top: 0px; left: 0px; position: fixed; display: none;');
		templateMessage.appendChild(templateMessage_content);

		// Build it all

		optionsContainer.id = 'templateOptionsContainer';
		optionsContainer.appendChild(header);
		optionsContainer.appendChild(autoApplyField);
		optionsContainer.appendChild(htmlPrefixField);
		optionsContainer.appendChild(htmlSuffixField);
		optionsContainer.appendChild(bbPrefixField);
		optionsContainer.appendChild(bbSuffixField);
		optionsContainer.appendChild(submitField);

		document.getElementsByClassName('OptOut')[document.getElementsByClassName('OptOut').length - 1].parentNode.insertBefore(optionsContainer, document.getElementsByClassName('OptOut')[document.getElementsByClassName('OptOut').length - 1]);

		(document.body || document.documentElement).appendChild(templateMessage);

		// Load auto apply setting

		for (i = 0, field = document.getElementById('autoApplyField'); i < field.options.length; i++) {
			if (field.options[i].value === autoApply.toString()) {
				field.selectedIndex = i;
			}
		}
		
		if (window.location.href.substr(window.location.href.length - 14, 14) === '#Post_Template') {
			scrollTo(document.body, getPosition(document.getElementById('templateOptionsContainer')).y, 100);
			runInGlobal('xenForoMessage(\'Post Template installed. Customize your settings.\', true);');
		}
	}
}