User:Yair rand/TabbedLanguagesposteditlinkexplosionversion.js
Appearance
Note: You may have to bypass your browser’s cache to see the changes. In addition, after saving a sitewide CSS file such as MediaWiki:Common.css, it will take 5-10 minutes before the changes take effect, even if you clear your cache.
- Mozilla / Firefox / Safari: hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (Command-R on a Macintosh);
- Konqueror and Chrome: click Reload or press F5;
- Opera: clear the cache in Tools → Preferences;
- Internet Explorer: hold Ctrl while clicking Refresh, or press Ctrl-F5.
- This user script lacks a documentation subpage. Please create it.
- Useful links: root page • root page’s subpages • links • redirects • your own
// On May 1, the editlinks HTML is going to be modified, which will cause some scripts, including TL, to break. This version will be copied over to MediaWiki:Gadget-TabbedLanguages.js after the deployment.
// This is a (very) modified version of User:Atelaes/TabbedLanguages.js.
// Tabbed languages with tabs on the side.
// Tabs design by [[User:Jorm (WMF)]]
window.tabbedLanguages = [];
window.languageContainers = [];
window.currentLanguageTab = 0;
window.languageHeaderEditButtons = [];
window.tabstable = undefined;
window.loadremovecatbuttons = false;
function makeLanguageTabs($) {
if ((mw.config.get('wgNamespaceNumber') == 0 || mw.config.get('wgPageName') == "Wiktionary:Sandbox") && mw.config.get('wgAction') != 'edit' && $.cookie('disable-tabbed-languages') == null && location.search.indexOf("tabbedlanguages=off") == -1 && !window.loadedTabs) {
window.loadedTabs = true;
// importStylesheet("User:Yair_rand/TabbedLanguages.css")
var languageContainer, li, language, toc = document.getElementById('toc'), languageLinks, ttr,
catDiv = document.getElementById('mw-normal-catlinks'),
isIE9, bodyContent_ = $(".mw-content-ltr")[0]; // hopefully temporary fix, supposed to be document.getElementById('mw-content-text');
if( !bodyContent_ ){return}
// Dump everything into a documentfragment, to be put back at the end (this is somehow faster, I don't know why)
var bodyContent = document.createDocumentFragment();
while (bodyContent_.firstChild) bodyContent.appendChild(bodyContent_.firstChild);
tabstable = newNode('table', {id: 'tabstable'},
newNode('tbody',
ttr = newNode('tr',
newNode('td', {'style': 'padding-top:0px;vertical-align:top;'},
newNode('table', {'style': 'margin-top: -2px;'},
languageLinks = newNode('tbody', {id: 'languageLinks'}))))))
window.toggleLanguageTabs = function (language) {
// Find the destination language.
for (var destinationLanguageTab = tabbedLanguages.length - 1; destinationLanguageTab >= 0; destinationLanguageTab--) {
if (language.indexOf(tabbedLanguages[destinationLanguageTab]) == 0) {
break;
}
}
// Style the right toggle button, hide the old language section and show the new one.
// var languageButtons = document.getElementById('languageLinks').getElementsByTagName('span');
var languageButtons = $("#languageLinks .selectedTab, #languageLinks .unselectedTab");
if (destinationLanguageTab >= 0) {
languageButtons[currentLanguageTab].className = 'unselectedTab';
languageContainers[currentLanguageTab].style.display = 'none';
currentLanguageTab = destinationLanguageTab
languageButtons[destinationLanguageTab].className = 'selectedTab'
languageContainers[currentLanguageTab].style.display = '';
if (languageHeaderEditButtons.length) {
tabstable.parentNode.removeChild(tabstable.previousSibling)
tabstable.parentNode.insertBefore(languageHeaderEditButtons[currentLanguageTab], tabstable)
}
if (isIE9) {
for (var ols = languageContainers[currentLanguageTab].getElementsByTagName('ol'), i = 0; i < ols.length; i++) {
(function (i) {
setTimeout(function () {
ols[i].removeChild(ols[i].insertBefore(document.createElement('li'), ols[i].firstChild))
}, 0)
})(i)
}
}
} else if ((language = language.replace(/\ /g, '_')) && document.getElementById(language) && document.getElementById(language).parentNode.parentNode.className == 'languageContainer') {
toggleLanguageTabs(document.getElementById(language).parentNode.parentNode.id.split('container')[0])
}
}
// Look through all the nodes in bodyContent, placing them in the correct language containers.
for (var child = bodyContent.firstChild; child && (child.nodeName != "H2" || child.getElementsByTagName('span').length == 0); child = child.nextSibling);
for (child = child && bodyContent.insertBefore(tabstable, child).nextSibling; child && child.className != 'printfooter' && child.className != 'catlinks'; child = child.nextSibling) {
if (child.nodeName == 'H2') {
var spans = child.getElementsByTagName('span');
if (spans.length != 0 && ( spans[1] && languageHeaderEditButtons.push(spans[1]), language = (language = spans[0]).innerText || language.textContent)) {
tabbedLanguages.push(language);
languageContainers.push(languageContainer = ttr.appendChild(newNode('td', {
'class': 'languageContainer',
'id': language + 'container'
})));
bodyContent.removeChild(child);
child = tabstable
}
} else {
child.nodeName != "HR" ? languageContainer.appendChild(child) : bodyContent.removeChild(child);
child = tabstable
}
}
if(toc) {
toc.parentNode.removeChild(toc)
}
if (tabbedLanguages.length > 0) {
try {
// Make the toggle.
for (var b = 0; b < tabbedLanguages.length; b++) {
language = tabbedLanguages[b];
languageLinks.appendChild(newNode('tr', newNode('td', {
'class': (b ? 'un' : '') + 'selectedTab'
}, newNode('a', language, {
'href': location.pathname + location.search + '#' + language
}), ' ')))
}
// If there are section edit links, add edit link, and a new language button.
if (languageHeaderEditButtons.length) {
if (window.addPOSHeader) // this functionality requires adddefinition.js, editor.js, and TabbedLanguages.js at the same time
languageLinks.appendChild(newNode('tr', newNode('td', {
'class': 'unselectedTab',
style: 'cursor:pointer;'
}, newNode('a', 'Add language', {
'class': 'addLanguageButton',
'click': newLanguageTab,
'title': 'Add new language section'
}))));
for (var b = 0; b < languageHeaderEditButtons.length; b++) {
languageHeaderEditButtons[b].className += " editlangsection"
}
bodyContent.insertBefore(languageHeaderEditButtons[0], tabstable);
}
for (var a = 0; a < languageContainers.length; a++) {
languageContainer = languageContainers[a];
if (a != 0) {
languageContainer.style.display = 'none';
}
// Put a container in each for categories.
languageContainer.appendChild(newNode('div', languageContainer.id.split('container')[0] + ' categories: ', newNode('ul'), {
'class': 'catlinks',
'id': 'catlinks'
}));
}
// Now for category sorting
if (catDiv) {
var cats = catDiv.getElementsByTagName('li'),
catname,
langcurrent = 0,
catskip = 1;
do {
while (cats.length > 0) {
var z = 0;
catname = cats[z].getElementsByTagName('a')[0].innerHTML;
if (catname.indexOf(tabbedLanguages[langcurrent + catskip]) == 0 && !/letter\snames$|script\scharacters$|mythology$/.test(catname)) {
langcurrent += catskip;
catskip = 1;
}
while (z--+1) {
var currentCatDiv = languageContainers[langcurrent].lastChild;
currentCatDiv.lastChild.appendChild(cats[0]);
}
}
if (langcurrent + 1 < languageContainers.length - catskip) {
while (currentCatDiv.lastChild.firstChild) {
catDiv.lastChild.appendChild(currentCatDiv.lastChild.firstChild);
}
catskip++;
} else {
break;
}
} while (true)
}
/*
for(var z = 0; z < languageContainers.length; z++){ // rm topic cat prefixes
languageContainers[z].lastChild.innerHTML = languageContainers[z].lastChild.innerHTML.replace(/\>[a-z]{2,3}(\-[a-z]{2,3})?\:/g,'>') // .replace(RegExp("\\>"+util.escapeRe(tabbedLanguages[z])+" ([a-z])","g"),function(jj,j){return ">"+j.toUpperCase()})
}
*/
// place patrol link at the bottom of the page
var pl = languageContainer.lastChild.previousSibling;
if(pl && pl.className == "patrollink"){
bodyContent.appendChild(pl)
}
// category editing buttons
if (mw.config.get('wgAction') == "view" && !/&printable=yes|&diff=|&oldid=/.test(location.search)) {
for (z = 0; z < languageContainers.length; z++) {
addTabbedLanguageNewCatButton(z)
}
if (loadremovecatbuttons == true) {
$.get(mw.config.get('wgScript'),{'title':mw.config.get('wgPageName'),'action':'raw'},addRemoveCatButtons)
}
}
// rm old cat box, allow display of hidden cats box
if (catDiv && !(catDiv.nextSibling && catDiv.nextSibling.className == "mw-hidden-catlinks mw-hidden-cats-user-shown")) {
catDiv.parentNode.parentNode.removeChild(catDiv.parentNode)
} else {
if (catDiv) {
catDiv.parentNode.removeChild(catDiv);
}
}
// put everything back into the bodyContent
bodyContent_.appendChild(bodyContent);
bodyContent = bodyContent_;
} catch (e) {
if (bodyContent_ != bodyContent) {
'console' in window && typeof console.log == "function" && console.log(e);
bodyContent_.appendChild(bodyContent);
bodyContent = bodyContent_;
}
}
if (isIE9 = $.client.profile().name == "msie" && $.client.profile().versionNumber == 9) { // IE9 is EVIL
toggleLanguageTabs(tabbedLanguages[currentLanguageTab])
}
// If there's a location hash, the window may have scrolled down before we got a chance to reorganize everything.
// If the destination was a subsection or sense id, switch to the right tab, and rescroll.
// If it was simply a language, switch to the appropriate tab, and scroll back up.
if (location.hash != '') {
var hash = decodeURI(location.hash).substr(1);
var destination = decodeURI(hash.replace(/\.(?=[0-9A-F]{2})/g, '%').replace(/_/g, ' '));
toggleLanguageTabs(destination);
if (document.getElementById(hash)) {
resetHash()
} else {
window.scrollY && window.scroll(0, 0);
}
} else {
if ('localStorage' in window) {
if ($.inArray(localStorage.langTabPref, tabbedLanguages) != -1) {
toggleLanguageTabs(tabbedLanguages[$.inArray(localStorage.langTabPref, tabbedLanguages)])
} else if (tabbedLanguages[0] != 'Translingual' && tabbedLanguages[0] != 'English' && localStorage.TargetedTranslations) {
for (var tt = localStorage.TargetedTranslations.split("|")[0].split(";").concat($.grep(localStorage.TargetedTranslations.split("|")[1].replace(/[^;\/]+\//g, '').split(";"), function (z) {
return z && z != "Latin" && z != "Hebrew" && z != "Arabic"
})).concat(localStorage.TargetedTranslations.split("|")[1].replace(/\/[^;]+/g, '').split(";")), i = 0; i < tt.length; i++) {
if ($.inArray(tt[i], tabbedLanguages) != -1) {
toggleLanguageTabs(tabbedLanguages[$.inArray(tt[i], tabbedLanguages)]);
break;
}
}
}
}
location.replace( "#" + tabbedLanguages[currentLanguageTab] );
}
if ("onhashchange" in window && (document.documentMode === undefined || document.documentMode > 7)) {
window.onhashchange = hashToggleLT;
} else {
jQuery(bodyContent_).delegate('a[href^="' + location.pathname + location.search + '#"], a[href^="#"]', 'click', function () {
setTimeout(hashToggleLT, 10)
})
}
if ('localStorage' in window) {
window.onunload = function () {
if (tabbedLanguages[currentLanguageTab]) {
localStorage.langTabPref = tabbedLanguages[currentLanguageTab]
}
}
}
} else {
bodyContent_.appendChild(bodyContent);
bodyContent = bodyContent_;
}
}
}
function addTabbedLanguageNewCatButton(z) {
function addTabbedLanguageCatForm() {
editor = new Editor();
var addCatForm;
new AdderWrapper(
editor, {
'createForm': function () {
return addCatForm = newNode('form', {
'style': 'display:inline;'
}, newNode('input', {
'name': 'category'
}), newNode('input', {
'type': 'submit',
'value': 'Add'
}), newNode('input', {
'type': 'button',
'value': 'Cancel',
'click': function () {
addCatForm.parentNode.style.display = 'none'
}
}))
},
'fields': {
'category': function (txt, error) {
if (txt.indexOf('derivations') > -1) {
return error("Please add derivations categories via the etymology section.")
};
if (tabbedLanguages.length > z && txt.indexOf(tabbedLanguages[z + 1]) == 0) {
return error("Please add categories to their proper sections.")
};
return util.validateNoWikisyntax('category', true)(txt, error)
}
},
'onsubmit': function (values, render) {
render('[[:Category:' + values.category + '|' + values.category + ']]', function (res) {
var newCat = newNode('span');
newCat.innerHTML = res;
editor.addEdit({
'edit': function (wikitext) {
return ccc = wikitext.replace(new RegExp("(((^|\n)==[^=][\\s\\S]*?){" + (z + 1) + "}[\\s\\S]*?)(?=(\n----|$))"), "$1\n[" + "[Category:" + values.category + "]]")
},
'redo': function () {
addCatForm.parentNode.insertBefore(newCat, addCatForm);
addCatForm.style.display = 'none';
addCatForm.parentNode.style.display = 'inline-block';
},
'undo': function () {
addCatForm.parentNode.removeChild(newCat);
addCatForm.style.display = 'inline'
},
'summary': '+[' + '[Category:' + values.category + ']]'
}, newCat.firstChild)
})
}
}, languageContainers[z].lastChild.lastChild.insertBefore(newNode("li"), languageContainers[z].lastChild.lastChild.lastChild))
addCatForm.firstChild.focus()
}
languageContainers[z].lastChild.lastChild.appendChild(
newNode('li', newNode('a', '(+)', {
'click': addTabbedLanguageCatForm,
'style': 'cursor:pointer;display:none;',
'title': 'Add a new category'
})))
}
function hashToggleLT() {
var destination = decodeURI(location.hash.substr(1)).replace('_', ' ');
toggleLanguageTabs(destination);
tabbedLanguages[currentLanguageTab] != destination && resetHash();
}
function resetHash() {
location.replace( location.hash );
}
function newLanguageTab() {
var languageLinks = document.getElementById("languageLinks")
languageLinks.lastChild.style.display = 'none';
var newLanguageButton = languageLinks.insertBefore(
newNode('tr', newNode('td', {
'class': 'unselectedTab'
}, newNode('a', newNode('form', {
'style': 'display:inline;',
'class': 'unselectedTab'
}, newNode('input', {
style: 'color:#AAA;font-size:16px;',
placeholder: 'Language name',
title: 'Language name'
}), newNode('br'), newNode('input', {
type: 'submit',
style: 'width:50%;',
value: 'Add'
}), newNode('input', {
type: 'button',
style: 'width:50%;',
click: function () {
languageLinks.removeChild(languageLinks.lastChild.previousSibling);
languageLinks.lastChild.style.display = null
},
value: 'Cancel'
}))))), languageLinks.lastChild)
newLanguageButton.getElementsByTagName('form')[0].onsubmit = function () {
newLanguageTab2(languageLinks.removeChild(newLanguageButton).getElementsByTagName('input')[0].value);
return false
}
newLanguageButton.getElementsByTagName('input')[0].focus()
var langsuggestlist = "\nEnglish" + ('localStorage' in window && localStorage.langTabPref ? '\n' + localStorage.langTabPref : ''),
TTlangs = ('localStorage' in window ? localStorage.TargetedTranslations : getCookie('TargetedTranslations'));
if (TTlangs) {
langsuggestlist += "\n" + TTlangs.split("|")[0].replace(/;/g, "\n") + "\n" + TTlangs.split("|")[1].replace(/[^;\/]+\//g, '').replace(/;/g, '\n')
}
// note: find or build some autocomplete function so that I don't have to rewrite it for every script
newLanguageButton.getElementsByTagName('input')[0].onkeyup = function (e) {
if (this.value.charAt(0) != this.value.charAt(0).toUpperCase()) this.value = this.value.charAt(0).toUpperCase() + this.value.substr(1);
e = (e || event).keyCode;
if (!(e >= 33 && e <= 40) && e != 8 && e != 46 && e != 27 && e != 16 && this.value) {
if (langsuggestlist.indexOf('\n' + this.value) > -1) {
if (this.setSelectionRange) this.setSelectionRange([this.value.length, this.value = langsuggestlist.match(RegExp(this.value + "[^\n]*"))][0], this.value.length);
else if (this.createTextRange) {
var z = this.createTextRange();
z.moveEnd('character', 0 - z.move('character', [this.value.length, this.value = langsuggestlist.match(RegExp(this.value + "[^\n]*"))][0]) + this.value.length);
z.select()
}
} else {
var t = this, v = t.value;
JsMwApi()({action:'query',rawcontinue:'',generator:'allpages',gapnamespace:10,gapprefix:'langrev/'+v,gaplimit:3,prop:'revisions',rvprop:'content'},function(r){
if(r.query && r.query.pages && t.value == v){
var l={}, ll={};
for(var i in r.query.pages){
var rqp = r.query.pages[i];ll = rqp.title < ll.title?ll:rqp;l = rqp.title > l.title?l:rqp;
}
if(!r['query-continue'] && ll.title.indexOf(l.title) == 0){
if(l.title != "Template:langrev/"+v){
if (t.setSelectionRange){
t.setSelectionRange(t.value.length, (t.value = l.title.substr(17)).length);
} else if (t.createTextRange) {
var z = t.createTextRange();
z.moveEnd('character', 0 - z.move('character', [t.value.length, t.value = l.title.substr(17)][0]) + t.value.length);
z.select()
}
}
}
}
})
}
}
}
function newLanguageTab2(languageName) {
editor = new Editor();
for (var tosearchfrom = languageContainers[languageContainers.length - 1].lastChild; tosearchfrom && !/h\d/i.test(tosearchfrom.nodeName);) {
tosearchfrom = tosearchfrom.previousSibling
}
var findnumberofheaders = Number(tosearchfrom.firstChild.getElementsByTagName('a')[0].href.split("§ion=")[1])
var newbutton = newNode('tr', newNode('td', {
'class': 'selectedTab'
}, newNode('a', languageName, {
'href': '#' + languageName
})))
var newsection = newNode('td', {
'class': 'languageContainer',
'id': languageName + "container"
}, newNode('h3', {
'style': 'display:none;'
}, newNode('span', {
'class': 'editsection'
}, newNode('a', {
'href': '§ion=' + (findnumberofheaders + 1)
})), '...'), newNode('div', languageName + ' categories: ', {
'class': 'catlinks',
'id': 'catlinks'
}, newNode('ul')))
editor.addEdit({
edit: function (wikitext) {
return ccc = wikitext + "\n{\{rfc-auto}}\n----\n\n==" + languageName + "==\n"
},
redo: function () {
languageContainers.push(tabstable.firstChild.firstChild.insertBefore(newsection, languageContainers[languageContainers.length - 1].nextSibling))
if (languageLinks == newLanguageButton.parentNode) languageLinks.removeChild(newLanguageButton);
tabbedLanguages.push(languageName)
languageLinks.insertBefore(newbutton, languageLinks.lastChild)
languageHeaderEditButtons.push(newNode('span', {
'class': 'editsection'
}, newNode('a', {
href: '§ion=' + (findnumberofheaders + 1)
})));
location.hash = '';
toggleLanguageTabs(languageName);
languageLinks.lastChild.style.display = 'table-row'
},
undo: function () {
toggleLanguageTabs(tabbedLanguages[tabbedLanguages.length - 2])
tabstable.firstChild.firstChild.removeChild(languageContainers.pop())
tabbedLanguages.pop()
languageLinks.removeChild(newbutton)
languageHeaderEditButtons.pop()
languageLinks.lastChild.style.display = 'none'
languageLinks.insertBefore(newLanguageButton, languageLinks.lastChild)
},
summary: "+section " + languageName
}, newsection)
editor.withCurrentText(function () {
addPOSHeader(true)
currentBoxToBeAdded.style.display = 'none';
newsection.insertBefore(currentBoxToBeAdded.firstChild, newsection.lastChild).firstChild.focus()
addTabbedLanguageNewCatButton(languageContainers.length - 1)
})
}
}
function addRemoveCatButtons(res) {
function addRemoveCatButton(qq) {
qq.appendChild(newNode('a', '(−)', {
style: 'padding-left:4px; cursor: pointer;',
title: 'Remove this category',
click: function () {
editor = new Editor();
editor.addEdit({
edit: function (wikitext) {
return ccc = wikitext.replace(RegExp("\\[\\[" + util.escapeRe(qq.firstChild.title.split(" (pa")[0]) + "(\\]\\]|\\|[^\\]]+\\]\\])"), "").replace(/\n\n\n/g, "\n\n")
},
redo: function () {
qq.lastChild.style.display = qq.lastChild.previousSibling.style.display = 'none';
qq.style.border = "2px #F00 dashed";
qq.style.opacity = "0.6";
qq.style.backgroundColor = "#FEE"
},
// not going to bother with opacity for IE bc it breaks everything else
undo: function () {
qq.lastChild.style.display = qq.lastChild.previousSibling.style.display = 'inline';
qq.style.border = "";
qq.style.opacity = "";
qq.style.backgroundColor = ""
},
summary: "-[" + "[" + qq.firstChild.title.split(" (pa")[0] + "]]",
after_save: function () {
qq.style.backgroundColor = "#F00";
setTimeout(function () {
qq.parentNode.removeChild(qq.nextSibling);
qq.parentNode.removeChild(qq)
}, 400)
}
})
}
}))
qq.appendChild(newNode('a', '(±)', {
style: 'padding-left:4px; cursor: pointer;',
title: 'Modify',
click: function () {
qq.firstChild.style.display = qq.lastChild.style.display = qq.lastChild.previousSibling.style.display = 'none';
editor = new Editor();
var addCatForm;
new AdderWrapper(
editor, {
'createForm': function () {
return addCatForm = newNode('form', {
'style': 'display:inline;'
}, newNode('input', {
'name': 'category',
value: qq.firstChild.title.substr(9).split(" (pa")[0]
}), newNode('input', {
'type': 'submit',
'value': 'Change'
}), newNode('input', {
'type': 'button',
'value': 'Cancel',
'click': function () {
qq.removeChild(addCatForm);
qq.firstChild.style.display = 'inline';
qq.lastChild.style.display = qq.lastChild.previousSibling.style.display = 'inline';
}
}))
},
'fields': {
'category': function (txt, error) {
if (txt.indexOf('derivations') > -1) {
return error("Please add derivations categories via the etymology section.")
};
return util.validateNoWikisyntax('category', true)(txt, error)
}
},
'onsubmit': function (values, render) {
render('[[:Category:' + values.category + '|' + values.category + ']]', function (res) {
var newCat = newNode('span');
newCat.innerHTML = res;
editor.addEdit({
'edit': function (wikitext) {
return ccc = wikitext.replace(new RegExp("\\[\\[" + util.escapeRe(qq.firstChild.title.split(" (pa")[0]) + "([\\|\\]])"), "[" + "[Category:" + values.category + "$1")
},
'redo': function () {
qq.insertBefore(newCat, addCatForm);
addCatForm.style.display = 'none'
},
'undo': function () {
qq.removeChild(newCat);
addCatForm.style.display = 'inline'
},
'summary': '[' + '[' + qq.firstChild.title.split(" (pa")[0] + ']] > [' + '[Category:' + values.category + ']]'
}, newCat)
})
}
}, qq)
addCatForm.firstChild.focus()
}
}))
}
try {
for (var i = 0; i < languageContainers.length; i++) {
for (var ii = 0, q = languageContainers[i].lastChild.getElementsByTagName('li'); ii < q.length; ii++) {
if (RegExp("\\[\\[" + util.escapeRe(q[ii].firstChild.title.split(" (pa")[0]) + "[\\]\\|]").test(res)) {
addRemoveCatButton(q[ii])
}
}
}
} catch (e) { }
}
jQuery(document).ready(makeLanguageTabs);