signature). Subclasses are likely to call at least doMainQuery
(possibly both), but other classes might too, because they were
public.
-
Also, some related hooks were deprecated, but this is not yet a
breaking change.
+* Removed 'jquery.arrowSteps' module. (deprecated since 1.28)
+* The 'jquery.autoEllipsis' ResourceLoader module is now deprecated.
== Compatibility ==
if ( $deprecationInfo ) {
$name = $this->getName();
$warning = 'This page is using the deprecated ResourceLoader module "' . $name . '".';
- if ( !is_bool( $deprecationInfo ) && isset( $deprecationInfo['message'] ) ) {
- $warning .= "\n" . $deprecationInfo['message'];
+ if ( is_string( $deprecationInfo ) ) {
+ $warning .= "\n" . $deprecationInfo;
}
return Xml::encodeJsCall(
'mw.log.warn',
"resources/src/mediawiki.toolbar",
"resources/src/mediawiki.widgets",
"resources/src/jquery/jquery.accessKeyLabel.js",
- "resources/src/jquery/jquery.arrowSteps.js",
"resources/src/jquery/jquery.autoEllipsis.js",
"resources/src/jquery/jquery.badge.js",
"resources/src/jquery/jquery.byteLength.js",
"rcfilters-highlightmenu-title": "Select a color",
"rcfilters-highlightmenu-help": "Select a color to highlight this property",
"rcfilters-filterlist-noresults": "No filters found",
+ "rcfilters-state-message-subset": "This filter has no effect because its results are included with those of the following, broader {{PLURAL:$2|filter|filters}} (try highlighting to distinguish it): $1",
+ "rcfilters-state-message-fullcoverage": "Selecting all filters in a group is the same as selecting none, so this filter has no effect. Group includes: $1",
"rcfilters-filtergroup-registration": "User registration",
"rcfilters-filter-registered-label": "Registered",
"rcfilters-filter-registered-description": "Logged-in editors.",
"rcfilters-highlightmenu-title": "Title for the highlight menu used to select the highlight color for an individual filter.",
"rcfilters-highlightmenu-help": "Tooltip for the highlight menu for individual filters.",
"rcfilters-filterlist-noresults": "Message showing no results found for searching a filter.",
+ "rcfilters-state-message-subset": "Tooltip shown when hovering over a filter tag when one or more broader filters that contain the hovered filter are also selected. This indicates that the hovered filter has no effect because all the results it matches are also matched by the broader filter(s). Parameters:\n* $1 - Comma-separated string of selected broader filters that this filter is a subset of\n* $2 - Count of filters in $1, for PLURAL",
+ "rcfilters-state-message-fullcoverage": "Tooltip shown when hovering over a filter tag when all the filters in its group are selected. This indicates that the hovered filter has no effect because the selected filters in the group cover all changes. Parameters:\n* $1 - Comma-separated string of selected filters in the group\n* $2 - Count of filters in $1, for PLURAL",
"rcfilters-filtergroup-registration": "Title for the filter group for editor registration type.",
"rcfilters-filter-registered-label": "Label for the filter for showing edits made by logged-in users.\n{{Identical|Registered}}",
"rcfilters-filter-registered-description": "Description for the filter for showing edits made by logged-in users.",
'targets' => [ 'mobile', 'desktop' ],
],
'jquery.appear' => [
- 'deprecated' => [
- 'message' => 'Please use "mediawiki.viewport" instead.',
- ],
+ 'deprecated' => 'Please use "mediawiki.viewport" instead.',
'scripts' => 'resources/lib/jquery/jquery.appear.js',
],
- 'jquery.arrowSteps' => [
- 'deprecated' => true,
- 'scripts' => 'resources/src/jquery/jquery.arrowSteps.js',
- 'styles' => 'resources/src/jquery/jquery.arrowSteps.css',
- 'targets' => [ 'desktop', 'mobile' ],
- ],
'jquery.async' => [
'scripts' => 'resources/lib/jquery/jquery.async.js',
],
'jquery.autoEllipsis' => [
+ 'deprecated' => 'Use CSS text-overflow instead.',
'scripts' => 'resources/src/jquery/jquery.autoEllipsis.js',
'dependencies' => 'jquery.highlightText',
'targets' => [ 'desktop', 'mobile' ],
'targets' => [ 'desktop', 'mobile' ],
],
'jquery.jStorage' => [
- 'deprecated' => [
- 'message' => 'Please use "mediawiki.storage" instead.',
- ],
+ 'deprecated' => 'Please use "mediawiki.storage" instead.',
'scripts' => 'resources/lib/jquery/jquery.jStorage.js',
],
'jquery.suggestions' => [
/* jQuery UI */
'jquery.ui.core' => [
- 'deprecated' => [
- 'message' => 'Please use "mediawiki.ui.button" or "oojs-ui" instead.',
- ],
+ 'deprecated' => 'Please use "mediawiki.ui.button" or "oojs-ui" instead.',
'scripts' => 'resources/lib/jquery.ui/jquery.ui.core.js',
'dependencies' => [
'jquery.ui.core.styles',
'rcfilters-highlightbutton-title',
'rcfilters-highlightmenu-title',
'rcfilters-highlightmenu-help',
+ 'rcfilters-state-message-subset',
+ 'rcfilters-state-message-fullcoverage',
'recentchanges-noresult',
+ 'quotation-marks',
],
'dependencies' => [
'oojs-ui',
+ 'mediawiki.language',
'mediawiki.rcfilters.filters.dm',
'oojs-ui.styles.icons-moderation',
'oojs-ui.styles.icons-editing-core',
+++ /dev/null
-.arrowSteps {
- list-style-type: none;
- list-style-image: none;
- border: 1px solid #666;
- position: relative;
-}
-
-.arrowSteps li {
- float: left;
- padding: 0;
- margin: 0;
- border: 0;
-}
-
-.arrowSteps li div {
- padding: 0.5em;
- text-align: center;
- white-space: nowrap;
- overflow: hidden;
-}
-
-.arrowSteps li.arrow div {
- /* @embed */
- background: url( images/jquery.arrowSteps.divider-ltr.png ) no-repeat right center;
-}
-
-/* applied to the element preceding the highlighted step */
-.arrowSteps li.arrow.tail div {
- /* @embed */
- background: url( images/jquery.arrowSteps.tail-ltr.png ) no-repeat right center;
-}
-
-/* this applies to all highlighted, including the last */
-.arrowSteps li.head div {
- /* @embed */
- background: url( images/jquery.arrowSteps.head-ltr.png ) no-repeat left center;
- font-weight: bold;
-}
-
-/* this applies to all highlighted arrows except the last */
-.arrowSteps li.arrow.head div {
- /* TODO: eliminate duplication of jquery.arrowSteps.head.png embedding */
- /* @embed */
- background: url( images/jquery.arrowSteps.head-ltr.png ) no-repeat right center;
-}
+++ /dev/null
-/*!
- * jQuery arrowSteps plugin
- * Copyright Neil Kandalgaonkar, 2010
- *
- * This work is licensed under the terms of the GNU General Public License,
- * version 2 or later.
- * (see http://www.fsf.org/licensing/licenses/gpl.html).
- * Derivative works and later versions of the code must be free software
- * licensed under the same or a compatible license.
- */
-
-/**
- * @class jQuery.plugin.arrowSteps
- */
-( function ( $ ) {
- /**
- * Show users their progress through a series of steps, via a row of items that fit
- * together like arrows. One item can be highlighted at a time.
- *
- * <ul id="robin-hood-daffy">
- * <li id="guard"><div>Guard!</div></li>
- * <li id="turn"><div>Turn!</div></li>
- * <li id="parry"><div>Parry!</div></li>
- * <li id="dodge"><div>Dodge!</div></li>
- * <li id="spin"><div>Spin!</div></li>
- * <li id="ha"><div>Ha!</div></li>
- * <li id="thrust"><div>Thrust!</div></li>
- * </ul>
- *
- * <script>
- * $( '#robin-hood-daffy' ).arrowSteps();
- * </script>
- *
- * @return {jQuery}
- * @chainable
- */
- $.fn.arrowSteps = function () {
- var $steps, width, arrowWidth, $stepDiv,
- $el = this,
- paddingSide = $( 'body' ).hasClass( 'rtl' ) ? 'padding-left' : 'padding-right';
-
- $el.addClass( 'arrowSteps' );
- $steps = $el.find( 'li' );
-
- width = parseInt( 100 / $steps.length, 10 );
- $steps.css( 'width', width + '%' );
-
- // Every step except the last one has an arrow pointing forward:
- // at the right hand side in LTR languages, and at the left hand side in RTL.
- // Also add in the padding for the calculated arrow width.
- $stepDiv = $steps.filter( ':not(:last-child)' ).addClass( 'arrow' ).find( 'div' );
-
- // Execute when complete page is fully loaded, including all frames, objects and images
- $( window ).on( 'load', function () {
- arrowWidth = parseInt( $el.outerHeight(), 10 );
- $stepDiv.css( paddingSide, arrowWidth.toString() + 'px' );
- } );
-
- $el.data( 'arrowSteps', $steps );
-
- return this;
- };
-
- /**
- * Highlights the element selected by the selector.
- *
- * $( '#robin-hood-daffy' ).arrowStepsHighlight( '#guard' );
- * // 'Guard!' is highlighted.
- *
- * // ... user completes the 'guard' step ...
- *
- * $( '#robin-hood-daffy' ).arrowStepsHighlight( '#turn' );
- * // 'Turn!' is highlighted.
- *
- * @param {string} selector
- */
- $.fn.arrowStepsHighlight = function ( selector ) {
- var $previous,
- $steps = this.data( 'arrowSteps' );
- $.each( $steps, function ( i, step ) {
- var $step = $( step );
- if ( $step.is( selector ) ) {
- if ( $previous ) {
- $previous.addClass( 'tail' );
- }
- $step.addClass( 'head' );
- } else {
- $step.removeClass( 'head tail lasthead' );
- }
- $previous = $step;
- } );
- };
-
- /**
- * @class jQuery
- * @mixins jQuery.plugin.arrowSteps
- */
-}( jQuery ) );
return this.param;
};
+ /**
+ * Get the details of the active conflict on this filter
+ *
+ * @param {Object} conflicts Conflicts to examine
+ * @param {string} [key='contextDescription'] Message key
+ * @return {Object} Object with conflict message and conflict items
+ * @return {string} return.message Conflict message
+ * @return {string[]} return.names Conflicting item labels
+ */
+ mw.rcfilters.dm.FilterItem.prototype.getConflictDetails = function ( conflicts, key ) {
+ var group,
+ conflictMessage = '',
+ itemLabels = [];
+
+ key = key || 'contextDescription';
+
+ $.each( conflicts, function ( filterName, conflict ) {
+ if ( !conflict.item.isSelected() ) {
+ return;
+ }
+
+ if ( !conflictMessage ) {
+ conflictMessage = conflict[ key ];
+ group = conflict.group;
+ }
+
+ if ( group === conflict.group ) {
+ itemLabels.push( mw.msg( 'quotation-marks', conflict.item.getLabel() ) );
+ }
+ } );
+
+ return {
+ message: conflictMessage,
+ names: itemLabels
+ };
+
+ };
+
+ /**
+ * Get the message representing the state of this model.
+ *
+ * @return {string} State message
+ */
+ mw.rcfilters.dm.FilterItem.prototype.getStateMessage = function () {
+ var messageKey, details, superset,
+ affectingItems = [];
+
+ if ( this.isConflicted() ) {
+ // First look in filter's own conflicts
+ details = this.getConflictDetails( this.getOwnConflicts() );
+ if ( !details.message ) {
+ // Fall back onto conflicts in the group
+ details = this.getConflictDetails( this.getGroupModel().getConflicts() );
+ }
+
+ messageKey = details.message;
+ affectingItems = details.names;
+ } else if ( this.isIncluded() ) {
+ superset = this.getSuperset();
+ // For this message we need to collect the affecting superset
+ affectingItems = this.getGroupModel().getSelectedItems( this )
+ .filter( function ( item ) {
+ return superset.indexOf( item.getName() ) !== -1;
+ } )
+ .map( function ( item ) {
+ return mw.msg( 'quotation-marks', item.getLabel() );
+ } );
+
+ messageKey = 'rcfilters-state-message-subset';
+ } else if ( this.isFullyCovered() ) {
+ affectingItems = this.getGroupModel().getSelectedItems( this )
+ .map( function ( item ) {
+ return mw.msg( 'quotation-marks', item.getLabel() );
+ } );
+
+ messageKey = 'rcfilters-state-message-fullcoverage';
+ }
+
+ if ( messageKey ) {
+ // Build message
+ return mw.msg(
+ messageKey,
+ mw.language.listToText( affectingItems ),
+ affectingItems.length
+ );
+ }
+
+ // Display description
+ return this.getDescription();
+ };
+
/**
* Get the model of the group this filter belongs to
*
};
/**
- * Get filter conflicts
+ * Get all conflicts associated with this filter or its group
*
* Conflict object is set up by filter name keys and conflict
* definition. For example:
* {
* filterName: {
* filter: filterName,
- * group: group1
+ * group: group1,
+ * label: itemLabel,
+ * item: itemModel
* }
* filterName2: {
* filter: filterName2,
* group: group2
+ * label: itemLabel2,
+ * item: itemModel2
* }
* }
*
return $.extend( {}, this.conflicts, this.getGroupModel().getConflicts() );
};
+ /**
+ * Get the conflicts associated with this filter
+ *
+ * @return {Object} Filter conflicts
+ */
+ mw.rcfilters.dm.FilterItem.prototype.getOwnConflicts = function () {
+ return this.conflicts;
+ };
+
/**
* Set conflicts for this filter. See #getConflicts for the expected
* structure of the definition.
adjustedConflicts = {};
conflicts.forEach( function ( conflict ) {
+ var filter;
+
if ( conflict.filter ) {
filterName = model.groups[ conflict.group ].getNamePrefix() + conflict.filter;
+ filter = model.getItemByName( filterName );
// Rename
adjustedConflicts[ filterName ] = $.extend(
{},
conflict,
- { filter: filterName }
+ {
+ filter: filterName,
+ item: filter
+ }
);
} else {
// This conflict is for an entire group. Split it up to
adjustedConflicts[ groupItem.getName() ] = $.extend(
{},
conflict,
- { filter: groupItem.getName() }
+ {
+ filter: groupItem.getName(),
+ item: groupItem
+ }
);
} );
}
}
} );
+ // Add items to the model
+ this.addItems( items );
+
// Expand conflicts
groupConflictResult = expandConflictDefinitions( groupConflictMap );
filterConflictResult = expandConflictDefinitions( filterConflictMap );
}
} );
- // Add items to the model
- this.addItems( items );
-
this.emit( 'initialize' );
};
.mw-rcfilters-ui-capsuleItemWidget {
background-color: #fff;
border-color: #979797;
- margin: 0 0.6em 0 0;
color: #222;
// Background and color of the capsule widget need a bit
direction: ltr;
&-popup {
- // We have to override OOUI's definition, which is set
- // on the inline style of the popup
- margin-top: 2em !important; /* stylelint-disable-line declaration-no-important */
+ margin-top: 1px;
max-width: 650px;
.oo-ui-popupWidget-body {
* @cfg {jQuery} [$overlay] A jQuery object serving as overlay for popups
*/
mw.rcfilters.ui.CapsuleItemWidget = function MwRcfiltersUiCapsuleItemWidget( controller, model, config ) {
- var $popupContent = $( '<div>' )
- .addClass( 'mw-rcfilters-ui-capsuleItemWidget-popup-content' ),
- descLabelWidget = new OO.ui.LabelWidget();
-
// Configuration initialization
config = config || {};
this.controller = controller;
this.model = model;
+ this.popupLabel = new OO.ui.LabelWidget();
this.$overlay = config.$overlay || this.$element;
this.positioned = false;
this.popupTimeoutShow = null;
padded: false,
align: 'center',
position: 'above',
- $content: $popupContent
- .append( descLabelWidget.$element ),
+ $content: $( '<div>' )
+ .addClass( 'mw-rcfilters-ui-capsuleItemWidget-popup-content' )
+ .append( this.popupLabel.$element ),
$floatableContainer: this.$element,
classes: [ 'mw-rcfilters-ui-capsuleItemWidget-popup' ]
}
}, config ) );
- // Set initial text for the popup - the description
- descLabelWidget.setLabel( this.model.getDescription() );
-
this.$highlight = $( '<div>' )
.addClass( 'mw-rcfilters-ui-capsuleItemWidget-highlight' );
* Respond to mouse enter event
*/
mw.rcfilters.ui.CapsuleItemWidget.prototype.onMouseEnter = function () {
- if ( this.model.getDescription() ) {
+ var labelText = this.model.getStateMessage();
+
+ if ( labelText ) {
+ this.popupLabel.setLabel( labelText );
+
if ( !this.positioned ) {
// Recalculate anchor position to be center of the capsule item
this.popup.$anchor.css( 'margin-left', ( this.$element.width() / 2 ) );
// Parent
mw.rcfilters.ui.FilterCapsuleMultiselectWidget.parent.call( this, $.extend( true, {
- popup: { $autoCloseIgnore: filterInput.$element.add( this.$overlay ) }
+ popup: {
+ $autoCloseIgnore: filterInput.$element.add( this.$overlay ),
+ $floatableContainer: filterInput.$element
+ }
}, config ) );
this.controller = controller;
'deprecated' => true,
],
'deprecatedTomorrow' => $base + [
- 'deprecated' => [
- 'message' => 'Will be removed tomorrow.'
- ],
+ 'deprecated' => 'Will be removed tomorrow.'
],
'htmlTemplateModule' => $base + [