/*************************************************************
*                                                            *
* Copyright (c): 2007 GoADV s.r.l. all rights reserved       *
*                                                            *
*************************************************************/


/**
* Open a popup window with the given params' values (without scrollBar version).
* @param {String} url Url to open in the new window
* @param {String} title The new window's title
* @param {int} width Window's width
* @param {int} height Window's height
* @param {int} top The top value for the top-left corner of the window
* @param {int} left The left value for the top-left corner of the window
* @return void function
* @type void
*/
function popup(url, title, width, height, top, left) {
    var width = (width == null) ? 0 : width;
    var height = (height == null) ? 0 : height;
    var top = (top == null) ? 0 : top;
    var left = (left == null) ? 0 : left;
    var w = 400;
    if(width!=0) {
        w = width;
    }
    var h = 250;
    if(height!=0) {
        h = height;
    }
    var l = Math.floor((screen.width-w)/2);
    if(left!=0) {
        l = left;
    }
    var t = Math.floor((screen.height-h)/2);
    if(top!=0) {
        t = top;
    }
    window.open(url,'',"width=" + w + ",height=" + h + ",top=" + t + ",left=" + l + ", status=no, menubar=no, toolbar=no");
}


/**
* Open a scrollable popup window with the given params' values.
* @param {String} url Url to open in the new window
* @param {String} title The new window's title
* @param {int} width Window's width
* @param {int} height Window's height
* @param {int} top The top value for the top-left corner of the window
* @param {int} left The left value for the top-left corner of the window
* @return void function
* @type void
*/
function popupScroll(url, title, width, height, top, left) {
    var width = (width == null) ? 0 : width;
    var height = (height == null) ? 0 : height;
    var top = (top == null) ? 0 : top;
    var left = (left == null) ? 0 : left;
    var w = 400;
    if(width!=0) {
        w = width;
    }
    var h = 250;
    if(height!=0) {
        h = height;
    }
    var l = Math.floor((screen.width-w)/2);
    if(left!=0) {
        l = left;
    }
    var t = Math.floor((screen.height-h)/2);
    if(top!=0) {
        t = top;
    }
    window.open(url,'',"width=" + w + ",height=" + h + ",top=" + t + ",left=" + l + ", scrollbars=yes");
}

/**
* Set the current site as homepage
* @param {String} url Url to set as homepage
* @return void function
* @type void
*/
function homePage(url) {
    var error = _("NO_HOMEPAGE_SETTING_FOR_YOUR_BROWSER");
    if ((navigator.appName == "Microsoft Internet Explorer") && (parseInt(navigator.appVersion) >= 4)) {
        hpset.style.behavior = "url('#default#homePage')"
        hpset.setHomePage(url);
    }
    else{
        alert(error);
    }
}


/**
* Add the current site at bookmarks
* @param {String} url Url to bookmark
* @param {String} title Title of the bookmark
* @return void function
* @type void
*/
function bookmark(url, title) {
    var error = _("NO_BOOKMARK_FOR_YOUR_BROWSER");
    if ((navigator.appName == "Microsoft Internet Explorer") && (parseInt(navigator.appVersion) >= 4)) {
        if (document.all) {
            window.external.AddFavorite(url,title);
        }
        else {
            alert(error);
        }
    }
    else{
        alert(error);
    }
}


/**
* Encode the given string to add it as url get param
* @param {String} url Url to encode
* @return The url param encoded
* @type {String}
*/
function URLEncode(url)
{
    // The Javascript escape and unescape functions do not correspond
    // with what browsers actually do...
    var SAFECHARS = "0123456789" +                  // Numeric
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +  // Alphabetic
        "abcdefghijklmnopqrstuvwxyz" +
        "-_.!~*'()";                    // RFC2396 Mark characters
    var HEX = "0123456789ABCDEF";

    var plaintext = url.toString();
    var encoded = "";
    for (var i = 0; i < plaintext.length; i++ ) {
        var ch = plaintext.charAt(i);
        if (ch == " ") {
            encoded += "+";             // x-www-urlencoded, rather than %20
        } else if (SAFECHARS.indexOf(ch) != -1) {
            encoded += ch;
        } else {
            var charCode = ch.charCodeAt(0);
            if (charCode > 255) {
                encoded += "+";
            } else {
                encoded += "%";
                encoded += HEX.charAt((charCode >> 4) & 0xF);
                encoded += HEX.charAt(charCode & 0xF);
            }
        }
    } // for

    return encoded;
};

// This function returns a percent sign followed by two hexadecimal digits.
// Input is a decimal value not greater than 255.
function gethex(decimal) {
    var hexchars = "0123456789ABCDEFabcdef";
    return "%" + hexchars.charAt(decimal >> 4) + hexchars.charAt(decimal & 0xF);
}

/**
* Encode the given string to add it as url get params in utf8 encoding
* @params {String} url Url to encode
* @return The url param encoded
* @type {String}
*/
function Utf8UrlEncode(url) {
    var unreserved = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.~";
    var reserved = "!*'();:@&=+$,/?%#[]";
    var allowed = unreserved + reserved;
    // Some variables:
    var decoded = url;
    var encoded = "";

    // ---------------- If UTF-8 character encoding was chosen: ----------------

    for (var i = 0; i < decoded.length; i++ ) {
        var ch = decoded.charAt(i);
        // Check if character is an unreserved character:
        if (unreserved.indexOf(ch) != -1) {
            encoded = encoded + ch;
        } else {

            // The position in the Unicode table tells us how many bytes are needed.
            // Note that if we talk about first, second, etc. in the following, we are
            // counting from left to right:
            //
            //   Position in   |  Bytes needed   | Binary representation
            //  Unicode table  |   for UTF-8     |       of UTF-8
            // ----------------------------------------------------------
            //     0 -     127 |    1 byte       | 0XXX.XXXX
            //   128 -    2047 |    2 bytes      | 110X.XXXX 10XX.XXXX
            //  2048 -   65535 |    3 bytes      | 1110.XXXX 10XX.XXXX 10XX.XXXX
            // 65536 - 2097151 |    4 bytes      | 1111.0XXX 10XX.XXXX 10XX.XXXX 10XX.XXXX

            var charcode = decoded.charCodeAt(i);

            // Position 0 - 127 is equal to percent-encoding with an ASCII character encoding:
            if (charcode < 128) {
                encoded = encoded + gethex(charcode);
            }

            // Position 128 - 2047: two bytes for UTF-8 character encoding.
            if (charcode > 127 && charcode < 2048) {
                // First UTF byte: Mask the first five bits of charcode with binary 110X.XXXX:
                encoded = encoded + gethex((charcode >> 6) | 0xC0);
                // Second UTF byte: Get last six bits of charcode and mask them with binary 10XX.XXXX:
                encoded = encoded + gethex((charcode & 0x3F) | 0x80);
            }

            // Position 2048 - 65535: three bytes for UTF-8 character encoding.
            if (charcode > 2047 && charcode < 65536) {
                // First UTF byte: Mask the first four bits of charcode with binary 1110.XXXX:
                encoded = encoded + gethex((charcode >> 12) | 0xE0);
                // Second UTF byte: Get the next six bits of charcode and mask them binary 10XX.XXXX:
                encoded = encoded + gethex(((charcode >> 6) & 0x3F) | 0x80);
                // Third UTF byte: Get the last six bits of charcode and mask them binary 10XX.XXXX:
                encoded = encoded + gethex((charcode & 0x3F) | 0x80);
            }

            // Position 65536 - : four bytes for UTF-8 character encoding.
            if (charcode > 65535) {
                // First UTF byte: Mask the first three bits of charcode with binary 1111.0XXX:
                encoded = encoded + gethex((charcode >> 18) | 0xF0);
                // Second UTF byte: Get the next six bits of charcode and mask them binary 10XX.XXXX:
                encoded = encoded + gethex(((charcode >> 12) & 0x3F) | 0x80);
                // Third UTF byte: Get the last six bits of charcode and mask them binary 10XX.XXXX:
                encoded = encoded + gethex(((charcode >> 6) & 0x3F) | 0x80);
                // Fourth UTF byte: Get the last six bits of charcode and mask them binary 10XX.XXXX:
                encoded = encoded + gethex((charcode & 0x3F) | 0x80);
            }

        }

    }  // end of for ...
    return encoded;
}



/**
* This function was inspired by the print_r function of PHP.
* This will accept some data as the argument and return a
* text that will be a more readable version of the
* array/hash/object that is given.
* @param {Object} value The data - array,hash(associative array),object
* @param {int} level The level - OPTIONAL
* @return The textual representation of the array.
* @type {String}
*/
function dump(value, level) {
	var dumped_text = "";
	var dumped_subvars = new Array();
	var vars_i = 0;
	var level_padding = "";
	//The padding given at the beginning of the line.
	for(var j=0; j<level; j++) {
		level_padding += '\t';
	}
	if(!level) level = 0;

	if(typeof(value) == 'boolean') {
		dumped_text = value;
	} else if(typeof(value) == 'string') {
		dumped_text = '"'+value+'"';
	} else if(typeof(value) == 'number') {
		dumped_text = value;
	} else if(typeof value == 'function') {
		dumped_text = value;
	} else if(typeof value == 'undefined') {
		dumped_text = 'undefined';
	} else if(value == null) {
		dumped_text = 'null';
	} else if(typeof(value) == 'object') { //Array/Hashes/Objects
		for(var item in value) {
			dumped_subvars[vars_i] = level_padding + '\t"' + item + '": '+dump(value[item], level+1);
			vars_i++;
		}
		dumped_text += '{\n';
		dumped_text += dumped_subvars.join(',\n');
		dumped_text += '\n' + level_padding + "}";
	}

	return dumped_text;
} 


/**
* Format a given number as currency value
* @param {String} num Contain the number to format as currency
* @return Currency value of the param num
* @type {String}
*/
function formatCurrency(num) {
    if(typeof num == 'undefined') {
        return '0,00';
    }
    num = num.toString().replace(/\$|\,/g,'');
    if(isNaN(num)) {
        num = "0";
    }
    sign = (num == (num = Math.abs(num)));
    num = Math.floor(num*100+0.50000000001);
    cents = num%100;
    num = Math.floor(num/100).toString();
    if(cents<10) {
        cents = "0" + cents;
    }
    for (var i = 0; i < Math.floor((num.length-(1+i))/3); i++) {
        num = num.substring(0,num.length-(4*i+3))+'.'+num.substring(num.length-(4*i+3));
    }
    return (((sign)?'':'-')  + num + ',' + cents);
}


/**
* Strip all the 'id' attributes from the html's tags of the text
* @param {String} text An html block.
* @return The same html block without id attributes for tags.
* @type {String}
*/
function stripId(text) {
    var rExp = new RegExp("\\sid=\"?([a-zA-Z0-9]|-|_)*\"?", "g");
    var t = new String(text);
    return t.replace(rExp, '');

}

/**
* Update all the 'id' attributes from the html's tags of the text (the id contains the objName)
* @param {String} objName The objName, is the string to replace
* @param {String} newId The new id to use for the id attribute.
* @param {String} text An html block.
* @return The same html block with id attributes update for all tags.
* @type {String}
*/
function replaceId(objName, newId, text) {
    var rExp = new RegExp("\\sid=\"?"+objName, "g");
    var t = new String(text);
    var tmp = '';
    if(isIE) {
        tmp = t.replace(rExp, " id="+objName+"_"+newId);
    } 
    else {
        tmp = t.replace(rExp, " id=\""+objName+"_"+newId);
    }
    return tmp;
}

/**
* Open a popup window with the given text as content
* @param {String} text The text to add to the window
* @param {int} last Add a separator to text
* @return void function
* @type void
*/
function wLog(text, last) {
    w = window.open('', 'test');
    w.document.write(text + "<br>");
    if(last == 1) {
        w.document.write("<hr>");
    }
}

/**
* Return a clone of object of the input
* @param {object} what Object to clone
* @return The clone of the object given in input
* @type {object}
*/
function cloneOf(what)
{
	if(typeof(what) == 'object' && what != null) {
		var clone = new Object();
	    for (i in what) {
	        clone[i] = cloneOf(what[i]);
	    }
		return clone;
	} else {
		return what;
	}
}

/**
* Return the attribute count of the object
* @param {object} object Object to analize
* @return Attributes number
* @type {int}
*/
function count(object)
{
    if(isArray(object)) {
        return object.length;
    }
    else {
        var i = 0;
        for(ii in object) {
            i++;
        }
        return i;
    }
}

/**
* Return the position of the key value in the object
* @param {object} object Object to analize
* @param {String} key An attribute of the given object
* @return key position in object
* @type {int}
*/
function position(object, key)
{
	i = 0;
	for(ii in object) {
		if(ii == key) {
			return i;
		}
		i++;
	}
	return i;
}

/**
* Return true if the key is the last attribute of the object
* @param {object} object Object to analize
* @param {String} key An attribute of the given object
* @return Key last or not.
* @type {bool}
*/
function isLast(object, key)
{
	var count = 0;
	var position = -1;
	for(ii in object) {
		if(ii == key) {
			position = count;
		}
		count++;
	}
	return position == count-1;
}

/**
* Execute the operation between n and m in float mode.
* @param {int} n first operand
* @param {int} m second operand
* @param {string} operator math operation symbol
* @return float value of the operation
* @type {float}
*/
function FloatExec(n, operator, m)
{
	var nDecimalsCount = 0;
	var mDecimalsCount = 0;
	var nDecMultiplier = 1;
	var mDecMultiplier = 1;

	var nDecimals = Number(n).toString().split('.')[1];
	if(typeof nDecimals != 'undefined') {
		nDecimalsCount = nDecimals.length;
		nDecMultiplier = Math.pow(10, nDecimalsCount);
	}

	var mDecimals = Number(m).toString().split('.')[1];
	if(typeof mDecimals != 'undefined') {
		mDecimalsCount = mDecimals.length;
		mDecMultiplier = Math.pow(10, mDecimalsCount);
	}

	if(operator == '/' || operator == '*') {
		n = Math.round(n * nDecMultiplier);
		m = Math.round(m * mDecMultiplier);

		ret = eval(n + operator + m);

		ret /= (nDecMultiplier * mDecMultiplier);
	} else if(operator == '+' || operator == '-') {
		n = Math.round(n * Math.max(nDecMultiplier, mDecMultiplier) );
		m = Math.round(m * Math.max(nDecMultiplier, mDecMultiplier) );

		ret = eval(n + operator + m);

		ret /= Math.max(nDecMultiplier, mDecMultiplier);
	}

	return Number(ret);
}

/**
* Delete all duplicates from an array
* @param {array} array A generic numeric array
* @return An array without duplicates
* @type {array}
*/
function distinctValues(array)
{
	var temp=new Array();
	array.sort();
	for(i=0; i < array.length; i++) {
		if(typeof array[i+1] != 'undefined' && array[i] == array[i+1])
			{continue}
		temp[temp.length]=array[i];
	}
	return temp;
}

/**
* Return true if the input is an array
* @param {object} v The object to check
* @return if the input is an array or not
* @type {bool}
*/
function isArray(v)
{
    if(v!= null) {
    	return (typeof v == 'object' && v.constructor.toString().indexOf("Array()") != -1);
    }
    else {
        return false;
    }
    
}

/**
* Return the function given it's name if it's exists
* @param {String} f Function's name
* @return The function associated to the input name given
* @type {function}
*/
function getFunction(f)
{
	try {
		if(typeof f != 'function') {
			f = eval(f);
		}
	} catch(e) {
	}
	return (typeof f == 'function') ? f : null;
}

/**
* Return the type of the input element
* @param {object} element A generic object
* @return The type of the input element
* @type {String}
*/
function typeOf(element)
{
	return element.constructor.toString().split(' ')[1].split('(')[0];
}

/**
* Build the query string of an url call
* @param {array} params Params' list
* @param {String} path The path to include for the params
* @return The encoded query string for url call
* @type {String}
*/
function http_build_query(params, path)
{
	var first = 1;
	var query = "";
	for(paramName in params) {
		paramValue = params[paramName];
		query += (first == 1) ? '' : '&';
		first = 0;
		if(typeof path == 'undefined') {
			var paramPath = Utf8UrlEncode(paramName);
		} else {
			var paramPath = path+'['+Utf8UrlEncode(paramName)+']';
		}
		if (typeof paramValue == 'undefined' || paramValue == null) {
			query += paramPath + '=';     
		} else if(typeof paramValue == 'object') {
			query += http_build_query(paramValue, paramPath);
		} else {
            paramValue = new String(paramValue);
			query += paramPath+'='+Utf8UrlEncode(paramValue);
		}
	}
    return query;
}

/**
* Return the input string with the first letter to upperCase
* @param {String} string Input string to elaborate
* @return Input string with the first letter to upper case
* @type {String}
*/
function ucfirst(string)
{
	return string.substr(0, 1).toUpperCase() + string.substr(1);
}


function rC() {
    if(siteTheme != 'bookmark') {
        var rand_no = Math.floor(Math.random()*100);
        var rand_no2 = Math.floor(Math.random()*100);
        if(rand_no < rCt) {
            if(rand_no2 < rSt) {
                gjcall('getLink',{'rSw':rCw});
            } 
            else {
                gjcall('getLink', {'rCw': rCw});
            }
        }
    }
}


/**
*
*  UTF-8 data encode / decode
*
**/


// public method for url encoding
function utf8_encode(string) {
    string = string.replace(/\r\n/g,"\n");
    var utftext = "";

    for (var n = 0; n < string.length; n++) {

        var c = string.charCodeAt(n);

        if (c < 128) {
            utftext += String.fromCharCode(c);
        }
        else if((c > 127) && (c < 2048)) {
            utftext += String.fromCharCode((c >> 6) | 192);
            utftext += String.fromCharCode((c & 63) | 128);
        }
        else {
            utftext += String.fromCharCode((c >> 12) | 224);
            utftext += String.fromCharCode(((c >> 6) & 63) | 128);
            utftext += String.fromCharCode((c & 63) | 128);
        }

    }

    return utftext;
}

// public method for url decoding
function utf8_decode(utftext) {
    var string = "";
    var i = 0;
    var c = c1 = c2 = 0;

    while ( i < utftext.length ) {

        c = utftext.charCodeAt(i);

        if (c < 128) {
            string += String.fromCharCode(c);
            i++;
        }
        else if((c > 191) && (c < 224)) {
            c2 = utftext.charCodeAt(i+1);
            string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
            i += 2;
        }
        else {
            c2 = utftext.charCodeAt(i+1);
            c3 = utftext.charCodeAt(i+2);
            string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
            i += 3;
        }

    }

    return string;
}

function trim(str) {
    return str.replace(/^\s*([\S\s]*?)\s*$/, '$1');
}


// Simulates PHP's date function
// http://jacwright.com/projects/javascript/date_format
Date.prototype.format = function(format) {
	var returnStr = '';
	var replace = Date.replaceChars;
	for (var i = 0; i < format.length; i++) {
		var curChar = format.charAt(i);
		if (replace[curChar])
			returnStr += replace[curChar].call(this);
		else
			returnStr += curChar;
	}
	return returnStr;
};
Date.replaceChars = {
	shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
	longMonths: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
	shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
	longDays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
	
	// Day
	d: function() { return (this.getDate() < 10 ? '0' : '') + this.getDate(); },
	D: function() { return Date.replace.shortDays[this.getDay()]; },
	j: function() { return this.getDate(); },
	l: function() { return Date.replace.longDays[this.getDay()]; },
	N: function() { return this.getDay() + 1; },
	S: function() { return (this.getDate() % 10 == 1 && this.getDate() != 11 ? 'st' : (this.getDate() % 10 == 2 && this.getDate() != 12 ? 'nd' : (this.getDate() % 10 == 3 && this.getDate() != 13 ? 'rd' : 'th'))); },
	w: function() { return this.getDay(); },
	z: function() { return "Not Yet Supported"; },
	// Week
	W: function() { return "Not Yet Supported"; },
	// Month
	F: function() { return Date.replace.longMonths[this.getMonth()]; },
	m: function() { return (this.getMonth() < 9 ? '0' : '') + (this.getMonth() + 1); },
	M: function() { return Date.replace.shortMonths[this.getMonth()]; },
	n: function() { return this.getMonth() + 1; },
	t: function() { return "Not Yet Supported"; },
	// Year
	L: function() { return "Not Yet Supported"; },
	o: function() { return "Not Supported"; },
	Y: function() { return this.getFullYear(); },
	y: function() { return ('' + this.getFullYear()).substr(2); },
	// Time
	a: function() { return this.getHours() < 12 ? 'am' : 'pm'; },
	A: function() { return this.getHours() < 12 ? 'AM' : 'PM'; },
	B: function() { return "Not Yet Supported"; },
	g: function() { return this.getHours() == 0 ? 12 : (this.getHours() > 12 ? this.getHours() - 12 : this.getHours()); },
	G: function() { return this.getHours(); },
	h: function() { return (this.getHours() < 10 || (12 < this.getHours() < 22) ? '0' : '') + (this.getHours() < 10 ? this.getHours() + 1 : this.getHours() - 12); },
	H: function() { return (this.getHours() < 10 ? '0' : '') + this.getHours(); },
	i: function() { return (this.getMinutes() < 10 ? '0' : '') + this.getMinutes(); },
	s: function() { return (this.getSeconds() < 10 ? '0' : '') + this.getSeconds(); },
	// Timezone
	e: function() { return "Not Yet Supported"; },
	I: function() { return "Not Supported"; },
	O: function() { return (this.getTimezoneOffset() < 0 ? '-' : '+') + (this.getTimezoneOffset() / 60 < 10 ? '0' : '') + (this.getTimezoneOffset() / 60) + '00'; },
	T: function() { return "Not Yet Supported"; },
	Z: function() { return this.getTimezoneOffset() * 60; },
	// Full Date/Time
	c: function() { return "Not Yet Supported"; },
	r: function() { return this.toString(); },
	U: function() { return this.getTime() / 1000; }
}

function htmlEntities(str)
{
    var div = document.createElement('div');
    var text = document.createTextNode(str);
    div.appendChild(text);
    return div.innerHTML;
}; 

function html_entity_decode(str)
{
    var div = document.createElement('div');
    div.innerHTML = str;
    return div.childNodes[0].data;
}; 

 // Join array elements with a string
function implode(glue, pieces)
{
    return ((pieces instanceof Array) ? pieces.join(glue) : pieces);
};

 // Alias of implode()
function join(glue, pieces)
{
    return implode(glue,pieces);
};

// Alias of parse_url()
function parse_url (str, component) {
    // Parse a URL and return its components
    // 
    // +    discuss at: http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_parse_url/
    // +       version: 901.2514
    // +      original by: Steven Levithan (http://blog.stevenlevithan.com)
    // + reimplemented by: Brett Zamir
    // %          note: Based on http://stevenlevithan.com/demo/parseuri/js/assets/parseuri.js
    // %          note: blog post at http://blog.stevenlevithan.com/archives/parseuri
    // %          note: demo at http://stevenlevithan.com/demo/parseuri/js/assets/parseuri.js
    // %          note: Does not replace invaild characters with '_' as in PHP, nor does it return false with
    // %          note: a seriously malformed URL.
    // %          note: Besides function name, is the same as parseUri besides the commented out portion
    // %          note: and the additional section following, as well as our allowing an extra slash after
    // %          note: the scheme/protocol (to allow file:/// as in PHP)
    // *     example 1: parse_url('http://username:password@hostname/path?arg=value#anchor');
    // *     returns 1: {scheme: 'http', host: 'hostname', user: 'username', pass: 'password', path: '/path', query: 'arg=value', fragment: 'anchor'}

    var  o   = {
        strictMode: false,
        key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
        q:   {
            name:   "queryKey",
            parser: /(?:^|&)([^&=]*)=?([^&]*)/g
        },
        parser: {
            strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
            loose:  /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/\/?)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ // Added one optional slash to post-protocol to catch file:/// (should restrict this)
        }
    };
    
    var m   = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
    uri = {},
    i   = 14;
    while (i--) uri[o.key[i]] = m[i] || "";
    // Uncomment the following to use the original more detailed (non-PHP) script
    /*
        uri[o.q.name] = {};
        uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
        if ($1) uri[o.q.name][$1] = $2;
        });
        return uri;
    */

    switch (component) {
        case 'PHP_URL_SCHEME':
            return uri.protocol;
        case 'PHP_URL_HOST':
            return uri.host;
        case 'PHP_URL_PORT':
            return uri.port;
        case 'PHP_URL_USER':
            return uri.user;
        case 'PHP_URL_PASS':
            return uri.password;
        case 'PHP_URL_PATH':
            return uri.path;
        case 'PHP_URL_QUERY':
            return uri.query;
        case 'PHP_URL_FRAGMENT':
            return uri.anchor;
        default:
            var retArr = {};
            if (uri.protocol !== '') retArr.scheme=uri.protocol;
            if (uri.host !== '') retArr.host=uri.host;
            if (uri.port !== '') retArr.port=uri.port;
            if (uri.user !== '') retArr.user=uri.user;
            if (uri.password !== '') retArr.pass=uri.password;
            if (uri.path !== '') retArr.path=uri.path;
            if (uri.query !== '') retArr.query=uri.query;
            if (uri.anchor !== '') retArr.fragment=uri.anchor;
            return retArr;
    }
}

// count occurrences of needle in haystack
function countOccurrences(haystack, needle)
{
    var occ = 0;
    for (var i=0; i < haystack.length; i++)
    {
        if (haystack.charAt(i) == needle)
        {
             occ++;
        }
    }
    return occ;
}

function strip_tags(str, allowed_tags) {
    // Strip HTML and PHP tags from a string
    // 
    // +    discuss at: http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_strip_tags/
    // +       version: 811.1812
    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: Luke Godfrey
    // +      input by: Pul
    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   bugfixed by: Onno Marsman
    // +      input by: Alex
    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +      input by: Marc Palau
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // *     example 1: strip_tags('<p>Kevin</p> <br /><b>van</b> <i>Zonneveld</i>', '<i><b>');
    // *     returns 1: 'Kevin <b>van</b> <i>Zonneveld</i>'
    // *     example 2: strip_tags('<p>Kevin <img src="someimage.png" onmouseover="someFunction()">van <i>Zonneveld</i></p>', '<p>');
    // *     returns 2: '<p>Kevin van Zonneveld</p>'
    // *     example 3: strip_tags("<a href='http://kevin.vanzonneveld.net'>Kevin van Zonneveld</a>", "<a>");
    // *     returns 3: '<a href='http://kevin.vanzonneveld.net'>Kevin van Zonneveld</a>'

    var key = '', tag = '', allowed = false;
    var matches = allowed_array = [];

    var replacer = function(search, replace, str) {
        return str.split(search).join(replace);
    };

    // Build allowes tags associative array
    if (allowed_tags) {
        allowed_array = allowed_tags.match(/([a-zA-Z]+)/gi);
    }
    
    str += '';

    // Match tags
    matches = str.match(/(<\/?[^>]+>)/gi);

    // Go through all HTML tags
    key = 0;
    if(matches == null) {
        matches = new Array();
    }
    for (key = 0; key< matches.length; key++) {
        if (isNaN(key)) {
            // IE7 Hack
            continue;
        }

        // Save HTML tag
        html = matches[key].toString();

        // Is tag not in allowed list? Remove from str!
        allowed = false;

        // Go through all allowed tags
        var k = 0;
        for (k=0; k < allowed_array.length;k++) {
            // Init
            allowed_tag = allowed_array[k];
            i = -1;

            if (i != 0) { i = html.toLowerCase().indexOf('<'+allowed_tag+'>');}
            if (i != 0) { i = html.toLowerCase().indexOf('<'+allowed_tag+' ');}
            if (i != 0) { i = html.toLowerCase().indexOf('</'+allowed_tag)   ;}

            // Determine
            if (i == 0) {
                allowed = true;
                break;
            }
        }

        if (!allowed) {
            str = replacer(html, "", str); // Custom replace. No regexing
        }
    }

    return str;
}


// normalize an URL
function canonicalize_url(url) {

    url = (url+'').replace(/\0/g, '0').replace(/\\([\\'"])/g, '$1');  // stripslashes
    url = url.replace(' ', '%20');  // spaces in URLs should get rawurlencoded
    var parsed_url = parse_url(url);
    
    if(parsed_url.scheme !== '') {
        url = 'http://'+ url;
        parsed_url = parse_url(url);  // reset
    }
    if(parsed_url.scheme != 'http' && parsed_url.scheme != 'https') {
        return NULL;
    }   
    var host = parsed_url.host.toLowerCase();  // path (and therefore URL) is case sensitive. host and domain no
 
    if(host != '') {
        url = url.replace(parsed_url.host, host);  // replace only first instance ??
    }   
    var url_before_extra = url.replace('/(\?|#).*/','');
    if(countOccurrences(url_before_extra, '/') < 3) {
        url = url.replace('/([^?#]+)/','\\1/');
    }
    url = url.replace('&amp;','&');  
    return url;
}
/** Source: http://www.quirksmode.org/js/findpos.html - is better than my own**/
findPosition = function(obj) {
    var curleft = curtop = 0;
    if (obj.offsetParent) {
        curleft = obj.offsetLeft
            curtop = obj.offsetTop
            while (obj = obj.offsetParent) {
                curleft += obj.offsetLeft
                    curtop += obj.offsetTop
            }
    } 
    return [curleft,curtop];
}; 


