MediaWiki:Gadget-RearrangeValues.js
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
/***
* This script enables users to easily change order of values in any statement.
* It adds a rearrange button to all statements with multiple values.
* After clicking the button, up/down arrows are added near each value, which
* can be used to move values up or down in the list.
* Alternatively, the statements can be dragged into the desired positions.
* Once the statements are in the right order, clicking save submits an edit
* for the changes.
*/
( function ( $, mw ) {
// This should work only in the main and property namespaces
if ( mw.config.get( 'wgNamespaceNumber' ) !== 0 && mw.config.get( 'wgNamespaceNumber' ) !== 120 ) {
return;
}
var translations = require( './RearrangeValues-i18n.json' );
$.i18n().load( translations );
var commons_upload = 'https://upload.wikimedia.org/wikipedia/commons/';
var merge_icon = commons_upload + 'b/b0/Symbol_merge_vote.svg';
var up_icon = commons_upload + 'c/ca/OOjs_UI_icon_upTriangle.svg';
var down_icon = commons_upload + '0/0c/OOjs_UI_icon_downTriangle.svg';
// Original order of values to return to when "cancel" is pressed
var statement_original_orders = {};
// Generate the rearrange button
function ra_img( element_id ) {
var img = $( '<img>' )
.attr( 'src', merge_icon )
.attr( 'alt', $.i18n( 'gadget-rearrangevalues-button' ) )
.attr( 'title', $.i18n( 'gadget-rearrangevalues-button' ) );
return $( '<a>' )
.attr( 'href', '#' )
.attr( 'tabindex', 0 )
.append( img )
.on( 'click', function () {
statement_selected( element_id );
return false;
} );
}
// Add rearrange buttons to all statements with multiple values
$( '.wikibase-statementgrouplistview' ).children().children().each( function () {
var values_elements = $( this ).find( ':nth-child(2) .wikibase-statementview' );
if ( values_elements.length >= 2 ) {
var element_id = this.id;
var img_div = $( '<div>' )
.attr( 'id', 'button' + element_id )
.addClass( 'rearrange-values' )
.append( ra_img( element_id ) );
$( this ).children().first().children().first().append( '<br>', img_div );
}
} );
// If one of the rearrange buttons is pressed, add save/cancel buttons
// and up/down arrow buttons for each value
function statement_selected( statement_p ) {
// Save/cancel buttons
var button_save = new OO.ui.ButtonWidget( {
label: mw.msg( 'wikibase-save' ),
icon: 'check',
flags: [ 'primary', 'progressive' ]
} ).on( 'click', function () {
save( statement_p );
} );
var button_cancel = new OO.ui.ButtonWidget( {
label: mw.msg( 'wikibase-cancel' ),
icon: 'close',
flags: [ 'primary', 'destructive' ]
} ).on( 'click', function () {
cancel( statement_p );
} );
$( '#button' + statement_p ).empty().append( button_save.$element, button_cancel.$element );
// List to store original order of values to return to if "cancel" is pressed
var original_order = [];
// Variables needed for drag and drop
var element_height;
var dragged_node_id;
var node_new_position;
var prev_node;
var next_node;
var first_node;
var last_node;
var dragover_counter = 0;
var index_of_dragged_node;
// Iterate over each value
$( '#' + statement_p + ' :nth-child(2) .wikibase-statementview' ).each( function () {
// Remove dollar sign from IDs because it's a special character in jquery
var value_id = this.id.replace( '$', '' );
$( this ).attr( 'id', value_id );
// Add the current ID to the list of original values
original_order.push( value_id );
// Add up/down buttons to the current value
var img_up = $( '<img>' )
.attr( 'src', up_icon )
.attr( 'alt', $.i18n( 'gadget-rearrangevalues-move-up' ) )
.attr( 'title', $.i18n( 'gadget-rearrangevalues-move-up' ) );
var arrow_up = $( '<a>' )
.attr( 'href', '#' )
.attr( 'tabindex', 0 )
.append( img_up )
.on( 'click', function () {
move_value_up( value_id );
return false;
} );
var img_down = $( '<img>' )
.attr( 'src', down_icon )
.attr( 'alt', $.i18n( 'gadget-rearrangevalues-move-down' ) )
.attr( 'title', $.i18n( 'gadget-rearrangevalues-move-down' ) );
var arrow_down = $( '<a>' )
.attr( 'href', '#' )
.attr( 'tabindex', 0 )
.append( img_down )
.on( 'click', function () {
move_value_down( value_id );
return false;
} );
var div_arrows = $( '<div>' )
.append( arrow_up, arrow_down )
.addClass( 'move_arrows_block' );
$( this ).find( '.wikibase-edittoolbar-container' ).append( div_arrows );
// Make values draggable
$( this ).attr( 'draggable', 'true' ).addClass( 'draggable' );
$( this ).on( 'dragstart', function ( event ) {
dragged_node_id = event.target.id;
if ( !dragged_node_id ) {
return;
}
element_height = document.getElementById( dragged_node_id ).offsetHeight;
setTimeout( function () {
$( '#' + dragged_node_id ).css( 'display', 'none' );
}, 0 );
} );
} );
// Add the original order of this statement to the list of original orders of all statements
statement_original_orders[ statement_p ] = original_order;
// What to do when an element is being dragged
$( 'body' ).on( 'dragover', function ( event ) {
event.preventDefault();
// Get the positions of all nodes
var nodes_positions = [];
$( '#' + statement_p + ' :nth-child(2) .wikibase-statementview' ).each( function ( index ) {
if ( this.id === dragged_node_id ) {
index_of_dragged_node = index;
return;
}
var element_id = this.id;
var element = document.getElementById( element_id );
if ( !element ) {
return;
}
var node_position = element.getBoundingClientRect();
nodes_positions.push( {
id: element_id,
y: ( node_position.top + node_position.bottom ) / 2
} );
if ( dragover_counter === 0 && index > index_of_dragged_node ) {
nodes_positions[ nodes_positions.length - 1 ].y += element_height;
}
$( this ).css( 'marginTop', '' );
$( this ).css( 'marginBottom', '' );
} );
// Get the position of the node currently being dragged
node_new_position = 0;
first_node = nodes_positions[ 0 ].id;
last_node = nodes_positions[ nodes_positions.length - 1 ].id;
for ( var i = 0; i < nodes_positions.length; i++ ) {
if ( nodes_positions[ i ].y < event.clientY ) {
node_new_position = i + 1;
prev_node = nodes_positions[ i ].id;
next_node = ( i + 1 < nodes_positions.length ) ? nodes_positions[ i + 1 ].id : '';
} else {
break;
}
}
// Create space for the node currently being dragged
if ( node_new_position === 0 ) {
$( '#' + first_node ).css( 'marginTop', element_height + 'px' );
} else if ( node_new_position === nodes_positions.length ) {
$( '#' + last_node ).css( 'marginBottom', element_height + 'px' );
} else {
$( '#' + prev_node ).css( 'marginBottom', element_height / 2 + 'px' );
$( '#' + next_node ).css( 'marginTop', element_height / 2 + 'px' );
}
dragover_counter++;
} );
// What to do when element is dropped
$( 'body' ).on( 'drop', function ( event ) {
event.preventDefault();
if ( node_new_position === 0 ) {
$( '#' + first_node ).before( $( '#' + dragged_node_id ) );
} else {
$( '#' + prev_node ).after( $( '#' + dragged_node_id ) );
}
} );
// What to do when drag ends (whether by drop or by failing the drag)
$( 'body' ).on( 'dragend', function ( event ) {
$( '#' + statement_p + ' :nth-child(2) .wikibase-statementview' ).each( function () {
$( this ).css( 'marginTop', '' );
$( this ).css( 'marginBottom', '' );
} );
$( '#' + dragged_node_id ).css( 'display', '' );
dragover_counter = 0;
} );
}
// Move value one position up
function move_value_up( value_id ) {
var previous_id = $( '#' + value_id ).prev().attr( 'id' );
if ( !previous_id ) {
return;
}
$( '#' + previous_id ).before( $( '#' + value_id ) );
}
// Move value one position down
function move_value_down( value_id ) {
var previous_id = $( '#' + value_id ).next().attr( 'id' );
if ( !previous_id ) {
return;
}
$( '#' + previous_id ).after( $( '#' + value_id ) );
}
// Run after saving or cancelling
function statement_exited( statement_p ) {
// Remove save/cancel buttons, re-add the rearrange button
$( '#button' + statement_p ).empty().append( ra_img( statement_p ) );
// Remove up/down buttons
$( '#' + statement_p + ' .move_arrows_block' ).remove();
// Make values no longer draggable
$( '#' + statement_p + ' :nth-child(2) .wikibase-statementview' ).each( function () {
$( this ).attr( 'draggable', 'false' ).removeClass( 'draggable' );
} );
}
// Run when the cancel button is pressed
function cancel( statement_p ) {
// If any value was deleted, it should be removed from the original order
for ( var i = 0; i < statement_original_orders[ statement_p ].length; i++ ) {
if ( $( '#' + statement_original_orders[ statement_p ][ i ] ).length === 0 ) {
statement_original_orders[ statement_p ].splice( i, 1 );
}
}
// Put values in the order they were before clicking rearrange-buttons
for ( var j = 0; j < statement_original_orders[ statement_p ].length - 1; j++ ) {
$( '#' + $( '#' + statement_p ).children().eq( 1 ).find( '.wikibase-statementview' ).eq( j ).attr( 'id' ) )
.before( $( '#' + statement_original_orders[ statement_p ][ j ] ) );
}
// Replace "save" and "cancel" buttons with rearrange-button, remove arrow buttons
statement_exited( statement_p );
}
// Run when the save button is pressed
function save( statement_p ) {
// Get JSON for values (claims)
var claims;
var api = new mw.Api();
api.post( {
action: 'wbgetentities',
ids: mw.config.get( 'wgTitle' ),
} ).done( function ( data ) {
claims = data.entities[ mw.config.get( 'wgTitle' ) ].claims[ statement_p ];
var i;
// Check if the order changed at all
// For this, first get a list of values as they are now
var actual_claims = [];
var id;
$( '#' + statement_p + ' :nth-child(2) .wikibase-statementview' ).each( function () {
id = this.id;
// If it is a value which was just added
if ( id === 'new' ) {
id = $( this ).attr( 'class' );
try {
id = id.match( /wikibase-statement-([QPq].+)$/ )[ 1 ].replace( '$', '' );
} catch ( e ) {
// No match
return;
}
}
actual_claims.push( id );
} );
// Then compare this list with the list of values in original order
var same = true;
for ( i = 0; i < claims.length; i++ ) {
if ( claims[ i ].id.replace( '$', '' ) !== actual_claims[ i ] ) {
same = false;
break;
}
}
// If the order did not change, no need to make an edit
if ( same ) {
mw.notify( $.i18n( 'gadget-rearrangevalues-notify-no-changes' ), { type: 'info' } );
statement_exited( statement_p );
return;
}
// Remove values in the old order
var remove_claims = [];
for ( i = 0; i < claims.length; i++ ) {
remove_claims.push( '{"id":"' + claims[ i ].id + '","remove":""}' );
}
remove_claims = remove_claims.join( ',' );
// Put values in the new order
var indexed_claims = {};
for ( i = 0; i < claims.length; i++ ) {
indexed_claims[ ( claims[ i ].id ).replace( '$', '' ) ] = claims[ i ];
}
var add_claims = [];
for ( i = 0; i < actual_claims.length; i++ ) {
add_claims.push( JSON.stringify( indexed_claims[ actual_claims[ i ] ] ) );
}
add_claims = add_claims.join( ',' );
// Save the edit
api.postWithEditToken( {
action: 'wbeditentity',
id: mw.config.get( 'wgTitle' ),
summary: 'Changing order of values for [[Property:' +
statement_p + '|' + statement_p + ']]',
tags: 'gadget-rearrangevalues',
data: '{"claims":[' + remove_claims + ',' + add_claims + ']}'
} ).done( function (data) {
if ( data.success === 1 ) {
mw.notify( $.i18n( 'gadget-rearrangevalues-notify-success', statement_p ), { type: 'success' } );
statement_exited( statement_p );
} else {
mw.notify( $.i18n( 'gadget-rearrangevalues-notify-failed' ), { type: 'error' } );
}
} ).fail( function () {
mw.notify( $.i18n( 'gadget-rearrangevalues-notify-failed' ), { type: 'error' } );
} );
} );
}
}( jQuery, mediaWiki ) );