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