Daniel Werner has submitted this change and it was merged.

Change subject: (bug 48145) List rotator widget refactoring
......................................................................


(bug 48145) List rotator widget refactoring

Some code optimizations, added some more documentation.

Change-Id: Ia618622a05acea4a43e97c4c06b418bf98362246
---
M ValueView/resources/jquery.ui/jquery.ui.listrotator.css
M ValueView/resources/jquery.ui/jquery.ui.listrotator.js
2 files changed, 93 insertions(+), 59 deletions(-)

Approvals:
  Daniel Werner: Verified; Looks good to me, approved
  jenkins-bot: Verified



diff --git a/ValueView/resources/jquery.ui/jquery.ui.listrotator.css 
b/ValueView/resources/jquery.ui/jquery.ui.listrotator.css
index 73c598d..e606613 100644
--- a/ValueView/resources/jquery.ui/jquery.ui.listrotator.css
+++ b/ValueView/resources/jquery.ui/jquery.ui.listrotator.css
@@ -13,10 +13,12 @@
 .ui-listrotator .ui-listrotator-label {
        background: none;
        border: none;
+       text-decoration: inherit;
 }
 
-.ui-listrotator .ui-state-active a {
+.ui-listrotator .ui-state-active .ui-listrotator-label {
        text-decoration: underline;
+       color: #000000;
 }
 
 .ui-listrotator .ui-listrotator-auto {
@@ -33,7 +35,7 @@
        background: none;
        text-align: right;
 }
-.ui-listrotator .ui-listrotator-prev a {
+.ui-listrotator .ui-listrotator-prev > * {
        float: left;
 }
 
@@ -44,7 +46,7 @@
        background: none;
        border: 1px solid #CCCCCC;
 }
-.ui-listrotator .ui-listrotator-curr a {
+.ui-listrotator .ui-listrotator-curr > * {
        float: left;
 }
 
@@ -54,7 +56,7 @@
        border: none;
        background: none;
 }
-.ui-listrotator .ui-listrotator-next a {
+.ui-listrotator .ui-listrotator-next > * {
        float :right;
 }
 
diff --git a/ValueView/resources/jquery.ui/jquery.ui.listrotator.js 
b/ValueView/resources/jquery.ui/jquery.ui.listrotator.js
index d60f4ac..1fc2420 100644
--- a/ValueView/resources/jquery.ui/jquery.ui.listrotator.js
+++ b/ValueView/resources/jquery.ui/jquery.ui.listrotator.js
@@ -1,29 +1,36 @@
 /**
  * List rotator widget
  *
- * The list rotator may be used to rotate through a list of values.
+ * The list rotator may be used to rotate through a list of values. The 
previous and next value
+ * according to the currently selected value are displayed as links next to 
the current value. In
+ * addition, clicking the current value reveals a drop-down list to directly 
select a value from the
+ * list values.
+ *
  * @licence GNU GPL v2+
  * @author H. Snater < [email protected] >
  *
- * @option {Object[]} values Array of objects containing the values to rotate.
+ * @option {Object[]} values (REQUIRED) Array of objects containing the values 
to rotate.
  *         Single object structure:
  *         { value: <actual value (being returned on value())>, label: <the 
value's label> }
  *
  * @option {Object} [menu] Options for the jQuery.menu widget used as 
drop-down menu:
- *         [menu.position] {Object} Default object passed to 
jQuery.ui.position when positioning the
- *                         menu.
+ *         {Object} [menu.position] Default object passed to 
jQuery.ui.position when positioning the
+ *         menu.
  *
  * @option {boolean} [auto] Whether to display the "auto" link.
- *         Default value: true
+ *         Default: true
  *
- * @option {string[]} [animationMargins] Defines how far the sections should 
be shifted when
- *         animating the rotation. First value when shifting to the left and 
vice versa. Values will
- *         be flipped in rtl context.
- *         Default value: ['-15px', '15px']
+ * @option {Object} [animation] Object containing parameters used for the 
rotation animation.
+ *         {string[]} [margins] Defines how far the sections should be shifted 
when animating the
+ *         rotation. First value when shifting to the left and vice versa. 
Values will be flipped in
+ *         rtl context.
+ *         Default: ['-15px', '15px']
+ *         {number} [duration] Defines the animation's duration in 
milliseconds.
+ *         Default: 150
  *
  * @option {boolean} [deferInit] Whether to defer initializing the section 
widths until initWidths()
  *         is called "manually".
- *         Default value: false
+ *         Default: false
  *
  * @event auto: Triggered when "auto" options is selected.
  *        (1) {jQuery.Event}
@@ -32,7 +39,7 @@
  *        (1) {jQuery.Event}
  *        (2) {*} Value as specified in the "values" option.
  *
- * @dependency jQuery.ui.Widget
+ * @dependency jQuery.Widget
  * @dependency jQuery.ui.menu
  * @dependency jQuery.ui.position
  */
@@ -106,13 +113,20 @@
                                }
                        },
                        auto: true,
-                       animationMargins: ['-15px', '15px'],
+                       animation: {
+                               margins: ['-15px', '15px'],
+                               duration: 150
+                       },
                        deferInit: false,
                        messages: {
                                'auto': mwMsgOrString( 
'valueview-listrotator-auto', 'auto' )
                        }
                },
 
+               /**
+                * Node of the selectable "auto" option.
+                * @type {jQuery}
+                */
                $auto: null,
 
                /**
@@ -173,7 +187,7 @@
                                self._trigger( 'auto' );
                        } )
                        .addClass( 'ui-state-active' );
-                       this.$auto.children( 'a' ).text( 
this.options.messages['auto'] );
+                       this.$auto.children( 'span' ).text( 
this.options.messages['auto'] );
 
                        // Construct the basic sections:
                        this.$curr = this._createSection( 'curr', function( 
event ) {
@@ -183,38 +197,34 @@
                                        self._hideMenu();
                                }
                        } )
-                       .append( $( '<a/>' ).addClass( 'ui-icon 
ui-icon-triangle-1-s' ) );
+                       .append( $( '<span/>' ).addClass( 'ui-icon 
ui-icon-triangle-1-s' ) );
 
                        this.$prev = this._createSection( 'prev', function( 
event ) {
                                self.prev();
                        } )
-                       .append( $( '<a/>' ).addClass( iconClasses[0] ) );
+                       .append( $( '<span/>' ).addClass( iconClasses[0] ) );
 
                        this.$next = this._createSection( 'next', function( 
event ) {
                                self.next();
                        } )
-                       .append( $( '<a/>' ).addClass( iconClasses[1] ) );
+                       .append( $( '<span/>' ).addClass( iconClasses[1] ) );
 
                        if( this.$auto ) {
                                this.element.append( this.$auto );
                        }
                        this.element.append( this.$prev ).append( this.$curr 
).append( this.$next );
 
-                       $.each( [ this.$auto, this.$curr, this.$prev, 
this.$next ], function( i, $node ) {
-                               $node.find( 'a' ).attr( 'href', 
'javascript:void(0);' );
-                       } );
-
                        // Construct and initialize menu widget:
                        this._createMenu();
 
                        // Attach event to html node to detect click outside of 
the menu closing the menu:
-                       if ( $( ':' + self.widgetBaseClass ).length === 1 ) {
-                               $( 'html' ).on( 'click.' + 
this.widgetBaseClass, function( event ) {
-                                       $( ':' + self.widgetBaseClass ).each( 
function( i, node ) {
-                                               $( node ).data( 'listrotator' 
).$menu.hide();
-                                       } );
+                       $( 'html' )
+                       .off( '.' + this.widgetName )
+                       .on( 'click.' + this.widgetBaseClass, function( event ) 
{
+                               $( ':' + self.widgetBaseClass ).each( function( 
i, node ) {
+                                       $( node ).data( 'listrotator' 
).$menu.hide();
                                } );
-                       }
+                       } );
 
                        // Prevent propagation of clicking on the "current" 
section as well as on the menu in
                        // order to not trigger the event handler assigned to 
the html element.
@@ -245,12 +255,12 @@
 
                        this.element.removeClass( this.widgetBaseClass + ' 
ui-widget-content' );
 
+                       $.Widget.prototype.destroy.call( this );
+
                        // Remove event attached to the html node if no 
instances of the widget exist anymore:
                        if ( $( ':' + this.widgetBaseClass ).length === 0 ) {
                                $( 'html' ).off( '.' + this.widgetBaseClass );
                        }
-
-                       $.Widget.prototype.destroy.call( this );
                },
 
                /**
@@ -307,14 +317,15 @@
                 * @return {jQuery}
                 */
                _createSection: function( classSuffix, clickCallback ) {
-                       return $( '<span/>' )
+                       return $( '<a/>' )
+                       .attr( 'href', 'javascript:void(0);' )
                        .addClass( this.widgetBaseClass + '-' + classSuffix )
                        .on( 'click.' + this.widgetBaseClass, function( event ) 
{
                                if( !$( this ).hasClass( 'ui-state-disabled' ) 
) {
                                        clickCallback( event );
                                }
                        } )
-                       .append( $( '<a/>' ).addClass( this.widgetBaseClass + 
'-label ui-state-default' ) );
+                       .append( $( '<span/>' ).addClass( this.widgetBaseClass 
+ '-label ui-state-default' ) );
                },
 
                /**
@@ -335,6 +346,7 @@
                                                .attr( 'href', 
'javascript:void(0);')
                                                .text( v.label )
                                                .on( 'click', function( event ) 
{
+                                                       event.stopPropagation();
                                                        self._trigger( 
'selected', null, [ self.value( v.value ) ] );
                                                        self.$menu.hide();
                                                } )
@@ -347,7 +359,8 @@
                },
 
                /**
-                * Sets/Gets the widget's value.
+                * Sets/Gets the widget's value. Setting the value involves 
setting the rotator to the
+                * specified value without any animation.
                 *
                 * TODO: Change behavior: value as setter should return "this" 
for allowing chaining calls
                 *  to the widget.
@@ -362,44 +375,44 @@
                                return this.$curr.data( 'value' );
                        }
 
-                       var index = 0;
+                       var values = this.options.values,
+                               index = 0;
 
                        this.$prev.add( this.$curr ).add( this.$next )
                        .children( '.' + this.widgetBaseClass + '-label' 
).empty();
 
-                       $.each( this.options.values, function( i, v ) {
+                       // Retrieve the index of the new value within the list 
of predefined values:
+                       $.each( values, function( i, v ) {
                                if ( value === v.value ) {
                                        index = i;
                                        return false;
                                }
                        } );
 
+                       // Re-construct each section:
                        this.$curr
-                       .data( 'value', this.options.values[index].value )
+                       .data( 'value', values[index].value )
                        .children( '.' + this.widgetBaseClass + '-label' )
-                       .text( this.options.values[index].label );
+                       .text( values[index].label );
 
                        if ( index > 0 ) {
                                this.$prev
-                               .data( 'value', this.options.values[index - 
1].value )
+                               .data( 'value', values[index - 1].value )
                                .children( '.' + this.widgetBaseClass + 
'-label' )
-                               .text( this.options.values[index - 1].label );
+                               .text( values[index - 1].label );
                        }
 
-                       if ( index < this.options.values.length - 1 ) {
+                       if ( index < values.length - 1 ) {
                                this.$next
-                               .data( 'value', this.options.values[index + 
1].value )
+                               .data( 'value', values[index + 1].value )
                                .children( '.' + this.widgetBaseClass + 
'-label' )
-                               .text( this.options.values[index + 1].label );
+                               .text( values[index + 1].label );
                        }
 
-                       this.$prev.css( 'visibility', 'visible' );
-                       this.$next.css( 'visibility', 'visible' );
-                       if ( index === 0 ) {
-                               this.$prev.css( 'visibility', 'hidden' );
-                       } else if ( index === this.options.values.length - 1 ) {
-                               this.$next.css( 'visibility', 'hidden' );
-                       }
+                       // Hide "previous"/"$next" section when the new value 
is at the end of the list the
+                       // predefined values:
+                       this.$prev.css( 'visibility', ( index === 0 ) ? 
'hidden' : 'visible' );
+                       this.$next.css( 'visibility', ( index === values.length 
- 1 ) ? 'hidden' : 'visible' );
 
                        // Alter menu item states:
                        this.$menu.children( 'li' ).each( function( i, li ) {
@@ -452,19 +465,25 @@
                 * @param {Function} [callback]
                 */
                rotate: function( newValue, callback ) {
-                       if( newValue === this._rotatingTo || !this._rotatingTo 
&& newValue === this.$curr.data( 'value' ) ) {
+                       if(
+                               newValue === this._rotatingTo
+                               || !this._rotatingTo && newValue === 
this.$curr.data( 'value' )
+                       ) {
+                               // Rotation is to the given target is in 
progress or has been performed already.
                                return;
                        }
 
-                       this._rotatingTo = newValue;
-
                        var self = this,
-                               margins = $.merge( [], 
this.options.animationMargins ),
+                               margins = $.merge( [], 
this.options.animation.margins ),
                                s = '.' + this.widgetBaseClass + '-label';
 
+                       // Nodes that shall be animated:
                        var $nodes = this.$prev.children( s )
                                .add( this.$curr.children( s ) )
                                .add( this.$next.children( s ) );
+
+                       // Set the rotation target:
+                       this._rotatingTo = newValue;
 
                        // Figure out whether rotating to the right or to the 
left:
                        var beforeCurrent = true;
@@ -493,18 +512,27 @@
                                        opacity: 0
                                }, {
                                        done: function() {
+                                               // Reset margins an opacity 
used for the animation effect:
                                                $nodes.css( {
                                                        marginLeft: '0',
                                                        marginRight: '0',
                                                        opacity: 1
                                                } );
+
+                                               // Rotation target changed in 
the meantime, just abort selecting:
+                                               if( self._rotatingTo !== 
newValue ) {
+                                                       return;
+                                               }
+
                                                self._trigger( 'selected', 
null, [ self.value( newValue ) ] );
+
                                                self._rotatingTo = null;
+
                                                if( $.isFunction( callback ) ) {
                                                        callback();
                                                }
                                        },
-                                       duration: 150
+                                       duration: 
this.options.animation.duration
                                }
                        );
                },
@@ -515,10 +543,12 @@
                 * @param {jQuery} [$section] Section to activate. "Current" 
section by default.
                 */
                activate: function( $section ) {
-                       this.$curr.add( this.$auto ).removeClass( 
'ui-state-hover' ).removeClass( 'ui-state-active' );
+                       this.$curr.add( this.$auto ).removeClass( 
'ui-state-hover ui-state-active' );
+
                        if( $section === undefined ) {
                                $section = this.$curr;
                        }
+
                        $section.addClass( 'ui-state-active' );
                },
 
@@ -533,10 +563,12 @@
                 * Shows the drop-down menu.
                 */
                _showMenu: function() {
-                       this.$menu.slideDown( 150 );
+                       this.$menu.slideDown( this.options.animation.duration );
+
                        this.$menu.position( $.extend( {
                                of: this.$curr
                        }, this.options.menu.position ) );
+
                        this.activate();
                },
 
@@ -544,7 +576,7 @@
                 * Hides the drop-down menu.
                 */
                _hideMenu: function() {
-                       this.$menu.slideUp( 150 );
+                       this.$menu.slideUp( this.options.animation.duration );
                        this.activate();
                },
 

-- 
To view, visit https://gerrit.wikimedia.org/r/64062
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Ia618622a05acea4a43e97c4c06b418bf98362246
Gerrit-PatchSet: 8
Gerrit-Project: mediawiki/extensions/DataValues
Gerrit-Branch: master
Gerrit-Owner: Henning Snater <[email protected]>
Gerrit-Reviewer: Daniel Werner <[email protected]>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to