Benutzer:Jakob Warkotsch (WMDE)/rev-slider-prototype.js

aus Wikipedia, der freien Enzyklopädie
Zur Navigation springen Zur Suche springen

Hinweis: Leere nach dem Veröffentlichen den Browser-Cache, um die Änderungen sehen zu können.

  • Firefox/Safari: Umschalttaste drücken und gleichzeitig Aktualisieren anklicken oder entweder Strg+F5 oder Strg+R (⌘+R auf dem Mac) drücken
  • Google Chrome: Umschalttaste+Strg+R (⌘+Umschalttaste+R auf dem Mac) drücken
  • Internet Explorer/Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
  • Opera: Strg+F5
// <nowiki>

// Page title
var gPageName = mw.config.get( 'wgPageName' );

// Gives us the most current revision ID for the page
var gCurRevisionId = mw.config.get( 'wgCurRevisionId' );

// Action: view/query/history
var gAction = mw.config.get( 'wgAction' );

// User name
var gUserName = mw.config.get( 'wgUserName' );

// The URL of the page, relative to DOCUMENT_ROOT
var gScript = mw.config.get( 'wgScript' );

// Revision ID of right revision on diff page
var gRightRevID = mw.config.get( 'wgRevisionId' );

// Revision ID of left revision on diff page
var gLeftRevID = mw.util.getParamValue( 'oldid' );

// Get value of diff param in URL
var gDiff = mw.util.getParamValue( 'diff' );

// Page ID
var gPageID = mw.config.get( 'wgArticleID' );

// Server
var gServer = mw.config.get( 'wgServer' );

// Slider
var revisionWidth = 10,
	POINTER_WIDTH = 30,
	$container = null,
	$revisionSlider = null,
	revs = [],
	pointerPosL = -1,
	pointerPosR = -1;

// Function called when a tick on the slider is clicked
// Params: v1 - Left revision ID; v2 - Right revision ID
function refresh( v1, v2 ) {
	if( v1 === -1 || v2 === -1 ) return;

	var $url = gServer + gScript + '?title=' + gPageName + '&diff=' + v2 + '&oldid=' + v1;
	location.href = $url;
}


// Formating date in JS
function formatDate( rawDate ) {
	var months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec' ];
	var f = new Date( rawDate );
	var fDate = f.getUTCDate();
	var fMonth = f.getUTCMonth();
	var fYear = f.getUTCFullYear();
	var fHours = ( '0' + f.getUTCHours()).slice( -2 );
	var fMinutes = ( '0' + f.getUTCMinutes()).slice( -2 );
	return ( fHours + ':' + fMinutes + ', ' + fDate + ' ' + months[ fMonth ] + ' ' + fYear ).toString();
}


// Setting the tick marks on the slider
// Params: element - jQuery slider; revs - revisions data from API
function setSliderTicks( element, revs ) {
	var $slider = $( element ),
		revData = getComposedRevData( revs ),
		maxChangeSizeLogged = Math.log( revData.maxChangeSize );

	for( var i = 1; i < revs.length; i++ ) {
		var diffSize = revs[ i ].size - revs[ i - 1 ].size,
			relativeChangeSize = Math.ceil( 65.0 * Math.log( Math.abs( diffSize ) ) / maxChangeSizeLogged ) + 5,
			section = getSection( revs[ i ].comment ),
			html = '<b>' + formatDate( revs[ i ].timestamp ) + '</b><br>';

		html += mw.html.escape( revs[ i ].user ) + '<br>';
		if( revs[ i ].comment !== '' ) {
			html += '<br><i>' + mw.html.escape( revs[ i ].parsedcomment ) + '</i>';
		}
		//html += '<br> ' + revs[i].minor ? '<b>K</b> ' : '';
		html += '<br>' + diffSize + ' byte';
		$( '<div class="ui-slider-tick-mark revision" title="<center>' + html + '</center>"/>' )
			.css( {
				'left': i + '%',
				'height': relativeChangeSize + 'px',
				'top': diffSize > 0 ? '-' + relativeChangeSize + 'px' : 0,
				'background': revData.sectionMap.get( section ) ? revData.sectionMap.get( section ) : 'black'
				//'opacity' : revs[i].minor ? 0.15 : 0.35
				//'background' : i%2 == 1 ? 'white' : 'black'
			} )
			.tipsy( {
				gravity: 's',
				html: true,
				fade: true
			} )
			.appendTo( $slider );
		$( '<div class="stopper"/>' )
			.css( 'left', (i - 1) + '.5%' )
			.appendTo( $slider );
	}
}

function getComposedRevData( revs ) {
	var max = 0,
		changeSize = 0,
		section,
		sectionMap = new Map(),
		result;

	for( i = 1; i < revs.length; i++ ) {
		changeSize = Math.abs( revs[ i ].size - revs[ i - 1 ].size );
		section = getSection( revs[ i ].comment );
		if( changeSize > max ) {
			max = changeSize;
		}
		if( section.length > 0 && !sectionMap.has( section ) ) {
			sectionMap.set( section );
		}
	}

	var i = 0;
	sectionMap.forEach( function( item, key, sectionMap ) {
		sectionMap.set( key, rainbow( sectionMap.size, i ) );
		i++;
	} );

	for( i = 0; i < sectionMap.length; i++ ) {
		sectionMap.set()
	}
	result = {
		maxChangeSize: max,
		sectionMap: sectionMap
	};

	return result;
}

function getSection( text ) {
	text = text.match(
		new RegExp(
			'(/\\* [^\\*]* \\*/)',
			'gi' ) );
	if( !text ) {
		return '';
	}
	return text[ 0 ].replace(
		new RegExp( ' \\*/|/\\* ', 'ig' ),
		'' );
}

var isPointerInRange = function( pointerPos, start, end ) {
	return pointerPos >= start
		&& pointerPos <= Math.min( revs.length, end );
};

var scroll = function( $container, direction ) {
	$container.animate( {
		scrollLeft: $container.scrollLeft() + ($container.width() * direction)
	} );
};

var revisionOfPosition = function( pos ) {
	return Math.floor( pos / revisionWidth );
};

var slideToPosition = function( $pointer, pos ) {
	var containerOffset = $container.offset().left - $revisionSlider.offset().left,
		left = (pos % 100) * revisionWidth;

	$pointer.animate( { left: left + containerOffset } );
};

var slideToSide = function( $pointer, pointerPos, direction ) {
	var containerOffset = $revisionSlider.find( '.arrow' ).outerWidth() + 20, // 20 == margin right
		isLeft = pointerPos < revisionOfPosition( $container.scrollLeft() ) + direction * revisionOfPosition( $container.width() ),
		sideFactor = isLeft ? -1 : 1,
		sideOffset = 3 * revisionWidth * sideFactor / 2,
		offsetRight = $pointer.hasClass( 'left-pointer' ) ? -revisionWidth : 0,
		xPos = isLeft ? containerOffset : $container.width() + containerOffset;

	$pointer.animate( { left: xPos + offsetRight + sideOffset } );
};

// Adding the initial barebones slider
// Params: revs - revisions data from API; vals - initial positions for the slider handles
function addSlider( revs ) {
	var $revisions = $( '<div class="revisions"></div>' ).css( 'width', revs.length * revisionWidth ),
		$leftPointer = $( '<div class="pointer left-pointer" />' ),
		$rightPointer = $( '<div class="pointer right-pointer" />' );

	$revisionSlider = $( '<div class="revision-slider" />' )
		.append( $( '<a class="arrow left-arrow" data-dir="-1"></a>' ) )
		.append( $( '<div class="revisions-container" />' ).append( $revisions ) )
		.append( $( '<a class="arrow right-arrow" data-dir="1"></a>' ) )
		.append( $( '<div style="clear: both" />' ) )
		.append(
			$( '<div class="pointer-container" />' )
				.append( $leftPointer )
				.append( $rightPointer )
		);

	$container = $revisionSlider.find( '.revisions-container' );

	$revisionSlider.find( '.arrow' ).click( function() {
		var direction = $( this ).data( 'dir' ),
			newStart = revisionOfPosition(
					Math.min( $container.find( '.revisions' ).width() - $container.width(), Math.max( 0, $container.scrollLeft() ) )
				) + (direction * revisionOfPosition( $container.width() )),
			newEnd = newStart + revisionOfPosition( $container.width() );

		if( isPointerInRange( pointerPosL, newStart, newEnd ) ) {
			slideToPosition( $leftPointer, pointerPosL );
		} else {
			slideToSide( $leftPointer, pointerPosL, direction );
		}

		if( isPointerInRange( pointerPosR, newStart, newEnd ) ) {
			slideToPosition( $rightPointer, pointerPosR );
		} else {
			slideToSide( $rightPointer, pointerPosR, direction );
		}

		scroll( $container, direction );
	} );

	setSliderTicks( $revisions, revs );

	$revisionSlider.find( '.pointer' ).draggable( {
		axis: 'x',
		snap: '.stopper',
		containment: '.revisions-container',
		stop: function() {
			var posLeft = parseInt( $( this ).css( 'left' ) ),
				offset = $revisionSlider.find( '.arrow' ).outerWidth() + 20,
				pos = Math.round( (posLeft + $container.scrollLeft() - offset) / revisionWidth );

			if( $( this ).hasClass( 'left-pointer' ) ) pointerPosL = pos;
			else pointerPosR = pos;

			// refresh( pointerPosL, pointerPosR );
		}
	} );

	$html = $( '<td colspan="4" style="text-align:center;" class="slider"></td>' ).append( $revisionSlider );
	$html2 = $( '<tr>' ).append( $html );
	$legendHtml = $( '<td colspan="4" style="text-align:center; font-size: 0.75em; padding: 1em 0 0.5em;"></td>' ).append( getSectionLegend( revs ) );
	$element = $( '.diff > tbody > tr' ).eq( 0 ).after( $legendHtml ).after( $html2 );

	revisionWidth = $( '.slider' ).width() * 90 / 10000;
	slideToSide( $leftPointer, -1, 1 );
	slideToSide( $rightPointer, -1, 1 );
}

function getSectionLegend( revs ) {
	var revData = getComposedRevData( revs ),
		html = '';
	revData.sectionMap.forEach( function( item, key, sectionMap ) {
		html += '<span class="rvslider-legend-box" style="color:' + item + ';"> ■</span>' + key + '';
	} );
	return html;
}

// Driver function
mw.loader.using( [ 'jquery.ui', 'jquery.tipsy' ], function() {
	$( document ).ready( function() {
		$.ajax( {
			url: mw.util.wikiScript( 'api' ),
			data: {
				action: 'query',
				prop: 'revisions',
				format: 'json',
				rvprop: 'ids|timestamp|user|comment|parsedcomment|size|flags',
				titles: gPageName,
				formatversion: 2,
				rvstartid: gCurRevisionId,
				"continue": "",
				rvlimit: "500"
			},
			success: function( data ) {
				revs = data.query.pages[ 0 ].revisions;
				if( !revs ) {
					return;
				}
				revs.reverse();

				addSlider( revs );
			}
		} );
	} );
} );


// see http://stackoverflow.com/a/7419630/4782503
function rainbow( numOfSteps, step ) {
	// This function generates vibrant, "evenly spaced" colours (i.e. no clustering). This is ideal for creating easily distinguishable vibrant markers in Google Maps and other apps.
	// Adam Cole, 2011-Sept-14
	// HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
	var r, g, b;
	var h = step / numOfSteps;
	var i = ~~(h * 6);
	var f = h * 6 - i;
	var q = 1 - f;
	switch( i % 6 ) {
		case 0:
			r = 1;
			g = f;
			b = 0;
			break;
		case 1:
			r = q;
			g = 1;
			b = 0;
			break;
		case 2:
			r = 0;
			g = 1;
			b = f;
			break;
		case 3:
			r = 0;
			g = q;
			b = 1;
			break;
		case 4:
			r = f;
			g = 0;
			b = 1;
			break;
		case 5:
			r = 1;
			g = 0;
			b = q;
			break;
	}
	var c = "#" + ("00" + (~~(r * 255)).toString( 16 )).slice( -2 ) + ("00" + (~~(g * 255)).toString( 16 )).slice( -2 ) + ("00" + (~~(b * 255)).toString( 16 )).slice( -2 );
	return (c);
}

// </nowiki>