MediaWiki:Gadget-RearrangeValues.js

/*** * 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 = $( ' ' ) .attr( 'src', merge_icon ) .attr( 'alt', $.i18n( 'gadget-rearrangevalues-button' ) ) .attr( 'title', $.i18n( 'gadget-rearrangevalues-button' ) ); return $( '' ) .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 = $( ' ' )				.attr( 'id', 'button' + element_id )				.addClass( 'rearrange-values' )				.append( ra_img( element_id ) );			$( this ).children.first.children.first.append( ' ', 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 = $( ' ' ) .attr( 'src', up_icon ) .attr( 'alt', $.i18n( 'gadget-rearrangevalues-move-up' ) ) .attr( 'title', $.i18n( 'gadget-rearrangevalues-move-up' ) ); var arrow_up = $( '' ) .attr( 'href', '#' ) .attr( 'tabindex', 0 ) .append( img_up ) .on( 'click', function {					move_value_up( value_id );					return false;				} ); var img_down = $( ' ' ) .attr( 'src', down_icon ) .attr( 'alt', $.i18n( 'gadget-rearrangevalues-move-down' ) ) .attr( 'title', $.i18n( 'gadget-rearrangevalues-move-down' ) ); var arrow_down = $( '' ) .attr( 'href', '#' ) .attr( 'tabindex', 0 ) .append( img_down ) .on( 'click', function {					move_value_down( value_id );					return false;				} ); var div_arrows = $( ' ' ) .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 ' + 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 ) );