Jump to content

User:Jeph paul/visualisationscript.js

From Wikipedia, the free encyclopedia
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
function htmlDiff() {
	var _htmlHash;
	var _revHtmlHash;
	var _currentHash;
	var _is_debug = false;
	
	function pushHash(tag) {
	  if (typeof(_htmlHash[tag]) == 'undefined') {
	  	var value = eval('"\\u'+_currentHash.toString(16)+'"');
	    _htmlHash[tag] = value;
	    _revHtmlHash[value] = tag;
	    _currentHash++;
	  }
	  return _htmlHash[tag];
	}
	
	this.clearHash = function() {
	  _htmlHash = {};
	  _revHtmlHash = {};
	  _currentHash = 44032; //朝鲜文音节 Hangul Syllables
	};
	function html2plain(html) {
	  html = html.replace(/<(S*?)[^>]*>.*?|<.*?\/>/g, function(tag){
	    //debug:
	    if (_is_debug) {
	      return pushHash(tag.replace(/</g, '&lt;').replace(/>/g, '&gt;'));
	    } else {
	      return pushHash(tag);
	    }
	  });
	  
	  return html;
	}
	function plain2html(plain) {
		var back = '';
		for (i=0;i<plain.length;i++){
			if(_revHtmlHash[plain[i]]){
				back += _revHtmlHash[plain[i]];
			}
			else{
				back += plain[i];
			}
		}
		return back;
	};	
	var dmp = new diff_match_patch();
	this.diff = function(first,second){
		console.time('html2plain');
		var convertedFirst =  html2plain(first);
		var convertedSecond = html2plain(second);
		console.timeEnd('html2plain');
		console.time('toDiff');
		var diffs = dmp.diff_main(convertedFirst,convertedSecond);
		dmp.diff_cleanupSemantic(diffs);
		console.timeEnd('toDiff');
		var modified = '';
		console.time('toModify');
		for (i=0;i<diffs.length;i++){
			var diff = diffs[i];
			if (diff[0]==0){
				modified += diff[1];
			}
			else if (diff[0]==1){
				modified += '<ins id='+i+'>'+diff[1]+'</ins>';
			}
			else {
				modified += '<del id='+i+'>'+diff[1]+'</del>';
			}
		}
		console.timeEnd('toModify');
		console.time('convertBack');
		var complete = plain2html(modified);
		console.timeEnd('convertBack');
		return complete;
	};
};
var userNotification = function(type){
		//add code to remove class & add the calls to the notification
		if (type == 'play'){
			$('.notify').removeClass().addClass('notify').addClass('notifyPlay').show().fadeOut(2000);
		}
		else if (type == 'pause'){
			if(!$('.notify').hasClass('notifyPause') && $('.notify').hasClass('notifyPlay')){
				$('.notify').removeClass().addClass('notify').addClass('notifyPause').show().fadeOut(2000);
			}
		}
		else{
			$('.notify').removeClass().addClass('notify').addClass('notifyLoad').show().fadeOut(2000);
		}
	}; 
var usingLanguageNamespace = 'en';
var infoBox = function (revInfo){
        for (key in revInfo){                   
            if(key == 'revid'){
                var urlBase = 'https://'+usingLanguageNamespace+'.wikipedia.org/w/index.php?oldid='+revInfo[key];
                var anchor =$('<a>'+revInfo[key]+'</a>').attr({'target':'_blank','href':urlBase});
                 $('#infoBox .revisionLink').html(anchor);
            }
            else if(key == 'timestamp'){
            	$('#infoBox .editDate').html(revInfo[key]);
            }
            else if(key == 'user'){
            	$('#infoBox .userName').html(revInfo[key]);
            }
            else{
            	var minor = revInfo[key]?revInfo[key]:'';
                $('#infoBox .minor').html(minor);
               	
            }
        }
	};
    function wikiSlider(options){
    	
		var url = 'https://en.wikipedia.org/w/api.php?callback=?';
	    var postDict ={
		        rvdir:'older',
		        format:'json',
		        rawcontinue:'',
		        action:'query',
		        prop:'revisions',
				titles:'',
		        rvprop:'user|timestamp|flags|ids|size',
		        rvlimit:'max',
	    	};
		
		this.height = options.height;
        this.barGraphBarwidth = options.barGraphBarwidth;
        this.enlargedBarGraphBarwidth = options.enlargedBarGraphBarwidth;
		
		/* Callbacks for the Primary & Secondry slider*/
		this.primarySliderCallback = null ;
		this.secondrySliderCallback = null;
		if (options.primarySliderMoveCallback && typeof(options.primarySliderMoveCallback) === 'function'){
			this.primarySliderCallback = options.primarySliderMoveCallback;
		}
		
		if (options.secondrySliderMoveCallback && typeof(options.secondrySliderMoveCallback) === 'function'){
			this.secondrySliderCallback = options.secondrySliderMoveCallback;
		}
		
		/* Used Variables */
		var svgWidth  = 0;
		var hoverUser = '';
		var completeRevData = [], secondrySliderSelection = [];
		var rvContinueHash = true,gettingDataFlag = true;
		var yscale = null, yscale2 = null, xscale = null;
		var brush = null;
		var peg = null,pegScale = null,pegHandle,pegHandleContainer;
		var toolTipDiv, svg, svgBox, svgEnlargedBox;
		var primaryContainer, primaryGraph, secondaryContainer, secondaryGraph, newGraph;
		var outerLength = 25, enlargedLength = 65;
		var endLine, startDate, endDate;
		var progressBar,progressBarWidth = 5;
		var bars;
		var timeFormat = "%Y-%m-%dT%H:%M:%SZ";
        var timeParse = d3.time.format(timeFormat);
        
        /* Selected Edits*/
		this.selectedEdits = [];
		this.pegMoved = true;
		var that = this;
		
		var outerScrollDateUpdate = function (){
    		var barGraphBarGap = 1;
    		var outerWidth = $('#outer').width();
    		var outerViewportHidden = $('#outer').eq(0).scrollLeft();
    		var outerViewportStart = outerViewportHidden/(that.barGraphBarwidth + barGraphBarGap);
    		var outerViewportEnd = (outerViewportHidden + outerWidth)/(that.barGraphBarwidth + barGraphBarGap);
    		var outerViewportShown =  completeRevData.slice(outerViewportStart,outerViewportEnd);
    		$('#outerEndDate').html(timeParse.parse(outerViewportShown[0].timestamp).toDateString().slice(4));
    		$('#outerStartDate').html(timeParse.parse(outerViewportShown[outerViewportShown.length -1].timestamp).toDateString().slice(4));
    		console.log('outer scroll');
    	};
    	
		this.init = function(){
			/*Enlarged view toggle*/
        	d3.select('.enlargedButton').on('click',function(){
	            if (d3.select('#enlarged').style('height').split('px')[0] > 20){
	                d3.select('#enlarged').style({'height':'15px','top':'145px'}).select('svg').style('display','none');
	                d3.select(this).classed('up',false);
	                d3.select(this).classed('down',true);
	            }
	            else{
	                d3.select('#enlarged').style({'height':enlargedLength*2+'px', top:0+'px'}).select('svg').style('display','block');
	                d3.select(this).classed('down',false);
	                d3.select(this).classed('up',true);
	            }
	            });
        
        	/* tooltip div */
        	tooltipDiv = d3.select("body").append("div").attr("class", "tooltip").style("opacity", 0);
        	
        	
        	svg = d3.select('#outer').append('svg').attr({'height':outerLength*2,'width':600});
            svgBox = svg.append('g').attr({'transform':'translate(0,0)'});
            svgEnlarged = d3.select('#enlarged').append('svg').attr({'height':enlargedLength*2,'width':450});
            /*Baseline in enlarged graph*/
        	d3.select('#enlarged svg').append('line').attr({'x1':0,'x2':450,
        													'y1':enlargedLength+progressBarWidth/2,'y2':enlargedLength+progressBarWidth/2,
        													'stroke':'#d0eeed',
        													'stroke-width':progressBarWidth});
        	progressBar =  d3.select('#enlarged svg').append('line').attr({'x1':0,'x2':450,
        													'y1':enlargedLength+progressBarWidth/2,'y2':enlargedLength+progressBarWidth/2,
        													'stroke':'#1ebce2',
        													'stroke-width':0});
        	
			d3.select('#enlarged svg').append('line').attr({'x1':0,'x2':0,
															'y1':10,'y2':enlargedLength+progressBarWidth/2,
															'stroke':'gray',
															'stroke-width':'.5'});
			endLine = d3.select('#enlarged svg').append('line').attr({'x1':450,'x2':450,
																	'y1':10,'y2':enlargedLength+progressBarWidth/2,
																	'stroke':'gray',
																	'stroke-width':'.5'});
			svgEnlargedBox = svgEnlarged.append('g').attr('transform','translate(0,0)');
			
	        primaryContainer = svgBox.append('g').attr('id','primaryContainer');
	        primaryGraph = primaryContainer.append('g').attr('id','primaryGraph');
	        //Commenting out Edit size
	        //primaryContainer.append('text').text('Edit Size').attr({'x':10,'y':10,'class':'legendText'});
	        secondaryContainer = svgEnlargedBox.append('g').attr({'id':'secondaryContainer'});
	        secondaryGraph = secondaryContainer.append('g').attr('id','secondaryGraph');
	        
        	
        	startDate = secondaryContainer.append('text').attr({'x':0,'y':15}).style('font-size',9);
			endDate = secondaryContainer.append('text').attr({'x':450,'y':15}).style('font-size',9);
			$('#outer').scroll(outerScrollDateUpdate);
			
			/* Calling it direcly with getData by the user*/
        	//this.getData();
        	
		};
		this.getData = function(title){
			if(title){
				postDict['titles'] = title;
			}
			if (gettingDataFlag && rvContinueHash){
				//console.log('postDict',postDict);
        		$.getJSON(url,postDict,function(data){
            		if ('query-continue' in data){
                    	rvContinueHash = data['query-continue'].revisions.rvcontinue;
                    	postDict['rvcontinue'] = rvContinueHash;
                	}
            		else{
                    	rvContinueHash = null;
            		}
	            console.log(rvContinueHash);
	            if (!rvContinueHash) $('#olderEditsInfo').hide();
	            var resultKey = Object.keys(data.query.pages);
	            var revData = data.query.pages[resultKey].revisions;
	            completeRevData = completeRevData.concat(revData);
	            parsedData = that.parseData(completeRevData);
	            that.fixScales();
	            that.addData(primaryGraph,completeRevData,yscale);
	            that.callBrush();
	            gettingDataFlag = true;
	            $('#outer').scroll();
        		}).error(function() { 
        			gettingDataFlag = true; 
        		});
        		gettingDataFlag = false;
        	}	
        	else{
            	return;
        	}	
		};
		
		this.cleanUp = function () {
			  completeRevData = [];
			  rvContinueHash = true;
			  postDict ={
		        rvdir:'older',
		        format:'json',
		        action:'query',
		        rawcontinue:'',
		        prop:'revisions',
				titles:'',
		        rvprop:'user|timestamp|flags|ids|size',
		        rvlimit:'max',
	    	};
		};
		//Cleanup big time
		/** To highlight the revision currently being animated **/
		this.modifySecondryGraph = function(field,value) {
			newGraph.filter(function(d){
				if(d[field] == value){
					progressBar.attr('x1',d.lastX);
					pegHandleContainer.transition().duration(750).call(peg.extent([d.lastX, d.lastX])).call(peg.event);
					return true;
				}
			});
		};
		//Cleanup big time
		/** To get the list of revisions selected in the slider **/
		this.getSelection = function () {
			var brushExtent = brush.extent();
			var start = Math.floor(brushExtent[0]/(that.barGraphBarwidth+1));
			var end = Math.ceil(brushExtent[1]/(that.barGraphBarwidth+1));
			return completeRevData.slice(start,end);
		};
		this.getSecondrySliderSelection = function (i){
			return i ? that.getSelection().slice(0,i+1).reverse():that.getSelection().reverse();
		};
		this.wikiNameSpace = function (language) {
	  		usingLanguageNamespace = language;
	  		baseUrl = 'https://'+language+'.wikipedia.org/w/api.php?callback=?';
		};
	
		this.parseData = function(data){
	        data.forEach(function(d,i,array){
	                if(array[i+1]){
	                    d.editSize = d.size - array[i+1].size;
	                    //Edit direction positive or negative
	                    if (d.editSize >= 0){
	                        d.dir = 'p';
	                    }
	                    else{
	                       d.dir = 'n';
	                       d.editSize = d.editSize * -1;
	                    }
	                    if (i == 0 ){
	                        d.timeDiff = 0;
	                    }
	                    else{
	                        d.timeDiff = Math.floor((timeParse.parse(array[i-1].timestamp) - timeParse.parse(d.timestamp) ) /86400000);
	                    }
	                }
	                else{
	                    d.editSize = null;
	                    d.timeDiff = 0;
	                }
	                //console.log(d.editSize);
	            if (d.parentid == 0){
	                d.editSize = d.size;
	                d.timeDiff = 0;
	                d.dir = 'p';
	            }
	            if ('minor' in d){
	                d.minor = true;
	            }
	            else{
	                d.minor = false;
	            }
	            d.date = timeParse.parse(d.timestamp);
	            });
        		data.svgWidth = data.length*(that.barGraphBarwidth+1) < 100 ? 100 : data.length*(that.barGraphBarwidth+1);
        		data.yscale = [0,d3.max(data, function(d) { return d.editSize; })];
        		
   		};
   		
   		this.fixScales = function (){
	        if(xscale == null){
				yscale = d3.scale.pow().exponent(.4).domain([0,d3.max(completeRevData, function(d) { return d.editSize; })])
													.range([1,outerLength]);
				yscale2 = d3.scale.pow().exponent(.4).domain([0,d3.max(completeRevData, function(d) { return d.editSize; })])
													.range([3,enlargedLength]);
				xscale = d3.scale.linear().domain([0,completeRevData.svgWidth]).range([0,completeRevData.svgWidth]);
				//cleanup pegscale
				pegScale = d3.scale.linear().domain([0,completeRevData.svgWidth]).range([0,completeRevData.svgWidth]);
				diffscale = d3.scale.linear().domain([0,d3.max(completeRevData, function(d) { return d.timeDiff; })]).range([0,10]);
			}
	        else{
				yscale.domain([0,d3.max(completeRevData, function(d) { return d.editSize; })]);
				yscale2.domain([0,d3.max(completeRevData, function(d) { return d.editSize; })]);
				xscale.domain([0,completeRevData.svgWidth]).range([0,completeRevData.svgWidth]);
				diffscale.domain([0,d3.max(completeRevData, function(d) { return d.timeDiff; })]);
	        }
	        svg.attr('width',completeRevData.svgWidth);
    	};
    	
    	this.addData = function (rect,data,yscale){
	        bars = rect.selectAll('rect').data(data);
	        bars.enter().append("rect");
	        bars.attr({
                        'x':function(d,i){ return i * that.barGraphBarwidth + i; },
                        'y':function(d,i){ return d.dir == 'p' ? outerLength - yscale(d.editSize) : outerLength; },
                        'width':that.barGraphBarwidth,
                        'height':function(d,i){ return yscale(d.editSize); },
                        'class':function(d,i){ return d.dir == 'p' ? 'blue':'red'; },
                        'timestamp':function(d){ return d.timestamp; }
                        });
	        bars.exit().remove();
    	};
    	
    	this.callBrush = function (){
	        if (brush != null){
		        brush.x(xscale);
		        d3.select('#primaryBrush').call(brush);
			}
			else{
		            brush = d3.svg.brush().x(xscale).extent([0, 70]).on("brush", brushmove);
		            var brushg = svgBox.append("g").attr("class", "brush")
		                                    .attr("id","primaryBrush")
		                                    .call(brush);
		            brushg.selectAll("rect").attr("height", 100)
		                                    .attr("y",0);
		            //Cleanup
		            brushg.selectAll(".resize rect").attr("width",2).attr("x",-2);
		            //remove it from here
		            $('#outer').scroll();
		            //Fix it
		            //brushmove();
			}
			temp();
			cleanupProgressBar();
		};
		this.callPeg = function(){
			if (peg != null){
		        peg.x(pegScale);
		        d3.select('#peg').call(peg);
			}
			else{
		            peg = d3.svg.brush().x(pegScale).extent([0,0]).on("brush", pegMove);
		            pegHandleContainer = svgEnlargedBox.append("g")
		                                    .attr("id","peg")
		                                    .call(peg);
		            
		            pegHandleContainer.selectAll(".extent,.resize").remove();
					
					pegHandleContainer.select(".background")
					    .attr("height", enlargedLength * 2);
					
					/* Peg */
					
					pegHandle = pegHandleContainer.append("g");
					pegHandle.append("rect")
					    .attr("class", "peg")
					    .attr("height",enlargedLength * 2)
					    .attr("width",2)
					    .attr("y", 0);
					pegHandle.append("rect")
					    .attr("class", "peg")
					    .attr("height",5)
					    .attr("width",10)
					    .attr("y", 0)
						.attr("x",-4);
		         }
		    var lastEditX = 0;
		    newGraph.filter(function(d){
		    	lastEditX = d.lastX;
		    });
			pegHandleContainer.call(peg.event)
								.transition() // gratuitous intro!
    							.duration(750)
    							.call(peg.extent([lastEditX,lastEditX]))
    							.call(peg.event);
		};
		this.handleScroll = function (){
        	//d3.select('#outer').transition().property('scrollLeft',brush.extent()[0]);
         	$('#outer').scrollLeft(brush.extent()[0]);
   		};
   		this.fixWidth = function (width){
			d3.select('#enlarged svg').style('width',width).select('line').attr('x2',width);
			endLine.attr({'x1':width,'x2':width});
    	};
		function brushmove(){
	        var brushExtent = brush.extent();
	        var slid_s = d3.event.target.extent();
	        if(slid_s[1] - slid_s[0] < 100){
	        
		        /* the secondary slider */
		        temp();
		       
		        if (brushExtent[1]> completeRevData.length*(that.barGraphBarwidth + 1) - 50){
		       		userNotification('load');
		       		that.getData();
		        }
	        }
	        else{
	            d3.event.target.extent([slid_s[0],slid_s[0]+95]); d3.event.target(d3.select(this));
	        }
	        /*Calling Primary Slider callback*/
	       	cleanupProgressBar();
			if (that.primarySliderCallback){
				that.primarySliderCallback();
			}
    	}
    	function cleanupProgressBar(){
    		progressBar.attr('stroke-width',0);
    	};
    	this.refreshProgressBar = function (d){
    		progressBar.attr({'stroke-width':progressBarWidth,
    						'x1':d.lastX,
    						'x2':d.lastX+progressBarWidth
    						});	
    	};
    	function highlightSelectedEdit(graph,field,id,color){
    		graph.filter(function(d){
    			return d[field] == id;
				}).style({'fill':color});
    	};
    	function cleanupHighlightSelectedEdit(graph,field,id,color,type){
    		graph.filter(function(d){
    			return type ? !(d[field] == id) : d[field] == id;
				}).style({'fill':color});
    	};
    	function temp(){
    			var brushExtent = brush.extent();
	    		 	var new_graph = completeRevData.slice(Math.floor(brushExtent[0]/(that.barGraphBarwidth+1)),Math.ceil(brushExtent[1]/(that.barGraphBarwidth+1)) );
	    		 	if (new_graph.length){
				        var diffScaleAbs = 0;
						var lastX = 0;
				        //console.log(brushExtent);
				        newGraph = secondaryGraph.selectAll("rect").data(new_graph);
				        newGraph.enter().append("rect");
				        newGraph.attr("x",function(d,i){
				            diffScaleAbs += d.timeDiff*3;
					    	lastX = diffScaleAbs +  i*that.enlargedBarGraphBarwidth + i;
					    	d.lastX = lastX;
					    	return lastX;
						})
				        .attr("y",function(d,i){
							return d.dir == 'p' ? enlargedLength - yscale2(d.editSize) : enlargedLength+progressBarWidth;})
						.attr("width",that.enlargedBarGraphBarwidth)
						.attr("height",function(d){ return yscale2(d.editSize); })
						.attr("class",function(d){ return d.dir=='p'?'pointer blue':'pointer red'; })
						.attr("timeDiff",function(d){ return d.timeDiff; })
						.attr("data-title",function(d){ return d.user; })
						.attr("number",function(d,i){ return i; });				        ;
				                               
						newGraph.exit().remove();
						
						cleanupHighlightSelectedEdit(newGraph,'user',hoverUser,'#b4e2ef',1);
						highlightSelectedEdit(newGraph,'user',hoverUser,'gold');
						startDate.text(timeParse.parse(new_graph[0].timestamp).toDateString().slice(4));
						endDate.text(timeParse.parse(new_graph[new_graph.length-1].timestamp).toDateString().slice(4)).attr({'x':lastX-47});
						enlargedBarGraphSvgWidth = lastX + that.enlargedBarGraphBarwidth;
						that.fixWidth(enlargedBarGraphSvgWidth);
						pegScale.domain([0,enlargedBarGraphSvgWidth]).range([0,enlargedBarGraphSvgWidth]);
						that.callPeg();
						
				}
    	}
    	var pegMove = function(){
			console.log('Peg moved');
			var relaxedSelectedBar;
			var value = peg.extent()[0];
			  if (d3.event.sourceEvent) { // not a programmatic event
			    value = pegScale.invert(d3.mouse(this)[0]);
			    peg.extent([value, value]);
			    that.pegMoved = true;
			    
			     //Pausing if the player is playing
			  if (that.primarySliderCallback){
				that.primarySliderCallback();
				cleanupProgressBar();
				}

			  }
				selectedBar = newGraph.filter(function(d,i){
					if (d.lastX < value && value < d.lastX + that.enlargedBarGraphBarwidth){
							cleanupHighlightSelectedEdit(bars,'user',hoverUser,'gray',0);
							cleanupHighlightSelectedEdit(newGraph,'user',hoverUser,'#b4e2ef',0);
						    hoverUser = d.user;    
							highlightSelectedEdit(bars,'user',hoverUser,'gold');	
							highlightSelectedEdit(newGraph,'user',hoverUser,'gold');
							
							var revInfo = {
								'revid': d.revid,
								'user': d.user,
								'timestamp': d.timestamp.slice(0,10),
								'minor': d.minor ? 'M' : null
							};
							infoBox(revInfo);
					}
					if (d.lastX < value){
						relaxedSelectedBar = i;
					}
					return d.lastX < value && value < d.lastX + that.enlargedBarGraphBarwidth;
				});
				
				//Getting the selected edits 
				that.selectedEdits = that.getSecondrySliderSelection(relaxedSelectedBar);
				
				
				console.log(selectedBar);
			  pegHandle.attr("transform","translate("+pegScale(value)+")");
			     	}; 
    	return this;
    };

function playback(){
	var diff = new htmlDiff();
	diff.clearHash();
	var listOfRevisions = [], pageTitle, startRev, endRev, revisionInfo, modifyList;
	var playAnimation = true;
	this.animationSpeed = 1000;
	var usingLanguageNamespace = 'en';
	var modifyList =[];
	var revisionListDict = {
		'format': 'json',
		'action': 'query',
		'prop': 'revisions',
		'rvprop': 'ids|user|timestamp|size|flags',
		'rvdir': 'newer',
	};
	
	var compareRevisionDict = {
		'format': 'json',
		'action': 'query',
		'prop': 'revisions',
		'rvprop': 'content',
		'rvexpandtemplates': '',
		'rvparse': '',
	};
	
	var baseUrl = 'https://en.wikipedia.org/w/api.php?callback=?';
	var that = this;
	this.wikiNameSpace = function (language) {
	  usingLanguageNamespace = language;
	  baseUrl = 'https://'+language+'.wikipedia.org/w/api.php?callback=?';
	};
	this.cleanUp = function(){
		listOfRevisions = [];
		$('#wikiBody').html('');
	};
	this.getRevisions = function (page,selectedEdits){

			listOfRevisions = selectedEdits;
	        pageTitle = page;
	        startRev = listOfRevisions.shift().revid;
	        revisionInfo = listOfRevisions[0];
	        endRev = revisionInfo.revid;
	        listOfRevisions.shift();
	        playAnimation = true;
	        $('body').trigger( "editAnimationBegins", [startRev] );
	        that.wikiDiff();
       
	};
	/** Caching the results memoization **/
	var hashTable = hashTable || {};
	function getRequest(revid){
		//var deferredReady = $.Deferred();
		if (revid in hashTable){
			//console.log('cache hit', revid);
			return true;
		}
		else{
			//console.log('cache fail',revid );
			compareRevisionDict['revids'] = revid;
			return $.getJSON(baseUrl,compareRevisionDict,function(data){
				var resultKey = Object.keys(data.query.pages);
				var dataRev = data.query.pages[resultKey].revisions[0]['*'];
				hashTable[revid] = dataRev;
			});
		}
	};
	var empty = function (list){
		var l =[];
		for (ll in list ){
			if ($(list[ll]).text().trim()){
				l.push(list[ll]);
				}
		} 
		return l;
	};
	//hard coding parent element
	var removeRef = function(parentElement){
		$('del a[href^=#cite_note],del a[href^=#cite_ref],del *[id^=cite_note]').remove();
		$('del sup a[href^=#cite_note]').parent().parent().remove();
		$('a[href^=#cite_note] del').remove();
		$('a[href^=#cite_note] ins').each(function(){
			var element = $(this);
			element.replaceWith(element.text());
		});
	};
	this.wikiDiff = function(){
	    //Creating the info box about the revisions
        var revInfo = {
				'revid': revisionInfo.revid,
				'user': revisionInfo.user,
				'timestamp': revisionInfo.timestamp.slice(0,10),
				'minor': revisionInfo.hasOwnProperty('minor')? 'M' : null
		};
		that.infoBox(revInfo);
		$.when(getRequest(startRev),getRequest(endRev)).done(function(){
			var dataFirstRev  = hashTable[startRev];
			var dataSecondRev = hashTable[endRev];
			console.time('diff');
			var modifiedHtml = diff.diff(dataFirstRev,dataSecondRev);
			console.timeEnd('diff');
			$('#wikiBody').html(modifiedHtml);
			// To remove ref changes 
			removeRef();
			//console.time('making array');
			modifyList = empty($.makeArray($('del,ins')));
			//console.timeEnd('making array');
			that.animateDiff();
		});		
	};
	
	this.infoBox = function (revInfo){
        //$('#infoBox').html('');
        for (key in revInfo){                   
            if(key == 'revid'){
                var urlBase = 'https://'+usingLanguageNamespace+'.wikipedia.org/w/index.php?oldid='+revInfo[key];
                var anchor =$('<a>'+revInfo[key]+'</a>').attr({'target':'_blank','href':urlBase});
                 $('#infoBox .revisionLink').html(anchor);
            }
            else if(key == 'timestamp'){
            	$('#infoBox .editDate').html(revInfo[key]);
            }
            else if(key == 'user'){
            	$('#infoBox .userName').html(revInfo[key]);
            }
            else{
            	var minor = revInfo[key]?revInfo[key]:'';
                $('#infoBox .minor').html(minor);
               	
            }
        }
	};
	
	this.animateDiff = function () {
		if(playAnimation){
                if(modifyList.length>0){
					var element = modifyList[0];
					var scrollPromise = that.customScrollIntoView('#wikiBody',element);
                    
                    $.when(scrollPromise).then(function(){
                    	if ($(element).prop('tagName') == 'DEL'){
                        	//console.log('scroll end:: animation begin add ',Date.now(),modifyList.length,element.id);
                        	return $(element).fadeOut(that.animationSpeed);
                    	}
                    	else{
                    		//console.log('scroll end:: animation begin delete ',Date.now(),modifyList.length,element.id);
                        	return $(element).fadeIn(that.animationSpeed).css('display','inline-block');                        
                    	}
                    	
                    }).then(function(){ 
                    		//console.log('animation end',Date.now(),modifyList.length,element.id);
                    		modifyList.shift();
                    		that.animateDiff();
                    	});
				}
                else{
                	$('body').trigger( "editAnimationBegins", [endRev] );
                    if(listOfRevisions.length>0){
                        startRev = endRev;
                        revisionInfo = listOfRevisions.shift();
                        endRev = revisionInfo.revid;
                        that.wikiDiff();
                    }
                    else{
						$('#playButton').removeClass().addClass('play');
                    } 
				}
		}
	};
	var getOffsetTop = function(element){
		if (element.parentElement.tagName == 'TD'){
			return element.parentElement.offsetTop;
		}
		else{
			return element.offsetTop;
		}
	};
	this.customScrollIntoView = function(parent,element){
		//console.log('scroll begin',Date.now(),modifyList.length,element);
		var offset = 0;
		if ($(element).css('display') == 'none'){
			offset = getOffsetTop($(element).css('display','inline-block')[0]);
			$(element).css('display','none');
		}
		else{
			offset = getOffsetTop(element);
		}
		return $(parent).animate({scrollTop: offset }, that.animationSpeed);
	};
	
	this.startPlayback = function(selectedEdits,reset){
		userNotification('play');
		$('#playButton').removeClass('play').addClass('pause');
		var page = $('#pageTitle').val();
	    //Handling the case where the the player was paused
		    if(!slider.pegMoved && listOfRevisions.length && listOfRevisions.length > 0){
		        playAnimation = true;
		        that.animateDiff();
		    }
		    else{
		    	//refractor this
		    	selectedEdits = slider.selectedEdits;
		    	slider.refreshProgressBar(selectedEdits[0]);
		    	slider.pegMoved = false;
		        that.getRevisions(page,selectedEdits); 
		    }
	};
	
	this.pausePlayback = function(button){
		userNotification('pause');
		$(button).removeClass('pause').addClass('play');
		$('del,ins').finish();
		playAnimation = false;
	};
	//Attaching control for play / pause
	this.playbackControl  = function(){
		$('#playButton').click(function(){
			var button = this;
			if($(button).hasClass('play')){
				that.startPlayback();		    
			}
			else{
				that.pausePlayback(button);
			}
		});
	};
};

function addLanguageOptions(languages){
	for(language in languages){
		var option = $('<option>').val(language).html(languages[language]);
		$('select.languageNamespace').append(option);
	}
};

//The fullscreen Api handling
function fullscreenApi(screen){
	var elem = $(screen).get(0);
	if (elem.requestFullscreen) {
		elem.requestFullscreen();
	} 
	else if (elem.mozRequestFullScreen) {
		elem.mozRequestFullScreen();
	} 
	else if (elem.webkitRequestFullscreen) {
		elem.webkitRequestFullscreen();
	}

};


function chooseRandomArticle(lang_code) {
	lang_code = lang_code ? lang_code : 'en';
	var api_url = 'https://' + lang_code + '.wikipedia.org/w/api.php?callback=?';
	var params = {
		'action': 'query',
		'list': 'random',
		'rnnamespace': 0,
		'rnlimit': 1,
		'format': 'json'
	};
	$.getJSON(api_url, params, function(data, e) {
		var page_title = data['query']['random'][0]['title'];
		console.log('Randomly selected:', page_title);
		$('#overlayTitle').val(page_title);
		$('#overlayLoad').trigger('click');
	});
};

loadTool = function(){
 
	//Setting the max-width of the slider to the screen width
	$('#outer').css('max-width',$(window).width()-240+'px'); 
    //Creating Language Namespace dropdown
    wikiPlayback = new playback();
    
    //Fullscreen
    $('#fullscreen').click(function(){
		fullscreenApi('body');            
        });
  
        //Speed Control
    $(".noUiSlider").noUiSlider({
        range: [500, 5000],
       	start: 4500,
        step: 200,
        handles: 1,
        slide: function(){
        	wikiPlayback.animationSpeed = 5500 - $(this).val();
        }        
    });
     
    //Attaching Play/Pause contorls
    wikiPlayback.playbackControl();
    
    //Attaching Event to get the list of revisions
    var pageTitle ;
    start = function(titleHolder){
		$('#wikiBody').show();        
		pageTitle = $(titleHolder).val();
		wikiPlayback.cleanUp();
		slider.cleanUp();
		slider.getData(pageTitle);
    };
    
    $('#pageButton').click(function(){
    	start('#pageTitle');
    }); 

    $('#overlayRand').click(function() {
        chooseRandomArticle();
    });
    
    $('#overlayLoad').click(function(){
    	$('#pageTitle').val($('#overlayTitle').val());
    	start('#pageTitle');
    	$('#overlay').slideUp(2500);
    	
    });
    var pause = function(){
    	var button = $('#playButton');
    	wikiPlayback.pausePlayback(button);
    };
    
    var play  = function(edits,reset){
    	var selectedEdits = reset ? edits : slider.getSecondrySliderSelection();
    	wikiPlayback.startPlayback(selectedEdits,reset);
    };
    
	slider = new wikiSlider({	height : 400,
								barGraphBarwidth : 1,
								enlargedBarGraphBarwidth : 5,
								primarySliderMoveCallback : pause,
								secondrySliderMoveCallback : play
							});
	slider.init(); 
	
	$('body').on( "editAnimationBegins", function( event,revid ) {
		slider.modifySecondryGraph('revid',revid);
	});
	/*
	addLanguageOptions(languageNamespace);
    $('select.languageNamespace').change(function(){
    	var language = $(this).val();
		wikiPlayback.wikiNameSpace(language);
		slider.wikiNameSpace(language);
	});
	*/
};