http://www.mediawiki.org/wiki/Special:Code/MediaWiki/71219

Revision: 71219
Author:   dale
Date:     2010-08-17 23:33:27 +0000 (Tue, 17 Aug 2010)

Log Message:
-----------
* improved undo redo handling for non-change edits
* improved click selection "editTool" updates
* improved audio track display and edit

Modified Paths:
--------------
    branches/MwEmbedStandAlone/libraries/jquery/jquery-1.4.2.js
    branches/MwEmbedStandAlone/modules/Sequencer/Sequencer.i18n.php
    
branches/MwEmbedStandAlone/modules/Sequencer/actions/mw.SequencerActionsEdit.js
    branches/MwEmbedStandAlone/modules/Sequencer/css/mw.style.Sequencer.css
    branches/MwEmbedStandAlone/modules/Sequencer/mw.Sequencer.js
    branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerConfig.js
    branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerTimeline.js
    branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerTools.js
    branches/MwEmbedStandAlone/modules/SmilPlayer/SmilPlayer.i8n.php
    branches/MwEmbedStandAlone/modules/SmilPlayer/mw.Smil.js
    branches/MwEmbedStandAlone/modules/SmilPlayer/mw.SmilLayout.js

Modified: branches/MwEmbedStandAlone/libraries/jquery/jquery-1.4.2.js
===================================================================
--- branches/MwEmbedStandAlone/libraries/jquery/jquery-1.4.2.js 2010-08-17 
23:11:08 UTC (rev 71218)
+++ branches/MwEmbedStandAlone/libraries/jquery/jquery-1.4.2.js 2010-08-17 
23:33:27 UTC (rev 71219)
@@ -1820,7 +1820,7 @@
 
                                // Only trigger if we've ever bound an event 
for it
                                if ( jQuery.event.global[ type ] ) {
-                                       jQuery.each( jQuery.cache, function() {
+                                       jQuery.each( jQuery.cache, function() { 
                                        
                                                if ( this.events && 
this.events[type] ) {
                                                        jQuery.event.trigger( 
event, data, this.handle.elem );
                                                }

Modified: branches/MwEmbedStandAlone/modules/Sequencer/Sequencer.i18n.php
===================================================================
--- branches/MwEmbedStandAlone/modules/Sequencer/Sequencer.i18n.php     
2010-08-17 23:11:08 UTC (rev 71218)
+++ branches/MwEmbedStandAlone/modules/Sequencer/Sequencer.i18n.php     
2010-08-17 23:33:27 UTC (rev 71219)
@@ -14,6 +14,7 @@
        'mwe-sequencer-loading-asset' => 'Loading asset ...',
 
        'mwe-sequencer-no_selected_resource' => '<h3>No resource selected</h3> 
Select a clip to enable editing.',
+       'mwe-sequencer-error_edit_multiple' => '<h3>Multiple resources 
selected</h3> Select a single clip to edit it.',
        'mwe-sequencer-no-sequence-start-new' => 'Empty sequence, [$1 browser 
for assets] to create a new sequence',
        'mwe-sequencer-video-track' => 'Video track',
        'mwe-sequencer-audio-track' => 'Audio track',
@@ -86,8 +87,7 @@
        'mwe-sequencer-pixle2sec' => 'pixels to seconds',
        'mwe-sequencer-rmclip' => 'Remove clip',
        'mwe-sequencer-clip_in' => 'clip in',
-       'mwe-sequencer-clip_out' => 'clip out',
-       'mwe-sequencer-error_edit_multiple' => '<h3>Multiple resources 
selected</h3> Select a single clip to edit it.',
+       'mwe-sequencer-clip_out' => 'clip out', 
        'mwe-sequencer-editor_options' => 'Editor options',
        'mwe-sequencer-editor_mode' => 'Editor mode',
        'mwe-sequencer-simple_editor_desc' => 'simple editor (iMovie style)',

Modified: 
branches/MwEmbedStandAlone/modules/Sequencer/actions/mw.SequencerActionsEdit.js
===================================================================
--- 
branches/MwEmbedStandAlone/modules/Sequencer/actions/mw.SequencerActionsEdit.js 
    2010-08-17 23:11:08 UTC (rev 71218)
+++ 
branches/MwEmbedStandAlone/modules/Sequencer/actions/mw.SequencerActionsEdit.js 
    2010-08-17 23:33:27 UTC (rev 71219)
@@ -47,7 +47,14 @@
         * Apply a smil xml transform state ( to support undo / redo ) 
         */
        registerEdit: function(){       
-               mw.log( 'ActionsEdit::registerEdit: stacksize' + 
this.editStack.length + ' editIndex: ' + this.editIndex );
+               //mw.log( 'ActionsEdit::registerEdit: stacksize' + 
this.editStack.length + ' editIndex: ' + this.editIndex );
+               // Make sure the edit is distinct from the latest in the stack:
+               var currentXML = this.sequencer.getSmil().getXMLString();
+               if( currentXML == this.editStack[ this.editStack-1 ] ){
+                       mw.log("ActionsEdit::registerEdit on identical smil xml 
state ( no edit stack modification ) ")
+                       return ;
+               }
+               
                // Throw away any edit history after the current editIndex: 
                if( this.editStack.length && this.editIndex > 
this.editStack.length ) {
                        this.editStack = this.editStack.splice(0, 
this.editIndex);
@@ -56,7 +63,7 @@
                // @@TODO could save space to just compute the diff in JS and 
store that
                // ie: http://code.google.com/p/google-diff-match-patch/
                // ( instead of the full xml text with "key-pages" every 10 
edits or something like that. 
-               this.editStack.push(  this.sequencer.getSmil().getXMLString() );
+               this.editStack.push( currentXML );
                
                // Update the editIndex
                this.editIndex = this.editStack.length - 1;

Modified: 
branches/MwEmbedStandAlone/modules/Sequencer/css/mw.style.Sequencer.css
===================================================================
--- branches/MwEmbedStandAlone/modules/Sequencer/css/mw.style.Sequencer.css     
2010-08-17 23:11:08 UTC (rev 71218)
+++ branches/MwEmbedStandAlone/modules/Sequencer/css/mw.style.Sequencer.css     
2010-08-17 23:33:27 UTC (rev 71219)
@@ -22,14 +22,16 @@
        list-style-position: inside;  
        cursor: hand; 
        cursor: pointer;
-       margin:0px;
+       margin-top:4px;
+       margin-bottom: 0px;
        padding: 4px;
 }
 
 .mwe-sequencer .trackNames{
        background-color: #EEE; 
        border: solid thin #999;
-       padding:4px
+       padding:4px;
+       margin-top: 4px;
 }
 
 .mwe-sequencer .timelineClip{
@@ -39,7 +41,6 @@
        border: 2px solid #555;
        overflow: hidden;
        width:120px;    
-       height:90px;
        
        display: inline-block;
        position: relative;

Modified: branches/MwEmbedStandAlone/modules/Sequencer/mw.Sequencer.js
===================================================================
--- branches/MwEmbedStandAlone/modules/Sequencer/mw.Sequencer.js        
2010-08-17 23:11:08 UTC (rev 71218)
+++ branches/MwEmbedStandAlone/modules/Sequencer/mw.Sequencer.js        
2010-08-17 23:33:27 UTC (rev 71219)
@@ -266,7 +266,7 @@
                        .append(                        
                                $j('<div />')
                                        .addClass( "ui-layout-center 
mwseq-edit" )
-                                       .html( this.getEditTools().defaultText 
),
+                                       .html(  
gM('mwe-sequencer-no_selected_resource') ),
                                $j('<div />')
                                        .addClass( "ui-layout-east 
mwseq-player" )
                                        .text( 
gM('mwe-sequencer-loading-player') ),    

Modified: branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerConfig.js
===================================================================
--- branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerConfig.js  
2010-08-17 23:11:08 UTC (rev 71218)
+++ branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerConfig.js  
2010-08-17 23:33:27 UTC (rev 71219)
@@ -27,6 +27,6 @@
        "Sequencer.TimelineTrackHeight" : 100,
        
        // Default timeline "audio / collapsed" size 
-       "Sequencer.TimelineColapsedTrackSize" : 30
+       "Sequencer.TimelineColapsedTrackSize" : 35
 })
        
\ No newline at end of file

Modified: branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerTimeline.js
===================================================================
--- branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerTimeline.js        
2010-08-17 23:11:08 UTC (rev 71218)
+++ branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerTimeline.js        
2010-08-17 23:33:27 UTC (rev 71219)
@@ -69,14 +69,18 @@
                var timelineHeight = mw.getConfig( 
'Sequencer.TimelineTrackHeight' );
                var smilSequenceTracks = 
this.sequencer.getSmil().getBody().getSeqElements();
                $j.each(smilSequenceTracks, function( trackIndex, 
smilSequenceTrack ){
-                       if( $j( smilSequenceTrack).attr('tracktype') == 'audio' 
){
-                               timelineHeight+= mw.getConfig( 
'Sequencer.TimelineColapsedTrackSize') 
-                       }else{
-                               timelineHeight+= mw.getConfig( 
'Sequencer.TimelineTrackHeight' ) 
-                       }                        
+                       timelineHeight+= _this.getSequenceTrackHeight( 
smilSequenceTrack )
                })
                return timelineHeight;
        },
+       // xxx may need to refactor to store collapsed expanded state info 
+       getSequenceTrackHeight: function( smilSequenceTrack ){
+               if( $j( smilSequenceTrack).attr('tracktype') == 'audio' ){
+                       return mw.getConfig( 
'Sequencer.TimelineColapsedTrackSize') 
+               }else{
+                       return mw.getConfig( 'Sequencer.TimelineTrackHeight' ) 
+               }                        
+       },
        // Get the selected sequence track index ( for now its always zero )  
        getSelectedTrackIndex: function(){
                return 0;
@@ -148,8 +152,10 @@
                        mw.log("ADD: " + _this.getTimelineClipId( $node ) + ' 
to ' + $clipTrackSet.attr('id') );
                        // Draw the node onto the timeline if the clip is not 
already there:
                        var $timelineClip = $clipTrackSet.find( '#' + 
_this.getTimelineClipId( $node ) )
-                       if( $timelineClip.length == 0 ){                        
        
-                               $timelineClip = _this.getTimelineClip( 
$clipTrackSet, $node );                                  
+                       if( $timelineClip.length == 0 ){                        
                                        
+                               $timelineClip = _this.getTimelineClip( 
smilSequenceTrack, $node );
+                               // Set the index order on the clip
+                               $timelineClip.data( 'indexOrder', 
$clipTrackSet.children().length );
                                if( $previusClip ){
                                        $previusClip.after( 
                                                $timelineClip 
@@ -225,19 +231,16 @@
         */
        getClipTrackSet: function( trackIndex, smilSequenceTrack ){
                var _this = this;
-        
-               var trackHeight = ( $j( smilSequenceTrack).attr('tracktype') == 
'audio' )? 
-                               mw.getConfig( 
'Sequencer.TimelineColapsedTrackSize') :
-                               mw.getConfig( 'Sequencer.TimelineTrackHeight' )
-                               
+                               
                return $j('<ul />')
                                .attr( 'id',  this.getTrackSetId( trackIndex ))
                                .data( 'trackIndex', trackIndex )
                                .addClass( 'clipTrackSet ui-corner-all' )
-                               .css( 'height', trackHeight )
+                               .css( 'height', _this.getSequenceTrackHeight( 
smilSequenceTrack ) )
                                // Add "sortable
                                .sortable({ 
                                    placeholder: "clipSortTarget timelineClip 
ui-corner-all",
+                                   forcePlaceholderSize: true,
                                    opacity: 0.6,       
                                    tolerance: 'pointer',
                                    cursor: 'move',
@@ -281,15 +284,16 @@
                        'width' : ( ( this.timelineThumbSize.width + 16) * 
trackClipCount) + 'px'
                });
        },
-       getTimelineClip: function( $clipTrackSet, $node ){
+       getTimelineClip: function( smilSequenceTrack, $node ){
                var _this = this;
+                               
                return $j('<li />')
                        .attr('id',  _this.getTimelineClipId( $node ) ) 
                        .data( {
-                               'smilId': $node.attr('id'),
-                               'indexOrder' : $clipTrackSet.children().length
+                               'smilId': $node.attr('id'),                     
        
                        })
-                       .addClass('timelineClip ui-corner-all')
+                       .css( 'height', this.getSequenceTrackHeight( 
smilSequenceTrack) - 10 )
+                       .addClass( 'timelineClip ui-corner-all' )
                        .loadingSpinner()                               
                        .click(function(){
                                //Add clip to selection
@@ -451,6 +455,7 @@
         * Handle multiple selections based on what clips was just "cliked" 
         */
        handleMultiSelect: function( clickClip ){
+               var _this = this;
                var keyBindings = this.sequencer.getKeyBindings();
                var $target = this.getTimelineContainer();
                var smil = this.sequencer.getSmil();
@@ -508,7 +513,32 @@
                        });     
                }
                
-               // xxx check if selected clip has changed hide tool edit 
interface
+               // Update the edit Tools window
+               var $selectedClips =  
_this.getTimelineContainer().find('.selectedClip');
+               // zero clips selected
+               var $toolTarget = _this.sequencer.getEditToolTarget();
+               
+               //( on an edit screen update the edit screen per selection )
+               if( $toolTarget.find( '.editToolsContainer' ).length != 0 ){    
                
+                       // multiple clips selected
+                       if( $selectedClips.length == 0 ){
+                               // Update edit window to no selected clips
+                               $toolTarget.empty().append( 
+                                       gM( 
'mwe-sequencer-no_selected_resource'  ),
+                                       $j('<div 
/>').addClass('editToolsContainer')
+                               )
+                       } else if( $selectedClips.length > 1 ){
+                               $toolTarget.empty().append( 
+                                       gM( 'mwe-sequencer-error_edit_multiple' 
),
+                                       $j('<div 
/>').addClass('editToolsContainer')
+                               )
+                       } else {
+                               // A single clip is selected edit that clip   
+                               _this.editClip( clickClip );
+                       }
+                       // Register the edit tools update for undo
+                       _this.sequencer.getActionsEdit().registerEdit();
+               }
        },
        
        /**
@@ -534,6 +564,12 @@
                var _this = this;
                var smil = this.sequencer.getSmil();    
                
+               var clipButtonCss = {
+                       'position' : 'absolute',
+                       'bottom' : '2px',
+                       'padding' : '2px',
+                       'cursor' : 'pointer'
+               };
                
                var $timelineClip = $j( '#' + _this.getTimelineClipId( $node ) 
);
                // Add Thumb target and remove loader
@@ -544,12 +580,9 @@
 
                        // Edit clip button: 
                        $j('<div />')
-                       .css({
-                               'position' : 'absolute',
-                               'right' : '32px',
-                               'bottom' : '5px',
-                               'padding' : '2px',
-                               'cursor' : 'pointer'
+                       .css( clipButtonCss )
+                       .css({                          
+                               'right' : '32px'                        
                        })
                        .addClass( 'clipEditLink ui-state-default 
ui-corner-all' )
                        .append( 
@@ -564,12 +597,9 @@
                        
                        // Remove clip button: 
                        $j('<div />')
-                       .css({
-                               'position' : 'absolute',
-                               'right' : '5px',
-                               'bottom' : '5px',
-                               'padding' : '2px',
-                               'cursor' : 'pointer'
+                       .css( clipButtonCss )
+                       .css({                  
+                               'right' : '5px'                         
                        })
                        .addClass( 'clipRemoveLink ui-state-default 
ui-corner-all' )
                        .append( 
@@ -580,7 +610,7 @@
                        .buttonHover()
                        .click( function(){                             
                                // Remove the associated clip:                  
        
-                               _this.getTimelineContainer().removeClass( 
'selectedClip' );                             
+                               
_this.getTimelineContainer().find('.selectedClip').removeClass( 'selectedClip' 
);                               
                                $timelineClip.addClass( 'selectedClip' );
                                _this.removeSelectedClips();
                        })
@@ -624,6 +654,7 @@
                                }
                        }, 5000);
                }                       
+               
                // Buffer the asset then render it into the layout target:      
        
                smil.getBuffer().bufferedSeek( $node, relativeTime, function(){ 
                                
                        // Add the seek, add to canvas and draw thumb request
@@ -653,19 +684,17 @@
                                        $j('<span />').addClass( 'ui-icon 
ui-icon-volume-on'),
                                        $j('<span />').text( gM( 
'mwe-sequencer-audio-track' ) )
                                )
-                       $trackNameContainer.css({
-                               'height' : mw.getConfig( 
'Sequencer.TimelineColapsedTrackSize') 
-                       });
                } else {
                        // for now default to "video" tracktype
                        $trackNameTitle.append(                                 
                                        $j('<span />').addClass( 'ui-icon 
ui-icon-video'),
                                        $j('<span />').text( gM( 
'mwe-sequencer-video-track' ) )
                                )                       
-                       $trackNameContainer.css({
-                               'height' : mw.getConfig( 
'Sequencer.TimelineTrackHeight' ) 
-                       });
                }
+               // Set track name height
+               $trackNameContainer.css({
+                       'height' : this.getSequenceTrackHeight( 
smilSequenceTrack )
+               });
                
                // Add the track title as a title attribute 
                if ( $j( smilSequenceTrack ).attr('title') ){

Modified: branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerTools.js
===================================================================
--- branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerTools.js   
2010-08-17 23:11:08 UTC (rev 71218)
+++ branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerTools.js   
2010-08-17 23:33:27 UTC (rev 71219)
@@ -14,7 +14,6 @@
        init: function( sequencer ){
                this.sequencer = sequencer;
        },
-       defaultText : gM('mwe-sequencer-no_selected_resource'),
        tools:{
                'trim':{
                        'title': gM('mwe-sequencer-cliptool-trim'),
@@ -132,14 +131,16 @@
                        // Return the trimTimeline edit widget
                        'draw': function( _this, target, smilClip ){
                                var smil = _this.sequencer.getSmil();
-                               // For now just have a thumbnail and a slider 
-                               $j(target).append(
-                                       $j('<div />')                           
        
-                                       .addClass( 'trimStartThumb 
ui-corner-all' ),                                    
-                                       $j('<div />')                           
        
-                                       .addClass( 'trimEndThumb ui-corner-all' 
),
-                                       $j('<div 
/>').addClass('ui-helper-clearfix') 
-                               )                       
+                               // check if thumbs are supported 
+                               if( _this.sequencer.getSmil().getRefType( 
smilClip ) == 'video' ){ 
+                                       $j(target).append(
+                                               $j('<div />')                   
                
+                                               .addClass( 'trimStartThumb 
ui-corner-all' ),                                    
+                                               $j('<div />')                   
                
+                                               .addClass( 'trimEndThumb 
ui-corner-all' ),
+                                               $j('<div 
/>').addClass('ui-helper-clearfix') 
+                                       )                       
+                               }
                                
                                // Add a trim binding: 
                                
$j('#editTool_trim_clipBegin,#editTool_trim_dur').change(function(){
@@ -148,7 +149,7 @@
                                // Update the thumbnails:
                                _this.editWidgets.trimTimeline.update( _this, 
target, smilClip);
                                
-                               // get the clip full duration to build out the 
timeline selector
+                               // Get the clip full duration to build out the 
timeline selector
                                smil.getBody().getClipAssetDuration( smilClip, 
function( fullClipDuration ) {
                                        
                                        var sliderToTime = function( sliderval 
){
@@ -227,6 +228,7 @@
                // get the toolId based on what "ref type" smilClip is:
                switch( this.sequencer.getSmil().getRefType( smilClip ) ){
                        case 'video':
+                       case 'audio':
                                toolId = 'trim';
                        break;
                        default:
@@ -243,7 +245,9 @@
                var tool = this.tools[ toolId ];
                
                // Append the title: 
-               $target.empty().append( 
+               $target.empty().append(
+                       $j('<div />').addClass( 'editToolsContainer' )
+                       ,
                        $j('<h3 />' ).append( 
                                tool.title 
                        )

Modified: branches/MwEmbedStandAlone/modules/SmilPlayer/SmilPlayer.i8n.php
===================================================================
--- branches/MwEmbedStandAlone/modules/SmilPlayer/SmilPlayer.i8n.php    
2010-08-17 23:11:08 UTC (rev 71218)
+++ branches/MwEmbedStandAlone/modules/SmilPlayer/SmilPlayer.i8n.php    
2010-08-17 23:33:27 UTC (rev 71219)
@@ -2,5 +2,6 @@
 $messages = array();
 
 $messages['en'] = array(
-       'mwe-embedplayer-ogg-player-smilPlayer' => 'SMIL Player'
+       'mwe-embedplayer-ogg-player-smilPlayer' => 'SMIL Player',
+       'mwe-smilplayer-untitled-audio' => 'Untitled audio'
 );
\ No newline at end of file

Modified: branches/MwEmbedStandAlone/modules/SmilPlayer/mw.Smil.js
===================================================================
--- branches/MwEmbedStandAlone/modules/SmilPlayer/mw.Smil.js    2010-08-17 
23:11:08 UTC (rev 71218)
+++ branches/MwEmbedStandAlone/modules/SmilPlayer/mw.Smil.js    2010-08-17 
23:33:27 UTC (rev 71219)
@@ -17,6 +17,8 @@
  * 
  */
 
+mw.includeAllModuleMessages();
+
 /* Add the hooks needed for playback */
 mw.Smil = function(options) {
        return this.init(options);

Modified: branches/MwEmbedStandAlone/modules/SmilPlayer/mw.SmilLayout.js
===================================================================
--- branches/MwEmbedStandAlone/modules/SmilPlayer/mw.SmilLayout.js      
2010-08-17 23:11:08 UTC (rev 71218)
+++ branches/MwEmbedStandAlone/modules/SmilPlayer/mw.SmilLayout.js      
2010-08-17 23:33:27 UTC (rev 71219)
@@ -115,7 +115,11 @@
        
        drawElementThumb: function( $target, $node, relativeTime ){             
        
                mw.log('SmilLayout::drawElementThumb: ' + relativeTime );
-               // parse the time incase it came in as human input
+               if( $target.length == 0 ){
+                       mw.log("Error drawElementThumb to empty target");
+                       return ;
+               }
+               // parse the time in case it came in as human input
                relativeTime = this.smil.parseTime( relativeTime );
                switch ( this.smil.getRefType( $node )){
                        case 'video':                           
@@ -125,10 +129,10 @@
                                // xxx we could eventually use canvas as well 
but for now just add it at 100%
                                $target.html(
                                        this.getSmilImgHtml( $node, {
-                                                       'width' : 
$target.width(),
-                                                       'height' : 
$target.height()
-                                               })
-                               );                              
+                                               'width' : $target.width(),
+                                               'height' : $target.height()
+                                       })
+                               );
                        break;
                        case 'cdata_html':
                                // Scale down the html into the target width
@@ -136,8 +140,20 @@
                                        this.getSmilCDATAHtml( $node, 
$target.width() )
                                )
                        break;
-                       case 'audio':
-                               // draw an audio icon in the target
+                       case 'audio':                   
+                               var titleStr = ( $node.attr('title') )?  
$node.attr('title') : gM( 'mwe-sequencer-untitled-audio' )
+                               // draw an audio icon / title the target
+                               $target.append(
+                                       $j('<span />')
+                                       .addClass( 'ui-icon ui-icon-volume-on')
+                                       .attr('title', titleStr)
+                                       .css( 'position', 'absolute')
+                                       ,
+                                       $j('<span />')
+                                       .attr('title', titleStr)
+                                       .css({'position': 'absolute', 
'left':'16px'})
+                                       .text( titleStr )
+                               )
                        break;
                }                                                       
        },
@@ -189,7 +205,7 @@
                                // update the drawElement 
                                drawElement = $j( '#' + 
_this.smil.getPageDomId( $tmpFrameNode ) ).get(0);
                                drawFrame( drawElement );
-                               // remove the temporary node from dom
+                               // Remove the temporary node from dom
                                $j( drawElement ).remove();
                        })                      
                }



_______________________________________________
MediaWiki-CVS mailing list
MediaWiki-CVS@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs

Reply via email to