Site Notice

hello, world

Difference between revisions of "MediaWiki:Js-i18n-js/editor.js"

From Project-EPB Commons
 
 
(5 intermediate revisions by the same user not shown)
Line 8: Line 8:
 
*/
 
*/
  
;(function() {
+
; (function () {
 
     'use strict';
 
     'use strict';
 
     // Configuration caching
 
     // Configuration caching
Line 17: Line 17:
 
         'wgUserLanguage'
 
         'wgUserLanguage'
 
     ]);
 
     ]);
 +
    mw.util.wikiGetlink = function (str) {
 +
        return mw.config.get('wgArticlePath').replace('$1', mw.util.wikiUrlencode(typeof str === 'string' ? str : mw.config.get('wgPageName')));
 +
    }
 
     // Scope limiting
 
     // Scope limiting
 
     if (
 
     if (
Line 94: Line 97:
 
         * calling this method as a callback
 
         * calling this method as a callback
 
         */
 
         */
         preload: function() {
+
         preload: function () {
 
             if (++this._loadCounter === 3) {
 
             if (++this._loadCounter === 3) {
 
                 window.dev.i18n.loadMessages('I18nEdit').done($.proxy(this.init, this));
 
                 window.dev.i18n.loadMessages('I18nEdit').done($.proxy(this.init, this));
Line 103: Line 106:
 
         * @param {Object} i18n I18n object returned by I18n-js
 
         * @param {Object} i18n I18n object returned by I18n-js
 
         */
 
         */
         init: function(i18n) {
+
         init: function (i18n) {
 
             i18n.useUserLang();
 
             i18n.useUserLang();
 
             this.i18n = i18n;
 
             this.i18n = i18n;
Line 116: Line 119:
 
         * @returns {String} URL part at the specified index
 
         * @returns {String} URL part at the specified index
 
         */
 
         */
         getUrlVar: function(i) {
+
         getUrlVar: function (i) {
 
             return conf.wgPageName.split('/').slice(2)[i];
 
             return conf.wgPageName.split('/').slice(2)[i];
 
         },
 
         },
Line 124: Line 127:
 
         */
 
         */
 
         storage: {
 
         storage: {
             get: function(key) {
+
             get: function (key) {
 
                 try {
 
                 try {
 
                     return localStorage.getItem(key);
 
                     return localStorage.getItem(key);
Line 131: Line 134:
 
                 }
 
                 }
 
             },
 
             },
             set: function(key, val) {
+
             set: function (key, val) {
 
                 try {
 
                 try {
 
                     localStorage.setItem(key, val);
 
                     localStorage.setItem(key, val);
                 } catch (ignore) {}
+
                 } catch (ignore) { }
 
             },
 
             },
             remove: function(key) {
+
             remove: function (key) {
 
                 try {
 
                 try {
 
                     localStorage.removeItem(key);
 
                     localStorage.removeItem(key);
                 } catch (ignore) {}
+
                 } catch (ignore) { }
 
             },
 
             },
 
         },
 
         },
Line 146: Line 149:
 
         * editor should be rendered
 
         * editor should be rendered
 
         */
 
         */
         dispatchForm: function() {
+
         dispatchForm: function () {
 
             var title = this.getUrlVar(0),
 
             var title = this.getUrlVar(0),
            lang = this.getUrlVar(1);
+
                lang = this.getUrlVar(1);
 
             if (title) {
 
             if (title) {
 
                 this.page = decodeURIComponent(title);
 
                 this.page = decodeURIComponent(title);
Line 169: Line 172:
 
         * @param {String} msg Message code of the title message
 
         * @param {String} msg Message code of the title message
 
         */
 
         */
         setTitle: function(msg) {
+
         setTitle: function (msg) {
 
             document.title = 'I18nEdit | ' + this.i18n.msg('title-' + msg, this.page, this.lang).plain();
 
             document.title = 'I18nEdit | ' + this.i18n.msg('title-' + msg, this.page, this.lang).plain();
 
         },
 
         },
Line 176: Line 179:
 
         * lists translatable scripts
 
         * lists translatable scripts
 
         */
 
         */
         initSearch: function() {
+
         initSearch: function () {
 
             $('.page-header__title, #firstHeading').text(this.msg('search-title'));
 
             $('.page-header__title, #firstHeading').text(this.msg('search-title'));
 
             this.setTitle('search');
 
             this.setTitle('search');
Line 218: Line 221:
 
                                             },
 
                                             },
 
                                             events: {
 
                                             events: {
                                                 click: function(e) {
+
                                                 click: function (e) {
 
                                                     e.preventDefault();
 
                                                     e.preventDefault();
 
                                                     var value = document.getElementById('I18nEditSearchFormSelect').value;
 
                                                     var value = document.getElementById('I18nEditSearchFormSelect').value;
Line 239: Line 242:
 
                 list: 'allpages',
 
                 list: 'allpages',
 
                 apnamespace: 8,
 
                 apnamespace: 8,
                 apprefix: 'Custom-',
+
                 apprefix: 'I18n-',
 
                 aplimit: 'max'
 
                 aplimit: 'max'
 
             }).done($.proxy(this.cbAllpages, this));
 
             }).done($.proxy(this.cbAllpages, this));
Line 247: Line 250:
 
         * @param {Object} d API call results
 
         * @param {Object} d API call results
 
         */
 
         */
         cbAllpages: function(d) {
+
         cbAllpages: function (d) {
 
             this.ui({
 
             this.ui({
                 children: d.query.allpages.filter(function(el) {
+
                 children: d.query.allpages.filter(function (el) {
                     return el.title.slice(-10) === '/i18n.json' &&
+
                     return el.title.slice(-5) === '.json' &&
                          el.title.slice(0, 17) === 'MediaWiki:Custom-';
+
                        el.title.slice(0, 15) === 'MediaWiki:I18n-';
                 }).map(function(el) {
+
                 }).map(function (el) {
 
                     return {
 
                     return {
 
                         type: 'option',
 
                         type: 'option',
Line 258: Line 261:
 
                             'class': 'I18nEditSearchFormOption'
 
                             'class': 'I18nEditSearchFormOption'
 
                         },
 
                         },
                         text: el.title.slice(17, -10)
+
                         text: el.title.slice(15, -5)
 
                     };
 
                     };
 
                 }),
 
                 }),
Line 271: Line 274:
 
         * translations and editing metadata
 
         * translations and editing metadata
 
         */
 
         */
         initTranslationPicker: function() {
+
         initTranslationPicker: function () {
 
             $('.page-header__title, #firstHeading').text(this.page);
 
             $('.page-header__title, #firstHeading').text(this.page);
 
             $('.page-header__page-subtitle, #contentSub').prepend(
 
             $('.page-header__page-subtitle, #contentSub').prepend(
Line 328: Line 331:
 
         * @param {Object} i18n I18n data for the specified script
 
         * @param {Object} i18n I18n data for the specified script
 
         */
 
         */
         cbMessagesLoad: function(i18n) {
+
         cbMessagesLoad: function (i18n) {
 
             this.pickerI18n = i18n;
 
             this.pickerI18n = i18n;
 
             this.finishPickerLoading();
 
             this.finishPickerLoading();
Line 337: Line 340:
 
         * @param {Object} d API query result
 
         * @param {Object} d API query result
 
         */
 
         */
         cbLanguagesLoad: function(d) {
+
         cbLanguagesLoad: function (d) {
             d.query.languages.forEach(function(el) {
+
             d.query.languages.forEach(function (el) {
                 if(!this.languages[el['*']]) {
+
                 if (!this.languages[el['*']]) {
 
                     this.languages[el['*']] = el.code;
 
                     this.languages[el['*']] = el.code;
 
                 }
 
                 }
Line 349: Line 352:
 
         * editor interface
 
         * editor interface
 
         */
 
         */
         finishPickerLoading: function() {
+
         finishPickerLoading: function () {
             if(--this._loadCounter !== 0) {
+
             if (--this._loadCounter !== 0) {
 
                 return;
 
                 return;
 
             }
 
             }
 
             var children = this.formChildren = [],
 
             var children = this.formChildren = [],
 
                 messages = I18nEdit.pickerI18n._messages;
 
                 messages = I18nEdit.pickerI18n._messages;
             $.each(this.languages, $.proxy(function(i, val) {
+
             $.each(this.languages, $.proxy(function (i, val) {
 
                 if (this.langBlacklist.indexOf(val) !== -1) {
 
                 if (this.langBlacklist.indexOf(val) !== -1) {
 
                     return;
 
                     return;
Line 403: Line 406:
 
                                 },
 
                                 },
 
                                 events: {
 
                                 events: {
                                     click: function(e) {
+
                                     click: function (e) {
 
                                         e.preventDefault();
 
                                         e.preventDefault();
 
                                         if ($(this).attr('disabled') === 'disabled') {
 
                                         if ($(this).attr('disabled') === 'disabled') {
Line 432: Line 435:
 
                                 },
 
                                 },
 
                                 events: {
 
                                 events: {
                                     click: function(e) {
+
                                     click: function (e) {
 
                                         e.preventDefault();
 
                                         e.preventDefault();
 
                                         window.location.pathname += '/metadata';
 
                                         window.location.pathname += '/metadata';
Line 445: Line 448:
 
                                 },
 
                                 },
 
                                 events: {
 
                                 events: {
                                     click: function(e) {
+
                                     click: function (e) {
 
                                         e.preventDefault();
 
                                         e.preventDefault();
 
                                         window.location.pathname += '/qqq';
 
                                         window.location.pathname += '/qqq';
Line 458: Line 461:
 
                 parent: '#I18nEditPickerForm'
 
                 parent: '#I18nEditPickerForm'
 
             });
 
             });
             $('#I18nEditPickerFormList input, #I18nEditPickerFormList label').click(function() {
+
             $('#I18nEditPickerFormList input, #I18nEditPickerFormList label').click(function () {
 
                 $('#I18nEditLanguage').removeAttr('disabled');
 
                 $('#I18nEditLanguage').removeAttr('disabled');
 
             });
 
             });
Line 468: Line 471:
 
         * @param {Event} e Click event
 
         * @param {Event} e Click event
 
         */
 
         */
         showLanguageModal: function(e) {
+
         showLanguageModal: function (e) {
 
             e.preventDefault();
 
             e.preventDefault();
             $.showCustomModal(this.escaped('add-new-language'), this.ui({
+
             ssi_modal.show({
                type: 'div',
+
                title: this.escaped('add-new-language'),
                 attr: {
+
                 content:
                     'class': 'I18nEditModal'
+
                     this.ui({
                },
 
                children: [
 
                    {
 
                        type: 'span',
 
                        attr: {
 
                            'class': 'I18nEditFilterSpan'
 
                        },
 
                        text: this.msg('filter')
 
                    },
 
                    {
 
                        type: 'input',
 
                        attr: {
 
                            'class': 'I18nEditLanguageInput',
 
                            type: 'text',
 
                            value: this.storage.get('i18nEdit-filter') || conf.wgUserLanguage
 
                        },
 
                        events: {
 
                            input: $.proxy(this.filterLanguages, this)
 
                        }
 
                    },
 
                    {
 
 
                         type: 'div',
 
                         type: 'div',
 
                         attr: {
 
                         attr: {
                             'class': 'I18nEditNewLanguageList'
+
                             'class': 'I18nEditModal'
 
                         },
 
                         },
                         children: this.formChildren.map(function(elem) {
+
                         children: [
                             elem.children = elem.children.map(function(child) {
+
                            {
                                 if (child.attr.id) {
+
                                type: 'span',
                                     child.attr.id = 'modal-' + child.attr.id;
+
                                attr: {
                                 } else {
+
                                    'class': 'I18nEditFilterSpan'
                                     child.attr['for'] = 'modal-' + child.attr['for'];
+
                                },
 +
                                text: this.msg('filter')
 +
                             },
 +
                            {
 +
                                 type: 'input',
 +
                                attr: {
 +
                                     'class': 'I18nEditLanguageInput',
 +
                                    type: 'text',
 +
                                    value: this.storage.get('i18nEdit-filter') || conf.wgUserLanguage
 +
                                 },
 +
                                events: {
 +
                                     input: $.proxy(this.filterLanguages, this)
 
                                 }
 
                                 }
                                 return child;
+
                            },
                            });
+
                            {
                            return elem;
+
                                type: 'div',
                        })
+
                                attr: {
                    }
+
                                    'class': 'I18nEditNewLanguageList'
                ]
+
                                },
            }), {
+
                                 children: this.formChildren.map(function (elem) {
 +
                                    elem.children = elem.children.map(function (child) {
 +
                                        if (child.attr.id) {
 +
                                            child.attr.id = 'modal-' + child.attr.id;
 +
                                        } else {
 +
                                            child.attr['for'] = 'modal-' + child.attr['for'];
 +
                                        }
 +
                                        return child;
 +
                                    });
 +
                                    return elem;
 +
                                })
 +
                            }
 +
                        ]
 +
                    }),
 
                 id: 'I18nEditNewLanguageModal',
 
                 id: 'I18nEditNewLanguageModal',
 
                 buttons: [
 
                 buttons: [
 
                     {
 
                     {
 
                         id: 'I18nEditModalButtonClose',
 
                         id: 'I18nEditModalButtonClose',
                         message: this.escaped('close'),
+
                         label: this.escaped('close'),
                         handler: function() {
+
                         method: function () {
 
                             $('#I18nEditNewLanguageModal').closeModal();
 
                             $('#I18nEditNewLanguageModal').closeModal();
 
                         }
 
                         }
Line 524: Line 530:
 
                     {
 
                     {
 
                         id: 'I18nEditModalButtonAdd',
 
                         id: 'I18nEditModalButtonAdd',
                         message: this.escaped('add-language'),
+
                         label: this.escaped('add-language'),
                         defaultButton: true,
+
                         className: 'btn primary',
                         handler: function() {
+
                         method: function () {
 
                             var $this = $(this);
 
                             var $this = $(this);
 
                             if ($this.attr('disabled') === 'disabled') {
 
                             if ($this.attr('disabled') === 'disabled') {
Line 532: Line 538:
 
                             }
 
                             }
 
                             var $radio = $('.I18nEditNewLanguageList input:checked'),
 
                             var $radio = $('.I18nEditNewLanguageList input:checked'),
                            lang = $radio.attr('value');
+
                                lang = $radio.attr('value');
 
                             if (!lang) {
 
                             if (!lang) {
 
                                 return;
 
                                 return;
Line 540: Line 546:
 
                     }
 
                     }
 
                 ],
 
                 ],
                callback: $.proxy(function() {
 
                    this.filterLanguages({ target: $('.I18nEditLanguageInput')[0] });
 
                    var $btn = $('#I18nEditModalButtonAdd');
 
                    $btn.attr('disabled', '');
 
                    $('.I18nEditNewLanguageList input, .I18nEditNewLanguageList label').click(function() {
 
                        $btn.removeAttr('disabled');
 
                    });
 
                    $('.I18nEditLanguageInput').popover({
 
                        content: this.ui({
 
                            type: 'div',
 
                            attr: {
 
                                'class': 'I18nEditFilterPopover'
 
                            },
 
                            text: this.msg('language-popover')
 
                        }),
 
                        placement: 'top'
 
                    });
 
                }, this)
 
 
             });
 
             });
 
         },
 
         },
Line 565: Line 553:
 
         * @param {Event} e Input event
 
         * @param {Event} e Input event
 
         */
 
         */
         filterLanguages: function(e) {
+
         filterLanguages: function (e) {
 
             var val = e.target.value.toLowerCase(),
 
             var val = e.target.value.toLowerCase(),
            filters = val.split('|');
+
                filters = val.split('|');
             $('.I18nEditNewLanguageList .noexist').hide().filter(function() {
+
             $('.I18nEditNewLanguageList .noexist').hide().filter(function () {
 
                 var $this = $(this),
 
                 var $this = $(this),
                text = $this.text().toLowerCase(),
+
                    text = $this.text().toLowerCase(),
                indexes = 0;
+
                    indexes = 0;
                 filters.forEach(function(lang) {
+
                 filters.forEach(function (lang) {
 
                     indexes += text.indexOf(lang);
 
                     indexes += text.indexOf(lang);
 
                 });
 
                 });
Line 582: Line 570:
 
         * Initializes the metadata editor interface
 
         * Initializes the metadata editor interface
 
         */
 
         */
         initMetadataEditor: function() {
+
         initMetadataEditor: function () {
 
             $('.page-header__title, #firstHeading').text(this.page + ' - Metadata');
 
             $('.page-header__title, #firstHeading').text(this.page + ' - Metadata');
 
             this.setTitle('metadata');
 
             this.setTitle('metadata');
Line 638: Line 626:
 
                             },
 
                             },
 
                             events: {
 
                             events: {
                                 click: $.proxy(function() {
+
                                 click: $.proxy(function () {
 
                                     var $sel = $('.I18nEditMetadataSelect');
 
                                     var $sel = $('.I18nEditMetadataSelect');
 
                                     if (!$sel.val()) {
 
                                     if (!$sel.val()) {
Line 644: Line 632:
 
                                     }
 
                                     }
 
                                     var $opt = $sel.find(':selected'),
 
                                     var $opt = $sel.find(':selected'),
                                    val = $opt.val();
+
                                        val = $opt.val();
 
                                     this.ui({
 
                                     this.ui({
 
                                         type: 'li',
 
                                         type: 'li',
Line 661: Line 649:
 
                                             },
 
                                             },
 
                                             events: {
 
                                             events: {
                                                 click: $.proxy(function(e) {
+
                                                 click: $.proxy(function (e) {
 
                                                     e.preventDefault();
 
                                                     e.preventDefault();
 
                                                     var $li = $(e.target).closest('li');
 
                                                     var $li = $(e.target).closest('li');
Line 697: Line 685:
 
                             },
 
                             },
 
                             events: {
 
                             events: {
                                 click: $.proxy(function(e) {
+
                                 click: $.proxy(function (e) {
 
                                     var $childs = $('.I18nEditMetadataUL').children();
 
                                     var $childs = $('.I18nEditMetadataUL').children();
 
                                     if (!$childs.length) {
 
                                     if (!$childs.length) {
Line 703: Line 691:
 
                                     } else {
 
                                     } else {
 
                                         this.messages._metadata = {
 
                                         this.messages._metadata = {
                                             noTranslate: $childs.toArray().map(function(el) {
+
                                             noTranslate: $childs.toArray().map(function (el) {
 
                                                 return el.getAttribute('data-value');
 
                                                 return el.getAttribute('data-value');
 
                                             }).sort()
 
                                             }).sort()
Line 721: Line 709:
 
         * Initializes the translation editor interface
 
         * Initializes the translation editor interface
 
         */
 
         */
         initTranslationEditor: function() {
+
         initTranslationEditor: function () {
 
             var lang = this.lang = this.getUrlVar(1);
 
             var lang = this.lang = this.getUrlVar(1);
 
             $('.page-header__title, #firstHeading').text(this.page);
 
             $('.page-header__title, #firstHeading').text(this.page);
Line 749: Line 737:
 
                     attr: {
 
                     attr: {
 
                         'class': 'I18nEditMain I18nEditEditor loading script-' + this.page +
 
                         'class': 'I18nEditMain I18nEditEditor loading script-' + this.page +
                                ' lang-' + lang
+
                            ' lang-' + lang
 
                     },
 
                     },
 
                     children: [
 
                     children: [
Line 772: Line 760:
 
                                         'english',
 
                                         'english',
 
                                         'translation'
 
                                         'translation'
                                     ].map(function(el) {
+
                                     ].map(function (el) {
 
                                         return {
 
                                         return {
 
                                             type: 'th',
 
                                             type: 'th',
Line 790: Line 778:
 
                             },
 
                             },
 
                             events: {
 
                             events: {
                                 keypress: function(e) {
+
                                 keypress: function (e) {
 
                                     if (e.keyCode == 13) {
 
                                     if (e.keyCode == 13) {
 
                                         $('#I18nEditEditorSubmit').click();
 
                                         $('#I18nEditEditorSubmit').click();
Line 809: Line 797:
 
                             },
 
                             },
 
                             events: {
 
                             events: {
                                 click: $.proxy(function() {
+
                                 click: $.proxy(function () {
 
                                     var key = prompt(this.msg('message-code'));
 
                                     var key = prompt(this.msg('message-code'));
 
                                     if (!key) {
 
                                     if (!key) {
Line 837: Line 825:
 
         * @param {Object} i18n I18n object
 
         * @param {Object} i18n I18n object
 
         */
 
         */
         cbMessagesLoad2: function(i18n) {
+
         cbMessagesLoad2: function (i18n) {
 
             this.messages = i18n._messages;
 
             this.messages = i18n._messages;
 
             var lang = this.lang;
 
             var lang = this.lang;
Line 848: Line 836:
 
             var trans = $.extend({}, this.messages.en, this.messages[lang]);
 
             var trans = $.extend({}, this.messages.en, this.messages[lang]);
 
             if (this.messages._metadata && this.messages._metadata.noTranslate && lang !== 'en') {
 
             if (this.messages._metadata && this.messages._metadata.noTranslate && lang !== 'en') {
                 this.messages._metadata.noTranslate.forEach(function(l) {
+
                 this.messages._metadata.noTranslate.forEach(function (l) {
 
                     delete trans[l];
 
                     delete trans[l];
 
                 });
 
                 });
Line 860: Line 848:
 
         * @param {Object} val Message in the row
 
         * @param {Object} val Message in the row
 
         */
 
         */
         addTableRow: function(i, val) {
+
         addTableRow: function (i, val) {
 
             var key = 'I18nEdit/' + this.page,
 
             var key = 'I18nEdit/' + this.page,
            json = this.storage.get(key),
+
                json = this.storage.get(key),
            obj = json ? JSON.parse(json) : {};
+
                obj = json ? JSON.parse(json) : {};
 
             this.ui({
 
             this.ui({
 
                 type: 'tr',
 
                 type: 'tr',
Line 895: Line 883:
 
                                 },
 
                                 },
 
                                 events: {
 
                                 events: {
                                     input: function() {
+
                                     input: function () {
 
                                         var json = I18nEdit.storage.get(key),
 
                                         var json = I18nEdit.storage.get(key),
                                        obj = json ? JSON.parse(json) : {};
+
                                            obj = json ? JSON.parse(json) : {};
 
                                         obj[i] = this.value;
 
                                         obj[i] = this.value;
 
                                         I18nEdit.storage.set(key, JSON.stringify(obj));
 
                                         I18nEdit.storage.set(key, JSON.stringify(obj));
Line 915: Line 903:
 
         * @param {Object} i18n I18n object
 
         * @param {Object} i18n I18n object
 
         */
 
         */
         cbMessagesLoad3: function(i18n) {
+
         cbMessagesLoad3: function (i18n) {
 
             this.messages = i18n._messages;
 
             this.messages = i18n._messages;
 
             var meta = this.messages._metadata = $.extend({
 
             var meta = this.messages._metadata = $.extend({
 
                 noTranslate: []
 
                 noTranslate: []
 
             }, this.messages._metadata),
 
             }, this.messages._metadata),
            messages = Object.keys(this.messages.en);
+
                messages = Object.keys(this.messages.en);
             $.each(meta.noTranslate, $.proxy(function(i, val) {
+
             $.each(meta.noTranslate, $.proxy(function (i, val) {
 
                 messages.splice(messages.indexOf(val), 1);
 
                 messages.splice(messages.indexOf(val), 1);
 
                 this.ui({
 
                 this.ui({
Line 939: Line 927:
 
                         },
 
                         },
 
                         events: {
 
                         events: {
                             click: $.proxy(function(e) {
+
                             click: $.proxy(function (e) {
 
                                 var $li = $(e.target).closest('li');
 
                                 var $li = $(e.target).closest('li');
 
                                 this.ui({
 
                                 this.ui({
Line 955: Line 943:
 
                 });
 
                 });
 
             }, this));
 
             }, this));
             $.each(messages, $.proxy(function(i, val) {
+
             $.each(messages, $.proxy(function (i, val) {
 
                 this.ui({
 
                 this.ui({
 
                     type: 'option',
 
                     type: 'option',
Line 970: Line 958:
 
         * @param {Event} e Click event
 
         * @param {Event} e Click event
 
         */
 
         */
         saveTranslations: function(e) {
+
         saveTranslations: function (e) {
 
             e.preventDefault();
 
             e.preventDefault();
 
             var lang = this.lang,
 
             var lang = this.lang,
            changedMessages = [];
+
                changedMessages = [];
 
             // Re-add removed translations by _metadata.noTranslate
 
             // Re-add removed translations by _metadata.noTranslate
 
             $.extend(this.messages[lang], lang === 'metadata' ? null : this.messages.en, this.messages[lang]);
 
             $.extend(this.messages[lang], lang === 'metadata' ? null : this.messages.en, this.messages[lang]);
             $('#I18nEditEditorTable .I18nEditTranslateInput').each($.proxy(function(_, el) {
+
             $('#I18nEditEditorTable .I18nEditTranslateInput').each($.proxy(function (_, el) {
 
                 var $this = $(el),
 
                 var $this = $(el),
                code = $this.parent().parent().data('code'),
+
                    code = $this.parent().parent().data('code'),
                val = $this.val();
+
                    val = $this.val();
 
                 if (this.messages[lang][code] === val) {
 
                 if (this.messages[lang][code] === val) {
 
                     return;
 
                     return;
Line 994: Line 982:
 
                 var confirmation = confirm(this.msg('remove-translations-prompt'));
 
                 var confirmation = confirm(this.msg('remove-translations-prompt'));
 
                 if (confirmation) {
 
                 if (confirmation) {
                     $.each(this.messages, $.proxy(function(i) {
+
                     $.each(this.messages, $.proxy(function (i) {
 
                         if (i === '_metadata') {
 
                         if (i === '_metadata') {
 
                             return;
 
                             return;
 
                         }
 
                         }
                         $.each(changedMessages, $.proxy(function(j, msg) {
+
                         $.each(changedMessages, $.proxy(function (j, msg) {
 
                             if (
 
                             if (
 
                                 this.messages._metadata &&
 
                                 this.messages._metadata &&
Line 1,012: Line 1,000:
 
             // Sort object keys, with exceptions
 
             // Sort object keys, with exceptions
 
             var messages = {},
 
             var messages = {},
            specials = {
+
                specials = {
                _metadata: 1,
+
                    _metadata: 1,
                en: 2,
+
                    en: 2,
                qqq: 3
+
                    qqq: 3
            };
+
                };
             Object.keys(this.messages).sort(function(a,b) {
+
             Object.keys(this.messages).sort(function (a, b) {
 
                 if (specials[a] && specials[b]) {
 
                 if (specials[a] && specials[b]) {
 
                     return specials[a] - specials[b];
 
                     return specials[a] - specials[b];
Line 1,027: Line 1,015:
 
                     return a.localeCompare(b);
 
                     return a.localeCompare(b);
 
                 }
 
                 }
             }).forEach(function(key) {
+
             }).forEach(function (key) {
 
                 messages[key] = this.messages[key];
 
                 messages[key] = this.messages[key];
 
             }, this);
 
             }, this);
 
             this.api.postWithEditToken({
 
             this.api.postWithEditToken({
 
                 action: 'edit',
 
                 action: 'edit',
                 title: 'MediaWiki:Custom-' + this.page + '/i18n.json',
+
                 title: 'MediaWiki:I18n-' + this.page + '.json',
                 text: '/* <syntaxhighlight lang="javascript"> */\n' + JSON.stringify(messages, null, 4),
+
                 text: JSON.stringify(messages, null, 4),
 
                 summary: '[I18nEdit] ' + (
 
                 summary: '[I18nEdit] ' + (
 
                     $('#I18nEditSummary').val() ||
 
                     $('#I18nEditSummary').val() ||
Line 1,046: Line 1,034:
 
         * @param {Object} d API result
 
         * @param {Object} d API result
 
         */
 
         */
         cbSave: function(d) {
+
         cbSave: function (d) {
             if(d.error) {
+
             if (d.error) {
                 new BannerNotification(this.escaped('error') + ': ' + d.error.code, 'error').show();
+
                 mw.notify(this.escaped('error') + ': ' + d.error.code);
 
             } else {
 
             } else {
 
                 this.storage.remove('I18nEdit/' + this.page);
 
                 this.storage.remove('I18nEdit/' + this.page);
                 new BannerNotification(this.escaped('success'), 'confirm').show();
+
                 mw.notify(this.escaped('success'));
                 setTimeout($.proxy(function() {
+
                 setTimeout($.proxy(function () {
 
                     window.location.pathname = '/wiki/Special:Blankpage/I18nEdit/' + this.page;
 
                     window.location.pathname = '/wiki/Special:Blankpage/I18nEdit/' + this.page;
 
                 }, this), 2000);
 
                 }, this), 2000);
Line 1,062: Line 1,050:
 
         * @returns {String} Translated message with the specified code
 
         * @returns {String} Translated message with the specified code
 
         */
 
         */
         msg: function(msg) {
+
         msg: function (msg) {
 
             return this.i18n.msg(msg).plain();
 
             return this.i18n.msg(msg).plain();
 
         },
 
         },
Line 1,070: Line 1,058:
 
         * @returns {String} Translated message with the specified code, escaped
 
         * @returns {String} Translated message with the specified code, escaped
 
         */
 
         */
         escaped: function(msg) {
+
         escaped: function (msg) {
 
             return this.i18n.msg(msg).escape();
 
             return this.i18n.msg(msg).escape();
 
         },
 
         },
Line 1,078: Line 1,066:
 
         * @param {String} page The page to load
 
         * @param {String} page The page to load
 
         */
 
         */
         i18nPage: function(page) {
+
         i18nPage: function (page) {
 
             var server = conf.wgServer
 
             var server = conf.wgServer
 
                 .match(/https?:\/\/([a-z0-9-]+)\.(?:fandom\.com|wikia\.org)/),
 
                 .match(/https?:\/\/([a-z0-9-]+)\.(?:fandom\.com|wikia\.org)/),
 
                 toLoad = page;
 
                 toLoad = page;
 
             if (server && server[1] !== 'dev') {
 
             if (server && server[1] !== 'dev') {
                 toLoad = 'u:' + server[1] + ':MediaWiki:Custom-' + page + '/i18n.json';
+
                 toLoad = 'u:' + server[1] + ':MediaWiki:I18n-' + page + '/i18n.json';
 
             }
 
             }
 
             return toLoad;
 
             return toLoad;
Line 1,089: Line 1,077:
 
     };
 
     };
 
     // Loading necessary resources
 
     // Loading necessary resources
     importArticles(
+
     mw.loader.load('https://common.wjghj.cn/js/i18n-js');
        {
+
    mw.loader.load('https://common.wjghj.cn/js/UI-js');
            type: 'script',
+
    mw.loader.load('https://common.wjghj.cn/css/i18n-js/editor', 'text/css');
            articles: [
 
                'u:dev:MediaWiki:I18n-js/code.js',
 
                'u:dev:MediaWiki:UI-js/code.js'
 
            ]
 
        },
 
        {
 
            type: 'style',
 
            articles: [
 
                'u:dev:MediaWiki:I18n-js/editor.css'
 
            ]
 
        }
 
    );
 
 
     // Run the main when scripts loads
 
     // Run the main when scripts loads
 
     mw.hook('dev.ui').add($.proxy(I18nEdit.preload, I18nEdit));
 
     mw.hook('dev.ui').add($.proxy(I18nEdit.preload, I18nEdit));

Latest revision as of 21:44, 29 March 2020

/* I18n-js JSON editor
 *
 * Provides a specialized internationalization editor at Special:BlankPage/i18nEdit
 *
 * @authors
    - KockaAdmiralac
    - Dorumin
*/

; (function () {
    'use strict';
    // Configuration caching
    var conf = mw.config.get([
        'wgCityId',
        'wgPageName',
        'wgServer',
        'wgUserLanguage'
    ]);
    mw.util.wikiGetlink = function (str) {
        return mw.config.get('wgArticlePath').replace('$1', mw.util.wikiUrlencode(typeof str === 'string' ? str : mw.config.get('wgPageName')));
    }
    // Scope limiting
    if (
        // conf.wgCityId !== '7931' ||
        conf.wgPageName.slice(0, 26).toLowerCase() !== 'special:blankpage/i18nedit' ||
        (window.dev && window.dev.I18nEdit)
    ) {
        return;
    }
    window.dev = window.dev || {};
    /**
     * Main object
     * @class I18nEdit
     */
    var I18nEdit = window.dev.I18nEdit = {
        // Used during resource preloading
        _loadCounter: 0,
        languages: {},
        // List of languages not supported by Wikia. Correct per:
        // https://github.com/Wikia/app/blob/4cda276c23a154186ea27c686dbd1ff57a7039dd/languages/WikiaLanguage.php#L42-L89
        langBlacklist: [
            'als',
            'bat-smg',
            'be-x-old',
            'crh',
            'de2',
            'de-at',
            'de-ch',
            'de-formal',
            'de-weigsbrag',
            'dk',
            'en-gb',
            'eshelp',
            'fihelp',
            'fiu-vro',
            'frc',
            'frhelp',
            'gan',
            'hif',
            'ithelp',
            'iu',
            'jahelp',
            'kbd',
            'kh',
            'kk',
            'kohelp',
            'kp',
            'ks',
            'ku',
            'nb',
            'mu',
            'nlhelp',
            'pthelp',
            'pt-brhelp',
            'roa-rup',
            'ruhelp',
            'ruq',
            'shi',
            'simple',
            'sr',
            'tg',
            'tp',
            'tt',
            'ug',
            'zh',
            'zh-classical',
            'zh-cn',
            'zh-min-nan',
            'zh-mo',
            'zh-my',
            'zh-sg',
            'zh-yue'
        ],
        /**
         * Handles resource loading
         * Each of the resources that are being preloaded are
         * calling this method as a callback
         */
        preload: function () {
            if (++this._loadCounter === 3) {
                window.dev.i18n.loadMessages('I18nEdit').done($.proxy(this.init, this));
            }
        },
        /**
         * Initializes the script
         * @param {Object} i18n I18n object returned by I18n-js
         */
        init: function (i18n) {
            i18n.useUserLang();
            this.i18n = i18n;
            this.ui = window.dev.ui;
            this.api = new mw.Api();
            $('.page-header__title, #firstHeading').text(this.msg('title'));
            this.dispatchForm();
        },
        /**
         * Returns the part of the URL at the specified index
         * @param {Number} i Index of the URL part to return
         * @returns {String} URL part at the specified index
         */
        getUrlVar: function (i) {
            return conf.wgPageName.split('/').slice(2)[i];
        },
        /**
         * Safely handle tasks involving the browser's
         * localStorage object
         */
        storage: {
            get: function (key) {
                try {
                    return localStorage.getItem(key);
                } catch (ignore) {
                    return null;
                }
            },
            set: function (key, val) {
                try {
                    localStorage.setItem(key, val);
                } catch (ignore) { }
            },
            remove: function (key) {
                try {
                    localStorage.removeItem(key);
                } catch (ignore) { }
            },
        },
        /**
         * Checks the URL to determine which page of the
         * editor should be rendered
         */
        dispatchForm: function () {
            var title = this.getUrlVar(0),
                lang = this.getUrlVar(1);
            if (title) {
                this.page = decodeURIComponent(title);
                if (lang) {
                    this.lang = decodeURIComponent(lang);
                    if (lang === 'metadata') {
                        this.initMetadataEditor();
                    } else {
                        this.initTranslationEditor();
                    }
                } else {
                    this.initTranslationPicker();
                }
            } else {
                this.initSearch();
            }
        },
        /**
         * Sets the title of the page
         * @param {String} msg Message code of the title message
         */
        setTitle: function (msg) {
            document.title = 'I18nEdit | ' + this.i18n.msg('title-' + msg, this.page, this.lang).plain();
        },
        /**
         * Initializes the "script search" interface, which
         * lists translatable scripts
         */
        initSearch: function () {
            $('.page-header__title, #firstHeading').text(this.msg('search-title'));
            this.setTitle('search');
            $('#mw-content-text').html(
                this.ui({
                    type: 'div',
                    attr: { 'class': 'I18nEditMain I18nEditSearch loading' },
                    children: [
                        {
                            type: 'p',
                            attr: { id: 'I18nEditSearchText' },
                            text: this.msg('search-text')
                        },
                        {
                            type: 'fieldset',
                            attr: { 'class': 'collapsible' },
                            children: [
                                {
                                    type: 'legend',
                                    text: this.msg('search-legend')
                                },
                                {
                                    type: 'form',
                                    attr: {
                                        method: 'GET',
                                        id: 'I18nEditSearchForm'
                                    },
                                    children: [
                                        {
                                            type: 'select',
                                            attr: {
                                                id: 'I18nEditSearchFormSelect',
                                                name: 'page'
                                            }
                                        },
                                        {
                                            type: 'input',
                                            attr: {
                                                type: 'submit',
                                                value: this.msg('search-button')
                                            },
                                            events: {
                                                click: function (e) {
                                                    e.preventDefault();
                                                    var value = document.getElementById('I18nEditSearchFormSelect').value;
                                                    if (!value) {
                                                        return;
                                                    }
                                                    window.location.pathname += '/' + value;
                                                }
                                            }
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                })
            );
            this.api.get({
                action: 'query',
                list: 'allpages',
                apnamespace: 8,
                apprefix: 'I18n-',
                aplimit: 'max'
            }).done($.proxy(this.cbAllpages, this));
        },
        /**
         * Callback after the API call to find all translatable scripts
         * @param {Object} d API call results
         */
        cbAllpages: function (d) {
            this.ui({
                children: d.query.allpages.filter(function (el) {
                    return el.title.slice(-5) === '.json' &&
                        el.title.slice(0, 15) === 'MediaWiki:I18n-';
                }).map(function (el) {
                    return {
                        type: 'option',
                        attr: {
                            'class': 'I18nEditSearchFormOption'
                        },
                        text: el.title.slice(15, -5)
                    };
                }),
                parent: '#I18nEditSearchFormSelect'
            });
            $('.I18nEditSearch').toggleClass('loading');
        },
        /**
         * Initializes the "translation picker" interface, which appears
         * after selecting a script to translate, lists all languages
         * the script is already translated to, allows adding new
         * translations and editing metadata
         */
        initTranslationPicker: function () {
            $('.page-header__title, #firstHeading').text(this.page);
            $('.page-header__page-subtitle, #contentSub').prepend(
                '< ',
                this.ui({
                    type: 'a',
                    attr: {
                        href: mw.util.wikiGetlink('Special:BlankPage/I18nEdit')
                    },
                    text: this.msg('search-title')
                }),
                ' | '
            );
            this.setTitle('picker');
            $('#mw-content-text').html(
                this.ui({
                    type: 'div',
                    attr: { 'class': 'I18nEditMain I18nEditPicker loading script-' + this.page },
                    children: [
                        {
                            type: 'p',
                            attr: { id: 'I18nEditPickerText' },
                            text: this.msg('picker-text')
                        },
                        {
                            type: 'form',
                            attr: {
                                id: 'I18nEditPickerForm',
                                method: 'GET'
                            },
                            children: [
                                {
                                    type: 'input',
                                    attr: {
                                        name: 'page',
                                        type: 'hidden',
                                        value: this.page
                                    }
                                }
                            ]
                        }
                    ]
                })
            );
            this._loadCounter = 2;
            window.dev.i18n.loadMessages(this.i18nPage(this.page), { noCache: true }).done($.proxy(this.cbMessagesLoad, this));
            this.api.get({
                action: 'query',
                meta: 'siteinfo',
                siprop: 'languages'
            }).done($.proxy(this.cbLanguagesLoad, this));
        },
        /**
         * Callback after I18n-js loads messages for the
         * specified script
         * @param {Object} i18n I18n data for the specified script
         */
        cbMessagesLoad: function (i18n) {
            this.pickerI18n = i18n;
            this.finishPickerLoading();
        },
        /**
         * Callback after the API call to list all
         * available languages on Wikia
         * @param {Object} d API query result
         */
        cbLanguagesLoad: function (d) {
            d.query.languages.forEach(function (el) {
                if (!this.languages[el['*']]) {
                    this.languages[el['*']] = el.code;
                }
            }, this);
            this.finishPickerLoading();
        },
        /**
         * Synchronizes the two asynchronous calls from the
         * editor interface
         */
        finishPickerLoading: function () {
            if (--this._loadCounter !== 0) {
                return;
            }
            var children = this.formChildren = [],
                messages = I18nEdit.pickerI18n._messages;
            $.each(this.languages, $.proxy(function (i, val) {
                if (this.langBlacklist.indexOf(val) !== -1) {
                    return;
                }
                children.push({
                    type: 'li',
                    attr: { 'class': messages[val] ? 'exists' : 'noexist' },
                    children: [
                        {
                            type: 'input',
                            attr: {
                                'class': 'I18nEditPickerFormRadio',
                                type: 'radio',
                                name: 'lang',
                                value: val,
                                id: 'lang-' + val
                            }
                        },
                        {
                            type: 'label',
                            attr: {
                                'for': 'lang-' + val
                            },
                            text: i + ' (' + val + ')'
                        }
                    ]
                });
            }, this));
            this.ui({
                children: [
                    {
                        type: 'ul',
                        attr: { id: 'I18nEditPickerFormList' },
                        children: children
                    },
                    {
                        type: 'div',
                        attr: {
                            id: 'I18nEditButtonContainer'
                        },
                        children: [
                            {
                                type: 'button',
                                attr: {
                                    id: 'I18nEditLanguage',
                                    disabled: 'disabled'
                                },
                                events: {
                                    click: function (e) {
                                        e.preventDefault();
                                        if ($(this).attr('disabled') === 'disabled') {
                                            return;
                                        }
                                        var value = $('.I18nEditPickerFormRadio:checked').val();
                                        if (value) {
                                            window.location.pathname += '/' + value;
                                        }
                                    }
                                },
                                text: this.msg('edit-language')
                            },
                            {
                                type: 'button',
                                attr: {
                                    id: 'I18nEditAddLanguage'
                                },
                                events: {
                                    click: $.proxy(this.showLanguageModal, this)
                                },
                                text: this.msg('add-new-language')
                            },
                            {
                                type: 'button',
                                attr: {
                                    id: 'I18nEditMetadata'
                                },
                                events: {
                                    click: function (e) {
                                        e.preventDefault();
                                        window.location.pathname += '/metadata';
                                    }
                                },
                                text: this.msg('edit-metadata')
                            },
                            {
                                type: 'button',
                                attr: {
                                    id: 'I18nEditQQQ'
                                },
                                events: {
                                    click: function (e) {
                                        e.preventDefault();
                                        window.location.pathname += '/qqq';
                                    }
                                },
                                text: this.msg('edit-qqq')
                            }
                        ],
                        parent: '#I18nEditPickerForm'
                    }
                ],
                parent: '#I18nEditPickerForm'
            });
            $('#I18nEditPickerFormList input, #I18nEditPickerFormList label').click(function () {
                $('#I18nEditLanguage').removeAttr('disabled');
            });
            $('.I18nEditPicker').toggleClass('loading');
        },
        /**
         * Shows the modal with languages the script can be
         * additionally translated to
         * @param {Event} e Click event
         */
        showLanguageModal: function (e) {
            e.preventDefault();
            ssi_modal.show({
                title: this.escaped('add-new-language'),
                content:
                    this.ui({
                        type: 'div',
                        attr: {
                            'class': 'I18nEditModal'
                        },
                        children: [
                            {
                                type: 'span',
                                attr: {
                                    'class': 'I18nEditFilterSpan'
                                },
                                text: this.msg('filter')
                            },
                            {
                                type: 'input',
                                attr: {
                                    'class': 'I18nEditLanguageInput',
                                    type: 'text',
                                    value: this.storage.get('i18nEdit-filter') || conf.wgUserLanguage
                                },
                                events: {
                                    input: $.proxy(this.filterLanguages, this)
                                }
                            },
                            {
                                type: 'div',
                                attr: {
                                    'class': 'I18nEditNewLanguageList'
                                },
                                children: this.formChildren.map(function (elem) {
                                    elem.children = elem.children.map(function (child) {
                                        if (child.attr.id) {
                                            child.attr.id = 'modal-' + child.attr.id;
                                        } else {
                                            child.attr['for'] = 'modal-' + child.attr['for'];
                                        }
                                        return child;
                                    });
                                    return elem;
                                })
                            }
                        ]
                    }),
                id: 'I18nEditNewLanguageModal',
                buttons: [
                    {
                        id: 'I18nEditModalButtonClose',
                        label: this.escaped('close'),
                        method: function () {
                            $('#I18nEditNewLanguageModal').closeModal();
                        }
                    },
                    {
                        id: 'I18nEditModalButtonAdd',
                        label: this.escaped('add-language'),
                        className: 'btn primary',
                        method: function () {
                            var $this = $(this);
                            if ($this.attr('disabled') === 'disabled') {
                                return;
                            }
                            var $radio = $('.I18nEditNewLanguageList input:checked'),
                                lang = $radio.attr('value');
                            if (!lang) {
                                return;
                            }
                            window.location.pathname += '/' + lang;
                        }
                    }
                ],
            });
        },
        /**
         * Filters languages in the modal after using the
         * search bar
         * @param {Event} e Input event
         */
        filterLanguages: function (e) {
            var val = e.target.value.toLowerCase(),
                filters = val.split('|');
            $('.I18nEditNewLanguageList .noexist').hide().filter(function () {
                var $this = $(this),
                    text = $this.text().toLowerCase(),
                    indexes = 0;
                filters.forEach(function (lang) {
                    indexes += text.indexOf(lang);
                });
                return indexes !== -filters.length;
            }).show();
            this.storage.set('i18nEdit-filter', val);
        },
        /**
         * Initializes the metadata editor interface
         */
        initMetadataEditor: function () {
            $('.page-header__title, #firstHeading').text(this.page + ' - Metadata');
            this.setTitle('metadata');
            $('.page-header__page-subtitle, #contentSub').prepend(
                '< ',
                this.ui({
                    type: 'a',
                    attr: {
                        href: mw.util.wikiGetlink('Special:BlankPage/I18nEdit')
                    },
                    text: this.msg('search-title')
                }),
                ' | ',
                this.ui({
                    type: 'a',
                    attr: {
                        href: mw.util.wikiGetlink('Special:BlankPage/I18nEdit/' + this.page.replace(/ /g, '_'))
                    },
                    text: this.page
                }),
                ' | '
            );
            $('#mw-content-text').html(
                this.ui({
                    type: 'div',
                    attr: {
                        'class': 'I18nEditMetadata I18nEditEditor loading'
                    },
                    children: [
                        {
                            type: 'p',
                            attr: {
                                'class': 'I18nEditMetadataEditorWarning'
                            },
                            text: this.msg('warning-metadata')
                        },
                        {
                            type: 'p',
                            attr: {
                                'class': 'I18nEditMetadataEditorText'
                            },
                            text: this.msg('metadata-editor-text')
                        },
                        {
                            type: 'select',
                            attr: {
                                'class': 'I18nEditMetadataSelect'
                            }
                        },
                        {
                            type: 'button',
                            attr: {
                                'class': 'I18nEditMetadataAddMessage',
                                disabled: 'disabled'
                            },
                            events: {
                                click: $.proxy(function () {
                                    var $sel = $('.I18nEditMetadataSelect');
                                    if (!$sel.val()) {
                                        return;
                                    }
                                    var $opt = $sel.find(':selected'),
                                        val = $opt.val();
                                    this.ui({
                                        type: 'li',
                                        text: val,
                                        attr: {
                                            'class': 'I18nEditMetadataListItem'
                                        },
                                        data: {
                                            value: val
                                        },
                                        children: [' [', {
                                            type: 'a',
                                            attr: {
                                                href: '#',
                                                'class': 'I18nEditRemoveMessage'
                                            },
                                            events: {
                                                click: $.proxy(function (e) {
                                                    e.preventDefault();
                                                    var $li = $(e.target).closest('li');
                                                    this.ui({
                                                        type: 'option',
                                                        text: $li.data('value'),
                                                        parent: '.I18nEditMetadataSelect'
                                                    });
                                                    $('.I18nEditMetadataAddMessage').removeAttr('disabled');
                                                    $li.remove();
                                                }, this)
                                            },
                                            text: 'remove'
                                        }, ']'],
                                        parent: '.I18nEditMetadataUL'
                                    });
                                    $opt.remove();
                                    if ($sel.children().length === 0) {
                                        $('.I18nEditMetadataAddMessage').attr('disabled', 'disabled');
                                    }
                                }, this)
                            },
                            text: 'Add message'
                        },
                        {
                            type: 'ul',
                            attr: {
                                'class': 'I18nEditMetadataUL'
                            }
                        },
                        {
                            type: 'button',
                            attr: {
                                'class': 'I18nEditSaveMetadata'
                            },
                            events: {
                                click: $.proxy(function (e) {
                                    var $childs = $('.I18nEditMetadataUL').children();
                                    if (!$childs.length) {
                                        delete this.messages._metadata;
                                    } else {
                                        this.messages._metadata = {
                                            noTranslate: $childs.toArray().map(function (el) {
                                                return el.getAttribute('data-value');
                                            }).sort()
                                        };
                                    }
                                    this.saveTranslations(e);
                                }, this)
                            },
                            text: this.msg('save-metadata')
                        }
                    ]
                })
            );
            window.dev.i18n.loadMessages(this.i18nPage(this.page), { noCache: true }).done($.proxy(this.cbMessagesLoad3, this));
        },
        /**
         * Initializes the translation editor interface
         */
        initTranslationEditor: function () {
            var lang = this.lang = this.getUrlVar(1);
            $('.page-header__title, #firstHeading').text(this.page);
            $('.page-header__page-subtitle, #contentSub').prepend(
                '< ',
                this.ui({
                    type: 'a',
                    attr: {
                        href: mw.util.wikiGetlink('Special:BlankPage/I18nEdit')
                    },
                    text: this.msg('search-title')
                }),
                ' | ',
                this.ui({
                    type: 'a',
                    attr: {
                        href: mw.util.wikiGetlink('Special:BlankPage/I18nEdit/' + this.page.replace(/ /g, '_'))
                    },
                    text: this.page
                }),
                ' | '
            );
            this.setTitle('editor');
            $('#mw-content-text').html(
                this.ui({
                    type: 'div',
                    attr: {
                        'class': 'I18nEditMain I18nEditEditor loading script-' + this.page +
                            ' lang-' + lang
                    },
                    children: [
                        {
                            type: 'p',
                            attr: { 'class': 'I18nEditEditorWarning' },
                            text: this.msg('warning-' + lang),
                            condition: ['en', 'qqq'].indexOf(lang) !== -1
                        },
                        {
                            type: 'table',
                            attr: {
                                id: 'I18nEditEditorTable',
                                'class': 'wikitable'
                            },
                            children: [
                                {
                                    type: 'tr',
                                    children: [
                                        'code',
                                        'description',
                                        'english',
                                        'translation'
                                    ].map(function (el) {
                                        return {
                                            type: 'th',
                                            text: this.msg('editor-table-' + el)
                                        };
                                    }, this)
                                }
                            ]
                        },
                        {
                            type: 'input',
                            attr: {
                                type: 'text',
                                placeholder: this.i18n.msg('summary', this.lang).plain(),
                                id: 'I18nEditSummary',
                                maxlength: '239'
                            },
                            events: {
                                keypress: function (e) {
                                    if (e.keyCode == 13) {
                                        $('#I18nEditEditorSubmit').click();
                                    }
                                }
                            }
                        },
                        {
                            type: 'button',
                            attr: { id: 'I18nEditEditorSubmit' },
                            events: { click: $.proxy(this.saveTranslations, this) },
                            text: this.msg('editor-save')
                        },
                        {
                            type: 'button',
                            attr: {
                                'class': 'I18nEditAddNewMessageButton'
                            },
                            events: {
                                click: $.proxy(function () {
                                    var key = prompt(this.msg('message-code'));
                                    if (!key) {
                                        return;
                                    }
                                    if (this.messages.qqq) {
                                        var qqq = prompt(this.msg('message-qqq'));
                                        if (qqq) {
                                            this.messages.qqq[key] = qqq;
                                        }
                                    }
                                    this.addTableRow(key, '');
                                }, this)
                            },
                            text: this.msg('add-new-message'),
                            condition: lang === 'en'
                        }
                    ]
                })
            );
            window.dev.i18n.loadMessages(this.i18nPage(this.page), { noCache: true }).done($.proxy(this.cbMessagesLoad2, this));
        },
        /**
         * Callback after the messages for a certain
         * script have been loaded by UI-js from the
         * editor interface
         * @param {Object} i18n I18n object
         */
        cbMessagesLoad2: function (i18n) {
            this.messages = i18n._messages;
            var lang = this.lang;
            this.messages[lang] = this.messages[lang] || {};
            if (lang === 'metadata') {
                this.messages.metadata = $.extend({
                    noTranslate: '[]'
                }, this.messages._metadata);
            }
            var trans = $.extend({}, this.messages.en, this.messages[lang]);
            if (this.messages._metadata && this.messages._metadata.noTranslate && lang !== 'en') {
                this.messages._metadata.noTranslate.forEach(function (l) {
                    delete trans[l];
                });
            }
            $.each(trans, $.proxy(this.addTableRow, this));
            $('.I18nEditEditor').toggleClass('loading');
        },
        /**
         * Adds a row to the editor table
         * @param {Number} i Number of the row
         * @param {Object} val Message in the row
         */
        addTableRow: function (i, val) {
            var key = 'I18nEdit/' + this.page,
                json = this.storage.get(key),
                obj = json ? JSON.parse(json) : {};
            this.ui({
                type: 'tr',
                data: { code: i },
                children: [
                    {
                        type: 'td',
                        children: [
                            {
                                type: 'code',
                                text: i
                            }
                        ]
                    },
                    {
                        type: 'td',
                        text: this.messages.qqq && this.messages.qqq[i] || 'N/A'
                    },
                    {
                        type: 'td',
                        text: this.messages.en[i] || 'N/A'
                    },
                    {
                        type: 'td',
                        children: [
                            {
                                type: 'textarea',
                                text: obj[i] || val,
                                attr: {
                                    'class': 'I18nEditTranslateInput'
                                },
                                events: {
                                    input: function () {
                                        var json = I18nEdit.storage.get(key),
                                            obj = json ? JSON.parse(json) : {};
                                        obj[i] = this.value;
                                        I18nEdit.storage.set(key, JSON.stringify(obj));
                                    }
                                }
                            }
                        ]
                    }
                ],
                parent: '#I18nEditEditorTable'
            });
        },
        /**
         * Callback after the messages for a certain
         * script have been loaded by UI-js from the
         * metadata editor interface
         * @param {Object} i18n I18n object
         */
        cbMessagesLoad3: function (i18n) {
            this.messages = i18n._messages;
            var meta = this.messages._metadata = $.extend({
                noTranslate: []
            }, this.messages._metadata),
                messages = Object.keys(this.messages.en);
            $.each(meta.noTranslate, $.proxy(function (i, val) {
                messages.splice(messages.indexOf(val), 1);
                this.ui({
                    type: 'li',
                    text: val,
                    attr: {
                        'class': 'I18nEditMetadataListItem'
                    },
                    data: {
                        value: val
                    },
                    children: [' [', {
                        type: 'a',
                        attr: {
                            href: '#',
                            'class': 'I18nEditRemoveMessage'
                        },
                        events: {
                            click: $.proxy(function (e) {
                                var $li = $(e.target).closest('li');
                                this.ui({
                                    type: 'option',
                                    text: $li.data('value'),
                                    parent: '.I18nEditMetadataSelect'
                                });
                                $('.I18nEditMetadataAddMessage').removeAttr('disabled');
                                $li.remove();
                            }, this)
                        },
                        text: 'remove'
                    }, ']'],
                    parent: '.I18nEditMetadataUL'
                });
            }, this));
            $.each(messages, $.proxy(function (i, val) {
                this.ui({
                    type: 'option',
                    text: val,
                    parent: '.I18nEditMetadataSelect'
                });
            }, this));
            if (messages.length) {
                $('.I18nEditMetadataAddMessage').removeAttr('disabled');
            }
        },
        /**
         * Saves the translations to the respective JSON page
         * @param {Event} e Click event
         */
        saveTranslations: function (e) {
            e.preventDefault();
            var lang = this.lang,
                changedMessages = [];
            // Re-add removed translations by _metadata.noTranslate
            $.extend(this.messages[lang], lang === 'metadata' ? null : this.messages.en, this.messages[lang]);
            $('#I18nEditEditorTable .I18nEditTranslateInput').each($.proxy(function (_, el) {
                var $this = $(el),
                    code = $this.parent().parent().data('code'),
                    val = $this.val();
                if (this.messages[lang][code] === val) {
                    return;
                }
                if (this.messages[lang][code]) {
                    changedMessages.push(code);
                }
                if (lang !== 'metadata') {
                    this.messages[lang][code] = $this.val();
                }
            }, this));
            // If the user changed English messages already translated
            if (lang === 'en' && changedMessages.length) {
                var confirmation = confirm(this.msg('remove-translations-prompt'));
                if (confirmation) {
                    $.each(this.messages, $.proxy(function (i) {
                        if (i === '_metadata') {
                            return;
                        }
                        $.each(changedMessages, $.proxy(function (j, msg) {
                            if (
                                this.messages._metadata &&
                                this.messages._metadata.noTranslate instanceof Array &&
                                this.messages._metadata.noTranslate.indexOf(msg) === -1
                            ) {
                                this.messages[i][msg] = this.messages.en[msg];
                            }
                        }));
                    }, this));
                }
            }
            // Sort object keys, with exceptions
            var messages = {},
                specials = {
                    _metadata: 1,
                    en: 2,
                    qqq: 3
                };
            Object.keys(this.messages).sort(function (a, b) {
                if (specials[a] && specials[b]) {
                    return specials[a] - specials[b];
                } else if (specials[a]) {
                    return -1;
                } else if (specials[b]) {
                    return 1;
                } else {
                    return a.localeCompare(b);
                }
            }).forEach(function (key) {
                messages[key] = this.messages[key];
            }, this);
            this.api.postWithEditToken({
                action: 'edit',
                title: 'MediaWiki:I18n-' + this.page + '.json',
                text: JSON.stringify(messages, null, 4),
                summary: '[I18nEdit] ' + (
                    $('#I18nEditSummary').val() ||
                    this.i18n.msg('summary', this.lang).plain()
                ),
                minor: true,
                bot: true
            }).done($.proxy(this.cbSave, this));
        },
        /**
         * Callback after the page has been saved
         * @param {Object} d API result
         */
        cbSave: function (d) {
            if (d.error) {
                mw.notify(this.escaped('error') + ': ' + d.error.code);
            } else {
                this.storage.remove('I18nEdit/' + this.page);
                mw.notify(this.escaped('success'));
                setTimeout($.proxy(function () {
                    window.location.pathname = '/wiki/Special:Blankpage/I18nEdit/' + this.page;
                }, this), 2000);
            }
        },
        /**
         * Returns a message with specified code as plain
         * @param {String} msg Code of the message
         * @returns {String} Translated message with the specified code
         */
        msg: function (msg) {
            return this.i18n.msg(msg).plain();
        },
        /**
         * Returns a message with specified code, escaped
         * @param {String} msg Code of the message
         * @returns {String} Translated message with the specified code, escaped
         */
        escaped: function (msg) {
            return this.i18n.msg(msg).escape();
        },
        /**
         * Gets the right i18n data page to load based on
         * the current wiki.
         * @param {String} page The page to load
         */
        i18nPage: function (page) {
            var server = conf.wgServer
                .match(/https?:\/\/([a-z0-9-]+)\.(?:fandom\.com|wikia\.org)/),
                toLoad = page;
            if (server && server[1] !== 'dev') {
                toLoad = 'u:' + server[1] + ':MediaWiki:I18n-' + page + '/i18n.json';
            }
            return toLoad;
        }
    };
    // Loading necessary resources
    mw.loader.load('https://common.wjghj.cn/js/i18n-js');
    mw.loader.load('https://common.wjghj.cn/js/UI-js');
    mw.loader.load('https://common.wjghj.cn/css/i18n-js/editor', 'text/css');
    // Run the main when scripts loads
    mw.hook('dev.ui').add($.proxy(I18nEdit.preload, I18nEdit));
    mw.hook('dev.i18n').add($.proxy(I18nEdit.preload, I18nEdit));
    mw.loader.using(['mediawiki.api.edit']).then($.proxy(I18nEdit.preload, I18nEdit));
})();