XenForo Post Style

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

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==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);');
		}
	}
}