משתמש:מושך בשבט/wikiTranslTools.js

מתוך ויקיטקסט, מאגר הטקסטים החופשי

הערה: לאחר הפרסום, ייתכן שיהיה צורך לנקות את זיכרון המטמון (cache) של הדפדפן כדי להבחין בשינויים.

  • פיירפוקס / ספארי: להחזיק את המקש Shift בעת לחיצה על טעינה מחדש (Reload) או ללחוץ על צירוף המקשים Ctrl-F5 או Ctrl-R (במחשב מק: ⌘-R).
  • גוגל כרום: ללחוץ על צירוף המקשים Ctrl-Shift-R (במחשב מק: ⌘-Shift-R).
  • אינטרנט אקספלורר / אדג': להחזיק את המקש Ctrl בעת לחיצה על רענן (Refresh) או ללחוץ על צירוף המקשים Ctrl-F5.
  • אופרה: ללחוץ על Ctrl-F5.
//<nowiki>
/* Wiki Translation Tools v. September 02, 2015
 * home page: https://en.wikipedia.org/wiki/User:V111P/js/Wiki_Translation_Tools
 * CC0 Public Domain Dedication:
 * http://creativecommons.org/publicdomain/zero/1.0/
 * If you use large parts of this code, please let me know.
 * You should also let others know where the code originates:
 * //en.wikipedia.org/wiki/User:V111P/js/wikiTranslTools.js
 * Thanks!
 */
window.wikiTranslTools = (function ()
{
    "use strict";
    var THIS_ARTICLE = mw.config.get('wgPageName').replace(/_/g, ' ');
    var nl = '\n';
    var localStorageItemName = 'WikiTranslationTools';

    // Target-wiki links will be added only to these namespaces:
    // Namespases: main=0, user=2, wikipedia/wp=4, template=10, help=12, category=14, portal=100
    var NamespacesWithInterwikis = [0, 2, 4, 10, 12, 14, 100];


    /*******
     * Target-Wiki Config
     ******/
    var targetWiki = {
        lang: '',
        server: '',
        translatedPageTemplate: '{{translated page|%sourceWiki%|%pageTitle%|version=%revisionId%}}'
    };

    var skin = { // values must be CSS/jQuery selectors
        interwikiDiv: '#p-lang',
        catLinks: '#mw-normal-catlinks' // the <div> with the non-hidden categories
        //  Leave empty to use the built-in wgCategories array (which includes hidden cats)
    };

    /*******
     * Local-Wiki Config
     ******/
    var thisWiki = {
        lang: mw.config.get('wgContentLanguage'),

        catNS: mw.config.get('wgFormattedNamespaces')[14] // The local name of the Category namespace
    };

    /******
     * Other customizable options
     ******/
    var options = {
        helpUrl: '//en.wikipedia.org/wiki/User:V111P/js/Wiki_Translation_Tools',

        defaultWiki: 'en', // used if targetWiki.lang is not set

        // number of rows for the output textarea
        taRows: 10,

        // this has no effect in Monobook and other skins
        taCols: 80,

        // if the article has that many categories or less, they will be auto-expanded
        //   when the Category Browser is shown for the first time on the page.
        maxCatsToAutoExpand: 5,

        // If more than that number of cats are in the target-wiki article, hide them (user needs to click a link)
        nOfCurrTargetCatsToShow: 5,

        // A function called with the wiki code just before displaying it in the text area
        // Must return the wiki code to be displayed
        hook: void(0)
    };


    /*******
     * The Messages
     ******/
    var msgs = {
        error: 'error',
        okButtonLabel: 'OK',
        translatedPageOption: 'Translated page',
        commonsOption: 'Commons',
        interwikis: 'Interwikis',
        categories: 'Categories',
        showPanelLink: 'Cat/Tr/Comm',
        showPanelTitle: 'Categories, {{Translated page}}, {{Commons}} ',
        show: 'show',
        hide: 'hide',
        alreadyListed: 'already listed',
        alreadyInLinkedArticle: 'already in linked article',
        moreThanOneInterwiki: 'More than one article in the target wiki links to this one.',
        catsAlreadyInTarget: 'Categories already in %targetArticle%',
        linksToLang: 'Links to %targetWiki%',
        enterTargetWiki: 'Please, enter the language code of the other wiki:',
        targetWiki: 'target wiki: %targetWiki%'
    };


    /*******
     * Private Variables
     ******/
    var siteDomain = mw.config.get('wgServer').replace(/^[^.]+./, ''); // e.g. wikipedia.org
    var allCats; // used for the Category Browser
    var currTargetWikiArticleCats; // used for the Category Browser


    // a shortcut for document.createTextNode()
    function textNode(str)
    {
        return document.createTextNode(str);
    }


    /*************************
     ***  INTERWIKIS
     ***
     ***/


    // reads the interwikis from the element with the specified in SKIN.INTERWIKI_DIV id
    function getInterwikiCode()
    {
        var interwikis = getInterwikis();
        var links = interwikis.links;
        var langs = interwikis.langs;

        /* adding this page's wikilink to the array */
        links[thisWiki.lang] = THIS_ARTICLE;
        langs.push(thisWiki.lang);
        langs.sort();

        var interwikisStr = '';
        for (var i = 0; i < langs.length; i++)
            interwikisStr += '[[' + langs[i] + ':' + links[langs[i]] + ']]' + nl;

        return interwikisStr;
    }


    function getInterwikis()
    {
        var links = [], langs = [];
        $(skin.interwikiDiv + ' a').each(function (){
            var result = $(this).attr('href').match(/\/\/([^\.]+)\.[^\/]+\/wiki\/(.+)/);
            if (!result || result[1] == 'www')
                return;
            var lang = result[1];
            var title = decodeURI(result[2]).replace(/_/g, ' ');
            if (lang != targetWiki.lang) {
                links[lang] = title;
                langs.push(lang);
            }
        });
        return {langs: langs, links: links};
    }


    /*************************
     ***  COMMONS templates
     ***
     ***/


    function getCommonsTemplates() {
        var commonsStr = '', template;
        var $commonsLinks = $('a[href*="//commons.wikimedia.org/wiki/"]');
        var commonsLinksArr = [];
        $commonsLinks.each(function (i, el) {
            var href = $(this).attr('href');
            if (href.match(/wiki\/(file|image|media):/i) || href.match(/Special:UploadWizard/))
                return;
            href = href.replace(/_/g, ' ').replace(/\?.*$/, '');
            var title = decodeURIComponent(href.replace(/.+?\/wiki\//, ''));
            var page = title.replace(/^Category:/, '');
            if (title != href) {
                if (title.match(/^Atlas of/))
                    template = '* {{wikiatlas|' + title.replace(/^Atlas of./, '') + '}}';
                else
                    template = '{{commons' + (page != title ? 'cat' : '')
                        + '|' + page + '}}';
                if ($.inArray(template, commonsLinksArr) == -1)
                    commonsLinksArr.push(template);
            }
        });
        commonsStr = commonsLinksArr.join('\n');
        if (commonsStr) commonsStr += '\n\n';

        return commonsStr;
    }


    /*************************
     ***  OPTIONS PANEL
     ***
     ***/


    /* PUBLIC */
    // called when the user clicks on the "Cat/Tr/FA/Comm" portlet link
    function showPanel()
    {
        $('#wikiTranslToolsCodeBox').remove();

        if (!$('#wikiTranslToolsPanel').show()[0])
            createThePanel();
    }


    function createThePanel()
    {
        if (!targetWiki.lang)
            setTargetWiki(prompt(msgs.enterTargetWiki, options.defaultWiki));

        var $wikiTranslToolsPanel = $('<div/>', {
            css: {
                color: '#000',
                backgroundColor: '#f4f4f4',
                fontSize: 'small',
                position: 'relative',
                margin: '1em auto'
            },
            id: 'wikiTranslToolsPanel'
        }).append(
            $('<a/>', {
                text: msgs.targetWiki.replace(/%targetWiki%/, (targetWiki.lang || options.defaultWiki)),
                href: '#',
                'class': 'wtt_targetWikiLangLink',
                css: {
                    marginRight: '1em',
                    position: 'absolute',
                    bottom: '2px',
                    right: '2px'
                },
                click: function (e) {
                    e.preventDefault();
                    setTargetWiki(prompt(msgs.enterTargetWiki, (targetWiki.lang || options.defaultWiki)));
                }
            }),
            $('<div/>', {
                css: {position: 'absolute', right: '2px', top: '2px'}
            }).append(
                $('<a/>', {
                    text: '[?]',
                    target: '_blank',
                    href: options.helpUrl
                }),
                $('<a/>', {
                    text: '[X]',
                    href: '#',
                    click: function (e) {
                        e.preventDefault();
                        $wikiTranslToolsPanel.hide();
                    }
                })
            )
        );

        var optionCheckboxes = [
            {val: 'translatedPage', checked: true, label: msgs.translatedPageOption},
            {val: 'commons', checked: true, label: msgs.commonsOption},
            {val: 'interwikis', checked: false, label: msgs.interwikis}
        ];

        if (targetWiki.translatedPageTemplate == 'none') {
            optionCheckboxes[0].checked = null;
            optionCheckboxes[0].disabled = 'disabled';
        }

        var $form = $('<form/>', {
            id: 'wikiTranslToolsForm',
            submit: function (e) {
                e.preventDefault();
                formOk(this);
            }
        })
        .append('<input type="submit" value="' + msgs.okButtonLabel + '"/>');

        $.each(optionCheckboxes, function (i, val) {
            $form.append($('<label style="margin-left:0.5em;"/>').append($('<input type="checkbox"/>').attr({
                name: 'options',
                value: val.val,
                checked: (val.checked ? 'checked' : null),
                disabled: (val.disabled ? 'disabled' : null)
            }), textNode(val.label)));
        });

        $form.append('<div style="margin-top:1em;"><b>'
                       + msgs.categories + '</b></div>',
            createTheCategoryBrowser(),
            '<input type="submit" value="' + msgs.okButtonLabel + '" style="margin-top:1em;"/>'
        );
        display($wikiTranslToolsPanel.append($form));
    } // createThePanel()


    function formOk(frm)
    {
        var checkedOptions = {};
        var optionCheckboxes = $('input:checkbox[name=options]:checked', frm);

        $.each(optionCheckboxes, function (i, val) {
            checkedOptions[val.value] = true;
        });

        var catsStr = getCheckedCats(frm);
        $('#wikiTranslToolsPanel').hide();

        displayCode(
            (checkedOptions.commons ? getCommonsTemplates() : '')
            + (checkedOptions.translatedPage ? targetWiki.translatedPageTemplate + nl + nl : '')
            + (catsStr ? catsStr + nl + nl : '')
            + (checkedOptions.interwikis ? getInterwikiCode() : '')
        );
    }


    /*************************
     ***  CATEGORIES
     ***
     ***/


    function createTheCategoryBrowser() {
        var catArr = [];

        // if possible, fill catArr with the non-hidden cats only,
        //  otherwise use wgCategories
        if (skin.catLinks)
            catArr = $(skin.catLinks + ' a').map(function(i) {
                if (i == 0) return null; // skip the 'Categories:' link
                return $(this).text();
            }).toArray();
        else
            catArr = mw.config.get('wgCategories');

        if (catArr.length == 0)
            return $([]);

        allCats = {};

        getTargetInterwiki();

        // create the main UL and sub li's with the category names
        var $top_ul = $('<ul/>', {'class': 'wtt_catBrowser'});

        var $top_li;
        for (var i = 0; i < catArr.length; i++)
        {
            var expandLink = createExpandCatLink();

            $top_li = $('<li/>').append(expandLink, textNode(' '), '<span>' + catArr[i] + '</span>');
            allCats[catArr[i]] = {
                name: catArr[i],
                liElement: $top_li // the innermost <li> within which the category's name is shown
            };

            $top_ul.append($top_li);

            if (catArr.length <= options.maxCatsToAutoExpand)
                expandLink.click();
        }

        return $top_ul[0];
    } // createTheCategoryBrowser


    function getTargetInterwiki() {
        var tLang = targetWiki.lang;
        var targetPgA = $(skin.interwikiDiv + " a[href^='//" + tLang + ".']");

        var siz = targetPgA.length;
        if (siz === 0)
            return;

        if (siz > 1) {
            // more than one articles in the target wiki is linked to this article
            // this is rare, so it's not supported here
            $('.wtt_catBrowser')
            .after($('<div>' + msgs.moreThanOneInterwiki + '</div>'));
            return;
        }

        var targetPg = (targetPgA.attr('href').match('.org/wiki/(.+)') || ['',''])[1];
        targetWiki.interwikiLinkedArticle = targetPg;

        var apiRequestUrl = '//' + tLang + '.' + siteDomain + '/w/api.php?action=query&prop=categories&titles='
               + targetPg.replace(/_/g, '%20')
               + '&cllimit=200&redirects&format=json'
               + '&callback=window.wikiTranslTools.wikiTranslToolsTargetArticleCatsReceived';
        $('body').append('<script src="' + apiRequestUrl + '"></script>');
    }


    function wikiTranslToolsTargetArticleCatsReceived(catsReply) {
        var pages = (catsReply && catsReply.query && catsReply.query.pages);
        var catObjs = [];

        if (!pages) {
            console.warn("wikiTranslTools: debug message: targetArticleCatsReceived(): !pages");
        }

        for (var key in pages) {
            if (pages.hasOwnProperty(key) && pages[key].categories) {
                catObjs = pages[key].categories;
                break;
            }
        }

        var cats = [];
        for (var i = 0; i < catObjs.length; i++) {
            cats.push(catObjs[i].title);
        }

        currTargetWikiArticleCats = cats; // copy to global var

        // In case there are already some target-wiki categories and checkboxes shown,
        //   check if any of these cats are already in the interwiki-linked target-wiki article
        //   and mark them
        $('.wtt_catBrowser input').filter(function (i, el) {
            return ( ($.inArray(el.value, cats) > -1) ? true : false )
        }).after($('<b title="' + msgs.alreadyInLinkedArticle + '"> @ </b>'));

        var targetSiteArticlePath = '//' + targetWiki.lang + '.' + siteDomain + '/wiki/';

        var $span = $('<span class="wtt_currCats"/>');
        for (var i = 0; i < cats.length; i++) {
            if (i > 0) $span.append(textNode(', '));
            $span.append($('<a target="_blank" href="'
                          + targetSiteArticlePath + cats[i].replace(/ /g, '_') + '">'
                          + cats[i].replace(/^[^:]+:/, '') + '</a>') );
        }
        $span.append(textNode(' '), $('<a/>', {
            href: '#',
            click: function (e) {
                e.preventDefault();
                $('.wtt_currCats').hide();
                $('.wtt_currCatsLnk').show();
            },
            title: msgs.hide,
            text: '<<'
        }));

        var iwlaName = targetWiki.interwikiLinkedArticle;
        var iwlaDisplayName = decodeURI(iwlaName).replace(/_/g, ' ');
        var iwlaUrl = targetSiteArticlePath + iwlaName;

        var catsAlreadyHtml = msgs.catsAlreadyInTarget
                     .replace(/%targetArticle%/, targetWiki.lang
                         + ':<a target="_blank" href="' + iwlaUrl + '">' + iwlaDisplayName
                         + '</a> (' + cats.length + '): ');

        $('.wtt_catBrowser')
        .after($('<div>' + catsAlreadyHtml + '</div>')
            .append($span, $('<a/>', {
                href: '#',
                click: function (e) {
                    e.preventDefault();
                    $('.wtt_currCats').show();
                    $('.wtt_currCatsLnk').hide();
                },
                'class': 'wtt_currCatsLnk',
                title: msgs.show,
                text: '...'
        })))
        .after('<br/>');

        if (cats.length < options.nOfCurrTargetCatsToShow)
            $('.wtt_currCatsLnk').hide();
        else
            $('.wtt_currCats').hide();
    }


    function createExpandCatLink()
    {
        return $('<a/>', {
            text: '[+]',
            href: '#',
            click: function() {
                expandCat(this);
                return false;
            },
            css: {textDecoration: 'none'}
        });
    }


    // Called when the user clicks on a [+] expand link in the Category Browser
    function expandCat(startCatExpandLinkEl)
    {
        var startCatLink = $(startCatExpandLinkEl);
        var catSpan = $('span', startCatLink.parent()); // it contains the name of the cat to be expanded

        startCatLink.off('click').text('...'); // replace "[+]" with "..."
        startCatLink.attr('class', 'waitingExpandLink');
        var cat = allCats[$(catSpan[0]).text()];
        var catNsAndNameEscaped = encodeURIComponent(thisWiki.catNS + ':' + cat.name);
        $.ajax({
            url: '/w/api.php?action=query&prop=langlinks|categories&titles='
                + catNsAndNameEscaped
                + '&lllang=' + (targetWiki.lang || options.defaultWiki)
                + '&cllimit=200&redirects&format=json',
            dataType: 'json',
            success: catInfoReceived(cat),
            error: function (XMLHttpRequest) {
                showCatParents(cat);
            }
        });
    }


    // creates and returns a function to be called when the requested Category page information is received
    function catInfoReceived(cat)
    {
        return function(data) {
            function parentCats(data) {
                var pages = data && data.query && data.query.pages;
                if (!pages)
                    return;

                var pg;
                for (var p in pages) {
                    if (pages.hasOwnProperty(p))
                        pg = pages[p];
                }

                var catObjs = pg && pg.categories;
                if (!catObjs)
                    return;

                var cats = [];
                for (var i = 0; i < catObjs.length; i++) {
                    cats.push(catObjs[i].title.replace(/^[^:]+:/, ''));
                }
                return cats;
            }

            showCatParents(cat, parentCats(data), getTargetwikiFromJson(data));
        };
    } // catInfoReceived


    // called by expandCat() to add the parent categories
    //  of the specified category to the displayed list.
    function showCatParents(cat, parents, targetWikiCat)
    {
        var top_li = cat.liElement;
        var server = mw.config.get('wgServer');

        $('.waitingExpandLink', top_li).remove();

        if (targetWikiCat) {
            var targetWikiLink = $('<a/>', {
                text: targetWikiCat.replace(/^[^:]+:/, ''),
                href: targetWiki.server + '/wiki/' + targetWikiCat,
                target: '_blank'
            });

            var alreadyInArticle = $.inArray(targetWikiCat, currTargetWikiArticleCats) > -1;
            top_li.append(textNode(' : '),
                '<input type="checkbox" name="targetWikiCats" value="' + targetWikiCat + '" />',
                (alreadyInArticle
                    ? $('<b title="' + msgs.alreadyInLinkedArticle + '"> @ </b>')
                    : textNode(' ')),
                targetWikiLink);
        }
        else if (parents && parents.length > 0) {
            var new_ul = $('<ul/>');

            var new_li;
            var alreadyListed;
            for (var i = 0; i < parents.length; i++) {
                new_li = $('<li/>');

                alreadyListed = false;

                if (allCats[parents[i]])
                    alreadyListed = $('<em>(' + msgs.alreadyListed + ')</em>');
                else {
                    new_li.append(createExpandCatLink(), textNode(' '));
                    allCats[parents[i]] = {
                        name: parents[i],
                        liElement: new_li // the innermost <li> within which the category's name is shown
                    };
                }

                new_li.append('<span>' + parents[i] + '</span>');
                if (alreadyListed)
                    new_li.append(textNode(' '), alreadyListed);
                new_ul.append(new_li);
            }

            top_li.append(new_ul);
        } // if (parents.length > 0)
        else { // if no target-wiki cat and no parent cats found, create a link to the cat,
            //  so the user can check manually
            var catSpan = $('span', top_li).empty();
            catSpan.append($('<a/>', {
                href: server + '/wiki/'
                    + thisWiki.catNS + ':' + cat.name,
                target: '_blank',
                text: cat.name
            }))
            .append(textNode(' '),
                // no parents arr is passed if the cat doesn't exist (404 or other error received)
                $('<em>(' + msgs.error + ')</em>')
            );
        }
    } // showCatParents


    function getCheckedCats(frm)
    {
        var targetWikiCats = $('input:checkbox[name=targetWikiCats]:checked', frm).map(function(){
            return this.value;
        }).toArray();

        if (targetWikiCats.length == 0)
            return '';
        else
            return '[[' + targetWikiCats.join(']]' + nl + '[[') + ']]';
    }


    /*************************
     ***  TRANSLATE LINKS
     ***
     ***/


    /* PUBLIC */
    // called when the user clicks on the "Links to (language code)" portlet link
    function translateLinks()
    {
        var namespaceIds = mw.config.get('wgNamespaceIds');
        var hWLs = $('.wtt_targetWikiLink');
        if (hWLs.length > 0) {
            hWLs.toggle(); // previously shown for the current language
            return;
        }

        if (!targetWiki.lang) {
            setTargetWiki(prompt(msgs.enterTargetWiki, options.defaultWiki));
            if (!targetWiki.lang)
                return;
        }

        $($('#mw-content-text')[0] || $('body')).find('a')
        .not('#wikiTranslToolsPanel a').not(skin.catLinks + ' a').not('a.external')
        .filter('[href^="/wiki/"]').after(function () {
            var article = this.href.match(/wiki\/([^#?]+)/);

            if (!article)
                return null;
            article = article[1];

            // Check if the namespace is in the array of approved namespaces: NamespacesWithInterwikis
            var ns = article.match(/[^:]+(?=:)/);
            if (ns) {
                var nsNum = namespaceIds[ns[0].toLowerCase()];
                if (nsNum && ($.inArray(nsNum, NamespacesWithInterwikis) == -1))
                    return null;
            }

            var span = $('<sup/>', {
                text: ' [[',
                'class': 'wtt_targetWikiLink reference'
            }).append($('<a/>', {
                text: targetWiki.lang + ':?',
                href: '#',
                data: {articleName: article},
                click: function () {
                    findTargetWikiLink($(this));
                    return false;
                }
            })).append(textNode(']]'));

            return span;
        });
    }


    // This function is called when the user clicks on one of the [[lang:?]] links
    function findTargetWikiLink($clickedLink)
    {
        var articleName = $clickedLink.data('articleName');
        $.ajax({
            url: '/w/api.php?action=query&prop=langlinks&titles=' + articleName
                + '&lllang=' + targetWiki.lang + '&redirects&format=json',
            dataType: 'json',
            success: function (data) {
                interwikiReceived(data, $clickedLink);
            }
        });
    }


    function interwikiReceived(data, $clickedLink)
    {
        var targetWikiPage = getTargetwikiFromJson(data);
        if (!targetWikiPage) {
            $clickedLink.replaceWith($('<span/>', {
                text: targetWiki.lang,
                css: {textDecoration: 'line-through'}
            }));
            return;
        }

        var newLink = $('<a/>', {
            href: targetWiki.server + '/wiki/' + targetWikiPage.replace(/ /, '_'),
            target: '_blank',
            text: targetWiki.lang + ':'
        });

        var input = $('<input type="text" style="vertical-align: bottom; direction: ltr;">').attr({
            value: targetWikiPage,
            size: Math.floor(targetWikiPage.length * 4/3),
            readonly: 'readonly'
        });

        var redirects = data && data.query && data.query.redirects;
        if (redirects)
            for (var i = 0; i < redirects.length; i++) {
                $clickedLink.before($('<span title="' + redirects[i].to + '">&gt;</span>'));
            }
        $clickedLink.after(input);
        $clickedLink.replaceWith(newLink);
        input.select();
    }


    /*************************
     ***  Other Functions
     ***
     ***/


    function getTargetwikiFromJson(data) {
        //{"query":{"pages":{"5152":{"pageid":5152,"ns":0,"title":"2007","langlinks":[{"lang":"bg","*":"2007"}]}}}}
        var pages = data && data.query && data.query.pages;
        if (!pages)
            return;

        var pg;
        for (var p in pages) {
            if (pages.hasOwnProperty(p))
                pg = pages[p];
        }

        return pg && pg.langlinks && pg.langlinks[0] && pg.langlinks[0]['*'];
    }


    function display($el) {
        // any normal skin || Cologne Blue skin:
        $('#contentSub').prepend($el)[0] || $('#mw-content-text').prepend($el);
        //    mw.util.$content.prepend($el) doesn't work in IE 7 (layout bug)
        $('html, body').scrollTop(0);
    }


    function displayCode(str)
    {
        str = $.trim(str);
        if ($.isFunction(options.hook))
            str = options.hook(str);

        var $ta = $('<textarea/>', {
            css: {direction: 'ltr'},
            cols: options.taCols,
            rows: options.taRows
        });

        var $codeBoxDiv = $('<div/>', {
            id: 'wikiTranslToolsCodeBox',
            css: {width: '80%', margin: '1em auto', position: 'relative'}
        }).append($ta,
            $('<div/>')
            .css({position: 'absolute', top: 0, right: '-4em', fontSize: 'small'})
            .append(
                $('<a/>', {
                    href: '#',
                    text: '[<]',
                    click: function (e) {
                        e.preventDefault();
                        $codeBoxDiv.remove();
                        showPanel();
                    }
                }),
                $('<a/>', {
                    href: options.helpUrl,
                    target: '_blank',
                    text: '[?]'
                }),
                $('<a/>', {
                    href: '#',
                    text: '[X]',
                    click: function (e) {
                        e.preventDefault();
                        $codeBoxDiv.remove();
                    }
                })
            )
        );

        display($codeBoxDiv);
        $ta.focus().val(str).select().scrollTop(0);
    }


    function addPortletLinks()
    {
        var ns = mw.config.get('wgCanonicalNamespace');
        var linksToLang = msgs.linksToLang.replace(/%targetWiki%/, (targetWiki.lang || '?'));

        if (ns != 'Special' && ns != 'MediaWiki') {
            if (!$('#wtt_showPanelLink')[0])
                mw.util.addPortletLink('p-tb', 'javascript:wikiTranslTools.showPanel(); void(0);',
                    msgs.showPanelLink,
                    'wtt_showPanelLink',
                    msgs.showPanelTitle
                );
        }
        if (!$('#wtt_translateLinksLink')[0])
            mw.util.addPortletLink('p-tb', 'javascript:wikiTranslTools.translateLinks(); void(0);',
                linksToLang,
                'wtt_translateLinksLink',
                linksToLang
            );
    }


    function targetWikiLangAtLocalStorage(valToWrite) {
        if (!window.localStorage) return '';

        if (valToWrite === undefined) {
            var storedVal = JSON.parse(localStorage.getItem(localStorageItemName) || 'null');
            return ( (storedVal && storedVal.targetWiki && storedVal.targetWiki.lang) || '' );
        }

        var obj = { targetWiki: { lang: valToWrite } };
        localStorage.setItem(localStorageItemName, JSON.stringify(obj));
    }


    /* PUBLIC */
    // Call it after updating window.wikiTranslToolsConfig
    // Will delete localStorage entry if wikiTranslToolsConfig.targetWiki.lang is set
    function init(noPortletLinks)
    {
        $('#wtt_showPanelLink').remove();
        $('#wtt_translateLinksLink').remove();
        $('#wikiTranslToolsPanel').remove();
        $('.wtt_targetWikiLink').remove();

        var wttC = window.wikiTranslToolsConfig;
        if (wttC) {
            var oldTargetWikiLang = targetWiki.lang;
            $.extend(targetWiki, wttC.targetWiki);
            $.extend(true, thisWiki, wttC.thisWiki);
            $.extend(skin, wttC.skin);
            $.extend(msgs, wttC.msgs);
            $.extend(options, wttC.options);

            if (targetWiki.lang != oldTargetWikiLang && checkIfValidWiki(targetWiki.lang))
                setTargetWiki(targetWiki.lang, true);
            else
                targetWiki.lang = oldTargetWikiLang;
        }

        if (!targetWiki.lang) {
            var savedTargetWikiLang = targetWikiLangAtLocalStorage();
            if (savedTargetWikiLang) setTargetWiki(savedTargetWikiLang, true);
        }

        if (!checkIfValidWiki(options.defaultWiki))
            options.defaultWiki = 'en';

        targetWiki.server = mw.config.get('wgServer') // needed if setTargetWiki isn't called above
            .replace(thisWiki.lang, (targetWiki.lang || options.defaultWiki));

        targetWiki.translatedPageTemplate = targetWiki.translatedPageTemplate
        .replace(/%sourceWiki%/g, thisWiki.lang)
        .replace(/%pageTitle%/g, THIS_ARTICLE)
        .replace(/%revisionId%/g, mw.config.get('wgRevisionId'));

        if (!noPortletLinks && mw.config.get('wgAction') == 'view')
            $(addPortletLinks);
    }


    function checkIfValidWiki(wikiCode)
    {
        return (wikiCode && !/[^A-Za-z0-9-]/.test(wikiCode));
    }


    // localStorage entry is set to newTargetWiki (the wiki's language code)
    // call with newTargetWiki == '' to delete localStorage entry without changing current targetWiki.lang
    function setTargetWiki(newTargetWiki, dontTouchLocalStorage)
    {
        newTargetWiki = $.trim(newTargetWiki);

        if (!dontTouchLocalStorage && newTargetWiki === '' && window.localStorage) {
            window.localStorage.removeItem(localStorageItemName);
            return;
        }

        if (!checkIfValidWiki(newTargetWiki)) {
            return;
        }

        targetWiki.lang = newTargetWiki;
        targetWiki.server = mw.config.get('wgServer').replace(thisWiki.lang, newTargetWiki);
        var targetLangInCfg = window.wikiTranslToolsConfig && window.wikiTranslToolsConfig.targetWiki
                           && window.wikiTranslToolsConfig.targetWiki.lang;
        if ( !checkIfValidWiki(targetLangInCfg) && !dontTouchLocalStorage )
            targetWikiLangAtLocalStorage(newTargetWiki);

        $('.wtt_targetWikiLink').remove();
        var newLinksToLangPortletText = msgs.linksToLang.replace(/%targetWiki%/, targetWiki.lang);
        $('#wtt_translateLinksLink a').attr('title', newLinksToLangPortletText).text(newLinksToLangPortletText);
        var $panel = $('#wikiTranslToolsPanel');
        if ($panel[0]) {
            $panel.find('.wtt_targetWikiLangLink').text(msgs.targetWiki.replace(/%targetWiki%/, targetWiki.lang));
            $panel.find('.wtt_catBrowser').replaceWith(createTheCategoryBrowser());
        }
    }


    init();


    /* All public member functions */
    return {
        init:               init,
        showPanel:          showPanel,
        translateLinks:     translateLinks,
        wikiTranslToolsTargetArticleCatsReceived: wikiTranslToolsTargetArticleCatsReceived
    };
}());
//</nowiki>