User:Yair rand/TabbedLanguages.js

Note – after saving, you may have to bypass your browser’s cache to see the changes.

  • 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 is a modified version of User:Atelaes/TabbedLanguages.js
var tabbedLanguages = [];
var languageContainers=[];
var currentLanguageTab = 0;
var languageHeaderEditButtons=[];
var tocLangSections;
var languageLinks;
var loadremovecatbuttons = true;

function makeLanguageTabs()
{
  if ((mw.config.get('wgNamespaceNumber') == 0 || mw.config.get('wgPageName') == "Wiktionary:Sandbox") && mw.config.get('wgAction') != 'edit') {
    importStylesheet("User:Yair_rand/TabbedLanguages.css")
    var languageContainer, li, language, toc = document.getElementById('toc'), catDiv = document.getElementById('catlinks'), isIE9;
    var bodyContent_ = document.getElementById('wikiPreview') || mw.util.$content[0];
    // 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);
    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, rm the old toc and add the new one.
      var languageButtons = document.getElementById('languageLinks').getElementsByTagName('span');
      if(destinationLanguageTab >= 0){
        languageButtons[currentLanguageTab].className = 'unselectedTab';
        languageContainers[currentLanguageTab].style.display = 'none';
        if (document.getElementById('toc')) {
          tocLangSections[currentLanguageTab].style.display='none';
          tocLangSections[destinationLanguageTab].style.display='list-item';
          }
        currentLanguageTab = destinationLanguageTab
        languageButtons[destinationLanguageTab].className = 'selectedTab'
        languageContainers[currentLanguageTab].style.display = 'block'
        if(languageHeaderEditButtons.length){
          languageLinks.removeChild(languageLinks.lastChild)
          languageLinks.appendChild(languageHeaderEditButtons[currentLanguageTab])
          }
        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.className != 'printfooter' && child.className != 'catlinks'; child = child.nextSibling) {
      if (child.nodeName == 'H2') {
        var spans = child.getElementsByTagName('span'); 
        if (spans.length != 0 && (language = (language = spans[1] && spans[1].className == 'mw-headline' && languageHeaderEditButtons.push(spans[0]) && spans[1]||spans[0]).innerText || language.textContent)) {
          tabbedLanguages.push(language);
          languageContainers.push(languageContainer = bodyContent.insertBefore(newNode('div', {'class' : 'languageContainer', 'id' : language + 'container'}), child));
          bodyContent.removeChild(child);
          child = languageContainer
          }
        }
      else{
        child.nodeName != "HR"?languageContainer.appendChild(child):bodyContent.removeChild(child);
        child = languageContainer
        }
      }
    if (toc) {
      VisibilityToggles.register('TOC',
        function () {
          toc.style.display='table';
          },
        function () {
          toc.style.display='none';
          });
      }
    if(tabbedLanguages.length > 0) {
      if (toc) {  
        if(toc.getElementsByClassName){
          tocLangSections = toc.getElementsByClassName('toclevel-1');
          for (var c = 1; c < tocLangSections.length; c++) {
            tocLangSections[c].style.display='none'
            }
          }
        else{
          tocLangSections = []
          for (var c = 0, temptocbox = toc.getElementsByTagName('ul')[0]; c < temptocbox.childNodes.length; c++) {
            if(temptocbox.childNodes[c].nodeName == "LI") {
              if(tocLangSections.length)
                temptocbox.childNodes[c].style.display='none';
              tocLangSections.push(temptocbox.childNodes[c])
              }
            }
          }
        }
      // Make the toggle.
      languageLinks = newNode('div', {'id' : 'languageLinks'});
      for (var b = 0; b < tabbedLanguages.length; b++) {
        language = tabbedLanguages[b];
        languageLinks.appendChild(newNode('span', {'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 && window.setUpBoxToBeAdded) // this functionality requires adddefinition.js, editor.js, and TabbedLanguages.js at the same time
          languageLinks.appendChild(newNode('span', {'class': 'unselectedTab'}, newNode('a', '+', {'click':newLanguageTab,'title':'Add new language section'})));
        languageLinks.appendChild(languageHeaderEditButtons[0]);
        }
      
      for (var a = 0; a < languageContainers.length; a++) {
        languageContainer = languageContainers[a];
        if (a == 0) {
          bodyContent.insertBefore(languageLinks, toc && toc.parentNode == bodyContent && toc || languageContainer);
          }
        else {
          languageContainer.style.display = 'none';
          }
        // Put a container in each for categories.  
        languageContainer.appendChild(newNode('div', languageContainer.id.split('container')[0] + ' categories: ', {'class' : 'catlinks'}));
        }
      // Now for category sorting
      if(catDiv.firstChild) {
        var cats = catDiv.firstChild.getElementsByTagName('span'), catname;
        var langcurrent=0;
        while (cats.length > 0) {
          var z=0;
          catname = cats[z].getElementsByTagName('a')[0].innerHTML;
          while(catname.indexOf("derivations") > 0 && cats.length > z+1) {
            catname = cats[++z].getElementsByTagName('a')[0].innerHTML;
            }
          if(catname.indexOf(tabbedLanguages[langcurrent+1])==0&&!/letter\snames$|script\scharacters$|mythology$/.test(catname))
            {langcurrent+=1}
          while(z--+1){
            var currentCatDiv = languageContainers[langcurrent].lastChild
            if (currentCatDiv.lastChild.nodeName == 'SPAN') {
              currentCatDiv.appendChild(document.createTextNode(' | '))
              }
            currentCatDiv.appendChild(cats[0])
            }
          }
        }
      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){
          window.Editor && (new Editor()).withCurrentText(addRemoveCatButtons)
          }
        }
      catDiv.parentNode.removeChild(catDiv);
      bodyContent_.appendChild(bodyContent);bodyContent=bodyContent_;
      if(isIE9 = $.client.profile().name == "msie" && $.client.profile().versionNumber == 9){ // IE9 is EVIL
        tocLangSections = toc.getElementsByClassName('toclevel-1');
        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 = hash.replace(/_/g, ' ');
        toggleLanguageTabs(destination);
        if (document.getElementById(hash)) {
          resetHash()
          }
        else{
          window.scrollY && window.scroll(0,0);
          }
        }
      else{
        if(tabbedLanguages[0]!='Translingual' && tabbedLanguages[0]!='English' && 'localStorage' in window){
          if($.inArray(localStorage.langTabPref, tabbedLanguages) != -1){
            toggleLanguageTabs(tabbedLanguages[$.inArray(localStorage.langTabPref, tabbedLanguages)])
            }
          else if(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;
                }
              }
            }
          }
        }
      if("onhashchange" in window){
        window.onhashchange = hashToggleLT;
        }
      else{
        jQuery('#bodyContent a[href^='+location.pathname+location.search+'#]').click(function(){setTimeout(hashToggleLT,10)})
        }
      if('localStorage' in window){
        window.onunload=function(){
          if(tabbedLanguages[currentLanguageTab] && tabbedLanguages[currentLanguageTab] != 'English' && tabbedLanguages[currentLanguageTab] != 'Translingual'){
            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.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'},
              'undo':function(){addCatForm.parentNode.removeChild(newCat);addCatForm.style.display='inline'},
              'summary':'+['+'[Category:' + values.category + ']]'
              }, newCat.firstChild)
            })
          }
      },
      languageContainers[z].lastChild,
      languageContainers[z].lastChild.lastChild
      )
    addCatForm.firstChild.focus()
    }
  if(languageContainers[z].lastChild.lastChild.nodeName=="SPAN")
    languageContainers[z].lastChild.appendChild(document.createTextNode(' | '));
  languageContainers[z].lastChild.appendChild(
    newNode('a','(+)',{'click':addTabbedLanguageCatForm,'style':'cursor:pointer;','title':'Add a new category'}))
  }

function hashToggleLT () 
{
  var destination = decodeURI(location.hash.substr(1)).replace('_', ' ');
  toggleLanguageTabs(destination);
  }

function resetHash()
{
  location.hash = [location.hash, location.hash = ''][0]
}

function newLanguageTab()
{
  languageLinks.lastChild.previousSibling.style.display='none'
  var newLanguageButton = languageLinks.insertBefore(newNode('form',{'style':'display:inline;','class':'unselectedTab'},newNode('input',{style:'color:#AAA;font-size:150%;'}),newNode('input',{type:'submit',value:'Add'}),newNode('input',{type:'button',click:function(){languageLinks.removeChild(newLanguageButton);languageLinks.lastChild.previousSibling.style.display='inline'},value:'Cancel'})),languageLinks.lastChild.previousSibling)
  newLanguageButton.onsubmit = function(){
    newLanguageTab2(languageLinks.removeChild(newLanguageButton).firstChild.value);
    return false}
  newLanguageButton.firstChild.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.firstChild.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()
          }
        }
      }
    }
  
  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("&section=")[1])
    var newbutton = newNode('span',newNode('a', languageName, {'href': '#' + languageName, 'click':function(){if(!"onhashchange" in window){setTimeout(hashToggleLT,10)}}}))
    var newsection = newNode('div', {'class':'languageContainer', 'id':languageName+"container"},newNode('h3',{'style':'display:none;'},newNode('span',{'class':'editsection'},newNode('a',{'href':'&section='+(findnumberofheaders+1)})),'...'),newNode('div', languageName+' categories: ', {'class':'catlinks'}))
    var toc=document.getElementById('toc')?document.getElementById('toc').getElementsByTagName('ul')[0]:null
    editor.addEdit({
      edit:function(wikitext){return ccc=wikitext+"\n{\{rfc-auto}}\n----\n\n=="+languageName+"==\n"},
      redo:function(){
        languageContainers.push(bodyContent.insertBefore(newsection, languageContainers[languageContainers.length-1].nextSibling))
        if(languageLinks==newLanguageButton.parentNode)languageLinks.removeChild(newLanguageButton);
        tabbedLanguages.push(languageName);
        languageLinks.insertBefore(newbutton, languageLinks.lastChild.previousSibling)
        languageHeaderEditButtons.push(newNode('span',{'class':'editsection'},newNode('a',{href:'&section='+(findnumberofheaders+1)})))
        toc && toc.appendChild(newNode('li',{'class':'toclevel-1'}));
        if(toc && tocLangSections.push)tocLangSections.push(toc.lastChild);
        location.hash = '';
        toggleLanguageTabs(languageName);
        languageLinks.lastChild.previousSibling.style.display='inline'
        },
      undo:function(){
        toggleLanguageTabs(tabbedLanguages[tabbedLanguages.length-2])
        bodyContent.removeChild(languageContainers.pop())
        tabbedLanguages.pop()
        languageLinks.removeChild(newbutton)
        languageHeaderEditButtons.pop()
        if(toc && tocLangSections.push)tocLangSections.pop();
        toc && toc.removeChild(toc.lastChild);
        languageLinks.lastChild.previousSibling.style.display='none'
        languageLinks.insertBefore(newLanguageButton, languageLinks.lastChild.previousSibling)
        },
      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("\\[\\[[Cc]ategory:"+qq.firstChild.innerHTML+"(\\]\\]|\\|[^\\]]+\\]\\])"),"").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:"-["+"[Category:"+qq.firstChild.innerHTML+"]]",
      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.innerHTML}),
            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("\\[\\[[Cc]ategory:"+qq.firstChild.innerHTML+"([\\|\\]])"),"["+"[Category:" + values.category+"$1")
                },
              'redo':function(){qq.insertBefore(newCat,addCatForm);addCatForm.style.display='none'},
              'undo':function(){qq.removeChild(newCat);addCatForm.style.display='inline'},
              'summary':'['+'[Category:' + qq.firstChild.innerHTML + ']] > ['+'[Category:' + values.category + ']]'
              }, newCat)
            })
          }
      },
      qq)
    addCatForm.firstChild.focus()
    }}))
  }
for(var i = 0; i < languageContainers.length; i++){
  for(var ii = 0, q = languageContainers[i].lastChild.getElementsByTagName('span'); ii < q.length; ii++){
    if(RegExp("\\[\\[[Cc]ategory:"+q[ii].firstChild.innerHTML+"[\\]\\|]").test(res)){
      addRemoveCatButton(q[ii])
      }
    }
  }
}

jQuery(document).ready( makeLanguageTabs );