🏠 Home 

MyAnimeList BBCode Toolbar

Advanced BBCode Editor for MyAnimeList.net


Install this script?
Author's suggested script

You may also like Ultra Popup Blocker.


Install this script
// ==UserScript==
// @name         MyAnimeList BBCode Toolbar
// @namespace    https://github.com/eskander
// @version      2022-05-26
// @description  Advanced BBCode Editor for MyAnimeList.net
// @author       eskander
// @license      MIT
// @include      https://myanimelist.net/forum/?topicid=*
// @include      https://myanimelist.net/forum/?action=message&msgid=*
// @include      https://myanimelist.net/forum/?action=message&topic_id=*
// @include      https://myanimelist.net/forum/?action=post*
// @include      https://myanimelist.net/forum/index.php?action=post&boardid=*
// @include      https://myanimelist.net/forum/index.php?topicid=*
// @include      https://myanimelist.net/panel.php?go=editmanga&id=*
// @include      https://myanimelist.net/panel.php?go=add&selected_series_id=*
// @include      https://myanimelist.net/panel.php?go=addmanga&selected_manga_id=*
// @include      https://myanimelist.net/panel.php?go=anime_series&do=add
// @include      https://myanimelist.net/panel.php?go=mangadb&do=add
// @include      https://myanimelist.net/mymessages.php?go=send*
// @include      https://myanimelist.net/mymessages.php?toname=*
// @include      https://myanimelist.net/people.php?id=*
// @include      https://myanimelist.net/people/*
// @include      https://myanimelist.net/ownlist/anime/*
// @include      https://myanimelist.net/ownlist/manga/*
// @include      https://myanimelist.net/editprofile.php*
// @include      https://myanimelist.net/myblog.php*
// @include      https://myanimelist.net/clubs.php?cid=*
// @include      https://myanimelist.net/editclub.php?cid=*
// @include      https://myanimelist.net/profile/*
// @include      https://myanimelist.net/modules.php?go=report&type=forummessage&id=*
// @include      https://myanimelist.net/comtocom.php?id1=*
// @include      https://myanimelist.net/comments.php?id=*
// @include      https://myanimelist.net/editlist.php?type=anime&id=*
// @include      https://myanimelist.net/myfriends.php?go=add&id=*
// @include      https://myanimelist.net/blog.php?eid=*
// @exclude      https://myanimelist.net/editprofile.php?go=stylepref&do=cssadv&id=*
// @exclude      https://myanimelist.net/profile/*/*
// @icon         https://cdn.myanimelist.net/images/favicon.ico
// @homepage     https://github.com/Eskander/myanimelist-bbcode-toolbar
// @supportURL   https://github.com/Eskander/myanimelist-bbcode-toolbar/issues/new
// @compatible   firefox
// @compatible   chrome
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// ==/UserScript==
/**************************************************************************************************/
/* Disclaimer: MyAnimeList BBCode Toolbar is based on "BBCodes for MAL" script by Al_eXs          */
/*             and its continuation by Serhiyko.                                                  */
/*             Original source code: https://userscripts-mirror.org/scripts/show/142850 (mirror)  */
/*             Continuation:         https://greasyfork.org/en/scripts/5709-bbcodes-for-mal       */
/**************************************************************************************************/
// ----------------- Toolbar styling ----------------- //
(function () {
// import font-awesome
var link = document.createElement('link');
link.setAttribute('rel', 'stylesheet');
link.setAttribute('type', 'text/css');
link.setAttribute('href', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta2/css/fontawesome.min.css');
document.head.appendChild(link);
// Greasemonkey compatibility
if (typeof GM_addStyle == "undefined") {
function GM_addStyle(css) {
var node = document.createElement("style");
node.type = "text/css";
node.appendChild(document.createTextNode(css));
var heads = document.getElementsByTagName("head");
if (heads.length > 0) {
heads[0].appendChild(node);
} else {
// no head yet, stick it whereever
document.documentElement.appendChild(node);
}
}
}
// toolbar style
GM_addStyle(`
#myBBcode {
margin: 10px 0px 5px 0px;
display: block;
background-color: #2e51a2;
width: 100%;
}
.bbcbtn {
background-color: #2e51a2;
border: none;
color: #fff;
width: 40px;
height: 40px;
font-family: 'FontAwesome';
text-align: center;
display: inline-block;
cursor: pointer;
outline:none;
}
.bbcbtn:hover {
background-color: #e1e7f5;
color: #000
}
.hidearrow {
/* for Firefox */
-moz-appearance: none;
/* for Chrome */
-webkit-appearance: none;
}
.divider {
display: inline;
border-left: 2px solid white;
}
.cpanel {
display: none;
color: #fff;
text-align: center;
padding: 10px;
}
.cfgbtn {
float: right;
}
`);
// forum quick reply overrides
if (window.location.href.includes('myanimelist.net/forum/?topicid')) {
GM_addStyle(`
#myBBcode {
background-color: #4f74c8 !important;
min-width: 750px !important;
}
.bbcbtn {
background-color: #4f74c8 !important;
}
.bbcbtn:hover {
background-color: #e1e7f5 !important;
}
`);
}
})();
var Interactive = GM_getValue('Interactive', true);
function interractivePopup(desc) {
if (Interactive) {
return prompt(desc, "");
}
else {
return "";
}
}
// ----------------- Functionality of each button ----------------- //
function addtag(snap, tag) {
var textareaNumber = getXpathSnapNumber(snap);
obj = document.getElementsByTagName("textarea")[textareaNumber];
beforeText = obj.value.substring(0, obj.selectionStart);
selectedText = obj.value.substring(obj.selectionStart, obj.selectionEnd);
afterText = obj.value.substring(obj.selectionEnd, obj.value.length);
newText = null;
switch (tag) {
case "bold":
tagOpen = "[b]";
tagClose = "[/b]";
newText = beforeText + tagOpen + selectedText + tagClose + afterText;
break;
case "strike":
tagOpen = "[s]";
tagClose = "[/s]";
newText = beforeText + tagOpen + selectedText + tagClose + afterText;
break;
case "italic":
tagOpen = "[i]";
tagClose = "[/i]";
newText = beforeText + tagOpen + selectedText + tagClose + afterText;
break;
case "underline":
tagOpen = "[u]";
tagClose = "[/u]";
newText = beforeText + tagOpen + selectedText + tagClose + afterText;
break;
case "code":
tagOpen = "[code]";
tagClose = "[/code]";
newText = beforeText + tagOpen + selectedText + tagClose + afterText;
break;
case "centre":
tagOpen = "[center]";
tagClose = "[/center]";
newText = beforeText + tagOpen + selectedText + tagClose + afterText;
break;
case "right":
tagOpen = "[right]";
tagClose = "[/right]";
newText = beforeText + tagOpen + selectedText + tagClose + afterText;
break;
case "spoiler":
spoiler = interractivePopup("Enter spoiler name (or leave blank)");
if (spoiler) {
spoiler = '="' + spoiler + '"';
}
tagOpen = "[spoiler" + spoiler + "]";
tagClose = "[/spoiler]";
newText = beforeText + tagOpen + selectedText + tagClose + afterText;
break;
case "url":
urlOrDesc = interractivePopup("Enter URL or URL description");
if (!urlOrDesc) {
urlOrDesc = selectedText;
selectedText = '';
}
if (urlOrDesc.indexOf("http://") == 0 || urlOrDesc.indexOf("https://") == 0) {
tagOpen = "[url=" + urlOrDesc + "]";
tagClose = "[/url]";
} else {
tagOpen = "[url=";
tagClose = "]" + urlOrDesc + "[/url]";
}
newText = beforeText + tagOpen + selectedText + tagClose + afterText;
break;
case "image":
imgValue = document.getElementById("Image").value;
imgURL = interractivePopup("Enter image URL");
if (imgURL) {
selectedText = imgURL;
}
tagOpen = "[img" + String(imgValue) + "]";
tagClose = "[/img]";
newText = beforeText + tagOpen + selectedText + tagClose + afterText;
break;
case "size":
txtSize = document.getElementById("Size");
if (txtSize.value == "enter") {
txtSizeName = interractivePopup("Enter the size (1 is minimum, 100 is default)");
} else {
txtSizeName = txtSize.value;
}
tagOpen = "[size=" + String(txtSizeName) + "]";
tagClose = "[/size]";
newText = beforeText + tagOpen + selectedText + tagClose + afterText;
break;
case "youtube":
yt = interractivePopup("Enter complete Youtube url");
if (yt.includes("youtube.com")) {
yt = yt.replace("https://", "http://");
yt = yt.replace("http://www.youtube.com/watch?v=", "http://youtube.com/watch?v=");
yt = yt.replace("http://youtube.com/watch?v=", "");
yt = yt.substring(0, 11);
}
tagOpen = "[yt]";
tagClose = "[/yt]";
newText = beforeText + tagOpen + yt + tagClose + afterText;
break;
case "colour":
colour = document.getElementById("Colour");
if (colour.value == "enter") {
colourName = interractivePopup("Enter the colour name or hex value (e.g. #abc123)");
} else {
colourName = colour.value;
}
tagOpen = "[color=" + String(colourName) + "]";
tagClose = "[/color]";
newText = beforeText + tagOpen + selectedText + tagClose + afterText;
break;
case "quote":
quote = interractivePopup("Enter quoted person name");
if (quote) {
quote = "=" + quote;
}
tagOpen = "[quote" + quote + "]";
tagClose = "[/quote]";
newText = beforeText + tagOpen + selectedText + tagClose + afterText;
break;
case "list":
tagOpen = "[list][*]";
tagClose = "[/list]";
newText = beforeText + tagOpen + selectedText + tagClose + afterText;
break;
case "list=1":
tagOpen = "[list=1][*]";
tagClose = "[/list]";
newText = beforeText + tagOpen + selectedText + tagClose + afterText;
break;
case "[*]":
tagOpen = "[*]";
tagClose = "";
newText = beforeText + tagOpen + selectedText + afterText;
break;
}
if (newText != null) {
caretStart = obj.selectionStart;
caretEnd = obj.selectionEnd;
if (selectedText.length == 0) {
caretEnd -= tagClose.length;
}
caretEnd += newText.length - obj.value.length;
caretStart = caretEnd;
obj.value = newText;
obj.setSelectionRange(caretStart, caretEnd);
}
obj.focus();
}
// ----------------- Some preliminary magic ----------------- //
function xpath(query, object) {
if (!object) var object = document;
return document.evaluate(query, object, null, XPathR###lt.UNORDERED_NODE_SNAPSHOT_TYPE, null);
}
function getXpathSnap() {
var path = xpath("//textarea[@class='textarea']");
if (path.snapshotLength == 0 || path.snapshotItem(0).id == "add_anime_tags" || path.snapshotItem(0).id == "add_manga_tags") {
path = xpath("//textarea[@class='inputtext']");
if (path.snapshotItem(j).previousElementSibling == null) {
return (path.snapshotLength > 0) ? path.snapshotItem(0) : false;
}
}
for (var j = 0; j < path.snapshotLength; j++) {
if (path.snapshotItem(j).previousElementSibling == null) {
if (path.snapshotItem(j).id != "add_anime_tags" && path.snapshotItem(j).id != "add_manga_tags") {
return path.snapshotItem(j);
}
} else {
if (path.snapshotItem(j).previousElementSibling.id != 'myBBcode' && (path.snapshotItem(j).previousElementSibling.tagName != 'GRAMMARLY-GHOST' && (path.snapshotItem(j).previousElementSibling.previousElementSibling === null || path.snapshotItem(j).previousElementSibling.previousElementSibling.id != 'myBBcode'))) {
return path.snapshotItem(j);
}
}
}
return false;
}
function getXpathSnapNumber(xpathToBeNumbered) {
var path = xpath("//textarea");
for (var i = 0; i < path.snapshotLength; i++) {
if (path.snapshotItem(i) === xpathToBeNumbered) {
return i;
}
}
}
setTimeout(function () {
var allReplies = xpath("//a[@title='Reply to this comment'] | //a[contains(@class, 'ml8')][text() = 'Reply'] | //input[@id='postReply'][@value='Submit']");
for (var i = 0; i < allReplies.snapshotLength; i++) {
(function (ind) {
allReplies.snapshotItem(ind).addEventListener("click", function () {
var repeatCount0 = 0;
var replyTimer0 = setInterval(function () {
var replied;
var replyButton = xpath("//input[@value='Submit Reply'] | //a[@class='quickEdit'][text() = 'Quick Edit']");
if (replyButton.snapshotLength > 0) {
xpathSnap = getXpathSnap();
createButtons();
replyButton.snapshotItem(0).addEventListener("click", function () {
replied = replyTimer();
}, false);  //Modern browsers
}
function replyTimer() {
var repeatCount = 0;
return setInterval(function () {
addCodeToEdits();
repeatCount += 1;
if (repeatCount >= 18) {
clearInterval(replied);
}
}, 500);
}
repeatCount0 += 1;
if (repeatCount0 >= 72) {
clearInterval(replyTimer0);
}
}, 100);
}, true);
})(i);
}
addCodeToEdits();
}, 100);
function addCodeToEdits() {
var allEdits = xpath("//a[@title='Edit Comment']");
var toEdit;
for (var i = 0; i < allEdits.snapshotLength; i++) {
(function (ind) {
allEdits.snapshotItem(ind).removeEventListener("click", function () {
toEdit = editTimer();
});
allEdits.snapshotItem(ind).addEventListener("click", function () {
toEdit = editTimer();
}, true);
})(i);
}
function editTimer() {
var repeatCount = 0;
return setInterval(function () {
xpathSnap = getXpathSnap();
createButtons();
repeatCount += 1;
if (repeatCount >= 12) {
clearInterval(toEdit);
}
}, 400);
}
}
while (xpathSnap = getXpathSnap()) {
createButtons();
}
// ----------------- Generate toolbar ----------------- //
function createButtons() {
if (xpathSnap) {
var xpathSnapCur = xpathSnap;
var div1 = document.createElement("div");
div1.align = "Left";
div1.id = "myBBcode";
div1.innerHTML = " ";
xpathSnap.parentNode.insertBefore(div1, xpathSnap);
// set button content
function setButton(name, glyph) {
var post = document.createElement("button");
post.type = "button";
post.innerHTML = glyph;
post.title = name[0].toUpperCase() + name.slice(1);
post.setAttribute('class', 'fa bbcbtn');
post.addEventListener('click', function () {
addtag(xpathSnapCur, name);
}, false);
div1.appendChild(post);
}
function setSize(size) {
var opt = document.createElement("option");
opt.value = size;
opt.appendChild(document.createTextNode(size + "%"));
postSize.appendChild(opt);
}
function setColor(color) {
var opt = document.createElement("option");
opt.value = color;
opt.appendChild(document.createTextNode(color));
postColour.appendChild(opt);
}
function setImage(imgText, imgVal) {
var opt = document.createElement("option");
opt.value = imgVal;
opt.appendChild(document.createTextNode(imgText));
postImage.appendChild(opt);
}
function setDivider() {
var opt = document.createElement("div");
opt.setAttribute('class', 'divider');
div1.appendChild(opt);
}
// ----------------- Toolbar layout ----------------- //
setButton("bold", "&#xf032;");
setButton("italic", "&#xf033;");
setButton("strike", "&#xf0cc;");
setButton("underline", "&#xf0cd;");
// Size selector
var postSize = document.createElement("select");
postSize.id = "Size";
postSize.title = "Size";
postSize.setAttribute('class', 'fa bbcbtn hidearrow');
// First size selector
var opt = document.createElement("option");
opt.value = "";
opt.setAttribute('selected', '');
opt.setAttribute('disabled', '');
opt.setAttribute('hidden', '');
opt.appendChild(document.createTextNode(""));
postSize.appendChild(opt);
// Predefined sizes
setSize("20");
setSize("50");
setSize("100");
setSize("300");
setSize("600");
// Custom size
var opt = document.createElement("option");
opt.value = "enter";
opt.appendChild(document.createTextNode('Enter size'));
postSize.appendChild(opt);
postSize.addEventListener('change', function () {
document.getElementById("Size").value = postSize.value;
addtag(xpathSnapCur, 'size');
postSize.value = 'Size';
this.selectedIndex = 0;
}, false);
div1.appendChild(postSize);
// Color selector
var postColour = document.createElement("select");
postColour.id = "Colour";
postColour.title = "Color";
postColour.setAttribute('class', 'fa bbcbtn hidearrow');
// First color selector
var opt = document.createElement("option");
opt.value = "";
opt.setAttribute('selected', '');
opt.setAttribute('disabled', '');
opt.setAttribute('hidden', '');
opt.appendChild(document.createTextNode(""));
postColour.appendChild(opt);
// Predefined colors
setColor("Grey");
setColor("Blue");
setColor("Red");
setColor("Green");
setColor("Yellow");
setColor("Pink");
setColor("Navy");
setColor("White");
setColor("Black");
setColor("Orange");
setColor("Purple");
// Custom color
var opt = document.createElement("option");
opt.value = "enter";
opt.appendChild(document.createTextNode('Enter colour'));
postColour.appendChild(opt);
postColour.addEventListener('change', function () {
document.getElementById("Colour").value = postColour.value;
addtag(xpathSnapCur, 'colour');
postColour.value = 'Select';
this.selectedIndex = 0;
}, false);
div1.appendChild(postColour);
setDivider();
setButton("centre", "&#xf037;");
setButton("right", "&#xf038;");
setDivider();
setButton("url", "&#xf0c1;");
setButton("spoiler", "&#xf06a;");
// Image selector
var postImage = document.createElement("select");
postImage.id = "Image";
postImage.title = "Image";
postImage.setAttribute('class', 'fa bbcbtn hidearrow');
// First image selector
var opt = document.createElement("option");
opt.value = "";
opt.setAttribute('selected', '');
opt.setAttribute('disabled', '');
opt.setAttribute('hidden', '');
opt.appendChild(document.createTextNode(""));
postImage.appendChild(opt);
// Predefined image alignments
setImage("Image", "");
setImage("Image right", " align=right");
setImage("Image left", " align=left");
postImage.addEventListener('change', function () {
document.getElementById("Image").value = postImage.value;
addtag(xpathSnapCur, 'image');
postImage.value = 'Select';
this.selectedIndex = 0;
}, false);
div1.appendChild(postImage);
setButton("youtube", "&#xf03d;");
setButton("code", "&#xf121;");
setButton("quote", "&#xf10d;");
setDivider();
setButton("list", "&#xf0ca;");
setButton("list=1", "&#xf0cb;");
setButton("[*]", "&#xf0fe;");
// ----------------- Settings panel ----------------- //
// Create settings button
var post = document.createElement("button");
post.type = "button";
post.innerHTML = "&#xf013;";
post.title = "Show/Hide settings";
post.setAttribute('class', 'fa bbcbtn cfgbtn');
post.addEventListener('click', function () {
if (document.getElementById("cpanel").style.display == "block") {
for (i=0; i < document.getElementsByClassName("cpanel").length; i++){
document.getElementsByClassName("cpanel")[i].style.display = "none"
}
} else {
for (i=0; i < document.getElementsByClassName("cpanel").length; i++){
document.getElementsByClassName("cpanel")[i].style.display = "block"
}
}
}, false);
div1.appendChild(post);
// Popups checkbox logic
var InteractiveBoxState = '';
if (Interactive) {
InteractiveBoxState = 'checked';
} else {
InteractiveBoxState = '';
}
var opt = document.createElement("div");
opt.setAttribute('class', 'cpanel');
opt.id = "cpanel";
opt.innerHTML = '<input type="checkbox" id="IntBox" ' + InteractiveBoxState + '><label for="IntBox" title="Show dedicated pop-ups for certain buttons.">Enable pop-ups.</label>';
div1.appendChild(opt);
// Remember setting across sessions
document.getElementById("IntBox").addEventListener('change', function () {
if (document.getElementById("IntBox").checked == true) {
Interactive = true;
GM_setValue('Interactive', true);
} else {
Interactive = false;
GM_setValue('Interactive', false);
}
}, false);
}
}