/*******************************************************************
* Glype is copyright and trademark 2007-2015 UpsideOut, Inc. d/b/a Glype
* and/or its licensors, successors and assigners. All rights reserved.
*
* Use of Glype is subject to the terms of the Software License Agreement.
* http://www.glype.com/license.php
*******************************************************************
* This file is the javascript library. The version downloaded by the
* user is compressed to save on bandwidth. This uncompressed version
* is available for you to make your own changes if desired.
******************************************************************/

/*****************************************************************
* Set up variables
******************************************************************/

if(parent.frames.length==0) {
	x=location.href;
	if (x.indexOf('f=frame')!=-1||x.indexOf('&frame')!=-1) {
		x=x.replace(/f=frame/,'');
		x=x.replace(/&frame/,'');
		parent.location.href=x;
	}
}


// Shortcut to <URL TO SCRIPT><SCRIPT NAME>
if (ginf) {
	if (ginf.enc.u && ginf.enc.x) {
		ginf.target.h=arcfour(ginf.enc.u,base64_decode(ginf.target.h));
		ginf.target.p=arcfour(ginf.enc.u,base64_decode(ginf.target.p));
		ginf.target.u=arcfour(ginf.enc.u,base64_decode(ginf.target.u));
	}
	siteURL = ginf.url+'/'+ginf.script;
}

// Convert all the document.domain references to a normal variable
// since our document.domain is obviously not what's expected.
ignore = '';

/*****************************************************************
* Helper functions - mostly javascript equivalents of the PHP
* function with the same name
******************************************************************/

function base64_encode(d){var q='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';var z,y,x,w,v,u,t,s,i=0,j=0,p='',r=[];if(!d){return d;}do{z=d.charCodeAt(i++);y=d.charCodeAt(i++);x=d.charCodeAt(i++);s=z<<16|y<<8|x;w=s>>18&0x3f;v=s>>12&0x3f;u=s>>6&0x3f;t=s&0x3f;r[j++]=q.charAt(w)+q.charAt(v)+q.charAt(u)+q.charAt(t);}while(i<d.length);p=r.join('');var r=d.length%3;return(r?p.slice(0,r-3):p)+'==='.slice(r||3);}
function base64_decode(d){var q='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';var z,y,x,w,v,u,t,s,i=0,j=0,r=[];if(!d){return d;}d+='';do{w=q.indexOf(d.charAt(i++));v=q.indexOf(d.charAt(i++));u=q.indexOf(d.charAt(i++));t=q.indexOf(d.charAt(i++));s=w<<18|v<<12|u<<6|t;z=s>>16&0xff;y=s>>8&0xff;x=s&0xff;if(u==64){r[j++]=String.fromCharCode(z);}else if(t==64){r[j++]=String.fromCharCode(z,y);}else{r[j++]=String.fromCharCode(z,y,x);}}while(i<d.length);return r.join('');}
function arcfour(k,d) {var o='';s=new Array();var n=256;l=k.length;for(var i=0;i<n;i++){s[i]=i;}for(var j=i=0;i<n;i++){j=(j+s[i]+k.charCodeAt(i%l))%n;var x=s[i];s[i]=s[j];s[j]=x;}for(var i=j=y=0;y<d.length;y++){i=(i+1)%n;j=(j+s[i])%n;x=s[i];s[i]=s[j];s[j]=x;o+=String.fromCharCode(d.charCodeAt(y)^s[(s[i]+s[j])%n]);}return o;}


// Make a replacement using position and length values
function substr_replace(str,replacement,start,length) {
	return str.substr(0, start) + replacement + str.substr(start +length);
}

// Find position of needle in haystack
function strpos(haystack, needle, offset) {

	// Look for next occurrence
	var i = haystack.indexOf(needle, offset);

	// indexOf returns -1 if not found, we want false
	return i >= 0 ? i : false
}

// Find length of initial segment matching mask
function strspn(input, mask, offset, length) {

	// Set up starting vars
	var length			= length ? offset + length : input.length;
	var i					= offset ? offset : 0;
	var matched			= 0;

	// Loop through chars
	while ( i < length ) {

		// Does this char match the mask?
		if ( mask.indexOf(input.charAt(i)) == -1 ) {

			// No match, end here
			return matched;
		}

		++matched;
		++i;
	}

	return matched;
}

// Get the AJAX object
function fetchAjaxObject() {
	var xmlHttp;

	try {
	  // Firefox, Opera 8.0+, Safari
	  xmlHttp = new XMLHttpRequest;
	} catch (e) {
		// Internet Explorer
		try {
			xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
		} catch (e) {
			try {
				xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
			} catch (e) {
				return false;
			}
		}
	}

	return xmlHttp;
}


/*****************************************************************
* URL encoding function - takes an absolute or relative URL and
* converts it so that when requested, the resource will be downloaded
* by the proxy. PHP equivalent is "proxyURL()"
******************************************************************/

function parseURL(input, flag) {

	// First, validate the input
	if (!input) {return '';}

	input = input.toString();

	// Is it an anchor?
	if (input.charAt(0)=='#') {return input;}

	// binary image data
	if (input.toLowerCase().indexOf('data:image')===0) {return input;}

	// Is it javascript?
	if (input.toLowerCase().indexOf('javascript:')===0) {return parseJS(input);}
	if (input.toLowerCase().indexOf('livescript:')===0) {return parseJS(input);}

	// Is it a non-page?
	if (input==='about:blank') {return '';}
	if (input.toLowerCase().indexOf('data:')===0) {return '';}
	if (input.toLowerCase().indexOf('file:')===0) {return '';}
	if (input.toLowerCase().indexOf('res:')===0) {return '';}
	if (input.toLowerCase().indexOf('C:')===0) {return '';}

	// Is it already proxied?
	if ( input.indexOf(siteURL) === 0 ) {
		return input;
	}

	// Ensure a complete URL
	if ( input.indexOf('http://') !== 0 && input.indexOf('https://') !== 0 ) {

		// No change if .
		if ( input == '.' ) {
			input = '';
		}

		// Relative from root
		if ( input.charAt(0) == '/' ) {

			// "//domain.com" is also acceptable so check next char as well
			if ( input.length > 0 && input.charAt(1) == '/' ) {

				// Prefix the HTTP and we're done
				input = 'http:' + input;
			} else {

				// Relative path from root so add scheme+host as prefix and we're done
				input = ginf.target.h + input;
			}
		} else if ( ginf.target.b ) {

			// Relative path from base href
			input = ginf.target.b + input;
		} else {

			// Relative from document
			input = ginf.target.h + ginf.target.p + input;
		}
	}

	// Simplify path
	// Strip ./ (refers to current directory)
	input = input.replace('/./', '/');

	// Strip double slash //
	if ( input.length > 8 && input.substr(8).indexOf('//') ) {
		input = input.replace(/[^:]\/\//g, '/');
	}
	 
	// Simplify path by converting /dir/../ to /
	if ( input.indexOf('/..') > 0 ) {
		var urlparts = input.substring(ginf.target.h.length).split(/\//);
		for (var i in urlparts) {
			if ( urlparts[i] == '..' ) {
				input = input.replace('/'+urlparts[i-1]+'/..','');
			}
		}
	}

	// Extract an #anchor
	var jumpTo = '';

	// Find position of #
	var hashPos = input.indexOf('#');

	if ( hashPos >= 0 ) {

		// Split into jumpTo (append it after proxying) and $url
		jumpTo = input.substr(hashPos);
		input = input.substr(0, hashPos);
	}

	// Add encoding
	if ( ginf.enc.e ) {

		// Part of our encoding is to remove HTTP (saves space and helps avoid detection)
		input = input.substr(4);

		// Are we using unique URLs?
		if ( ginf.enc.u ) {

			// Encrypt
			input = base64_encode(arcfour(ginf.enc.u,input));
		}
	}

	// Protect chars that have other meaning in URLs
	input = encodeURIComponent(input);

	// Return in path info format (only when encoding is on)
	if ( ginf.enc.p && ginf.enc.e ) {
		input = input.replace(/%/g,'_');
		return siteURL + '/' + input + '/b' + ginf.b + '/' + ( flag ? 'f' + flag + '/' : '') + jumpTo;
	}

	// Otherwise, return in 'normal' (query string) format
	return siteURL + '?u=' + input + '&b=' + ginf.b + ( flag ? '&f=' + flag : '' ) + jumpTo;
}


/*****************************************************************
* Read options and URL from our form, convert to proxied URL and load it.
* If javascript is disabled, the form POSTs to /includes/process.php
******************************************************************/

function updateLocation(form) {
	 
	// Reset bitfield
	ginf.b = 0;
	 
	// Array of options
	var options = new Array();
	 
	// Loop through form elements
	for ( i=0; i < form.elements.length; i++ ) {
	 
		if ( form.elements[i].name == 'u' ) {

			// Record URL
			url = form.elements[i].value;
		  
		} else if ( form.elements[i].type == 'checkbox' ) {

			// Add option
			options.push(form.elements[i]);

			// Update encode option (for generating the new URL)
			if ( form.elements[i].name == 'encodeURL' ) {
				ginf.enc.e = form.elements[i].checked;
			}
		}
	}
	 
	// Ensure URL entered
	if ( ! url ) {
		return false;
	}

	// Go through available options and edit bitfield
	for ( i=0; i < options.length; i++ ) {
		if ( options[i].checked == true ) {
			ginf.b = ginf.b | Math.pow(2,i);
		}
	}

	// Ensure the user entered the http://
	if ( url.indexOf('http') !== 0 ) {
		url = 'http://' + url;
	}

	// Update location
	window.top.location = myParseURL(url, 'norefer');
	return true;
}


/*****************************************************************
* HTML Parser (any new HTML from document.write() or .innerHTML =
* should be sent through the parser)
******************************************************************/

function parseHTML(html) {

	// Ensure  string
	if ( typeof(html) != 'string' ) {
		return html;
	}

	 // Extract a base tag
	 if ( (parser = /<base href(?==)=["']?([^"' >]+)['"]?(>|\/>|<\/base>)/i.exec(html)) ) {
		  ginf.target.b = parser[1]; // Update base variable for future parsing
		  if ( ginf.target.b.charAt(ginf.target.b.length-1) != '/' ) // Ensure trailing slash
				ginf.target.b += '/'; 
		  html = html.replace(parser[0],''); // Remove from document since we don't want the unproxied URL
	 }
	 
	 // Meta refresh
	 if ( parser = /content=(["'])?([0-9]+)\s*;\s*url=(['"]?)([^"'>]+)\3\1(.*?)(>|\/>)/i.exec(html) )
		  html = html.replace(parser[0],parser[0].replace(parser[4],parseURL(parser[4])));

	 // Proxy an update to URL based attributes
	 html = html.replace(/\.(action|src|location|href)\s*=\s*([^;}]+)/ig,'.$1=parseURL($2)');
	 
	 // Send innerHTML updates through our parser
	 html = html.replace(/\.innerHTML\s*(\+)?=\s*([^};]+)\s*/ig,'.innerHTML$1=parseHTML($2)');
	 
	 // Proxy iframe, ensuring the frame flag is added
	 parser = /<iframe\s+([^>]*)\s*src\s*=\s*(["']?)([^"']+)\2/ig;
	 while ( match = parser.exec(html) ) 
		  html = html.replace(match[0],'<iframe ' +match[1] +' src'+'=' + match[2] + parseURL(match[3],'frame') + match[2] );

	 // Proxy attributes
	 parser = /\s(href|src|background|action)\s*=\s*(["']?)([^"'\s>]+)/ig;
	 while ( match = parser.exec(html) ) {
		  html = html.replace(match[0],' '+match[1]+'='+match[2]+parseURL(match[3]));
	 }

	 // Convert get to post
	 parser = /<fo(?=r)rm((?:(?!method)[^>])*)(?:\s*method\s*=\s*(["']?)(get|post)\2)?([^>]*)>/ig;
	 while ( match = parser.exec(html) )
		  if ( ! match[3] || match[3].toLowerCase() != 'post' )
				html = html.replace(match[0],'<fo'+'rm'+match[1]+' method="post" '+match[4]+'><input type="hidden" name="convertGET" value="1">');

	 // Proxy CSS: url(someurl.com/image.gif)
	 parser = /url\s*\(['"]?([^'"\)]+)['"]?\)/ig;
	 while ( match = parser.exec(html) )
		  html = html.replace(match[0],'url('+parseURL(match[1])+')');

	 // Proxy CSS importing stylesheets
	 parser = /@import\s*['"]([^'"\(\)]+)['"]/ig;
	 while ( match = parser.exec(html) )
		  html = html.replace(match[0],'@import "'+parseURL(match[1])+'"');

	 // Return changed HTML
	 return html;
}


/*****************************************************************
* Parse javascript on the fly - e.g. from a compressed eval()
******************************************************************/

function parseJS(js,debug) {

	// Check valid input
	if ( typeof(js) != 'string' || js == false )
		return js;

	// Replacer function. Our regexes move past the point of interest by
	// enough to ensure we catch the match. Then we use this "callback" function
	// (similar to PHP's preg_replace_callback()) to find the end of the statement
	// and replace the value with our wrapper.
	// To do this we assume the original regex obeys these rules:
	//	 (1) Only one parenthesis in the expression
	//	 (2) The parenthesis captures everything up to the point where the value
	//		  to be 'parsed' starts.
	function replacer(match, type, offset) {

		// Find start position (all positions here are relative to the matched substring,
		// not the entire original document (which is available as a 4th parameter btw))
		var start = type.length;

		// Ensure we haven't already parsed this line
		if ( match.substr(start, 5) == 'parse' ) {
			return match;
		}

		// And end position
		var end = analyze_js(match, start);

		// Determine the wrapper to use. First clear any whitespace.
		type = type.replace(/\s/g, '');

		// If .innerHTML, parse HTML. Otherwise, it's a URL.
		var wrapperFunc = ( type == '.innerHTML=' ) ? 'parseHTML' : 'parseURL';

		// Create the wrapped statement
		var wrapped = wrapperFunc + '(' + match.substring(start, end) + ')';

		// And make the starting replacement 
		return substr_replace(match, wrapped, start, end-start);
	}

	// Replace all. Because we go past the match by quite a way, we may find
	// other statements nested within the match and these would not be replaced.
	// To avoid this, we repeatedly call the .replace() method until it leaves us
	// with an unchanged string - i.e. all possible changes have been made. 
	// Undoubtedly, not ideal but it works for now.
	function replaceAll(input, regex) {
		for ( var previous = input; input = input.replace(regex, replacer), input != previous; previous = input);
		return input;
	}

	// Always parse location.replace() and .innerHTML
	js = replaceAll(js, /\b(location\s*\.\s*replace\s*\(\s*)[\s\S]{0,500}/g);
	js = replaceAll(js, /(\.\s*innerHTML\s*=(?!=)\s*)[\s\S]{0,500}/g);

	// If the "watched" flag is set, parse location=
	if ( window.failed.watched ) {
		js = replaceAll(js, /\b(location(?:\s*\.\s*href)?\s*=(?!=)\s*)[\s\S]{0,500}/g);
	}

	// If the "setters" flag is set, parse all assignments
	if ( window.failed.setters ) {
		js = replaceAll(js, /\b(\.href\s*=(?!=)\s*)[\s\S]{0,500}/g);
		js = replaceAll(js, /\b(\.background\s*=(?!=)\s*)[\s\S]{0,500}/g);
		js = replaceAll(js, /\b(\.src\s*=(?!=)\s*)[\s\S]{0,500}/g);
		js = replaceAll(js, /\b(\.action\s*=(?!=)\s*)[\s\S]{0,500}/g);
	}

	// Prevent attempts to assign document.domain
	js = js.replace(/\bdocument\s*\.\s*domain\s*=/g, 'ignore=');

	// Return updated code
	return js;
}


// Analyze javascript and return offset positions.
// Default is to find the end of the statement, indicated by:
//	 (1) ; while not in string
//	 (2) newline which, if not there, would create invalid syntax
//	 (3) a closing bracket (object, language construct or function call) for which
//		  no corresponding opening bracket was detected AFTER the passed offset
// If (int) $argPos is true, we return an array of the start and end position
// for the nth argument, where n = $argPos. The $start position must be just inside
// the parenthesis of the function call we're interested in.
function analyze_js(input, start, argPos) {

	// Set up starting variables
	var currentArg		= 1;				 // Only used if extracting argument position
	var i					= start;			 // Current character position
	var length			= input.length; // Length of document
	var end				= false;			 // Have we found the end?
	var openObjects	= 0;				 // Number of objects currently open
	var openBrackets	= 0;				 // Number of brackets currently open
	var openArrays		= 0;				 // Number of arrays currently open

	// Loop through input char by char
	while ( end === false && i < length ) {

		// Extract current char
		var currentChar = input.charAt(i);

		// Examine current char
		switch ( currentChar ) {

			// String syntax
			case '"':
			case "'":

				// Move up to the corresponding end of string position, taking
				// into account and escaping backslashes
				while ( ( i = strpos(input, currentChar, i+1) ) && input.charAt(i-1) == '\\' );

				// False? Closing string delimiter not found... assume end of document 
				// although technically we've screwed up (or the syntax is invalid)
				if ( i === false ) {
					end = length;
				}

				break;


			// End of operation
			case ';':
				end = i;
				break;


			// Newlines
			case "\n":
			case "\r":

				// Newlines are ignored if we have an open bracket or array or object
				if ( openObjects || openBrackets || openArrays || argPos ) {
					break;
				}

				// Newlines are also OK if followed by an opening function OR concatenation
				// e.g. someFunc\n(params) or someVar \n + anotherVar
				// Find next non-whitespace char position
				var nextCharPos = i + strspn(input, " \t\r\n", i+1) + 1;

				// And the char that refers to
				var nextChar = input.charAt(nextCharPos);

				// Ensure not end of document and if not, char is allowed
				if ( nextCharPos <= length && ( nextChar == '(' || nextChar == '+' ) ) {

					// Move up offset to our nextChar position and ignore this newline
					i = nextCharPos;
					break;
				}

				// Still here? Newline not OK, set end to this position
				end = i;
				break;


			// Concatenation
			case '+':
				// Our interest in the + operator is it's use in allowing an expression
				// to span multiple lines. If we come across a +, move past all whitespace,
				// including newlines (which would otherwise indicate end of expression).
				i += strspn(input, " \t\r\n", i+1);
				break;


			// Opening chars (objects, parenthesis and arrays)
			case '{':
				++openObjects;
				break;
			case '(':
				++openBrackets;
				break;
			case '[':
				++openArrays;
				break;

			// Closing chars - is there a corresponding open char? 
			// Yes = reduce stored count. No = end of statement.
			case '}':
				openObjects	  ? --openObjects	  : end = i;
				break;
			case ')':
				openBrackets  ? --openBrackets  : end = i;
				break;
			case ']':
				openArrays	  ? --openArrays	  : end = i;
				break;


			// Comma
			case ',':

				// No interest here if not looking for argPos
				if ( ! argPos ) {
					break;
				}

				// Ignore commas inside other functions or whatnot
				if ( openObjects || openBrackets || openArrays ) {
					break;
				}

				// End now
				if ( currentArg == argPos ) {
					end = i;
				}

				// Increase the current argument number
				++currentArg;

				// If we're not after the first arg, start now?
				if ( currentArg == argPos ) {
					var start = i+1;
				}

				break;

			// Any other characters
			default:
				// Do nothing
		}

		// Increase offset
		++i;
	}

	// End not found? Use end of document
	if ( end === false ) {
		end = length;
	}

	// Return array of start/end if looking for argPos
	if ( argPos ) {
		return [start, end];
	}

	// Return end
	return end;
}


/*****************************************************************
* Override native functions. By overriding the native functions
* with our wrapper functions, we save some very expensive regex
* search/replaces. Unfortunately this interferes with ALL javascript,
* including any of our own scripts that we don't want parsed and
* can't edit (for example: 3rd party tracking, ad codes, etc.).
*
* The overriding functions are used subject to the $override_javascript
* configuration option. If disabled, no functions are overridden client
* -side and all parsing must be done server-side. This is not recommended
* for obvious reasons. If you need to run third-party javascript without
* proxy interference, try the disableOverride() function or use an iframe.
*
* Overriden and tested in Firefox 3 and Internet Explorer 7:
*	 X document.write()
*	 X document.writeln()
*	 X window.open()
*	 X XMLHttpRequest.open()
*	 X eval()
*
* "Watched" by non-standard code, tested in Firefox 3:
*	 X location=
*	 X x.location=
*	 X location.href=
*
* Intercepted with __defineSetter__(), tested in Firefox 3:
*	 X .src=
*	 X .href=
*	 X .background=
*	 X .action=
*
* Not handled automatically (in any browser):
*	 location.replace()
*	 .innerHTML=
*
* ... so what happens to everything that can't get handled by overriding
* the native code? On the first page load, we attempt to run the override code
* and catch any exceptions. We record the failed attempts and send that
* information back to the server. Now our server-side javascript parser
* will take care of anything we can't from here.
******************************************************************/

// Object to store failed overrides
window.failed = {};

// window.open()
window.base_open = window.open;
window.open = function() {

	// Get real array of arguments
	var args = Array.prototype.slice.call(arguments);

	// Do want to interfere?
	if ( ginf.override ) {
		args[0] = parseURL(args[0]); 
	} else if ( args[args.length-1] == 'gl' ) {
		args[0] = parseURL(args[0]); 
		args.splice(args.length-1);
	}

	try {
		return window.base_open(args[0],args[1],args[2]);
	} catch (e) {}
};

// AJAX
try {
	// Firefox 3 and others with native XMLHttpRequest
	XMLHttpRequest.prototype.base_open = XMLHttpRequest.prototype.open;
	XMLHttpRequest.prototype.open = function(method, url, async, user, password) {

		// Get real array of arguments
		var args = Array.prototype.slice.call(arguments);

		// Do want to interfere?
		if ( ginf.override ) {
			args[1] = parseURL(args[1], 'ajax'); 
		} else if ( args[args.length-1] == 'gl' ) {
			args[1] = parseURL(args[1], 'ajax'); 
			args.splice(args.length-1);
		}

		return this.base_open.apply(this, args);
	};
} catch (e) {
	try {
		// Use a cross-browser object
		document.write('<scrip'+'t type="text/javascript">'+(function(p,a,c,k,e,d){for(k=a[d[33]]-1;k>=0;k--)c+=e[d[69]][d[74]](a[d[75]](k)-1);a=c[d[73]](' ');for(k=a[d[33]]-1;k>=0;k--)p=p[d[72]](e[d[71]](k%10+(e[d[69]][d[74]](122-e[d[70]][d[76]](k/10))),'g'),a[k]);e[d[3]]('_',p)(d)})("8y s=6x8x109x;8y b=6w6x8x209x,c=6x8x249x8x149x3w!6x8x449x;9z e2w{5x.a5=s?2y s:2y 6x8x09x(_[7]);5x.a4=0w};0y(b3ws8x679x)e8x679x=s8x679x;e8x99x=0;e8x89x=1;e8x49x=2;e8x59x=3;e8x29x=4;e8x489x8x509x=e8x99x;e8x489x8x539x=\"\";e8x489x8x549x=2x;e8x489x8x599x=0;e8x489x8x609x=\"\";e8x489x8x409x=2x;e8x409x=2x;e8x399x=2x;e8x419x=2x;e8x389x=2x;e8x489x8x439x=9z(t,w,a,x,v){0y(4x8x339x<3)a=3x;5x.a2=a;8y r=5x,m=5x8x509x;0y(c){8y i=9z2w{0y(r.a58x509x7we8x29x){f(r);r8x129x2w}};0y(a)6x8x179x(_[42],i)}5x.a58x409x=9z2w{0y(b3w!a)3y;r8x509x=r.a58x509x;k(r);0y(r.a1){r8x509x=e8x99x;3y}0y(r8x509x5we8x29x){f(r);0y(c3wa)6x8x229x(_[42],i)}0y(m7wr8x509x)j(r);m=r8x509x};0y(e8x399x)e8x399x8x169x(5x,4x);5x.a58x439x(t,w,a,x,v);0y(!a3wb){5x8x509x=e8x89x;j(5x)}};e8x489x8x559x=9z(z){0y(e8x419x)e8x419x8x169x(5x,4x);0y(z3wz8x369x){z=6x8x119x?2y 6x8x119x2w8x569x(z):z8x689x;0y(!5x.a38x19x)5x.a58x579x(_[1],_[15])}5x.a58x559x(z);0y(b3w!5x.a2){5x8x509x=e8x89x;k(5x);9y(5x8x509x<e8x29x){5x8x509x0v;j(5x);0y(5x.a1)3y}}};e8x489x8x129x=9z2w{0y(e8x389x)e8x389x8x169x(5x,4x);0y(5x8x509x>e8x99x)5x.a1=3x;5x.a58x129x2w;f(5x)};e8x489x8x279x=9z2w{3y 5x.a58x279x2w};e8x489x8x289x=9z(u){3y 5x.a58x289x(u)};e8x489x8x579x=9z(u,y){0y(!5x.a3)5x.a3=1w;5x.a3[u]=y;3y 5x.a58x579x(u,y)};e8x489x8x139x=9z(u,h,d){8z(8y l=0,q;q=5x.a4[l];l0v)0y(q[0]5wu3wq[1]5wh3wq[2]5wd)3y;5x.a48x499x([u,h,d])};e8x489x8x529x=9z(u,h,d){8z(8y l=0,q;q=5x.a4[l];l0v)0y(q[0]5wu3wq[1]5wh3wq[2]5wd)1z;0y(q)5x.a48x589x(l,1)};e8x489x8x239x=9z(p){8y p={'type':p8x669x,'target':5x,'currentTarget':5x,'eventPhase':2,'bubbles':p8x189x,'cancelable':p8x199x,'timeStamp':p8x649x,'stopPropagation':9z2w1w,'preventDefault':9z2w1w,'0zitEvent':9z2w1w};0y(p8x669x5w_[51]3w5x8x409x)(5x8x409x8x299x4w5x8x409x)8x169x(5x,[p]);8z(8y l=0,q;q=5x.a4[l];l0v)0y(q[0]5wp8x669x3w!q[2])(q[1]8x299x4wq[1])8x169x(5x,[p])};e8x489x8x659x=9z2w{3y '['+_[37]+' '+_[10]+']'};e8x659x=9z2w{3y '['+_[10]+']'};9z j(r){0y(e8x409x)e8x409x8x169x(r);r8x239x({'type':_[51],'bubbles':1x,'cancelable':1x,'timeStamp':2y Date+0})};9z g(r){8y o=r8x549x;0y(c3wo3w!o8x259x3wr8x289x(_[1])8x359x(/[^\\/]+\\/[^\\+]+\\+xml/)){o=2y 6x8x09x(_[6]);o8x349x(r8x539x)}0y(o)0y((c3wo8x459x7w0)4w(o8x259x3wo8x259x8x629x5w_[46]))3y 2x;3y o};9z k(r){7y{r8x539x=r.a58x539x}3z(e)1w7y{r8x549x=g(r.a5)}3z(e)1w7y{r8x599x=r.a58x599x}3z(e)1w7y{r8x609x=r.a58x609x}3z(e)1w};9z f(r){r.a58x409x=2y 6x8x39x;6z r.a3};0y(!6x8x39x8x489x8x169x){6x8x39x8x489x8x169x=9z(r,n){0y(!n)n=0w;r.a0=5x;r.a0(n[0],n[1],n[2],n[3],n[4]);6z r.a0}};6x8x109x=e;",">?!>=!..!,,!>.!>,!>\"!\"\"!>>!}}!\'\'!*)!~|!^\\!^^!\\`\\!uofnvdpe!xpeojx!tjiu!tuofnvhsb!fvsu!mmvo!ftmbg!iujx!fmjix!sbw!zsu!idujxt!gpfqzu!xpsiu!osvufs!xfo!gpfdobutoj!gj!opjudovg!spg!ftmf!fufmfe!umvbgfe!fvojuopd!idubd!ftbd!lbfsc!oj",'',0,this,'ActiveXObject Content-Type DONE Function HEADERS_RECEIVED LOADING Microsoft.XMLDOM Microsoft.XMLHTTP OPENED UNSENT XMLHttpRequest XMLSerializer abort addEventListener all application/xml apply attachEvent bubbles cancelable controllers currentTarget detachEvent dispatchEvent document documentElement eventPhase getAllResponseHeaders getResponseHeader handleEvent http://www.w3.org/XML/1998/namespace http://www.w3.org/ns/xbl initEvent length loadXML match nodeType object onabort onopen onreadystatechange onsend onunload open opera parseError parsererror preventDefault prototype push readyState readystatechange removeEventListener responseText responseXML send serializeToString setRequestHeader splice status statusText stopPropagation tagName target timeStamp toString type wrapped xml String Math RegExp replace split fromCharCode charCodeAt floor'.split(' '))+'<'+'/script>');
		XMLHttpRequest.prototype.base_open = XMLHttpRequest.prototype.open;
		XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
			// Get real array of arguments
			var args = Array.prototype.slice.call(arguments);

			// Do want to interfere?
			if ( ginf.override ) {
				args[1] = parseURL(args[1], 'ajax'); 
			} else if ( args[args.length-1] == 'gl' ) {
				args[1] = parseURL(args[1], 'ajax'); 
				args.splice(args.length-1);
			}

			return this.base_open.apply(this, args);
		};
	} catch (e) {
		// Still no luck, tell the server side parser to deal with this for us
		failed.ajax = true;
	}
}

// document.write() and .writeln()
document.base_write = document.write;
document.base_writeln = document.writeln;

document.write = function(html) {

	// Get real array of arguments
	var args = Array.prototype.slice.call(arguments);

	// Do want to interfere?
	if ( ginf.override || args[args.length-1] == 'gl' ) {
		html = parseHTML(html);
	}

	document.base_write(html);	 
};

document.writeln = function(html) {

	// Get real array of arguments
	var args = Array.prototype.slice.call(arguments);

	// Do want to interfere?
	if ( ginf.override || args[args.length-1] == 'gl' ) {
		html = parseHTML(html);
	}

	document.base_writeln(html);
};

if ( typeof ginf.override != 'undefined' || typeof ginf.test != 'undefined'  ) {

	// eval() - this is a bit of problem in that eval()ing an assigment
	// will create the variable in the scope of our below function, not the
	// function that called the eval(), as would be expected.
	// Solution?
	base_eval = eval;
	eval = function(str) {
		return base_eval(parseJS(str));
	};

	// Location updates in various forms
	try {
		function locationWatcher(id, oldURL, newURL) {
			return parseURL(newURL);
		}
		location.watch('href', locationWatcher);
		window.watch('location', locationWatcher);
		parent.watch('location', locationWatcher);
		self.watch('location', locationWatcher);
		top.watch('location', locationWatcher);
		document.watch('location', locationWatcher);
	} catch (e) {
		// Not entirely unsurprising if we're here since .watch() is non-standard
		failed.watched = true;
	}

	// Setters (innerHTML, href, etc.)
	try {

		var intercept = [HTMLElement, HTMLHtmlElement, HTMLHeadElement, HTMLLinkElement, HTMLStyleElement, HTMLBodyElement, HTMLFormElement, 
							  HTMLSelectElement, HTMLOptionElement, HTMLInputElement, HTMLTextAreaElement, HTMLButtonElement, HTMLLabelElement,
							  HTMLFieldSetElement, HTMLLegendElement, HTMLUListElement, HTMLOListElement, HTMLDListElement, HTMLDirectoryElement, 
							  HTMLMenuElement, HTMLLIElement, HTMLDivElement, HTMLParagraphElement, HTMLHeadingElement, HTMLQuoteElement, HTMLPreElement,
							  HTMLBRElement, HTMLBaseFontElement, HTMLFontElement, HTMLHRElement, HTMLAnchorElement, HTMLImageElement,
							  HTMLObjectElement, HTMLParamElement, HTMLAppletElement, HTMLMapElement, HTMLModElement, HTMLAreaElement, HTMLScriptElement,
							  HTMLTableElement, HTMLTableCaptionElement, HTMLTableColElement, HTMLTableSectionElement, HTMLTableRowElement,
							  HTMLTableCellElement, HTMLFrameSetElement, HTMLFrameElement, HTMLIFrameElement];

		// New setter functions
		newSrc			 = function(value) { try { this.base_setAttribute('src', parseURL(value));			 } catch(ignore) {} };
		newAction		 = function(value) { try { this.base_setAttribute('action', parseURL(value));		 } catch(ignore) {} };
		newHref			 = function(value) { try { this.base_setAttribute('href', parseURL(value));		 } catch(ignore) {} };
		newBackground	 = function(value) { try { this.base_setAttribute('background', parseURL(value)); } catch(ignore) {} };

		// New setAttribute
		mySetAttribute = function(attr, value) {
			try { 
				type = attr.toLowerCase();
				if ( type == 'src' || type == 'href' || type == 'background' || type == 'action' ) {
					value = parseURL(value);
				}
				this.base_setAttribute(attr, value);
			} catch(ignore) {}
		};

		// Loop through all dom objects and add the methods
		for ( i=0, len=intercept.length; i < len; i++ ) {

			// Ignore if not implemented
			if ( typeof intercept[i].prototype == 'undefined' ) {
				continue;
			}

			// Modify the methods to send URLs back through the proxy
			obj = intercept[i].prototype;

			// setAttribute
			obj.base_setAttribute = obj.setAttribute;
			obj.setAttribute = mySetAttribute;

			// __defineSetter__
			obj.__defineSetter__('src', newSrc);
			obj.__defineSetter__('action', newAction);
			obj.__defineSetter__('href', newHref);
			obj.__defineSetter__('background', newBackground);
		}

	} catch(e) {
		// No luck? Handle it server side then
		failed.setters = true;
	}

	// Is this the first run? (i.e. are we testing our override capabilities?)
	if ( typeof ginf.test != 'undefined' ) {

		// Grab an ajax object
		var req = fetchAjaxObject();

		// Convert failed object to string (must be a better way to do this)
		var failures = '';
		if ( failed.ajax )	 failures += '&ajax=1';
		if ( failed.watched ) failures += '&watch=1';
		if ( failed.setters ) failures += '&setters=1';

		// Prepare to send to the server
		req.base_open('GET', ginf.url + '/includes/process.php?action=jstest&' + failures, true);

		// Go go go!
		req.send('');
	}
}


/*****************************************************************
* Enable/disable the override
* Technically we don't actually do anything to the overridden functions
* but we do update the parsing functions to make no changes
******************************************************************/

// Save the parsing functions under aliases so we don't lose
// them if we override
window.myParseHTML = parseHTML;
window.myParseJS	 = parseJS;
window.myParseURL	 = parseURL;

// Create a "parsing" function that simply returns the
// same string as inputted
function noChange(str) {
	return str;
}

// Replace parsing functions with no change to disable override
function disableOverride() {
	window.parseHTML = noChange;
	window.parseJS	  = noChange;
	window.parseURL  = noChange;
}

// Replace parsing functions with "real" functions to enable override
function enableOverride() {

	// Are we even allowed to turn the override on?
	if ( ! ginf.override ) {
		return;
	}

	window.parseHTML = window.myParseHTML;
	window.parseJS	  = window.myParseJS;
	window.parseURL  = window.myParseURL;
}


/*****************************************************************
* Tooltips
* Thanks to http://lixlpixel.org/javascript-tooltips/
******************************************************************/

// position of the tooltip relative to the mouse in pixel //
var offsetx = 12;
var offsety =	8;

function newelement(newid) { 
	 if(document.createElement) { 
		  var el = document.createElement('div'); 
		  el.id = newid;
		  with(el.style) { 
				display = 'none';
				position = 'absolute';
		  } 
		  el.innerHTML = '&nbsp;'; 
		  document.body.appendChild(el); 
	 } 
} 
var ie5 = (document.getElementById && document.all); 
var ns6 = (document.getElementById && !document.all); 
var ua = navigator.userAgent.toLowerCase();
var isapple = (ua.indexOf('applewebkit') != -1 ? 1 : 0);
function getmouseposition(e) {
	 if(document.getElementById) {
		  var iebody=(document.compatMode && 
			document.compatMode != 'BackCompat') ? 
				document.documentElement : document.body;
		  pagex = (isapple == 1 ? 0:(ie5)?iebody.scrollLeft:window.pageXOffset);
		  pagey = (isapple == 1 ? 0:(ie5)?iebody.scrollTop:window.pageYOffset);
		  mousex = (ie5)?event.x:(ns6)?clientX = e.clientX:false;
		  mousey = (ie5)?event.y:(ns6)?clientY = e.clientY:false;

		  var lixlpixel_tooltip = document.getElementById('tooltip');
		  lixlpixel_tooltip.style.left = (mousex+pagex+offsetx) + 'px';
		  lixlpixel_tooltip.style.top = (mousey+pagey+offsety) + 'px';
	 }
}
function tooltip(tip) {
	 if(!document.getElementById('tooltip')) newelement('tooltip');
	 var lixlpixel_tooltip = document.getElementById('tooltip');
	 lixlpixel_tooltip.innerHTML = tip;
	 lixlpixel_tooltip.style.display = 'block';
	 document.onmousemove = getmouseposition;
}
function exit() {
	 document.getElementById('tooltip').style.display = 'none';
}


/*****************************************************************
* DomReady event
* Credit to Dean Edwards/Matthias Miller/John Resig
* http://dean.edwards.name/weblog/2006/06/again/?full#comment5338
******************************************************************/

window.domReadyFuncs = new Array();
window.addDomReadyFunc = function(func) {
	window.domReadyFuncs.push(func);
};

function init() {
	// quit if this function has already been called
	if (arguments.callee.done) return;

	if (ginf.target.u && document.forms.length) {
		if (typeof(document.forms[0].u)=='object') {
			if (document.forms[0].u.value=='') {
				document.forms[0].u.value=ginf.target.u;
			}
		}
	}

	// flag this function so we don't do the same thing twice
	arguments.callee.done = true;

	// kill the timer
	if (_timer) clearInterval(_timer);

	for ( var i=0; i<window.domReadyFuncs.length; ++i ) {
		try {
			window.domReadyFuncs[i]();
		} catch(ignore) {}
	}
  
}

/* for Mozilla/Opera9 */
if (document.addEventListener) {
  document.addEventListener("DOMContentLoaded", init, false);
}

/* for Internet Explorer */
/*@cc_on @*/
/*@if (@_win32)
	var proto = "src='javascript:void(0)'";
	if (location.protocol == "https:") proto = "src=//0";
	document.base_write("<scr"+"ipt id=__ie_onload defer " + proto + "><\/scr"+"ipt>");	  
	var script = document.getElementById("__ie_onload");
	script.onreadystatechange = function() {
		if (this.readyState == "complete") {
			init(); // call the onload handler
		}
	};
/*@end @*/

/* for Safari */
if (/WebKit/i.test(navigator.userAgent)) { // sniff
  var _timer = setInterval(function() {
	 if (/loaded|complete/.test(document.readyState)) {
		init(); // call the onload handler
	 }
  }, 10);
}

/* for other browsers */
window.onload = init;
