// ==UserScript==
// @name           AniWiki
// @namespace      http://ejohn.org/
// @description    View an animation of the revision history for a Wikipedia entry.
// @include        http://*.wikipedia.org/wiki/*
// ==/UserScript==

(function() {
	var start = 0;
	var end = 0;
	var all = new Array();
	var users = new Array();
	var word = "";
	var niceWord = "";
	var total = 0;
	var cur = 0;
	var every = 5;
	var length = 3;
	var rv = 0;
	var movePage;
	
	function init() {
		total = 0;
		//var r = new RegExp( "\s+", "g" );
		niceWord = document.getElementsByTagName("h1")[0].innerHTML;
		//niceWord = niceWord.replace( r, "" );
		var h = window.location.href;
		word = h.substr( h.lastIndexOf("/") + 1, h.length );
		all = new Array();
		
		document.body.innerHTML += '<form id="wikimenu" style="position:fixed;top:-93px;left:153px;z-index:100;background:#FFF;border:2px solid #DDD;width:610px;height:110px;font-family:Arial;font-size:11px;-moz-border-radius-bottomleft:8px;-moz-border-radius-bottomright:8px;">\
			<div style="font-family:Arial;font-size:12px;position:absolute;top:1px;right:3px;text-align:right;">\
			<a href="#" id="time" style="text-decoration:none;color:#000;font-weight:bold;">Time</a> | <!--<a href="#" id="rev">by Revisions</a> |--> <a href="#" id="cus">Custom</a> | <a href="#" id="opt">Options</a>\
			</div>\
			<div style="font-family:Arial;font-size:13px;position:absolute;top:2px;left:5px;">\
			Est. Running Time: <strong id="runtime">0:00</strong>\
			</div>\
			<div style="font-family:Arial;font-size:11px;position:absolute;bottom:3px;right:5px;text-align:center;width:605px;">\
			Created <strong id="startDay"></strong> | \
			<span id="numPeople"></span> <a href="/wiki/index.php?title=' + word + '&action=history">People</a> | \
			<span id="numReverts"></span> Reverts | \
			<span id="numRevisions"></span> <a href="/wiki/index.php?title=' + word + '&action=history">Revisions</a> | \
			<span id="numComments"></span> <a href="/wiki/Talk:' + word + '">Comments</a> | \
			<span id="numLinks"></span> <a href="/wiki/index.php?title=Special:Whatlinkshere&target=' + word + '">Links</a>\
			</div>\
			<div style="font-family:Arial;font-size:11px;position:absolute;bottom:3px;right:5px;text-align:right;">\
			<a href="#" id="showhide">Show</a>\
			</div>\
			<input type="button" id="play" value="Play" style="padding:0;font-family:Arial;font-size:11px;position:absolute;bottom:1px;left:5px;"/>\
			<div id="timelayer" style="background:#FFF;position:absolute;height:45px;top:35px;width:610px;">\
				<div id="timeSlide" style="border:1px solid #DDD;width:600px;height:30px;position:absolute;left:5px;top:0px;font-weight:bold;">\
				</div>\
				<div style="position:absolute;left:4px;top:32px;" id="startDate"></div>\
				<div style="position:absolute;left:4px;top:32px;width:602px;text-align:center;font-weight:bold;font-size:12px;">Time</div>\
				<div style="position:absolute;right:4px;top:32px;" id="endDate"></div>\
			</div>\
			<div id="revlayer" style="display:none;background:#FFF;position:absolute;height:45px;top:35px;width:610px;">\
				<div id="revSlide" style="border:1px solid #DDD;width:600px;height:30px;position:absolute;left:5px;top:0px;font-weight:bold;">\
					<div id="startRev" style="opacity:0.75;background:#0F0;width:4px;height:40px;position:absolute;top:-10px;left:-2px;"><span style="position:absolute;top:-4px;left:8px;">start</span></div>\
					<div id="endRev" style="opacity:0.75;background:#F00;width:4px;height:40px;position:absolute;top:-10px;left:598px;"><span style="position:absolute;top:-4px;right:29px;text-align:right;">end</span></div>\
					<div id="curRev" style="background:#000;width:4px;height:43px;position:absolute;top:-13px;left:598px;display:none;"><span style="position:absolute;top:-13px;left:-9px;text-align:center;"></span></div>\
				</div>\
				<div style="position:absolute;left:4px;top:32px;">0</div>\
				<div style="position:absolute;left:4px;top:32px;width:602px;text-align:center;font-weight:bold;font-size:12px;">Revisions</div>\
				<div style="position:absolute;right:4px;top:32px;" id="lastRev"></div>\
			</div>\
			<div id="cuslayer" style="text-align:center;display:none;background:#FFF;position:absolute;height:45px;top:35px;width:610px;">\
			</div>\
			<div id="optlayer" style="display:none;background:#FFF;position:absolute;height:45px;top:35px;width:610px;">\
			<div style="float:right;">\
			Show each revision for <input type="text" id="length" value="'+ GM_getValue("length","3") + '" size="2" style="font-family:Arial;font-size:11px;"/> second(s)<br/>\
			Skip every <input type="text" id="every" value="'+ GM_getValue("every","5") + '" size="2" style="font-family:Arial;font-size:11px;"/> revision(s)\
			</div>\
			<input type="checkbox" id="loadrev"'+ GM_getValue("loadrev"," checked") + '/> Load Revisions Automatically\
			</div>\
			</form>\
			<div style="position:fixed;top:0px;right:0px;z-index:150;color:#FFF;font-weight:bold;">\
			<span id="iabove"></span>\
			<span id="dabove" style="background:#F00;"></span>\
			</div>\
			<div style="position:fixed;bottom:0px;right:0px;z-index:150;color:#FFF;font-weight:bold;">\
			<span id="ibelow" style="background:green;"></span>\
			<span id="dbelow" style="background:#F00;"></span>\
			</div>';

		GM_xmlhttpRequest({
			method: "GET",
			url: "http://en.wikipedia.org/w/index.php?title=" + word + "&action=history&limit=5000&offset=0",
			onload: loadAnim
		});
		GM_xmlhttpRequest({
			method: "GET",
			url: "http://en.wikipedia.org/w/index.php?target=" + word + "&action=history&limit=999&title=Special:Whatlinkshere",
			onload: loadLink
		});
		GM_xmlhttpRequest({
			method: "GET",
			url: "http://en.wikipedia.org/w/index.php?title=Talk:" + word,
			onload: loadTalk
		});
			
		setTimeout( bind, 100 );
		
		return true;
	}
	
	function bind() {
		get("showhide").onclick = showHide;
		get("loadrev").onclick = function() {
			GM_setValue( "loadrev", ( this.checked ? " checked": "" ) );
		};
		get("play").onclick = loadRevisions;
		get("content").style.position = "relative";

		//bindHandle( new Array( "startRev", "endRev", "curRev" ) );
		bindToggle( new Array( "time", "cus", "opt" ) );
	}
	
	function onRevLoad() {
		get("startDay").innerHTML = niceDate(all[end].time);
		get("startDate").innerHTML = niceDate(all[end].time);
		get("endDate").innerHTML = niceDate(all[start].time);
		get("lastRev").innerHTML = all.length;
		
		get("cuslayer").innerHTML = '<strong>From:</strong> <select>' + getOptions(end) + '</select>&nbsp&nbsp;&nbsp;\
			<strong>To:</strong> <select>' + getOptions(start) + '</select>';
		
		var c = 0;
		for ( var i in users ) c++;
		get("numPeople").innerHTML = c;
		get("numRevisions").innerHTML = all.length;
		get("numReverts").innerHTML = rv;
	}
	
	function loadLink(e) {
		var r = new RegExp( '<li><a', 'g' );
		var m = r.exec( e.responseText );
		var c = 0;
		while ( m != null ) {
			var m = r.exec( e.responseText );
			c++
		}
		get("numLinks").innerHTML = c;
	}
	
	function loadTalk(e) {
		var r = new RegExp( '<dd>', 'g' );
		var m = r.exec( e.responseText );
		var c = 0;
		while ( m != null ) {
			var m = r.exec( e.responseText );
			c++
		}
		get("numComments").innerHTML = c;
	}
	
	function niceDate( time ) {
		var months = new Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", 
			"Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
		var d = new Date( time );
		return months[ d.getMonth() ] + " " + d.getDate() + ", " + d.getFullYear();
	}
		
	function loadAnim(e) {
		var find = new RegExp("rv|revert|vandal", "ig");
		var c = 0;
		var m = find.exec( e.responseText );
		while ( m != null ) {
			var m = find.exec( e.responseText );
			rv++;
		}

		var r = new RegExp( 'href="([^#"]*)" title="' + niceWord + '">([^<]*)<.*?>([^<]*)</a>', 'g' ); //' + niceWord + '">([^<]*)<', 'g' );
		var m = r.exec( e.responseText );

		var amp = new RegExp( "&amp;", "g" );
		var line = new Array();
		var months = { Jan: 0, Feb: 1, Mar: 2, Apr: 3, May: 4, Jun: 5,
			Jul: 6, Aug: 7, Sep: 8, Oct: 9, Nov: 10, Dec: 11 };

		while ( m != null ) {
			if ( m[2] != "cur" ) {
				m[1] = m[1].replace( amp, "&" );
				var t = m[2].split(" ");
				if ( users[m[3]] == null )
					users[m[3]] = 0;
				//users[m[3]]++;
				//if ( m[4].match( find ) ) rv++;
				all.unshift({ url: m[1], date: m[2], //minor: ( m[4].indexOf("<b>m") == -1 ),
					time: Date.UTC( t[3], months[t[2]], t[1] ) });
			}
			var m = r.exec( e.responseText );
		}
		
		start = all.length - 1;
		
		var max = 0;
		for ( var i = start; i >= end; i-- ) {
			var n = Math.floor(((all[i].time - all[start].time) / (all[end].time - all[start].time)) * 99);
			if ( line[n] == null ) line[n] = 0;
			line[n]++;
			if ( line[n] > max ) max = line[n];
		}
		
		var bar = "";
		for ( var i = 99; i >= 0; i-- ) {
			var n = Math.round((200 * (line[i] == null ? 0 : line[i])) / max) / 100;
			bar += "<div style='z-index:80;padding-left:1%;float:left;background:#002bb8;height:30px;width:0px;opacity:" + ( n > 1 ? 1 : n ) + ";'><span style='visibility:hidden;'>--------</span></div>";
		}
		get("timeSlide").innerHTML = bar + 
			'<div id="startTime" style="z-index:100;opacity:0.75;background:#0F0;width:4px;height:40px;position:absolute;top:-10px;left:-2px;"><span style="position:absolute;top:-4px;left:8px;">start</span></div>\
					<div id="endTime" style="opacity:0.75;background:#F00;width:4px;height:40px;position:absolute;top:-10px;left:598px;"><span style="position:absolute;top:-4px;right:29px;text-align:right;">end</span></div>\
					<div id="curTime" style="background:#000;width:4px;height:43px;position:absolute;top:-13px;left:598px;display:none;"><span style="position:absolute;top:-13px;left:-9px;text-align:center;"></span></div>';
		bindHandle( new Array( "startTime", "endTime", "curTime" ) );
		get("startTime").onDrag = function(x,y) {
			var t = Math.floor(((x+2)*(all[all.length-1].time-all[0].time))/600) + all[0].time;
			for ( var i = 0; i < all.length; i++ )
				if ( all[i].time >= t && i != start ) {
					paused = null;
					start = i;
					return true;
				}
			
		};
		get("endTime").onDrag = function(x,y) {
			var t = Math.floor(((x+2)*(all[all.length-1].time-all[0].time))/600) + all[0].time;
			for ( var i = 0; i < all.length; i++ )
				if ( all[i].time >= t && i != end ) {
					paused = null;
					end = i;
					return true;
				}
		};
	
		var bar = "";
		for ( var i = 99; i >= 0; i-- ) {
			var n = Math.floor( all.length / i );
			var bg = "002bb8";
			bar += "<div style='z-index:80;padding-left:1%;float:left;background:#" + bg + ";height:30px;width:0px;'><span style='visibility:hidden;'>--------</span></div>";
		}
		get("revSlide").innerHTML += bar;
		
		
		onRevLoad();
		
		setTimeout( function(){ checkTime(); }, 100 );
	}
	
	function checkTime() {
		if ( curLayer == "cus" ) {
			var s = document.getElementById("cuslayer").getElementsByTagName("select");
			if ( start != s[0].value - 0 )
				start = s[0].value - 0;
			if ( end != s[1].value - 0 )
				end = s[1].value - 0;
		} else if ( curLayer == "time" ) {
			get("startTime").onDrag(parseInt(get("startTime").style.left),0);
			get("endTime").onDrag(parseInt(get("endTime").style.left),0);
		}
		every = get("every").value - 0;
		GM_setValue( "every", every );
		length = get("length").value - 0;
		GM_setValue( "length", length );
		get("runtime").innerHTML = calcTime();
		setTimeout( function(){ checkTime(); }, 100 );
	}
	
	function calcTime() {
		total = 0;
		if ( every > 0 ) {
			if ( start < end ) {
				for ( var i = start; i <= end + every - 1; i += every ) {
					if ( i > end ) i = end;
					total++;
				}
			} else {
				for ( var i = end; i <= start + every - 1; i += every ) {
					if ( i > start ) i = start;
					total++;
				}
			}
		}
		var min = Math.floor( (total * length) / 60 );
		var sec = Math.floor( (total * length) % 60 );
		if ( sec < 10 )
			sec = "0" + sec;
		return min + ":" + sec;
	}
	
	var paused;
	
	function loadRevisions() {
		if ( this.value == "Play" ) {
			this.value = "Pause";
			if ( get("wikimenu").style.top == '-2px' )
				showHide();
			if ( paused != null ) {
				showPage( paused.next, paused.index );
			} else {
				if ( start < end ) {
					for ( var i = start; i < end + every; i += every ) {
						if ( i > end ) i = end;
						getPage( i );
					}
				} else {
					for ( var i = end; i < start + every; i += every ) {
						if ( i > start ) i = start;
						getPage( i );
					}
				}
			}
		} else {
			clearTimeout(movePage);
			this.value = "Play";
		}
		return false;
	}
	
	function getOptions( index ) {
		var str = "";
		for ( var i = 0; i < all.length; i++ )
			str += "<option value='" + i + "'" + 
				( index == i ? " selected" : "" ) +
		    ">" + all[i].date + "</option>";
		return str;
	}
	
	function updateTotal() {
		var t = Math.floor( ( cur * 100 ) / total );
		if ( document.getElementById("total") != null )
			document.getElementById("total").innerHTML = t + "% Loaded";
			
		if ( cur == 1 )
			showPage( start );
	}
	
	function showPage( index, prev ) {
		if ( (index < end && index < start) || (index > end && index > start) ) {
			get("play").value = "Play";
			return false;
		}
		index -= 0;
		/*get("bodyContent").style.zIndex = 100;
		var d = document.createElement("div");
		d.style.background = "#FFF";
		d.id = "newContent";
		d.style.position = "absolute";
		d.style.top = "50px;"
		d.style.zIndex = "90";*/
		
		if ( prev != null )
			get("bodyContent").innerHTML = diffString( all[prev].html, all[index].html );
		else
			get("bodyContent").innerHTML = all[index].html;
		
		//get("content").appendChild( d );
		
		//fade( get("bodyContent"), 0.1 );
		setTimeout( checkPos, 100 );
		
		if ( start < end ) {
			var next = index + every;
			if ( next > end && next < end + every ) next = end;
		} else {
			var next = index - every;
			if ( next < end && next > end - every ) next = end;
		}
		paused = { next: next, index: index };
		movePage = setTimeout( function(){ showPage( next, index ); }, length * 1000 );
	}
	
	function getPage( index ) {
		GM_xmlhttpRequest({
			method: "GET",
			url: "http://en.wikipedia.org" + all[index].url,
			onload: function(e) {
				var html = e.responseText;
				html = html.split("\n").join(' ');
				html = html.split("\r").join(' ');
				//var r = new RegExp( '<body[^>]*>(.+)</body>', "gim" );
				var r = new RegExp( '<div id="bodyContent">(.+)<div id="column-one"', "gim" );
				var m = r.exec( html );
				all[index].html = m[1];
				//alert( index + " " + m[1].split(". ").length );
				cur++;
				if ( index == start ) {
					get("play").value = "Pause";
					showPage( start );
				}
				//updateTotal();
			}
		});
	}
	
	var curLayer = "time";

	
	function checkPos() {
		var max = 70;
		var ib = 0;
		var ia = 0;
		var db = 0;
		var da = 0;
		
		var ins = get("bodyContent").getElementsByTagName("b");
		for ( var i = 0; i < ins.length; i++ ) {
			var p = findPosY( ins[i] );
			if ( p < self.pageYOffset ) ia++;
			if ( p > self.pageYOffset + self.innerHeight ) ib++;
		}
	
		get("iabove").innerHTML = "<div style='opacity:0.5;float:left;height:16px;width:" + Math.floor( (ia*max) / ins.length ) + "px;background:green;'></div>";
		get("ibelow").innerHTML = "<div style='opacity:0.5;float:left;height:16px;width:" + Math.floor( (ib*max) / ins.length ) + "px;background:green;'></div>";
		
		var del = get("bodyContent").getElementsByTagName("s");
		for ( var i = 0; i < del.length; i++ ) {
			var p = findPosY( del[i] );
			if ( p < self.pageYOffset ) da++;
			if ( p > self.pageYOffset + self.innerHeight ) db++;
		}
	
		get("dabove").innerHTML = "<div style='opacity:0.5;float:left;height:16px;width:" + Math.floor( (da*max) / del.length ) + "px;background:#F00;'></div>";;
		get("dbelow").innerHTML = "<div style='opacity:0.5;float:left;height:16px;width:" + Math.floor( (db*max) / del.length ) + "px;background:#F00;'></div>";
		
		setTimeout( checkPos, 500 );
	}
	
	function findPosY(obj) {
		var curtop = 0;
		if (obj.offsetParent)
			while (obj.offsetParent) {
				curtop += obj.offsetTop
				obj = obj.offsetParent;
			}
		else if (obj.y)
			curtop += obj.y;
		return curtop;
	}
	
	function get( name ) {
		return document.getElementById(name);
	}
	
	function showHide() {
		if ( get("wikimenu").style.top == '-2px' ) {
			get("wikimenu").style.top = '-93px';
			get("showhide").innerHTML = "Show";
		} else {
			get("wikimenu").style.top = '-2px';
			get("showhide").innerHTML = "Hide";
		}
		return false;
	}
	
	function bindHandle( names ) {
		for ( var i in names )
			Drag.init( get( names[i] ), null, -2, 598, -10, -10);
	}
	
	function bindToggle( names ) {
		for ( var i in names )
			document.getElementById( names[i] ).onclick = toggle;
	}
	
	function toggle() {
		get( curLayer ).style.textDecoration = "";
		get( curLayer ).style.color = "";
		get( curLayer ).style.fontWeight = "";
		this.style.textDecoration = "none";
		this.style.color = "#000";
		this.style.fontWeight = "bold";
		
		get( curLayer + "layer" ).style.display = 'none';
		get( this.id + "layer" ).style.display = 'block';
		curLayer = this.id;
		return false;
	}
	
	document.onkeydown = function(e) {
		var k = e.keyCode;
		switch(k) {
			case 32:
				if ( e.ctrlKey )
					get("play").onclick();
				break;
			case 37:
				if ( e.ctrlKey )
					showPage( cur - 1 );
				break;
			case 39:
				if ( e.ctrlKey )
					showPage( cur + 1 );
				break;
			case 38:
				if ( e.ctrlKey )
					showPage( start );
				break;
			case 40:
				if ( e.ctrlKey )
					showPage( end );
				break;
			default:
				//alert(k);
				break;
		}
	};
	
	function fade( obj, a ) {
		if ( obj.style.opacity == "" )
			obj.style.opacity = 0.99;
		obj.style.opacity -= a;
		if ( obj.style.opacity > 0 ) {
			setTimeout( function(){ fade( obj, a ); }, 100 );
		} else {
			get("content").removeChild( get("bodyContent") );
			get("newContent").id = "bodyContent";
		}
	}
	
	/**************************************************
	 * dom-drag.js
	 * 09.25.2001
	 * www.youngpup.net
	 **************************************************
	 * 10.28.2001 - fixed minor bug where events
	 * sometimes fired off the handle, not the root.
	 **************************************************/
	
	var Drag = {
	
			obj : null,
	
			init : function(o, oRoot, minX, maxX, minY, maxY, bSwapHorzRef, bSwapVertRef, fXMapper, fYMapper)
			{
					o.onmousedown    = Drag.start;
	
					o.hmode            = bSwapHorzRef ? false : true ;
					o.vmode            = bSwapVertRef ? false : true ;
	
					o.root = oRoot && oRoot != null ? oRoot : o ;
	
					if (o.hmode  && isNaN(parseInt(o.root.style.left  ))) o.root.style.left   = "0px";
					if (o.vmode  && isNaN(parseInt(o.root.style.top   ))) o.root.style.top    = "0px";
					if (!o.hmode && isNaN(parseInt(o.root.style.right ))) o.root.style.right  = "0px";
					if (!o.vmode && isNaN(parseInt(o.root.style.bottom))) o.root.style.bottom = "0px";
	
					o.minX    = typeof minX != 'undefined' ? minX : null;
					o.minY    = typeof minY != 'undefined' ? minY : null;
					o.maxX    = typeof maxX != 'undefined' ? maxX : null;
					o.maxY    = typeof maxY != 'undefined' ? maxY : null;
	
					o.xMapper = fXMapper ? fXMapper : null;
					o.yMapper = fYMapper ? fYMapper : null;
	
					o.root.onDragStart    = new Function();
					o.root.onDragEnd    = new Function();
					o.root.onDrag        = new Function();
			},
	
			start : function(e)
			{
					var o = Drag.obj = this;
					e = Drag.fixE(e);
					var y = parseInt(o.vmode ? o.root.style.top  : o.root.style.bottom);
					var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
					o.root.onDragStart(x, y);
	
					o.lastMouseX    = e.clientX;
					o.lastMouseY    = e.clientY;
	
					if (o.hmode) {
							if (o.minX != null)    o.minMouseX    = e.clientX - x + o.minX;
							if (o.maxX != null)    o.maxMouseX    = o.minMouseX + o.maxX - o.minX;
					} else {
							if (o.minX != null) o.maxMouseX = -o.minX + e.clientX + x;
							if (o.maxX != null) o.minMouseX = -o.maxX + e.clientX + x;
					}
	
					if (o.vmode) {
							if (o.minY != null)    o.minMouseY    = e.clientY - y + o.minY;
							if (o.maxY != null)    o.maxMouseY    = o.minMouseY + o.maxY - o.minY;
					} else {
							if (o.minY != null) o.maxMouseY = -o.minY + e.clientY + y;
							if (o.maxY != null) o.minMouseY = -o.maxY + e.clientY + y;
					}
	
					document.onmousemove    = Drag.drag;
					document.onmouseup        = Drag.end;
	
					return false;
			},
	
			drag : function(e)
			{
					e = Drag.fixE(e);
					var o = Drag.obj;
	
					var ey    = e.clientY;
					var ex    = e.clientX;
					var y = parseInt(o.vmode ? o.root.style.top  : o.root.style.bottom);
					var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
					var nx, ny;
	
					if (o.minX != null) ex = o.hmode ? Math.max(ex, o.minMouseX) : Math.min(ex, o.maxMouseX);
					if (o.maxX != null) ex = o.hmode ? Math.min(ex, o.maxMouseX) : Math.max(ex, o.minMouseX);
					if (o.minY != null) ey = o.vmode ? Math.max(ey, o.minMouseY) : Math.min(ey, o.maxMouseY);
					if (o.maxY != null) ey = o.vmode ? Math.min(ey, o.maxMouseY) : Math.max(ey, o.minMouseY);
	
					nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
					ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));
	
					if (o.xMapper)        nx = o.xMapper(y)
					else if (o.yMapper)    ny = o.yMapper(x)
	
					Drag.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
					Drag.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
					Drag.obj.lastMouseX    = ex;
					Drag.obj.lastMouseY    = ey;
	
					Drag.obj.root.onDrag(nx, ny);
					return false;
			},
	
			end : function()
			{
					document.onmousemove = null;
					document.onmouseup   = null;
					Drag.obj.root.onDragEnd(    parseInt(Drag.obj.root.style[Drag.obj.hmode ? "left" : "right"]), 
																			parseInt(Drag.obj.root.style[Drag.obj.vmode ? "top" : "bottom"]));
					Drag.obj = null;
			},
	
			fixE : function(e)
			{
					if (typeof e == 'undefined') e = window.event;
					if (typeof e.layerX == 'undefined') e.layerX = e.offsetX;
					if (typeof e.layerY == 'undefined') e.layerY = e.offsetY;
					return e;
			}
	};
	
	function diffString( o, n ) {
		var out = diff( o.split(/\s+/), n.split(/\s+/) );
		var str = "";
	
		for ( var i = 0; i < out.n.length - 1; i++ ) {
			if ( out.n[i].text == null ) {
				if ( out.n[i].indexOf('"') == -1 && out.n[i].indexOf('<') == -1 && out.n[i].indexOf('=') == -1 )
					str += "<b style='background:#E6FFE6;'> " + out.n[i] +"</b>";
				else
					str += " " + out.n[i];
			} else {
				var pre = "";
				if ( out.n[i].text.indexOf('"') == -1 && out.n[i].text.indexOf('<') == -1 && out.n[i].text.indexOf('=') == -1 ) {
					
					var n = out.n[i].row + 1;
					while ( n < out.o.length && out.o[n].text == null ) {
						if ( out.o[n].indexOf('"') == -1 && out.o[n].indexOf('<') == -1 && out.o[n].indexOf(':') == -1 && out.o[n].indexOf(';') == -1 && out.o[n].indexOf('=') == -1 )
							pre += " <s style='background:#FFE6E6;'>" + out.o[n] +" </s>";
						n++;
					}
				}
				str += " " + out.n[i].text + pre;
			}
		}
		
		return str;
	}
	
	function diff( o, n ) {
		var ns = new Array();
		var os = new Array();
		
		for ( var i = 0; i < n.length; i++ ) {
			if ( ns[ n[i] ] == null )
				ns[ n[i] ] = { rows: new Array(), o: null };
			ns[ n[i] ].rows.push( i );
		}
		
		for ( var i = 0; i < o.length; i++ ) {
			if ( os[ o[i] ] == null )
				os[ o[i] ] = { rows: new Array(), n: null };
			os[ o[i] ].rows.push( i );
		}
		
		for ( var i in ns ) {
			if ( ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1 ) {
				n[ ns[i].rows[0] ] = { text: n[ ns[i].rows[0] ], row: os[i].rows[0] };
				o[ os[i].rows[0] ] = { text: o[ os[i].rows[0] ], row: ns[i].rows[0] };
			}
		}
		
		for ( var i = 0; i < n.length - 1; i++ ) {
			if ( n[i].text != null && n[i+1].text == null && o[ n[i].row + 1 ].text == null && 
					 n[i+1] == o[ n[i].row + 1 ] ) {
				n[i+1] = { text: n[i+1], row: n[i].row + 1 };
				o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1 };
			}
		}
		
		for ( var i = n.length - 1; i > 0; i-- ) {
			if ( n[i].text != null && n[i-1].text == null && o[ n[i].row - 1 ].text == null && 
					 n[i-1] == o[ n[i].row - 1 ] ) {
				n[i-1] = { text: n[i-1], row: n[i].row - 1 };
				o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1 };
			}
		}
		
		return { o: o, n: n };
	}
	
	init();
})();

