MediaWiki:Multilingual description.js/public

From Wikimedia Commons, the free media repository
Jump to navigation Jump to search
/**
 * to benefit of [[:Template:Multilingual_description]] or [[:Template:MLD]] template
 * you need to load this javascript into your monobook.js
 * by putting in it 
 *
 * importScript('MediaWiki:Multilingual description.js');
 *
 * 
 * Implements language selection for multilingual elements
 * 
 * In certain environments, it's not feasible to neatly box away each
 * different language into its own section of the site. By marking elements
 * multilingual, you can emulate this behavior by only displaying the 
 * message in the user's language. This reduced the "Tower of Babel" effect.
 * 
 * @author Edward Z. Yang (Ambush Commander)
 * @version $Id: language_select.js 1358 2007-02-19 15:34:59Z Edward $
 * Changes performed since:
 * - Transformed the form to have only one button with a text that alternates.
 * - Fixed multiples forms being loaded if several multilingual div were in the page.
 * - Multiples linguistic templates eg: {{en|text1...}} {{en|text2...}} are taken into account correctly.
 * - The inline bug is fixed. See http://commons.wikimedia.org/w/index.php?title=Commons:Help_desk&diff=next&oldid=18063001#Adding_language-specific_captions_to_images for the description of the issue.
 */


// logging facitily

function appendErro(str){
//   throw new Error("DEBUG: "+str)
}

// disable it when putting in production
function log(str)
{
   //setTimeout("appendErro('"+str+"')", 1)
}

var log_inc=0;

function loginc(str)
{
  log_inc = log_inc +1;
  log(log_inc + ' ' + str);
}

function logresetinc()
{
  log_inc=0;
}

function getElemInfo(elem)
{
  res = elem.nodeName + ' ' + elem.lang + " " ;

  for (var i=0; i<elem.attributes.length ; i++) 
  {
     if (elem.attributes[i].nodeName == 'class')
     res = res + elem.attributes[i].nodeName + ": " + elem.attributes[i].nodeValue + " ";
  }

  for (var i=0; i<elem.childNodes.length ; i++) 
  {
    if (elem.childNodes[i].nodeName.toUpperCase() == 'A')
     res = res + elem.childNodes[i].href + " ";
  }

  return res; 
} 

/* Configuration: */
 
// in your monobook.js, set ls_enable = false to stop the javascript
// maybe it should be cookie configurable. However, you can achieve
// something almost to this effect through cookie settings
if(typeof ls_enable == 'undefined') ls_enable = true;

var ls_mul = 'mul' ; // code used to define that all languages must be displayed...
 
// the cookie name we use to stash the info.
// change this if you are porting it to another wiki!
var ls_cookie = 'commonswiki_language_js';
 
// link to the language select page
var ls_help_url = 'http://meta.wikimedia.org/wiki/Meta:Language_select';
 
// strings that are part of the widgets
var ls_string_help = {
    'de': 'Sprachauswahl',
    'en': 'Language select:',
    'fr': 'Selecteur de langue:',
    'ko': '언어 선택:',
    'mk': 'Јазик:',
    'nds':'Spraakutwahl:',
    'nl': 'Taal',
    'pl': 'Wybierz język:'
};
var ls_string_select = {
    'de': 'Auswahl',
    'en': 'Select',
    'fr': 'Seulement ce langage',
    'ko': '선택',
    'mk': 'Одбери',
    'nds': 'Utwählen',
    'nl': 'Selecteer',
    'pl': 'Wybierz'

};
var ls_string_showall = {
    'de': 'Alle anzeigen',
    'en': 'Show all',
    'fr': 'Tous les langages',
    'ko': '모두 보기',
    'mk': 'Сите',
    'nds': 'All wiesen',
    'nl': 'Toon alles',
    'pl': 'Pokaż wszystkie'
};
 
// define some meta-variables
var ls__first = true; // used to build the widget

var ls_g_form = false;
 
// node compatability fix
if (!window.Node) {
    var Node = {
        ELEMENT_NODE : 1,
        ATTRIBUTE_NODE: 2,
        TEXT_NODE: 3,
        COMMENT_NODE: 8,
        DOCUMENT_NODE: 9,
        DOCUMENT_FRAGMENT_NODE: 11
    };
}
 
// autodetects a browser language
function ls_getBrowserLanguage() {
    return navigator.userLanguage || navigator.language || navigator.browserLanguage;
}
 
// grabs language from cookie
function ls_getCookieLanguage() {
    var allcookies = document.cookie;
    var marker = ls_cookie + '=';
    var pos = allcookies.indexOf(marker);
 
    // cookie isn't set, so no behavior defined
    if (pos === -1) return null;
 
    // cookie is set
    var start = pos + marker.length;
    var end   = allcookies.indexOf(';', start);
    if (end == -1) end = allcookies.length;
 
    var raw   = allcookies.substring(start,end);
    var value = unescape(raw);
 
    return value;
}
 
// sets a new language to the cookie
function ls_setCookieLanguage(language) {
    var today = new Date();
    var expiry = new Date(today.getUTCFullYear() + 30, 1);
    document.cookie = ls_cookie + '=' + escape(language) + '; expires=' + expiry.toGMTString();
}
 
// deletes the cookie
function ls_deleteCookieLanguage(language) {
    document.cookie = ls_cookie + '=; expires=Fri, 02-Jan-1970 00:00:00 GMT';
}
 
// grabs the ISO 639 language code based
// on either the browser or a supplied cookie
// return of ls_mul will display all available strings
function ls_getLanguage() {
    var language = '';
 
    // Priority:
    //  1. Cookie
    //  2. wgUserLanguage global variable
    //  3. Browser autodetection
 
    // grab according to cookie
    language = ls_getCookieLanguage();
 
    // grab according to wgUserLanguage if user is logged in
    if (!language && window.wgUserLanguage && mw.config.get('wgUserGroups') !== null) {
        language = wgUserLanguage;
    }
 
    // grab according to browser if none defined
    if (!language) {
        language = ls_getBrowserLanguage();
    }
 
    // inflexible: can't accept multiple languages
 
    // remove dialect/region code, leaving only the ISO 639 code
    var length;
    // possible bug: supposedly the language string could be en_US
    // switch to regexps when we get the chance
    if ((length = language.indexOf('-')) !== -1) {
        language = language.substr(0, length);
    }
 
    return language;
}
 
// walks all child elements and finds all elements with multilingual in them
function ls_getAllMultilingualElements(n) {
    var elements = new Array();
    // possible bug if we have a classname that includes the word multilingual
    //   but it's unlikely
    if (n.className && n.className.indexOf('multilingual') != -1) {
        elements = elements.concat(n);
    }
    var children = n.childNodes;
    for(var i=0; i < children.length; i++) {
        if (children[i].nodeType !== Node.ELEMENT_NODE) continue;
        elements = elements.concat(ls_getAllMultilingualElements(children[i]));
    }
    return elements;
}

 
function ls_check(elem, language, language_exist ) {

  // we don't care about components without language 
  if (!elem.lang) return;

  //we ignore the components without lang or description names
  if (
  (elem.className.indexOf('lang') == -1) 
  && (elem.className.indexOf('description') == -1)
  && (elem.className.indexOf('langlabel') == -1)
  )
   return;

  // hiding language label when only one language is displayed and is found.
   if (language_exist && (language != 'mul' ) 
   && ( 
      (elem.className.indexOf('langlabel') != -1) // used by mld
      || (elem.className.indexOf('language') != -1) // used by templates like {{en|...} 
      )
   && (language == elem.lang ) )
   {
      elem.style.display ="none";
      loginc('hiding ' + ' ' + getElemInfo(elem) );
      return;
   }

   // do not hide if the language was not found or if the language is corresponding
    if (!language_exist || (elem.lang == language ) ) 
    {
      elem.style.display = "";
      loginc('showing ' + ' ' + getElemInfo(elem) ); 
    }
    else // hide
    {
      elem.style.display ="none";
      loginc('hiding ' + ' ' + getElemInfo(elem) );
    }
}



// walks a hash and hides all non-matching languages
function ls_check_all(lang_element_hash, language, language_exist) {
  for (var i in lang_element_hash) 
   { 
     var elem = lang_element_hash[i];
     ls_check(elem,language, language_exist); 

     containerlist = elem.getElementsByTagName('span');     
     for (var j in containerlist )     
     {
       var span = containerlist[j];
       ls_check(span, language, language_exist);
     } 
  }

}

var ls_string_help_text = ls_string_help[wgUserLanguage] || ls_string_help['en'];
// build widget for changing the language cookie
function ls_buildWidget(language) {
    // preventing multiple widget to be build per page
    if (ls_g_form) return;

    // set up the floating form
    var form = document.createElement('form');

    form.className = 'lang_info';
    form.onsubmit = function() {
        if (this.elements[1].ls_mul_flag) {
            var language = ls_mul;
        } else {
            ls_setCookieLanguage(this.elements[0].value);            
            var language = this.elements[0].value;
        }
        ls_applyLanguageSelect(language);
 
        return false; // don't perform action
    };
    form.appendSpace = function() {
        this.appendChild(document.createTextNode(' '));
    };
 
    // link to language select description page
    var link = document.createElement('a');
    link.href = ls_help_url;
    link.className = 'ls_link';
    link.appendChild(document.createTextNode(ls_string_help_text));
    form.appendChild(link);
 
    form.appendSpace();      
 
    // input box for the language
    var input = document.createElement('input');
    input.setAttribute('type', 'text');
    input.setAttribute('size', '2');
    input.setAttribute('maxlength', '3');
    input.onclick = function() { this.select(); };
    input.className = 'ls_input';
    input.value = language;
    form.appendChild(input);

    form.appendSpace();
 
    // save button
    var submit = document.createElement('input');
    submit.setAttribute('type', 'submit');
    submit.value = ls_string_showall[wgUserLanguage] || ls_string_showall['en'];
    submit.className = 'ls_select';
    submit.onclick = function() {
       if (this.ls_mul_flag) 
       {
         submit.value = ls_string_showall[wgUserLanguage] || ls_string_showall['en'];
       }
       else
       {
         submit.value = ls_string_select[wgUserLanguage] || ls_string_select['en'];    
       }
       this.ls_mul_flag = !(this.ls_mul_flag);
    };
    form.appendChild(submit);

    var bc = document.getElementById('bodyContent');
    var f  = document.getElementById('file'); 
    if (f != null) { f.appendChild(form); } else { bc.insertBefore(form, bc.childNodes[0]); }

   ls_g_form = form;
   ls_g_form.elements[1].ls_mul_flag = false;
}
 var mls;
// main body of the function
function ls_applyLanguageSelect(language) {

    // possible site for cookie checking to disable language select
    if (!ls_enable) return;

   //disabling the gadget on special pages
    if(mw.config.get('wgCanonicalNamespace') == "Special") return;


   // only activated in view , purge, historysubmit or submit mode
    if (!( (mw.config.get('wgAction') == 'view')
        ||  (mw.config.get('wgAction') == 'purge')
        ||  (mw.config.get('wgAction') == 'edit')
        ||  (mw.config.get('wgAction') == 'historysubmit')
        ||  (mw.config.get('wgAction') == 'submit')
       )) 
      return;

    // if language is blank, delete the cookie and then recalculate
    if (!language) { 
        log('language is blank, deleting cookie');
        ls_deleteCookieLanguage();
        language = ls_getLanguage();
    }

    // grab the body element (only one)
    var body = document.getElementsByTagName('body')[0];
 
    // grab an array of multilingual elements
    mls = ls_getAllMultilingualElements(body);
    
    // Only build form if there are MLDs on page. Without this check the form is put on all pages.
    if (mls && mls.constructor==Array && mls.length==0)
    {
        return;
    }

   // if it's the first iteration...
   if (ls__first)
   { 
      ls_buildWidget(language);
      ls__first = false;
   }

    log('language detected: ' + language);
    logresetinc();
 
    // this will get overwritten many times, 
    var language_element_hash;

   // update widget if the language is not set to ls_mul
   if (language != ls_mul)
     ls_g_form.elements[0].value = language;
 
    // iterate through all those elements
    for (var i = 0; i < mls.length; i++) {
        var ml   = mls[i];        // the current multilingual container
        var ml_c = ml.childNodes; // children of the container   

        // variable used to check if the target language was found
        // this is used to not hide if the language target was not found
        // (eg: no fr version if you are browsing all fr will show all versions available.)
        var language_exists  = false;

       // checking ml.parentNode to verify if it is not of type 'image_annotation_content'; this allows to not handle the mld div inside annotation.
        if (ml.parentNode.className.indexOf('image_annotation_content')==-1)
        {
          // iterate through all languages and set up a hash
          // with references to each of the language nodes
          lang_element_hash = new Object();
          for (var j = 0; j < ml_c.length; j++) 
          {
            var n = ml_c[j];
            if (n.nodeType != Node.ELEMENT_NODE) continue; // skip non-elements
            if (n.lang.indexOf(language) === 0) 
            { // it turns out our language is here
              language_exists  = true;
            }
          lang_element_hash[j] = n;
          } 
          ls_check_all(lang_element_hash, language,language_exists);
        }
    }
    log('mls length: ' + mls.length ); 
}
 
function ls_applyDefaultLanguageSelect() {
    ls_applyLanguageSelect(ls_getLanguage(), false);
}
 
// register as onload function
$(ls_applyDefaultLanguageSelect);