Utilisateur:Phe/Aide-LangConverter.js

Note : après avoir enregistré vos modifications, il se peut que vous deviez forcer le rechargement complet du cache de votre navigateur pour voir les changements.

  • Firefox / Safari : Maintenez la touche Maj (Shift) en cliquant sur le bouton Actualiser ou pressez Ctrl-F5 ou Ctrl-R (⌘-R sur un Mac) ;
  • Google Chrome : Appuyez sur Ctrl-Maj-R (⌘-Shift-R sur un Mac) ;
  • Internet Explorer : Maintenez la touche Ctrl en cliquant sur le bouton Actualiser ou pressez Ctrl-F5 ;
  • Opera : Allez dans Menu → Settings (Opera → Préférences sur un Mac) et ensuite à Confidentialité & sécurité → Effacer les données d'exploration → Images et fichiers en cache.
/**
 * LangConverter-Helper.js
 *
 * Auteur : Phe
 *
 *   The ui is a jquery dialog containing one tab per variant, tabs contain
 * three list (ul) sortable enabled between list. User build data to generate
 * the template by drag&drop'ing the item to the right list.
 *
 *   The list in the center of the dialog contains suggestion, they come from other
 * local dict or from a spell checking of the page contents html. Right list
 * contains the list used to generate the template, this list is pre-filled
 * with the actual content of the local dict which match the html text of
 * the current page (useless local dict entry are silently ignored). Left
 * list acts as a blacklist, all entry dropped in this list will be ignored
 * in any later call to this script on any page (the ignore list is global).
 * (Poorly implemented on the server side atm) Blacklist is per wiki, not per
 * lang variant.
 *
 *
 * cookie used : 'lc_ui_setting', json stringified data of the dialog size.
 *
 * TODO:
 *
 * Dropping back to the candidate container does not put it in
 * in the right place, annoying for spelling suggestion as it split
 * the group in multiple sub-group.
 *
 * Merge with LanguageConverter.
 *
 * string localisation.
 *
 * search FIXME in the code.
 *
 * Add transparency to the dialog so the text under the dialog is visible.
 *
 * double-click to move from/to central list/right list, double-click to move from left to central list.
 *
 * auto update cache on server side, can be done by sending data to the server ? but we don't know the new rev id.
 *
 * add an edit field to allow to add dict entry not provide through the central list.
 *
 * find all occurrence of a word when clicking a word, but ... conflict a bit with double click ?
 *
 * vertical disposition of the ui ?
 *
 * Sort list.
 *
 * write docs.
 */

/**
 * Setup the Language Converter global:
 */
if ( window.LanguageConverter === undefined ) {
	window.LanguageConverter = {};
}
 
/**
 * The global Language Converter object
 */
( function ( mw, $, lc ) {
'use strict';

/**** Customization ****/

// Same as lc.variant_list but exclude the original variant, these names are used
// as #id tab suffix, they need to be the same name as server variant name.
// See https://github.com/phil-el/phetools/blob/master/modernization/modernization.py
// variable dict_config['xxx']['variant'] for the supported lang and variant.
lc.variant_name = [ 'FR' ];
// Tab name, always needed but hidden in the dialog if there is only one variant.
lc.variant_tab_name = { 'FR' : 'Français moderne' };

// template name to generate
lc.template_modernize = 'modernisation';
// Each template variant is expanded only if the dict for this variant is empty. For pt.ws
// something ala, the key must match a lc.variant_name.
// lc.template_variant = { 'PT' : '\n|pt-PT=\n@@PT@@\n</div>', 'BR' : '.....' }
lc.template_variant = { 'FR' : '|\n@@FR@@' };
// a regex to match the modernization template, text matched, if any, will be removed from the page
// and replaced by the template build with the above setting.
lc.template_regex = /{{\s*[mM]odernisation[\s\S]*?}}/m;

/**** End of customization ****/

// return the first elt class name matching regexp or null if none.
lc.get_class_name = function (elt, regex) {
   var result = null;

   var class_list = $(elt).attr("class").split(/\s+/);
   $.each(class_list, function(index, item) {
       if (regex.test(item)) {
           result = item;
           return false;
       }
   });

   return result;
};

lc.write_page = function (pagename, text, comment) {
        var api = new mw.Api();

        return api.postWithEditToken( {
           action : 'edit',
           text : text,
           comment : comment,
           title : pagename,
           } );
};

lc.load_ui_setting = function (name) {
        var result = { width : 300, height : 480 };

        var cookie_value = $.cookie('lc_ui_setting');

        if (cookie_value) {
	    var value = JSON.parse(cookie_value);
	    result = $.extend({}, result, value);
        }
	return result;
};

lc.save_ui_setting = function (event, ui) {
	var value = { width : $( "#lc-dialog" ).dialog( "option", "width" ),
		      height : $( "#lc-dialog" ).dialog( "option", "height" ) };

	var serialized = JSON.stringify(value);
        $.cookie('lc_ui_setting', serialized, { path : '/wiki/', expires : 180 } );
};

lc.save_result = function (page_text) {

    if (lc.blacklist.length) {
        $.getJSON( 'https://tools.wmflabs.org/phetools/modernization_cgi.py', {
                  cmd: 'blacklist',
                  blacklist: JSON.parse(lc.blacklist),
                  lang: mw.config.get('wgContentLanguage')
        } ).done( function ( data ) {
                  mw.log('Blacklist request answser: ' + JSON.parse(data));
        } );
    }

    var template_text = '{{' + lc.template_modernize;
    for (var i in lc.variant_name) {
        var variant_text = '';
        for (var j in lc.template_content[lc.variant_name[i]]) {
            variant_text += '* ' + lc.template_content[lc.variant_name[i]][j] + '\n';
        }
        if (variant_text) {
              variant_text = lc.template_variant[lc.variant_name[i]].replace('@@' + lc.variant_name[i] + '@@', variant_text);
              template_text += variant_text;
        }
    }
    template_text += '}}';

    var match = lc.template_regex.exec(page_text);
    if (match) {
        page_text = page_text.replace(match, template_text);
    } else {
        page_text = page_text + '\n' + template_text;
    }
    var $promise = lc.write_page(mw.config.get('wgPageName'), page_text, 'mise à jour du modèle modernisation');
    $.when($promise.done()).then(function () { window.location = window.location; } );
};

lc.change_page = function (pagename) {
	$.ajax({
		url: mw.util.wikiScript( 'api' ),
		dataType: 'json',
		data: {
			'format': 'json',
			'action': 'query',
			'titles': pagename,
			'prop': 'revisions',
			'rvprop': 'content',
			'indexpageids': '1'
		},
                success : function (res) {
                    if (res.query && res.query.pages && res.query.pageids && res.query.pageids.length) {
                    var pages = res.query.pages;
                    var pageids = res.query.pageids ;
                    if ( pages[ pageids[0] ].pageid ) {
                        var page_text = pages[ pageids[0] ].revisions[0]['*'];
                        lc.save_result(page_text);
                    }
                }
            }
	});
};

lc.on_dialog_ok = function(event, ui) {
        lc.blacklist = [];
        $('#lc-sortable-blacklist li').each(function () {
            lc.blacklist.push($(this).text());
        });

        lc.template_content = {};
        for (var i in lc.variant_name) {
            lc.template_content[lc.variant_name[i]] = [];
            $('#lc-tabs-' + lc.variant_name[i] + ' #lc-sortable-modernize li').each(function () {
                lc.template_content[lc.variant_name[i]].push($(this).text());
            }); // FIXME: warning don't make functions within a loop
        }

        var text = lc.change_page(mw.config.get('wgPageName'));

        $("#lc-dialog").dialog("close");
};

lc.on_create_dialog = function(event, ui) {
        $(event.target).parent().css( { position : 'fixed' } );
};

// The dialog is opened after the server call is done as we need the data
// to fill the dialog box.
lc.open_dialog = function() {
        $.getJSON( 'https://tools.wmflabs.org/phetools/modernization_cgi.py', {
              cmd: 'suggest',
              title: mw.config.get('wgPageName'),
              lang: mw.config.get('wgContentLanguage')
         } ).done( function ( data ) {
	         lc.setup_dialog(data);
         } );
};

lc.list_from_array = function (arr) {
        var html = '';
        for (var i in arr) {
            html += '<li>' + arr[i][0] + ' : ' + arr[i][1] + '</li>\n';
        }
        return html;
};

lc.list_from_suggest = function (arr) {
       var html = '';
       for (var i in arr) {
           // group_name is a class name used to hide/unhide all other element except the one dropped.
           var group_name = 'lc-group-suggest-' + i;

           //$('<style type="text/css"></style>')
           //    .html(".ui-selecting { background: silver; }\n.ui-selected { background: gray; }")
           //   .appendTo("head");

           for (var j in arr[i][1]) {
                var style = '';
/*
                if (j === 0) {
                   style += 'style="padding-top:0.15em;"';
                } else if (j == arr[i][1].length - 1) {
                   style += 'style="padding-bottom:0.15em;"';
                }
*/
                html += '<li class="' + group_name + '" '  + style + '>' + arr[i][0] + ' : ' + arr[i][1][j] + '</li>\n';
           }
       }
       return html;
};

lc.resize_ul = function () {
        // Hack $('#ui-dialog-titlebar').height() and $('#ui-dialog-buttonpane').height() return 0, so hard code a guessed value.
        var max_height = $('#lc-dialog').height() - 48; /*( $('#ui-dialog-titlebar').height() + $('#ui-dialog-buttonpane').height() ); Broken return 0 */
        $('#lc-sortable-blacklist, #lc-sortable-candidate, #lc-sortable-modernize').height(max_height);
};

lc.setup_dialog = function (data) {

        for (var key in data) {
             var local_dict_used = data[key].local_dict_used;
             var html = lc.list_from_array(local_dict_used);
             $('#lc-tabs-' + key + ' #lc-sortable-modernize').html(html);

             var suggest_local_dict = data[key].suggest_local_dict;
             html = lc.list_from_array(suggest_local_dict);   
             var speller_suggest = data[key].speller_suggest;
             html += lc.list_from_suggest(speller_suggest);
             $('#lc-tabs-' + key + ' #lc-sortable-candidate').html(html);
        }

        var buttons = [
            {
                text: "Ok",
                click: function() { lc.on_dialog_ok(); }
            },
            {
                text: "Annuler",
                click: function() { $(this).dialog("close"); }
            }
        ];

        var defaults = lc.load_ui_setting();
 
        $("#lc-dialog").dialog({
            create: lc.on_create_dialog,
            close : lc.save_ui_setting,

            resizeStop: function(event, ui) {
                lc.resize_ul();
                //$(event.target).parent().css('position', 'fixed');
                //$(dlg).dialog('option','position', 'right');
            },

            buttons : buttons,
            position : 'right',
            height : defaults.height,
            width  : defaults.width,
            minHeight: 180,
            maxHeight : 640 /* Why a max? */
            });
 
        /* FIXME: must be unbinded when closing the dialog or must be setup in setup() */
        $(window).resize(function() {
            $("#lc-dialog").dialog('option', 'position', 'right');
        });

        $('.lc-sortable').hide();

        $('.lc-sortable').sortable({
		connectWith: '.lc-sortable',
                remove: function (event, ui) {
                    var class_name = lc.get_class_name(ui.item, /^lc-group-suggest-[\d]+$/);

/*                  FIXME: do we need something like this in some case, drop to blacklist hide all
                    but dropping to candidate must hide only for the current tab not for the inactive tab
                    var $tabs = $('#lc-tabs').tabs();
                    // Jquery >= 1.9, 'selected' rather than 'active' for < 1.9
                    var active = $tabs.tabs('option', 'active');
                    var active_tab_id = $('div[id^="lc-tabs-"]')[active].getAttribute('id');
                    if (this === $('#' + active_tab_id + ' #lc-sortable-candidate')[0]) {
                        $('.' + class_name).show();
                    } else {
                        $('.' + class_name).hide();
                    }
*/
                    // See http://stackoverflow.com/questions/9440664/jquery-ui-sortable-drag-initiation-is-slow-when-container-has-hidden-items
                    if (ui.item[0].parentNode.id == 'lc-sortable-candidate') {
                           $('.' + class_name).removeClass('hidden');
                    } else {
                           $('.' + class_name).addClass('hidden');
                    }
                    $(ui.item).removeClass('hidden');
                },
	});

        $('.lc-sortable').show();

        lc.resize_ul();
};

lc.init_and_open = function() {

        if (!$('#lc-dialog').length) {

            $('<style type="text/css"></style>')
                .html(".hidden {  height:0; overflow:hidden;margin-bottom:0; }")
                .appendTo("head");

            // jquery remove the display:none when opening the dialog, adding it here is needed to avoid
            // to see part of the dialog on the page between creation and opening.
            $('#content').append('<div id="lc-dialog" class="help-lang-converter" title="Aide à la modernisation" style="display:none;"></div>');

            // html struct
            // div dialog
            //    div > ul blacklist at left
            //    div tabs at right
            //      for each tabs
            //         div > ul candidate
            //         div > ul modernize
 
            var inner_tab = '<div style="text-align:center;display:block;float:left;width:48%;">Candidat&nbsp;:<ul id="lc-sortable-candidate" style="text-align:left;min-height:32px;overflow-y:scroll;" class="lc-sortable"></ul></div><div style="text-align:center;display:block;float:right;width:48%;">Modernise&nbsp;:<ul id="lc-sortable-modernize" class="lc-sortable" style="text-align:left;min-height:32px;overflow-y:scroll;"></ul></div>';
             var tabs_list_html = '';
             for (var i in lc.variant_name) {
                   tabs_list_html += '<li><a href="#lc-tabs-' + lc.variant_name[i] + '">' + lc.variant_tab_name[lc.variant_name[i]] + '</a></li>\n';
             }

             // padding-top:1em; is a kludge to top-align this container with the contents of the tabs container 
             var html = '<div style="text-align:center;display:block;float:left;width:30%;padding-top:1em;">Suppression&nbsp;:<ul style="text-align:left;min-height:32px;overflow-y:scroll;" id="lc-sortable-blacklist" class="lc-sortable"></ul></div><div id="lc-tabs">\n<ul>\n' + tabs_list_html + '</ul>\n';
             for (var j in lc.variant_name) {
                  html += '<div id="lc-tabs-' + lc.variant_name[i] + '" style="display:block;float:right;width:60%">' + inner_tab + '</div></div>\n';
             }
             html += '</div>';

             $('#lc-dialog').html(html);

             // We always build and activate tabs even if there is only one tab, in this case
             // we hide the tab, it simplify the used selector as they don't depend on the
             // number of variant.
             if (lc.variant_name.length == 1) {
                 // pointeless to show the tab if there is only one variant.
                 $('#lc-tabs > ul').attr('style', 'display:none');
                 $('#lc-tabs').css('border', '0px');
             }

             $('#lc-tabs').tabs();
        }

    lc.open_dialog();
};

lc.init = function() {
        // FIXME: remove this condition when integrating to LanguageConverter or use the same condition as lc but with an implicit selection_mode == OR
        // as we want to allow to work on a page w/o a modernization template.
	if (mw.config.get('wgNamespaceNumber') === 0 && (mw.config.get('wgAction') === 'view' || mw.config.get('wgAction') === 'history')) {
		mw.util.addPortletLink(
      		'p-cactions',
		 null,
      		'Modernisation',
      		'ca-dict-helper',
      		'Open modernization dict helper'
                );
        } else {
                return;
        }


        $('li#ca-dict-helper a').click(function() {
                lc.init_and_open();
        });
};

$.when(
	mw.loader.using( [ 'mediawiki.api', 'mediawiki.util', 'jquery.ui', 'jquery.ui', 'ext.gadget.modernisation' /*, 'jquery.ui' */ ] ),
	$.ready
).then( function () {
	lc.init();
});

}( mediaWiki, jQuery, window.LanguageConverter ) );