|
[MediaWiki-CVS] SVN: [54116] trunk/extensions/UsabilityInitiative: msg#01494mediawiki-cvs
http://www.mediawiki.org/wiki/Special:Code/MediaWiki/54116 Revision: 54116 Author: catrope Date: 2009-07-31 18:53:46 +0000 (Fri, 31 Jul 2009) Log Message: ----------- UsabilityInitiative: Some performance fixes for the toolbar. About 80% of toolbar loading time is spent on special characters; reduced specialchars loading by 25%, but could probably still use improvement. Modified Paths: -------------- trunk/extensions/UsabilityInitiative/EditToolbar/EditToolbar.js trunk/extensions/UsabilityInitiative/UsabilityInitiative.hooks.php trunk/extensions/UsabilityInitiative/js/jquery.combined.js trunk/extensions/UsabilityInitiative/js/jquery.combined.min.js trunk/extensions/UsabilityInitiative/js/jquery.toolbar.js Property Changed: ---------------- trunk/extensions/UsabilityInitiative/js/jquery.toolbar.js trunk/extensions/UsabilityInitiative/js/jquery.wikiOutline.js Modified: trunk/extensions/UsabilityInitiative/EditToolbar/EditToolbar.js =================================================================== --- trunk/extensions/UsabilityInitiative/EditToolbar/EditToolbar.js 2009-07-31 18:47:15 UTC (rev 54115) +++ trunk/extensions/UsabilityInitiative/EditToolbar/EditToolbar.js 2009-07-31 18:53:46 UTC (rev 54116) @@ -2,7 +2,7 @@ js2AddOnloadHook( function() { $j( 'textarea#wpTextbox1' ) - .wrap( $j( '<div></div>' ) .attr( 'id', 'edit-ui' ) ) + .wrap( $j( '<div></div>' ).attr( 'id', 'edit-ui' ) ) .wrap( $j( '<div></div>' ).attr( 'id', 'edit-ui-bottom' ) ) .wrap( $j( '<div></div>' ).attr( 'id', 'edit-ui-text' ) ); $j( 'div#edit-ui' ).prepend( @@ -11,12 +11,10 @@ .append( $j( '<div></div>' ) .attr( 'id', 'edit-toolbar' ) - .toolbar( - $j( 'textarea#wpTextbox1' ), - editToolbarConfiguration - ) ) ); + $j( 'div#edit-toolbar' ).toolbar( $j( 'textarea#wpTextbox1' ), + editToolbarConfiguration ); }); // Generate special chars tools from an array of characters Modified: trunk/extensions/UsabilityInitiative/UsabilityInitiative.hooks.php =================================================================== --- trunk/extensions/UsabilityInitiative/UsabilityInitiative.hooks.php 2009-07-31 18:47:15 UTC (rev 54115) +++ trunk/extensions/UsabilityInitiative/UsabilityInitiative.hooks.php 2009-07-31 18:53:46 UTC (rev 54116) @@ -17,10 +17,10 @@ private static $scriptFiles = array( 'base_sets' => array( 'combined' => array( - array( 'src' => 'js/jquery.combined.js', 'version' => 2 ), + array( 'src' => 'js/jquery.combined.js', 'version' => 3 ), ), 'combined-min' => array( - array( 'src' => 'js/jquery.combined.min.js', 'version' => 2 ), + array( 'src' => 'js/jquery.combined.min.js', 'version' => 3 ), ), 'raw' => array( array( 'src' => 'js/jquery.js', 'version' => 2 ), @@ -28,7 +28,7 @@ array( 'src' => 'js/jquery.browser.js', 'version' => 2 ), array( 'src' => 'js/jquery.cookie.js', 'version' => 2 ), array( 'src' => 'js/jquery.textSelection.js', 'version' => 2 ), - array( 'src' => 'js/jquery.toolbar.js', 'version' => 2 ), + array( 'src' => 'js/jquery.toolbar.js', 'version' => 3 ), array( 'src' => 'js/jquery.wikiOutline.js', 'version' => 2 ), ), ), Modified: trunk/extensions/UsabilityInitiative/js/jquery.combined.js =================================================================== --- trunk/extensions/UsabilityInitiative/js/jquery.combined.js 2009-07-31 18:47:15 UTC (rev 54115) +++ trunk/extensions/UsabilityInitiative/js/jquery.combined.js 2009-07-31 18:53:46 UTC (rev 54116) @@ -4910,12 +4910,9 @@ $(this).addToolbarSection( tools.main, textbox, 'main' ); } var tabDiv = $( '<div></div>' ) - .attr( 'class', 'tabs' ) - .appendTo( $(this) ); + .attr( 'class', 'tabs' ); var sectionsDiv = $( '<div></div>' ) - .attr( 'class', 'sections' ) - .appendTo( $(this) ); - $(this).append( $( '<div></div>' ).addClass( 'break' ) ); + .attr( 'class', 'sections' ); var sectionCookie = 'edittoolbar-' + $(this).attr( 'id' ) + '-section'; var sectionQueue = []; for ( section in tools ) { @@ -4927,14 +4924,14 @@ 'class': 'section', 'id': $(this).attr( 'id' ) + '-section-' + section } ) - .appendTo( sectionsDiv ) .addClass( 'loading' ) .append( $( '<div></div>' ) .addClass( 'progress' ) .text( gM( 'edittoolbar-loading' ) ) - ); + ) + .appendTo( sectionsDiv ); var current = false; if ( $.cookie( sectionCookie ) == sectionDiv.attr( 'id' ) ) { sectionDiv.attr( 'style', 'display:block' ); @@ -4991,6 +4988,9 @@ ) ); } + $(this).append( tabDiv ) + .append( sectionsDiv ) + .append( $( '<div></div>' ).addClass( 'break' ) ); $.eachAsync( sectionQueue, { bulk: 0, loop: function( index, value ) { @@ -5032,8 +5032,7 @@ } for ( group in section.groups ) { var groupDiv = $( '<div></div>' ) - .attr( 'class', 'group' ) - .appendTo( $(this) ); + .attr( 'class', 'group' ); if ( msgSet( section.groups[group], 'label' ) ) { groupDiv.append( $( '<div></div>' ) @@ -5103,6 +5102,7 @@ default: break; } } + $(this).append( groupDiv ); } break; case 'booklet': @@ -5110,8 +5110,7 @@ return; } var indexDiv = $( '<div></div>' ) - .attr( 'class', 'index' ) - .appendTo( $(this) ); + .attr( 'class', 'index' ); var bookletCookie = 'edittoolbar-' + $(this).attr( 'id' ) + '-booklet-' + id; var selectedID = $.cookie( bookletCookie ); @@ -5151,13 +5150,11 @@ ); } var pagesDiv = $( '<div></div>' ) - .attr( 'class', 'pages' ) - .appendTo( $(this) ); + .attr( 'class', 'pages' ); for ( page in section.pages ) { var pageDiv = $( '<div></div>' ) .attr( 'class', 'page page-' + page ) - .css( 'display', page === selectedID ? 'block' : 'none' ) - .appendTo( pagesDiv ); + .css( 'display', page === selectedID ? 'block' : 'none' ); switch ( section.pages[page].layout ) { case 'table': var contentTable = $( '<table></table>' ) @@ -5166,10 +5163,8 @@ 'cellspacing': '0', 'border': '0', 'width': '100%' - } ) - .appendTo( pageDiv ); - var headingRow = $( '<tr></tr>' ) - .appendTo( contentTable ); + } ); + var headingRow = $( '<tr></tr>' ); for ( heading in section.pages[page].headings ) { $( '<th></th>' ) .text( @@ -5181,9 +5176,9 @@ ) .appendTo( headingRow ); } + contentTable.append( headingRow ); for ( row in section.pages[page].rows ) { - var contentRow = $( '<tr></tr>' ) - .appendTo( contentTable ); + var contentRow = $( '<tr></tr>' ); for ( cell in section.pages[page].rows[row] ) { $( '<td></td>' ) .attr( { @@ -5202,13 +5197,14 @@ ) .appendTo( contentRow ); } + contentTable.append( contentRow ); } + pageDiv.append( contentTable ); break; case 'characters': var charsDiv = $( '<div />' ) .attr( section.pages[page].attributes ) - .css( section.pages[page].styles ) - .appendTo( pageDiv ); + .css( section.pages[page].styles ); for ( character in section.pages[page].characters ) { switch ( section.pages[page].characters[character].type @@ -5228,17 +5224,18 @@ ) .data( 'context', context) .click( action ) - .click( - function() { return false; } - ) ); break; } } + pageDiv.append( charsDiv ); break; default: break; } + pagesDiv.append( pageDiv ); } + $(this).append( indexDiv ) + .append( pagesDiv ); break; default: break; } Modified: trunk/extensions/UsabilityInitiative/js/jquery.combined.min.js =================================================================== --- trunk/extensions/UsabilityInitiative/js/jquery.combined.min.js 2009-07-31 18:47:15 UTC (rev 54115) +++ trunk/extensions/UsabilityInitiative/js/jquery.combined.min.js 2009-07-31 18:53:46 UTC (rev 54116) @@ -471,28 +471,31 @@ return this.each(function(){$(this).focus();if(this.selectionStart||this.selectionStart=='0'){this.selectionStart=pos;this.selectionEnd=pos;$(this).scrollTop(getCaretScrollPosition(this));}else if(document.selection&&document.selection.createRange){range=document.selection.createRange();oldPos=$(this).bytePos();goBack=false;if(oldPos==pos){pos++;goBack=true;} range.moveToElementText(this);range.collapse();range.move('character',pos);range.select();this.scrollTop+=range.offsetTop;if(goBack){range.move('character',-1);range.select();}} $(this).trigger('scrollToPosition');});}});})(jQuery);(function($){$.fn.extend({toolbar:function(textbox,tools){return this.each(function(){if('main'in tools){$(this).addToolbarSection(tools.main,textbox,'main');} -var tabDiv=$('<div></div>').attr('class','tabs').appendTo($(this));var sectionsDiv=$('<div></div>').attr('class','sections').appendTo($(this));$(this).append($('<div></div>').addClass('break'));var sectionCookie='edittoolbar-'+$(this).attr('id')+'-section';var sectionQueue=[];for(section in tools){if(section=='main'){continue;} -var sectionDiv=$('<div></div>').attr({'class':'section','id':$(this).attr('id')+'-section-'+section}).appendTo(sectionsDiv).addClass('loading').append($('<div></div>').addClass('progress').text(gM('edittoolbar-loading')));var current=false;if($.cookie(sectionCookie)==sectionDiv.attr('id')){sectionDiv.attr('style','display:block');current=true;} +var tabDiv=$('<div></div>').attr('class','tabs');var sectionsDiv=$('<div></div>').attr('class','sections');var sectionCookie='edittoolbar-'+$(this).attr('id')+'-section';var sectionQueue=[];for(section in tools){if(section=='main'){continue;} +var sectionDiv=$('<div></div>').attr({'class':'section','id':$(this).attr('id')+'-section-'+section}).addClass('loading').append($('<div></div>').addClass('progress').text(gM('edittoolbar-loading'))).appendTo(sectionsDiv);var current=false;if($.cookie(sectionCookie)==sectionDiv.attr('id')){sectionDiv.attr('style','display:block');current=true;} sectionQueue[sectionQueue.length]={'sectionDiv':sectionDiv,'tools':tools[section],'textbox':textbox};tabDiv.append($('<span></span>').attr('class','tab').append($('<a></a>').text(tools[section].label||gM(tools[section].labelMsg)).attr({'href':'#','rel':section,'class':current?'current':null}).data('sectionDiv',sectionDiv).data('sectionCookie',sectionCookie).data('textbox',textbox).click(function(){$(this).blur();var show=($(this).data('sectionDiv').css('display')=='none');$(this).data('sectionDiv').parent().children().hide();$(this).parent().parent().find('a').removeClass('current');if(show){$(this).data('sectionDiv').show();$(this).addClass('current');} $.cookie($(this).data('sectionCookie'),show?$(this).data('sectionDiv').attr('id'):null);return false;})));} -$.eachAsync(sectionQueue,{bulk:0,loop:function(index,value){value.sectionDiv.addToolbarSection(value.tools,value.textbox,index);value.sectionDiv.removeClass('loading')}})});},addToolbarSection:function(section,textbox,id){var imagePath=wgScriptPath+'/extensions/UsabilityInitiative/EditToolbar/images/';function msgSet(object,property){return property in object||property+'Msg'in object;} +$(this).append(tabDiv).append(sectionsDiv).append($('<div></div>').addClass('break'));$.eachAsync(sectionQueue,{bulk:0,loop:function(index,value){value.sectionDiv.addToolbarSection(value.tools,value.textbox,index);value.sectionDiv.removeClass('loading')}})});},addToolbarSection:function(section,textbox,id){var imagePath=wgScriptPath+'/extensions/UsabilityInitiative/EditToolbar/images/';function msgSet(object,property){return property in object||property+'Msg'in object;} function msg(object,property){return object[property]||gM(object[property+'Msg']);} var action=function(event){$(this).useTool($(this).data('context').tool,$(this).data('context').textbox);event.preventDefault();};switch(section.type){case'toolbar':if(!('groups'in section)){return;} -for(group in section.groups){var groupDiv=$('<div></div>').attr('class','group').appendTo($(this));if(msgSet(section.groups[group],'label')){groupDiv.append($('<div></div>').attr('class','label').text(msg(section.groups[group],'label')))} +for(group in section.groups){var groupDiv=$('<div></div>').attr('class','group');if(msgSet(section.groups[group],'label')){groupDiv.append($('<div></div>').attr('class','label').text(msg(section.groups[group],'label')))} for(tool in section.groups[group].tools){if('filters'in section.groups[group].tools[tool]){var filters=section.groups[group].tools[tool].filters;var skip=false;for(filter in filters){if($(filters[filter]).size()==0){skip=true;}} if(skip){continue;}} var context={'tool':section.groups[group].tools[tool],'textbox':textbox};var label=msg(section.groups[group].tools[tool],'label');switch(section.groups[group].tools[tool].type){case'button':groupDiv.append($('<input />').attr({src:imagePath+ section.groups[group].tools[tool].icon,alt:label,title:label,'class':'tool','type':'image'}).data('context',context).click(action));break;case'select':var selectDiv=$('<select></select>').data('context',context).change(action).append($('<option></option>').text(label)).appendTo(groupDiv);for(option in section.groups[group].tools[tool].list){selectDiv.append($('<option></option>').text(msg(section.groups[group].tools[tool].list[option],'label')).attr('value',option));} -break;default:break;}}} +break;default:break;}} +$(this).append(groupDiv);} break;case'booklet':if(!('pages'in section)){return;} -var indexDiv=$('<div></div>').attr('class','index').appendTo($(this));var bookletCookie='edittoolbar-'+$(this).attr('id')+'-booklet-'+id;var selectedID=$.cookie(bookletCookie);for(page in section.pages){if(selectedID===null){selectedID=page;} +var indexDiv=$('<div></div>').attr('class','index');var bookletCookie='edittoolbar-'+$(this).attr('id')+'-booklet-'+id;var selectedID=$.cookie(bookletCookie);for(page in section.pages){if(selectedID===null){selectedID=page;} indexDiv.append($('<div></div>').attr('class',page===selectedID?'current':null).text(msg(section.pages[page],'label')).data('page',page).data('cookie',bookletCookie).click(function(){$(this).parent().parent().find('div.pages > div.page').hide().end().parent().find('div').removeClass('current').end().parent().parent().find('div.pages > div.page-'+ $(this).data('page')).show();$(this).addClass('current');$.cookie($(this).data('cookie'),$(this).data('page'));}));} -var pagesDiv=$('<div></div>').attr('class','pages').appendTo($(this));for(page in section.pages){var pageDiv=$('<div></div>').attr('class','page page-'+page).css('display',page===selectedID?'block':'none').appendTo(pagesDiv);switch(section.pages[page].layout){case'table':var contentTable=$('<table></table>').attr({'cellpadding':'0','cellspacing':'0','border':'0','width':'100%'}).appendTo(pageDiv);var headingRow=$('<tr></tr>').appendTo(contentTable);for(heading in section.pages[page].headings){$('<th></th>').text(msg(section.pages[page].headings[heading],'content')).appendTo(headingRow);} -for(row in section.pages[page].rows){var contentRow=$('<tr></tr>').appendTo(contentTable);for(cell in section.pages[page].rows[row]){$('<td></td>').attr({'class':cell,'valign':'top'}).append($('<span></span>').text(msg(section.pages[page].rows[row][cell],'content'))).appendTo(contentRow);}} -break;case'characters':var charsDiv=$('<div />').attr(section.pages[page].attributes).css(section.pages[page].styles).appendTo(pageDiv);for(character in section.pages[page].characters){switch(section.pages[page].characters[character].type){case'link':var context={'tool':section.pages[page].characters[character],'textbox':textbox};charsDiv.append($('<a />').attr('href','#').text(section.pages[page].characters[character].label).data('context',context).click(action).click(function(){return false;}));break;}} -break;default:break;}} -break;default:break;}},useTool:function(tool,textbox){function performAction(action,textbox){switch(action.type){case'encapsulate':var parts={'pre':'','peri':'','post':''};for(part in parts){if(part+'Msg'in action.options){parts[part]=gM(action.options[part+'Msg'],(action.options[part]||null));}else{parts[part]=(action.options[part]||'')}} +var pagesDiv=$('<div></div>').attr('class','pages');for(page in section.pages){var pageDiv=$('<div></div>').attr('class','page page-'+page).css('display',page===selectedID?'block':'none');switch(section.pages[page].layout){case'table':var contentTable=$('<table></table>').attr({'cellpadding':'0','cellspacing':'0','border':'0','width':'100%'});var headingRow=$('<tr></tr>');for(heading in section.pages[page].headings){$('<th></th>').text(msg(section.pages[page].headings[heading],'content')).appendTo(headingRow);} +contentTable.append(headingRow);for(row in section.pages[page].rows){var contentRow=$('<tr></tr>');for(cell in section.pages[page].rows[row]){$('<td></td>').attr({'class':cell,'valign':'top'}).append($('<span></span>').text(msg(section.pages[page].rows[row][cell],'content'))).appendTo(contentRow);} +contentTable.append(contentRow);} +pageDiv.append(contentTable);break;case'characters':var charsDiv=$('<div />').attr(section.pages[page].attributes).css(section.pages[page].styles);for(character in section.pages[page].characters){switch(section.pages[page].characters[character].type){case'link':var context={'tool':section.pages[page].characters[character],'textbox':textbox};charsDiv.append($('<a />').attr('href','#').text(section.pages[page].characters[character].label).data('context',context).click(action));break;}} +pageDiv.append(charsDiv);break;default:break;} +pagesDiv.append(pageDiv);} +$(this).append(indexDiv).append(pagesDiv);break;default:break;}},useTool:function(tool,textbox){function performAction(action,textbox){switch(action.type){case'encapsulate':var parts={'pre':'','peri':'','post':''};for(part in parts){if(part+'Msg'in action.options){parts[part]=gM(action.options[part+'Msg'],(action.options[part]||null));}else{parts[part]=(action.options[part]||'')}} textbox.encapsulateSelection(parts.pre,parts.peri,parts.post);break;default:break;}} switch(tool.type){case'button':case'link':performAction(tool.action,textbox);break;case'select':if($(this).val()in tool.list){performAction(tool.list[$(this).val()].action,textbox);} $(this).find(":selected").attr('selected',false);$(this).find(":first").attr('selected',true);break;default:break;}},parseCharinsert:function(charinsert){var retval={};for(page in charinsert){var pageKey=page.replace(/[^A-Za-z]/g,'-');var characters=[],attributes={},styles={};var i=0;for(line in charinsert[page]){if(!(charinsert[page][line]instanceof Array)){for(attr in charinsert[page][line]){switch(attr){case'class':case'lang':attributes[attr]=charinsert[page][line][attr];break;default:styles[attr]=charinsert[page][line][attr];}} Modified: trunk/extensions/UsabilityInitiative/js/jquery.toolbar.js =================================================================== --- trunk/extensions/UsabilityInitiative/js/jquery.toolbar.js 2009-07-31 18:47:15 UTC (rev 54115) +++ trunk/extensions/UsabilityInitiative/js/jquery.toolbar.js 2009-07-31 18:53:46 UTC (rev 54116) @@ -16,12 +16,9 @@ $(this).addToolbarSection( tools.main, textbox, 'main' ); } var tabDiv = $( '<div></div>' ) - .attr( 'class', 'tabs' ) - .appendTo( $(this) ); + .attr( 'class', 'tabs' ); var sectionsDiv = $( '<div></div>' ) - .attr( 'class', 'sections' ) - .appendTo( $(this) ); - $(this).append( $( '<div></div>' ).addClass( 'break' ) ); + .attr( 'class', 'sections' ); var sectionCookie = 'edittoolbar-' + $(this).attr( 'id' ) + '-section'; var sectionQueue = []; for ( section in tools ) { @@ -33,14 +30,14 @@ 'class': 'section', 'id': $(this).attr( 'id' ) + '-section-' + section } ) - .appendTo( sectionsDiv ) .addClass( 'loading' ) .append( $( '<div></div>' ) .addClass( 'progress' ) .text( gM( 'edittoolbar-loading' ) ) - ); + ) + .appendTo( sectionsDiv ); var current = false; if ( $.cookie( sectionCookie ) == sectionDiv.attr( 'id' ) ) { sectionDiv.attr( 'style', 'display:block' ); @@ -97,6 +94,9 @@ ) ); } + $(this).append( tabDiv ) + .append( sectionsDiv ) + .append( $( '<div></div>' ).addClass( 'break' ) ); $.eachAsync( sectionQueue, { bulk: 0, loop: function( index, value ) { @@ -138,8 +138,7 @@ } for ( group in section.groups ) { var groupDiv = $( '<div></div>' ) - .attr( 'class', 'group' ) - .appendTo( $(this) ); + .attr( 'class', 'group' ); if ( msgSet( section.groups[group], 'label' ) ) { groupDiv.append( $( '<div></div>' ) @@ -209,6 +208,7 @@ default: break; } } + $(this).append( groupDiv ); } break; case 'booklet': @@ -216,8 +216,7 @@ return; } var indexDiv = $( '<div></div>' ) - .attr( 'class', 'index' ) - .appendTo( $(this) ); + .attr( 'class', 'index' ); var bookletCookie = 'edittoolbar-' + $(this).attr( 'id' ) + '-booklet-' + id; var selectedID = $.cookie( bookletCookie ); @@ -257,13 +256,11 @@ ); } var pagesDiv = $( '<div></div>' ) - .attr( 'class', 'pages' ) - .appendTo( $(this) ); + .attr( 'class', 'pages' ); for ( page in section.pages ) { var pageDiv = $( '<div></div>' ) .attr( 'class', 'page page-' + page ) - .css( 'display', page === selectedID ? 'block' : 'none' ) - .appendTo( pagesDiv ); + .css( 'display', page === selectedID ? 'block' : 'none' ); switch ( section.pages[page].layout ) { case 'table': var contentTable = $( '<table></table>' ) @@ -272,10 +269,8 @@ 'cellspacing': '0', 'border': '0', 'width': '100%' - } ) - .appendTo( pageDiv ); - var headingRow = $( '<tr></tr>' ) - .appendTo( contentTable ); + } ); + var headingRow = $( '<tr></tr>' ); for ( heading in section.pages[page].headings ) { $( '<th></th>' ) .text( @@ -287,9 +282,9 @@ ) .appendTo( headingRow ); } + contentTable.append( headingRow ); for ( row in section.pages[page].rows ) { - var contentRow = $( '<tr></tr>' ) - .appendTo( contentTable ); + var contentRow = $( '<tr></tr>' ); for ( cell in section.pages[page].rows[row] ) { $( '<td></td>' ) .attr( { @@ -308,13 +303,14 @@ ) .appendTo( contentRow ); } + contentTable.append( contentRow ); } + pageDiv.append( contentTable ); break; case 'characters': var charsDiv = $( '<div />' ) .attr( section.pages[page].attributes ) - .css( section.pages[page].styles ) - .appendTo( pageDiv ); + .css( section.pages[page].styles ); for ( character in section.pages[page].characters ) { switch ( section.pages[page].characters[character].type @@ -334,17 +330,18 @@ ) .data( 'context', context) .click( action ) - .click( - function() { return false; } - ) ); break; } } + pageDiv.append( charsDiv ); break; default: break; } + pagesDiv.append( pageDiv ); } + $(this).append( indexDiv ) + .append( pagesDiv ); break; default: break; } Property changes on: trunk/extensions/UsabilityInitiative/js/jquery.toolbar.js ___________________________________________________________________ Added: svn:eol-style + native Property changes on: trunk/extensions/UsabilityInitiative/js/jquery.wikiOutline.js ___________________________________________________________________ Added: svn:eol-style + native _______________________________________________ MediaWiki-CVS mailing list MediaWiki-CVS@xxxxxxxxxxxxxxxxxxx https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs
|
|
||||||||||||||||||||||||||
|
|
|
| News | Mail Home | sitemap | FAQ | advertise |