Henning Snater has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/78246


Change subject: Using listview widget to group qualifiers
......................................................................

Using listview widget to group qualifiers

(bug 52391)
Qualifier snaks featuring the same property are grouped in separate 
snaklistview widgets now.
These snaklistview widgets are managed by a listview widget. (The grouping is 
applied only
when the qualifiers are initialized and not directly when a new qualifier snak 
is added.)

Change-Id: I5a195c2d144e4cbab7d23dc785c27b8255ef4646
---
M lib/resources/jquery.wikibase/jquery.wikibase.claimlistview.js
M lib/resources/jquery.wikibase/jquery.wikibase.claimview.js
M lib/resources/jquery.wikibase/jquery.wikibase.listview.js
M lib/resources/jquery.wikibase/jquery.wikibase.snaklistview.js
M lib/resources/jquery.wikibase/jquery.wikibase.statementview.js
M lib/resources/wikibase.datamodel/wikibase.Claim.js
M lib/resources/wikibase.datamodel/wikibase.SnakList.js
M selenium/lib/modules/qualifiers_module.rb
8 files changed, 384 insertions(+), 79 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Wikibase 
refs/changes/46/78246/1

diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.claimlistview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.claimlistview.js
index b381812..616ca4e 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.claimlistview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.claimlistview.js
@@ -475,16 +475,26 @@
                        var $target = $( event.target ),
                                statementview = $target.data( 'statementview' ),
                                enable = statementview.isValid() && 
!statementview.isInitialValue(),
-                               edittoolbar = $target.data( 'edittoolbar' );
+                               $btnSave = $target.data( 'edittoolbar' 
).toolbar.editGroup.$btnSave,
+                               snaklistviews = ( statementview._qualifiers ) ? 
statementview._qualifiers.value() : [],
+                               areInitialQualifiers = true;
 
-                       if ( statementview._qualifiers && (
-                               !statementview._qualifiers.isValid()
-                               || statementview._qualifiers.isInitialValue() 
&& statementview.isInitialValue()
-                       ) ) {
+                       // Statementview's isValid() validated the qualifiers 
already. However, the information
+                       // whether all qualifiers (grouped by property) have 
changed, needs to be gathers
+                       // separately:
+                       if( enable && snaklistviews.length ) {
+                               for( var i = 0; i < snaklistviews.length; i++ ) 
{
+                                       if( !snaklistviews[i].isInitialValue() 
) {
+                                               areInitialQualifiers = false;
+                                       }
+                               }
+                       }
+
+                       if( areInitialQualifiers && 
statementview.isInitialValue() ) {
                                enable = false;
                        }
 
-                       edittoolbar.toolbar.editGroup.$btnSave.data( 
'toolbarbutton' )[ enable ? 'enable' : 'disable' ]();
+                       $btnSave.data( 'toolbarbutton' )[ enable ? 'enable' : 
'disable' ]();
                }
        },
        options: {
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.claimview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.claimview.js
index 8e0681b..82aad83 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.claimview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.claimview.js
@@ -101,11 +101,21 @@
        _claim: null,
 
        /**
-        * Reference to the listview widget managing the qualifier snaks. 
Basically, just a short-cut
-        * for this.$qualifiers.data( 'listview' )
+        * Reference to the listview widget managing the qualifier 
snaklistviews. Basically, just a
+        * short-cut for this.$qualifiers.data( 'listview' )
         * @type {jquery.wikibase.listview}
         */
        _qualifiers: null,
+
+       /**
+        * Caches the snak list of the qualifiers the claimview has been 
initialized with. The
+        * qualifiers are split into groups featuring the same property. 
Removing one of those groups
+        * results in losing the reference to those qualifiers. Therefore, 
_initialQualifiers is used
+        * to rebuild the list of qualifiers when cancelling and is used to 
query whether the qualifiers
+        * represent the initial state.
+        * @type {wikibase.SnakList}
+        */
+       _initialQualifiers: null,
 
        /**
         * Whether the Claim is currently in edit mode.
@@ -136,18 +146,22 @@
                } );
 
                // Initialize qualifiers:
-               // TODO: Allow adding qualifiers when adding a new claim.
-               if ( this.option( 'value' ) ) {
-                       var $qualifiers = $( '<div/>' )
-                       .prependTo( this.$qualifiers )
-                       .snaklistview( {
-                               value: ( this._claim ) ? 
this._claim.getQualifiers() : null
-                       } )
-                       .on( 'snaklistviewchange', function( event ) {
-                               self._trigger( 'change' );
-                       } );
+               this._initialQualifiers = ( this._claim ) ? 
this._claim.getQualifiers() : new wb.SnakList();
 
-                       this._qualifiers = $qualifiers.data( 'snaklistview' );
+               if( this._claim ) { // TODO: Allow adding qualifiers when 
adding a new claim.
+                       var qualifiers = this._claim.getQualifiers();
+
+                       // Group qualifiers by property id:
+                       if( qualifiers.length ) {
+                               var propertyIds = qualifiers.getPropertyOrder(),
+                                       groupedQualifierSnaks = [];
+
+                               for( var i = 0; i < propertyIds.length; i++ ) {
+                                       groupedQualifierSnaks.push( 
qualifiers.getFilteredSnakList( propertyIds[i] ) );
+                               }
+
+                               this._createQualifiersListview( 
groupedQualifierSnaks );
+                       }
                }
 
                this._attachEditModeEventHandlers();
@@ -166,15 +180,83 @@
        },
 
        /**
-        * Returns whether the claimview is valid according to its current 
contents. An Empty value
+        * Creates the listview widget containing the qualifier snaklistview 
widgets. Omitting
+        * groupedQualifierSnaks parameter generates an empty list widget.
+        * @since 0.4
+        *
+        * @param {wikibase.SnakList} [groupedQualifierSnaks]
+        */
+       _createQualifiersListview: function( groupedQualifierSnaks ) {
+               var self = this;
+
+               // Using the property id, qualifier snaks are split into groups 
of snaklistviews. These
+               // snaklistviews are managed in a listview:
+               var $qualifiers = $( '<div/>' )
+                       .prependTo( this.$qualifiers )
+                       .listview( {
+                               listItemAdapter: new 
$.wikibase.listview.ListItemAdapter( {
+                                       listItemWidget: $.wikibase.snaklistview,
+                                       listItemWidgetValueAccessor: 'value',
+                                       newItemOptionsFn: function( value ) {
+                                               return {
+                                                       value: value || null
+                                               };
+                                       }
+                               } ),
+                               value: groupedQualifierSnaks || null
+                       } )
+                       .on( 'snaklistviewchange.' + this.widgetName, function( 
event ) {
+                               self._trigger( 'change' );
+                       } )
+                       .on( 'listviewitemremoved.' + this.widgetName, 
function( event, value, $itemNode ) {
+                               if( event.target === 
self._qualifiers.element.get( 0 ) ) {
+                                       self._trigger( 'change' );
+                                       return;
+                               }
+
+                               // Check if last snaklistview of a qualifier 
listview item has been removed and
+                               // remove the listview item if so:
+                               var $snaklistview = $( event.target ).closest( 
':wikibase-snaklistview' ),
+                                       snaklistview = $snaklistview.data( 
'snaklistview' );
+
+                               if( !snaklistview.value() ) {
+                                       self._qualifiers.removeItem( 
snaklistview.element );
+                               }
+                       } );
+
+               this._qualifiers = $qualifiers.data( 'listview' );
+       },
+
+       /**
+        * Destroys the listview widget containing the qualifier snaklistview 
widgets.
+        */
+       _destroyQualifiersListView: function() {
+               if( this._qualifiers ) {
+                       this._qualifiers.destroy();
+                       this.$qualifiers.empty();
+                       this._qualifiers = null;
+               }
+       },
+
+       /**
+        * Returns whether the claimview is valid according to its current 
contents. An empty value
         * will be considered not valid (also, an empty value can not be saved).
         * @since 0.4
         *
         * @return {boolean}
         */
        isValid: function() {
-               if( this._qualifiers && !this._qualifiers.isValid() ) {
-                       return false;
+               // Validate qualifiers:
+               if( this._qualifiers ) {
+                       var snaklistviews = this._qualifiers.value();
+
+                       if( snaklistviews.length ) {
+                               for( var i = 0; i < snaklistviews.length; i++ ) 
{
+                                       if( !snaklistviews[i].isValid() ) {
+                                               return false;
+                                       }
+                               }
+                       }
                }
 
                try {
@@ -182,6 +264,7 @@
                } catch( e ) {
                        return false;
                }
+
                return true;
        },
 
@@ -190,11 +273,29 @@
         * the claim has been initialized with.
         * @since 0.4
         *
-        * @returns {boolean}
+        * @return {boolean}
         */
        isInitialValue: function() {
-               return this.$mainSnak.data( 'snakview' ).isInitialSnak()
-                       && ( !this._qualifiers || 
this._qualifiers.isInitialValue() );
+               if( this._claim ) {
+                       var snaklistviews = ( this._qualifiers ) ? 
this._qualifiers.value() : [],
+                               qualifiers = new wb.SnakList();
+
+                       // Generate a SnakList object featuring all current 
qualifier snaks to be able to
+                       // compare it to the SnakList object the claimview has 
been initialized with:
+                       if( snaklistviews.length ) {
+                               for( var i = 0; i < snaklistviews.length; i++ ) 
{
+                                       if( snaklistviews[i].value() ) {
+                                               qualifiers.add( 
snaklistviews[i].value() );
+                                       }
+                               }
+                       }
+
+                       if( !qualifiers.equals( this._initialQualifiers ) ) {
+                               return false;
+                       }
+               }
+
+               return this.$mainSnak.data( 'snakview' ).isInitialSnak();
        },
 
        /**
@@ -214,9 +315,23 @@
                natively: function( e ) {
                        this.$mainSnak.data( 'snakview' ).startEditing();
 
-                       if ( this._qualifiers ) {
-                               this._qualifiers.startEditing();
+                       if( !this._qualifiers && this._claim ) {
+                               this._createQualifiersListview();
                        }
+
+                       // Start edit mode of all qualifiers:
+                       if( this._qualifiers ) {
+                               var snaklistviews = this._qualifiers.value();
+                               if ( snaklistviews.length ) {
+                                       for( var i = 0; i < 
snaklistviews.length; i++ ) {
+                                               snaklistviews[i].startEditing();
+                                       }
+                               }
+                               // If there are no snaklistviews, there is no 
way for the "add qualifier" toolbar
+                               // to be
+                               this._qualifiers.element.trigger( 
'qualifiersstartediting' );
+                       }
+
 
                        this.element.addClass( 'wb-edit' );
                        this._isInEditMode = true;
@@ -258,8 +373,39 @@
                                        this.$mainSnak.data( 'snakview' 
).stopEditing( dropValue );
                                }
 
-                               if ( this._qualifiers ) {
-                                       this._qualifiers.stopEditing( dropValue 
);
+                               // Stop edit mode of qualifier snaklistviews:
+                               if( this._qualifiers ) {
+                                       var snaklistviews = 
this._qualifiers.value();
+
+                                       if ( snaklistviews.length ) {
+                                               for( var i = 0; i < 
snaklistviews.length; i++ ) {
+                                                       
snaklistviews[i].stopEditing( dropValue );
+
+                                                       // Remove snaklistview 
from qualifier listview if no snakviews left in
+                                                       // that snaklistview:
+                                                       if( 
!snaklistviews[i].value() ) {
+                                                               
this._qualifiers.removeItem( snaklistviews[i].element );
+                                                       }
+                                               }
+                                       }
+                               }
+
+                               // Destroy listview which will also send out 
events to erase the "add qualifier"
+                               // toolbar:
+                               this._destroyQualifiersListView();
+
+                               // Refill the qualifier listview with the 
initial qualifiers:
+                               if( this._initialQualifiers.length > 0 ) {
+                                       var qualifierSnakLists = [],
+                                               propertyIds = 
this._initialQualifiers.getPropertyOrder();
+
+                                       for( var i = 0; i < propertyIds.length; 
i++ ) {
+                                               qualifierSnakLists.push(
+                                                       
this._initialQualifiers.getFilteredSnakList( propertyIds[i] )
+                                               );
+                                       }
+
+                                       this._createQualifiersListview( 
qualifierSnakLists );
                                }
 
                                self.enable();
@@ -275,8 +421,14 @@
                                .done( function( savedClaim, pageInfo ) {
                                        self.$mainSnak.data( 'snakview' 
).stopEditing( dropValue );
 
-                                       if ( self._qualifiers ) {
-                                               self._qualifiers.stopEditing();
+                                       if( self._qualifiers ) {
+                                               var snaklistviews = 
self._qualifiers.value();
+
+                                               if ( snaklistviews.length ) {
+                                                       for( var i = 0; i < 
snaklistviews.length; i++ ) {
+                                                               
snaklistviews[i].stopEditing();
+                                                       }
+                                               }
                                        }
 
                                        self.enable();
@@ -341,7 +493,7 @@
                        defaultHandling( event, dropValue );
                } );
 
-               if ( this._qualifiers ) {
+               if( this._qualifiers && this._qualifiers.value().length ) {
                        this._qualifiers.element.one( 
'snaklistviewstopediting', function( event, dropValue ) {
                                defaultHandling( event, dropValue );
                        } );
@@ -355,7 +507,7 @@
        _detachEditModeEventHandlers: function() {
                this.$mainSnak.off( 'snakviewstopediting' );
 
-               if ( this._qualifiers ) {
+               if ( this._qualifiers && this._qualifiers.value().length ) {
                        this._qualifiers.element.off( 'snaklistviewstopediting' 
);
                }
        },
@@ -379,9 +531,17 @@
         * @throws {Error} In case the widget's current value is insufficient 
for building a claim.
         */
        _instantiateClaim: function( guid ) {
+               var qualifiers = new wb.SnakList(),
+                       snaklistviews = this._qualifiers.value();
+
+               // Combine qualifiers grouped by property to a single SnakList:
+               for( var i = 0; i < snaklistviews.length; i++ ) {
+                       qualifiers.add( snaklistviews[i].value() );
+               }
+
                return new wb.Claim(
                        this.$mainSnak.data( 'snakview' ).snak(),
-                       ( this._qualifiers ) ? this._qualifiers.value() : null,
+                       qualifiers,
                        guid
                );
        },
@@ -415,12 +575,6 @@
 
                        // Update model of represented Claim:
                        self._claim = savedClaim;
-
-                       // If the claim was pending (adding a new claim instead 
of editing an existing one),
-                       // there are no qualifiers set yet.
-                       if ( self._qualifiers ) {
-                               self._qualifiers.value( 
savedClaim.getQualifiers() );
-                       }
                } );
        },
 
@@ -519,13 +673,53 @@
        id: 'claim-qualifiers-snak',
        selector: '.wb-claim-qualifiers',
        events: {
-               snaklistviewstartediting: 'create',
-               snaklistviewafterstopediting: 'destroy',
+               'listviewcreate snaklistviewstartediting': function( event ) {
+                       var $target = $( event.target ),
+                               $qualifiers = $target.closest( 
'.wb-claim-qualifiers' ),
+                               listview = $target.closest( 
':wikibase-listview' ).data( 'listview' );
+
+                       if(
+                               event.type === 'listviewcreate' && 
listview.items().length === 0
+                               || event.type === 'snaklistviewstartediting'
+                       ) {
+                               $qualifiers.addtoolbar( {
+                                       customAction: function( event ) {
+                                               listview.enterNewItem();
+                                               
listview.value()[listview.value().length - 1].enterNewItem();
+                                       },
+                                       eventPrefix: 
$.wikibase.snaklistview.prototype.widgetEventPrefix,
+                                       addButtonLabel: mw.msg( 
'wikibase-addqualifier' )
+                               } );
+                       }
+               },
+               'listviewdestroy snaklistviewafterstopediting': function( event 
) {
+                       var $target = $( event.target ),
+                               $qualifiers = $target.closest( 
'.wb-claim-qualifiers' );
+
+                       if ( $qualifiers.data( 'addtoolbar' ) && ( 
$target.closest( ':wikibase-listview' ).length === 0 || event.type !== 
'listviewdestroy' ) ) {
+                               $qualifiers.data( 'addtoolbar' ).destroy();
+                               $qualifiers.removeData( 'addtoolbar' );
+                               $qualifiers
+                               .children( '.' + 
$.wikibase.addtoolbar.prototype.widgetBaseClass )
+                               .remove();
+                       }
+               },
                snaklistviewchange: function( event ) {
-                       var snaklistview = $( event.target ).data( 
'snaklistview' ),
-                               addToolbar = $( event.target ).data( 
'addtoolbar' );
+                       var $target = $( event.target ),
+                               snaklistview = $target.data( 'snaklistview' ),
+                               $qualifiers = $target.closest( 
'.wb-claim-qualifiers' ),
+                               addToolbar = $qualifiers.data( 'addtoolbar' ),
+                               $listview = $target.closest( 
':wikibase-listview' ),
+                               snaklistviews = $listview.data( 'listview' 
).value();
+
                        if ( addToolbar ) {
-                               addToolbar.toolbar[snaklistview.isValid() ? 
'enable' : 'disable']();
+                               addToolbar.toolbar.enable();
+                               for( var i = 0; i < snaklistviews.length; i++ ) 
{
+                                       if( !snaklistviews[i].isValid() ) {
+                                               addToolbar.toolbar.disable();
+                                               break;
+                                       }
+                               }
                        }
                },
                snaklistviewdisable: function( event ) {
@@ -562,13 +756,6 @@
                                addToolbar.toolbar.enable();
                        }
                }
-       },
-       options: {
-               customAction: function( event, $parent ) {
-                       $parent.data( 'snaklistview' ).enterNewItem();
-               },
-               eventPrefix: 
$.wikibase.snaklistview.prototype.widgetEventPrefix,
-               addButtonLabel: mw.msg( 'wikibase-addqualifier' )
        }
 } );
 
@@ -576,18 +763,18 @@
        id: 'claim-qualifiers-snak',
        selector: '.wb-claim-qualifiers',
        events: {
-               'snakviewstartediting snakviewcreate listviewitemadded 
listviewitemremoved': function( event ) {
+               'snakviewstartediting': function( event ) {
                        var $target = $( event.target ),
-                               listview = $target.closest( '.wb-snaklistview' 
).data( 'snaklistview' )._listview;
+                               $snaklistview = $target.closest( 
'.wb-snaklistview' ),
+                               qualifierPorpertyGroupListview = 
$snaklistview.data( 'snaklistview' )._listview,
+                               qualifiersListview = $snaklistview.closest( 
'.wb-listview' ).data( 'listview' );
 
-                       if ( event.type.indexOf( 'snakview' ) !== -1 ) {
-                               // Create toolbar for each snakview widget:
-                               $target.removetoolbar( {
-                                       action: function( event ) {
-                                               listview.removeItem( $target );
-                                       }
-                               } );
-                       }
+                       // Create toolbar for each snakview widget:
+                       $target.removetoolbar( {
+                               action: function( event ) {
+                                       
qualifierPorpertyGroupListview.removeItem( $target );
+                               }
+                       } );
                },
                snaklistviewafterstopediting: function( event ) {
                        // Destroy the snakview toolbars:
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.listview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.listview.js
index 918096e..c49dae6 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.listview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.listview.js
@@ -48,6 +48,9 @@
  * @event enternewitem: Triggered when initializing the process of adding a 
new item to the list.
  *        (1) {jQuery.Event}
  *        (2) {jQuery} The DOM node pending to be added permanently to the 
list.
+ *
+ * @event destroy: Triggered then the widget has been destroyed.
+ *        (1) {jQuery.Event}
  */
 $.widget( 'wikibase.listview', PARENT, {
        widgetBaseClass: 'wb-listview',
@@ -97,6 +100,7 @@
        destroy: function() {
                this.element.removeClass( this.widgetBaseClass );
                $.Widget.prototype.destroy.call( this );
+               this._trigger( 'destroy' );
        },
 
        /**
@@ -126,6 +130,43 @@
        },
 
        /**
+        * Sets/gets the listview's list item instances.
+        *
+        * @param {*[]} [value]
+        * @return {*[]}
+        */
+       value: function( value ) {
+               var self = this;
+
+               // Getter:
+               if( value === undefined ) {
+                       var values = [];
+
+                       this.items().each( function( i, node ) {
+                               values.push( self._lia.liInstance( $( node ) ) 
);
+                       } );
+
+                       return values;
+               }
+
+               // Clear listview:
+               this.items().each( function( i, node ) {
+                       var $node = $( node );
+                       self._lia.liInstance( $node ).destroy();
+                       $node.remove();
+               } );
+
+               // Add new values:
+               for( var i = 0; i < value.length; i++ ) {
+                       var $newLi = $( '<div/>' );
+                       this.element.append( $newLi );
+                       this._lia.newListItem( $newLi, value[i] );
+               }
+
+               return value;
+       },
+
+       /**
         * Returns all list item nodes.
         *
         * @since 0.4
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.snaklistview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.snaklistview.js
index 8994df4..4a3fe0f 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.snaklistview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.snaklistview.js
@@ -307,8 +307,15 @@
                                throw new Error( 'Value has to be an instance 
of wikibase.SnakList' );
                        }
 
+                       var wasInEditMode = this.isInEditMode();
+
                        this._snakList = snakList;
                        this.createListView();
+
+                       if( wasInEditMode ) {
+                               this.startEditing();
+                       }
+
                        return this._snakList;
                }
                // getter:
diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.statementview.js 
b/lib/resources/jquery.wikibase/jquery.wikibase.statementview.js
index 062d1fc..140eff3 100644
--- a/lib/resources/jquery.wikibase/jquery.wikibase.statementview.js
+++ b/lib/resources/jquery.wikibase/jquery.wikibase.statementview.js
@@ -33,7 +33,7 @@
                        '', // TODO: This toolbar placeholder should be removed 
from the template.
                        '', // .wb-claim-mainsnak
                        '', // Qualifiers
-                       '', // Rreferences heading
+                       '', // References heading
                        '' // List of references
                ],
                templateShortCuts: {
@@ -158,9 +158,22 @@
         * @return {wb.Statement}
         */
        _instantiateClaim: function( guid ) {
+               var qualifiers = null;
+
+               // Gather qualifiers split into property groups in one single 
wb.SnakList object:
+               if( this._qualifiers ) {
+                       var snaklistviews = this._qualifiers.value();
+
+                       qualifiers = new wb.SnakList();
+
+                       for( var i = 0; i < snaklistviews.length; i++ ) {
+                               qualifiers.add( snaklistviews[i].value() );
+                       }
+               }
+
                return new wb.Statement(
                        this.$mainSnak.data( 'snakview' ).snak(),
-                       ( this._qualifiers ) ? this._qualifiers.value() : null,
+                       qualifiers,
                        this.getReferences(),
                        null, // TODO: Rank
                        guid
diff --git a/lib/resources/wikibase.datamodel/wikibase.Claim.js 
b/lib/resources/wikibase.datamodel/wikibase.Claim.js
index 94b7199..8883b84 100644
--- a/lib/resources/wikibase.datamodel/wikibase.Claim.js
+++ b/lib/resources/wikibase.datamodel/wikibase.Claim.js
@@ -84,8 +84,20 @@
         *
         * @return wb.SnakList
         */
-       getQualifiers: function() {
-               return this._qualifiers;
+       getQualifiers: function( propertyId ) {
+               if( !propertyId ) {
+                       return this._qualifiers;
+               }
+
+               var filteredQualifiers = new wb.SnakList();
+
+               this._qualifiers.each( function( i, snak ) {
+                       if( snak.getPropertyId() === propertyId ) {
+                               filteredQualifiers.addSnak( snak );
+                       }
+               } );
+
+               return filteredQualifiers;
        },
 
        /**
diff --git a/lib/resources/wikibase.datamodel/wikibase.SnakList.js 
b/lib/resources/wikibase.datamodel/wikibase.SnakList.js
index 97384c1..8b7a45f 100644
--- a/lib/resources/wikibase.datamodel/wikibase.SnakList.js
+++ b/lib/resources/wikibase.datamodel/wikibase.SnakList.js
@@ -97,6 +97,30 @@
        },
 
        /**
+        * Returns a snak list with the snaks that feature the specified 
property id. If the property id
+        * parameter is omitted, a copy of the whole SnakList object is 
returned.
+        * @since 0.4
+        *
+        * @param {string} [propertyId]
+        * @return {wikibase.SnakList}
+        */
+       getFilteredSnakList: function( propertyId ) {
+               if( !propertyId ) {
+                       return this.newFromJSON( this.toJSON() );
+               }
+
+               var filteredQualifiers = new wb.SnakList();
+
+               this.each( function( i, snak ) {
+                       if( snak.getPropertyId() === propertyId ) {
+                               filteredQualifiers.addSnak( snak );
+                       }
+               } );
+
+               return filteredQualifiers;
+       },
+
+       /**
         * Returns whether the list contains a Snak equal to a given one.
         *
         * @since 0.4
@@ -149,6 +173,17 @@
         */
        each: function( fn ) {
                $.each.call( null, this._snaks, fn );
+       },
+
+       /**
+        * Adds the snaks of another snak list to this snak list.
+        * @since 0.4
+        *
+        * @param {wikibase.SnakList} snakList
+        */
+       add: function( snakList ) {
+               $.merge( this._snaks, snakList.toArray() );
+               this.length += snakList.length;
        },
 
        /**
@@ -206,7 +241,7 @@
 /**
  * Creates a new Snak Object from a given JSON structure.
  *
- * @param {string} json
+ * @param {Object} json
  * @param {string[]} [order] List of property ids defining the order of the 
snaks grouped by
  *        property.
  * @return {wikibase.SnakList|null}
diff --git a/selenium/lib/modules/qualifiers_module.rb 
b/selenium/lib/modules/qualifiers_module.rb
index 9677d1a..db24e9c 100644
--- a/selenium/lib/modules/qualifiers_module.rb
+++ b/selenium/lib/modules/qualifiers_module.rb
@@ -10,21 +10,21 @@
   include PageObject
   # qualifiers UI elements
   div(:qualifiersContainer, :class => "wb-claim-qualifiers")
-  link(:addQualifier,                  :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]/div[contains(@class, 
'wb-snaklistview')]/span[contains(@class, 
'wb-addtoolbar')]/div/span/span/a[not(contains(@class, 
'wikibase-toolbarbutton-disabled'))][text()='add qualifier']")
-  link(:addQualifierDisabled,  :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]/div[contains(@class, 
'wb-snaklistview')]/span[contains(@class, 
'wb-addtoolbar')]/div/span/span/a[contains(@class, 
'wikibase-toolbarbutton-disabled')][text()='add qualifier']")
-  link(:removeQualifierLine1,  :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]/div[contains(@class, 
'wb-snaklistview')]/div[contains(@class, 
'wb-snaklistview-listview')]/div[contains(@class, 
'wb-snakview')][1]/span[contains(@class, 
'wb-removetoolbar')]/div/span/span/a[not(contains(@class, 
'wikibase-toolbarbutton-disabled'))][text()='remove']")
-  link(:removeQualifierLine2,  :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]/div[contains(@class, 
'wb-snaklistview')]/div[contains(@class, 
'wb-snaklistview-listview')]/div[contains(@class, 
'wb-snakview')][2]/span[contains(@class, 
'wb-removetoolbar')]/div/span/span/a[not(contains(@class, 
'wikibase-toolbarbutton-disabled'))][text()='remove']")
+  link(:addQualifier,                  :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]//span[contains(@class, 
'wb-addtoolbar')]//a[not(contains(@class, 
'wikibase-toolbarbutton-disabled'))][text()='add qualifier']")
+  link(:addQualifierDisabled,  :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]//span[contains(@class, 
'wb-addtoolbar')]//a[contains(@class, 
'wikibase-toolbarbutton-disabled')][text()='add qualifier']")
+  link(:removeQualifierLine1,  :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]/div/div[contains(@class, 
'wb-snaklistview')][1]//span[contains(@class, 
'wb-removetoolbar')]//a[not(contains(@class, 
'wikibase-toolbarbutton-disabled'))][text()='remove']")
+  link(:removeQualifierLine2,  :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]/div/div[contains(@class, 
'wb-snaklistview')][2]//span[contains(@class, 
'wb-removetoolbar')]//a[not(contains(@class, 
'wikibase-toolbarbutton-disabled'))][text()='remove']")
   text_area(:qualifierValueInput1, :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]//textarea[contains(@class, 'valueview-input')]", :index 
=> 0)
   text_area(:qualifierValueInput2, :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]//textarea[contains(@class, 'valueview-input')]", :index 
=> 1)
 
-  link(:qualifierPropertyLink1, :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]/div[contains(@class, 
'wb-snaklistview')]/div[contains(@class, 
'wb-snaklistview-listview')]/div[contains(@class, 
'wb-snakview')][1]/div[contains(@class, 'wb-snak-property-container')]/div/a")
-  link(:qualifierPropertyLink2, :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]/div[contains(@class, 
'wb-snaklistview')]/div[contains(@class, 
'wb-snaklistview-listview')]/div[contains(@class, 
'wb-snakview')][2]/div[contains(@class, 'wb-snak-property-container')]/div/a")
-  div(:qualifierProperty1, :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]/div[contains(@class, 
'wb-snaklistview')]/div[contains(@class, 
'wb-snaklistview-listview')]/div[contains(@class, 
'wb-snakview')][1]/div[contains(@class, 'wb-snak-property-container')]/div")
-  div(:qualifierProperty2, :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]/div[contains(@class, 
'wb-snaklistview')]/div[contains(@class, 
'wb-snaklistview-listview')]/div[contains(@class, 
'wb-snakview')][2]/div[contains(@class, 'wb-snak-property-container')]/div")
-  link(:qualifierValueLink1, :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]/div[contains(@class, 
'wb-snaklistview')]/div[contains(@class, 
'wb-snaklistview-listview')]/div[contains(@class, 
'wb-snakview')][1]/div[contains(@class, 
'wb-snak-value-container')]/div[contains(@class, 'wb-snak-value')]/div/div/a")
-  link(:qualifierValueLink2, :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]/div[contains(@class, 
'wb-snaklistview')]/div[contains(@class, 
'wb-snaklistview-listview')]/div[contains(@class, 
'wb-snakview')][2]/div[contains(@class, 
'wb-snak-value-container')]/div[contains(@class, 'wb-snak-value')]/div/div/a")
-  div(:qualifierValue1, :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]/div[contains(@class, 
'wb-snaklistview')]/div[contains(@class, 
'wb-snaklistview-listview')]/div[contains(@class, 
'wb-snakview')][1]/div[contains(@class, 
'wb-snak-value-container')]/div[contains(@class, 'wb-snak-value')]/div/div")
-  div(:qualifierValue2, :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]/div[contains(@class, 
'wb-snaklistview')]/div[contains(@class, 
'wb-snaklistview-listview')]/div[contains(@class, 
'wb-snakview')][2]/div[contains(@class, 
'wb-snak-value-container')]/div[contains(@class, 'wb-snak-value')]/div/div")
+  div(:qualifierProperty1,             :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]/div/div[contains(@class, 
'wb-snaklistview')][1]//div[contains(@class, 
'wb-snak-property-container')]/div")
+  div(:qualifierProperty2,             :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]/div/div[contains(@class, 
'wb-snaklistview')][2]//div[contains(@class, 
'wb-snak-property-container')]/div")
+  link(:qualifierPropertyLink1,        :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]/div/div[contains(@class, 
'wb-snaklistview')][1]//div[contains(@class, 'wb-snak-property-container')]//a")
+  link(:qualifierPropertyLink2,        :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]/div/div[contains(@class, 
'wb-snaklistview')][2]//div[contains(@class, 'wb-snak-property-container')]//a")
+  link(:qualifierValueLink1,   :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]/div/div[contains(@class, 
'wb-snaklistview')][1]//div[contains(@class, 'wb-snak-value')]//a")
+  link(:qualifierValueLink2,   :xpath => "//div[contains(@class, 
'wb-claim-qualifiers')]/div/div[contains(@class, 
'wb-snaklistview')][2]//div[contains(@class, 'wb-snak-value')]//a")
+  div(:qualifierValue1,                        :xpath => 
"//div[contains(@class, 'wb-claim-qualifiers')]/div/div[contains(@class, 
'wb-snaklistview')][1]//div[contains(@class, 'wb-snak-value')]/div/div")
+  div(:qualifierValue2,                        :xpath => 
"//div[contains(@class, 'wb-claim-qualifiers')]/div/div[contains(@class, 
'wb-snaklistview')][2]//div[contains(@class, 'wb-snak-value')]/div/div")
 
   def wait_for_qualifier_value_box
     wait_until do

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I5a195c2d144e4cbab7d23dc785c27b8255ef4646
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: Henning Snater <henning.sna...@wikimedia.de>

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

Reply via email to