/**
 * Cookie plugin
 *
 * Copyright (c) 2006 Klaus Hartl (stilbuero.de)
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 */

/**
 * Create a cookie with the given name and value and other optional parameters.
 *
 * @example $.cookie('the_cookie', 'the_value');
 * @desc Set the value of a cookie.
 * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
 * @desc Create a cookie with all available options.
 * @example $.cookie('the_cookie', 'the_value');
 * @desc Create a session cookie.
 * @example $.cookie('the_cookie', null);
 * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
 *       used when the cookie was set.
 *
 * @param String name The name of the cookie.
 * @param String value The value of the cookie.
 * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
 * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
 *                             If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
 *                             If set to null or omitted, the cookie will be a session cookie and will not be retained
 *                             when the the browser exits.
 * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
 * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
 * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
 *                        require a secure protocol (like HTTPS).
 * @type undefined
 *
 * @name $.cookie
 * @cat Plugins/Cookie
 * @author Klaus Hartl/klaus.hartl@stilbuero.de
 */

/**
 * Get the value of a cookie with the given name.
 *
 * @example $.cookie('the_cookie');
 * @desc Get the value of a cookie.
 *
 * @param String name The name of the cookie.
 * @return The value of the cookie.
 * @type String
 *
 * @name $.cookie
 * @cat Plugins/Cookie
 * @author Klaus Hartl/klaus.hartl@stilbuero.de
 */
jQuery.cookie = function(name, value, options) {
    if (typeof value != 'undefined') { // name and value given, set cookie
        options = options || {};
        if (value === null) {
            value = '';
            options.expires = -1;
        }
        var expires = '';
        if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
            var date;
            if (typeof options.expires == 'number') {
                date = new Date();
                date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
            } else {
                date = options.expires;
            }
            expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
        }
        // CAUTION: Needed to parenthesize options.path and options.domain
        // in the following expressions, otherwise they evaluate to undefined
        // in the packed version for some reason...
        var path = options.path ? '; path=' + (options.path) : '';
        var domain = options.domain ? '; domain=' + (options.domain) : '';
        var secure = options.secure ? '; secure' : '';
        document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
    } else { // only name given, get cookie
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
};

/**
 *  jQuery Plugin highlightFade (jquery.offput.ca/highlightFade)
 *  (c) 2006 Blair Mitchelmore (offput.ca) blair@offput.ca
 */
/**
 * This is version 0.7 of my highlightFade plugin. It follows the yellow fade technique of Web 2.0 fame
 * but expands it to allow any starting colour and allows you to specify the end colour as well.
 *
 * For the moment, I'm done with this plug-in. Unless I come upon a really cool feature it should have
 * this plug-in will only receive updates to ensure future compatibility with jQuery.
 *
 * As of now (Aug. 16, 2006) the plugin has been written with the 1.0.1 release of jQuery (rev 249) which
 * is available from http://jquery.com/src/jquery-1.0.1.js
 *
 * A note regarding rgb() syntax: I noticed that most browsers implement rgb syntax as either an integer 
 * (0-255) or percentage (0-100%) value for each field, that is, rgb(i/p,i/p,i/p); however, the W3C 
 * standard clearly defines it as "either three integer values or three percentage values" [http://www.w3.org/TR/CSS21/syndata.html] 
 * which I choose to follow despite the error redundancy of the typical behaviour browsers employ.
 *
 * Changelog:
 *
 *    0.7:
 *        - Added the awesome custom attribute support written by George Adamson (slightly modified)
 *        - Removed bgColor plugin dependency seeing as attr is customizable now...
 *    0.6:
 *        - Abstracted getBGColor into its own plugin with optional test and data retrieval functions
 *        - Converted all $ references to jQuery references as John's code seems to be shifting away
 *          from that and I don't want to have to update this for a long time.
 *    0.5:
 *        - Added simple argument syntax for only specifying start colour of event
 *        - Removed old style argument syntax
 *        - Added 'interval', 'final, and 'end' properties
 *        - Renamed 'color' property to 'start'
 *        - Added second argument to $.highlightFade.getBGColor to bypass the e.highlighting check
 *    0.4:
 *        - Added rgb(%,%,%) color syntax
 *    0.3:
 *        - Fixed bug when event was called while parent was also running event corrupting the
 *          the background colour of the child
 *    0.2:
 *        - Fixed bug where an unspecified onComplete function made the page throw continuous errors
 *        - Fixed bug where multiple events on the same element would speed each subsequent event
 *    0.1:
 *        - Initial Release
 * 
 * @author          Blair Mitchelmore (blair@offput.ca)
 * @version         0.5
 */
jQuery.fn.highlightFade = function(settings) {
	var o = (settings && settings.constructor == String) ? {start: settings} : settings || {};
	var d = jQuery.highlightFade.defaults;
	var i = o['interval'] || d['interval'];
	var a = o['attr'] || d['attr'];
	var ts = {
		'linear': function(s,e,t,c) { return parseInt(s+(c/t)*(e-s)); },
		'sinusoidal': function(s,e,t,c) { return parseInt(s+Math.sin(((c/t)*90)*(Math.PI/180))*(e-s)); },
		'exponential': function(s,e,t,c) { return parseInt(s+(Math.pow(c/t,2))*(e-s)); }
	};
	var t = (o['iterator'] && o['iterator'].constructor == Function) ? o['iterator'] : ts[o['iterator']] || ts[d['iterator']] || ts['linear'];
	if (d['iterator'] && d['iterator'].constructor == Function) t = d['iterator'];
	return this.each(function() {
		if (!this.highlighting) this.highlighting = {};
		var e = (this.highlighting[a]) ? this.highlighting[a].end : jQuery.highlightFade.getBaseValue(this,a) || [255,255,255];
		var c = jQuery.highlightFade.getRGB(o['start'] || o['colour'] || o['color'] || d['start'] || [255,255,128]);
		var s = jQuery.speed(o['speed'] || d['speed']);
		var r = o['final'] || (this.highlighting[a] && this.highlighting[a].orig) ? this.highlighting[a].orig : jQuery.curCSS(this,a);
		if (o['end'] || d['end']) r = jQuery.highlightFade.asRGBString(e = jQuery.highlightFade.getRGB(o['end'] || d['end']));
		if (typeof o['final'] != 'undefined') r = o['final'];
		if (this.highlighting[a] && this.highlighting[a].timer) window.clearInterval(this.highlighting[a].timer);
		this.highlighting[a] = { steps: ((s.duration) / i), interval: i, currentStep: 0, start: c, end: e, orig: r, attr: a };
		jQuery.highlightFade(this,a,o['complete'],t);
	});
};

jQuery.highlightFade = function(e,a,o,t) {
	e.highlighting[a].timer = window.setInterval(function() { 
		var newR = t(e.highlighting[a].start[0],e.highlighting[a].end[0],e.highlighting[a].steps,e.highlighting[a].currentStep);
		var newG = t(e.highlighting[a].start[1],e.highlighting[a].end[1],e.highlighting[a].steps,e.highlighting[a].currentStep);
		var newB = t(e.highlighting[a].start[2],e.highlighting[a].end[2],e.highlighting[a].steps,e.highlighting[a].currentStep);
		jQuery(e).css(a,jQuery.highlightFade.asRGBString([newR,newG,newB]));
		if (e.highlighting[a].currentStep++ >= e.highlighting[a].steps) {
			jQuery(e).css(a,e.highlighting[a].orig || '');
			window.clearInterval(e.highlighting[a].timer);
			e.highlighting[a] = null;
			if (o && o.constructor == Function) o.call(e);
		}
	},e.highlighting[a].interval);
};

jQuery.highlightFade.defaults = {
	start: [255,255,128],
	interval: 50,
	speed: 400,
	attr: 'backgroundColor'
};

jQuery.highlightFade.getRGB = function(c,d) {
	var result;
	if (c && c.constructor == Array && c.length == 3) return c;
	if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))
		return [parseInt(result[1]),parseInt(result[2]),parseInt(result[3])];
	else if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))
		return [parseFloat(result[1])*2.55,parseFloat(result[2])*2.55,parseFloat(result[3])*2.55];
	else if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))
		return [parseInt("0x" + result[1]),parseInt("0x" + result[2]),parseInt("0x" + result[3])];
	else if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))
		return [parseInt("0x"+ result[1] + result[1]),parseInt("0x" + result[2] + result[2]),parseInt("0x" + result[3] + result[3])];
	else
		return jQuery.highlightFade.checkColorName(c) || d || null;
};

jQuery.highlightFade.asRGBString = function(a) {
	return "rgb(" + a.join(",") + ")";
};

jQuery.highlightFade.getBaseValue = function(e,a,b) {
	var s, t;
	b = b || false;
	t = a = a || jQuery.highlightFade.defaults['attr'];
	do {
		s = jQuery(e).css(t || 'backgroundColor');
		if ((s  != '' && s != 'transparent') || (e.tagName.toLowerCase() == "body") || (!b && e.highlighting && e.highlighting[a] && e.highlighting[a].end)) break; 
		t = false;
	} while (e = e.parentNode);
	if (!b && e.highlighting && e.highlighting[a] && e.highlighting[a].end) s = e.highlighting[a].end;
	if (s == undefined || s == '' || s == 'transparent') s = [255,255,255];
	return jQuery.highlightFade.getRGB(s);
};

jQuery.highlightFade.checkColorName = function(c) {
	if (!c) return null;
	switch(c.replace(/^\s*|\s*$/g,'').toLowerCase()) {
		case 'aqua': return [0,255,255];
		case 'black': return [0,0,0];
		case 'blue': return [0,0,255];
		case 'fuchsia': return [255,0,255];
		case 'gray': return [128,128,128];
		case 'green': return [0,128,0];
		case 'lime': return [0,255,0];
		case 'maroon': return [128,0,0];
		case 'navy': return [0,0,128];
		case 'olive': return [128,128,0];
		case 'purple': return [128,0,128];
		case 'red': return [255,0,0];
		case 'silver': return [192,192,192];
		case 'teal': return [0,128,128];
		case 'white': return [255,255,255];
		case 'yellow': return [255,255,0];
	}
};

/*	http://plugins.jquery.com/project/Watermark
*/
(function($){$.extend($,{clearwatermarks:function(){$("[wmwrap='true']").find("input,textarea").watermark({remove:true})},addwatermarks:function(){$("[watermark]").each(function(num,el){$(el).watermark($(el).attr("watermark"))})},watermark:function(o){o.el=$(o.el);if(o.remove){if(o.el.parent().attr("wmwrap")=="true"){o.el.parent().replaceWith(o.el)}}else{if(o.el.parent().attr("wmwrap")!="true"){o.el=o.el.wrap("<span class='watermark-wrapper' wmwrap='true' style='position:relative;'/>");var l=$("<label/>");if(o.html){l.html(o.html)}if(o.cls){l.addClass(o.cls)}if(o.css){l.css(o.css)}l.css({position:"absolute",left:"3px",top:parseInt(o.el.css("paddingTop")),display:"inline",cursor:"text"});if(o.el.is("TEXTAREA")){if($.browser.msie){l.css("width",o.el.width())}if($.browser.mozilla||$.browser.safari){l.css("top","")}}if(!o.cls&&!o.css){l.css("color","#ccc")}var focus=function(){l.hide()};var blur=function(){if(!o.el.val()){l.show()}else{l.hide()}};var click=function(){o.el.focus()};if(o.inherit){if(typeof o.inherit=="string"){l.css(o.inherit,o.el.css(o.inherit))}else{for(var x=0;x<o.inherit.length;x++){l.css(o.inherit[x],o.el.css(o.inherit[x]))}}}o.el.focus(focus).blur(blur);l.click(click);o.el.before(l);if(o.el.val()){l.hide()}}}return o.el}});$.fn.watermark=function(o){return this.each(function(){if(typeof (o)=="string"){try{o=eval("("+o+")")}catch(ex){o={html:o}}}o.el=this;return $.watermark(o)})}})(jQuery);$().ready(function(){$.addwatermarks()});

/* vldtr */

function htmlEncode(text) {
	return $('<div/>').text(text).html();
}


// partially from: http://www.ajaxify.com/run/favicon/scroll/favicon.js
var favicon = {
	"change":function(url) {
		try {
			if (url) {
				this.addLink(url, true);
			}
		} catch (ex) { }
	},
	"addLink":function(url) {
		var link = document.createElement("link");
		link.type = "image/x-icon";
		link.rel = "shortcut icon";
		link.href = url;
		this.removeLinkIfExists();
		document.getElementsByTagName("head")[0].appendChild(link);
	},
	"removeLinkIfExists":function() {
		var links = document.getElementsByTagName("head")[0].getElementsByTagName("link");
		for (var i=0; i<links.length; i++) {
			var link = links[i];
			if (link.type=="image/x-icon" && link.rel=="shortcut icon") {
				document.getElementsByTagName("head")[0].removeChild(link);
				return; // Assuming only one match at most.
			}
		}
	},
	"set":function(status) {
		try {
			// set favicon
			if (-1 === status) {
				favicon.change("http://vldtr.com/_style/error_16.png");
			} else if (0 === status) {
				favicon.change("http://vldtr.com/_style/invalid_16.png");
			} else {
				favicon.change("http://vldtr.com/_style/valid_16.png");
			}
		} catch (ex) { }
	},
	"reset":function() {
		favicon.change("http://vldtr.com/_style/logo_16.png");
	}
};

var dbgr = {
	"writeln":function(text) {
		try {
			$("#debug").html($("#debug").html() + text + "<br />");
		} catch (ex) { }
	},
	"clear":function() {
		try {
			$("#debug").html("");
		} catch (ex) { }
	}
};

var url_cookie = {
	"read":function() {
		try {
			var ck = $.cookie("urls");
			if (null !== ck) {
				var urls = ck.split("|||");
				for (j = 0; j < urls.length; j++) {
					if ("null" !== urls[j] && "" !== urls[j]) {
						try {
							var values = urls[j].split("||");
							url_list.addWithScore(values[0], values[2], values[1]);
						} catch (ex) { }
					}
				}
			}
		} catch(ex) { }
	},
	"update":function() {
		var urls = url_list.toCookieString();
		$.cookie("urls", urls, { expires: 99999 });
	},
	"clear":function() {
		$.cookie("urls", "", { expires: 99999 });
	}
};

var set_cookie = {
	"add":function(key) {
		var sets = $.cookie("sets");
		if (null !== sets && "" !== sets) { sets += ";"; } else { sets = ""; }
		$.cookie("sets", sets + key, { expires: 99999 });
	},
	"remove":function(key) {
		var sets = $.cookie("sets");
		if (null !== sets) { 
			sets = sets.replace(key, "");
			$.cookie("sets", sets, { expires: 99999 });
		}
	}
};

var url_list = {
	"init":function() {
		if (0 === $("#.pages li span").size()) { 
			url_cookie.read();
		} else {
			if (0 < $(".pages li span:not(.errorcount):not(.error):not(.valid):not(.invalid):not(.error):not(.options)").size()) {
				favicon.reset();
			} else {
				if (0 < $(".pages li span.error").size()) {
					favicon.set(-1);
				} else if (0 < $(".pages li span.invalid").size()) {
					favicon.set(0);
				} else {
					favicon.set(1);
				}
			}
			validateUnvalidated();

		}

	},
	"add":function(url, suppressHighlight, silent) {
		if ("http" !== url.substring(0, 4)) { url = "http://" + url; }
		var $existing = url_list.getItemByUrl(url);
		if (!$existing) {
			dbgr.writeln("add " + url);
			$(".pages").append('<li data-url="' + url + '"><span class="options"> <img class="button detailButton" alt="details" src="_style/details.png"/> <a class="view-source" href="view-source:' + htmlEncode(url) + '"><img alt="unknown" src="_style/unknown.png"/></a> <img class="button deleteButton" alt="delete" src="_style/delete.png"/></span> <span class="errorcount" title="0 errors">0</span><span class="">' + htmlEncode(url) + '</span><div class="details"></div></li>');
			if (undefined === silent|| false === silent)  { url_list.onchange(); }
		} else {
			if (undefined === suppressHighlight || false === suppressHighlight) { $existing.parent().highlightFade({color:'#fffc7c',speed:1500,iterator:'exponential',complete: function() {$existing.parent().removeAttr("style");}}); }
		}
		return url_list.getItemByUrl(url);
	},
	"addWithScore":function(url, type, score) {
		$item = url_list.add(url, false, true);

		var valid = 1;
		if (0 < score) valid = 0;
		if (0 > score) valid = -1;

		if (score && "" != score) url_list.setValues($item, valid, "", type, Math.abs(score));

		url_list.onchange();
	
	},
	"setValues":function(item, valid, validatorUrl, type, errorcount, errors) {
		var options1 = "";
		var options2 = "";
		var options3 = "";
		var details = "";

		var clss = "";

		// normalise item
		var item = $(item)[0];

		if (1 === valid) {
			clss = "valid";
		} else if (0 === valid) {
			clss = "invalid";

			if (errors) {
				var errorlist = "<ol class='items'>";
				for (i = 0; i < errors.length; i++) {
					errorlist += "<li class='detail message-" + errors[i].id + "'><h4>" + (i+1) + "/" + errors.length + ". " + errors[i].message + "</h4><div class='source'>" + errors[i].source + "</div><div class='location'>on line " + errors[i].line + ", col " + errors[i].col + " - <a class='view-results' href='" + validatorUrl + "'>go to w3c results page</a> <input type='button' class='revalidateButton' value='revalidate' /></div></li>";
				}
				errorlist += "</ol>";
				details = "<div class='scrollable'><img class='prev' src='_style/blank.png' alt='&nbsp;' />" + errorlist + "<img class='next' src='_style/blank.png' alt='&nbsp;' /></div>"
			} else {
				details = "<p class='no-info'><input type='button' class='revalidateButton' value='Revalidate' /> to get detailed error information, or click [Revalidate All] below.</p><div class='w3c'><a class='view-results' href='http://validator.w3.org/check?uri=" + htmlEncode($(item).html()) + "'>go to w3c results page</a></div>";
			}
		} else {
			clss = "error";

			if (errors) {
				var errorlist = "<ol class='items'>";
				errorlist += "<li class='detail message-" + errors[0].id + "'><h4>" + errors[0].message + "</h4><div class='error'>" + errors[0].explanation + "</div><div class='location'><a class='view-results' href='" + validatorUrl + "'>go to w3c results page</a> <input type='button' class='revalidateButton' value='revalidate' /></div></li>";
				errorlist += "</ol>";
				details = "<div class='scrollable'><img class='prev' src='_style/blank.png' alt='&nbsp;' />" + errorlist + "<img class='next' src='_style/blank.png' alt='&nbsp;' /></div>"
			} else {
				details = "<div class='scrollable'><ol class='items'><li><h4>Error</h4><div>Sorry, something went wrong.</div><div>If the url is valid then you can help by sending the url that caused the error to vldtr@kruisit.nl</div><div class='location'><a class='view-results' href='" + validatorUrl + "'>go to w3c results page</a> <input type='button' class='revalidateButton' value='revalidate' /></div></li></ol></div>";
			}

		}

		item.className = clss;
		item.parentNode.className = clss;
		var $node = $(item).parent();
		$node.find(".errorcount").html(errorcount);
		$node.find(".options a img").attr("src", "_style/" + type + ".png").attr("alt", type);

		setDetailText($(item).parent().find(".details"), details);
		url_list.onvalidated();
	},
	"validate":function() {

	},
	"clear":function() {
		$(".pages").html("");
		$("body").removeAttr("class");
		url_list.onchange();
		url_cookie.clear();
		dbgr.clear();
	},
	"getItemByUrl":function(url) {
		var existing = false;
		$(".pages li span:not(.options):not(.errorcount)").each(function(event) {
			if (url == $(this).html()) { existing = $(this); }
		});
		return existing;
	},
	"items":function() {
		return $(".pages li span:not(.errorcount):not(.options)");
	},
	"reset":function($item) {

		if ($item) {
			$item.find(".details").each(function() {
				setDetailText($(this), "<p class='no-info'>validating ...</p>");
			});
			$item.find("span.working").removeClass("working");
			$item.find("span.error").removeClass("error");
			$item.find("span.valid").removeClass("valid");
			$item.find("span.invalid").removeClass("invalid");
			$item.removeClass("working");
			$item.removeClass("error");
			$item.removeClass("valid");
			$item.removeClass("invalid");
		} else {
			$("body").removeAttr("class");
			$(".pages li .details").each(function() {
				setDetailText($(this), "<p class='no-info'>validating ...</p>");
			});
			$(".pages li, .pages li span.working").removeClass("working");
			$(".pages li, .pages li span.error").removeClass("error");
			$(".pages li, .pages li span.valid").removeClass("valid");
			$(".pages li, .pages li span.invalid").removeClass("invalid");
			$(".pages li, .pages li.working").removeClass("working");
			$(".pages li, .pages li.error").removeClass("error");
			$(".pages li, .pages li.valid").removeClass("valid");
			$(".pages li, .pages li.invalid").removeClass("invalid");
			favicon.reset();
		}
	},
	"remove":function($item) {
		$item.remove();
		url_list.onchange();
	},
	"toString":function() {
		var urls = "";
		$(".pages > li > span:not(.options):not(.errorcount)").each(function(event) {
			if ("" !== urls) { urls += ";;"; }
			urls += $(this).parent().attr("data-url");
		});
		return urls;
	},
	"toCookieString":function() {
		var urls = "";
		$(".pages > li > span:not(.options):not(.errorcount)").each(function(event) {
			if ("" !== urls) { urls += "|||"; }

			var errorcount = $(this).parent().find(".errorcount").html();
			if ($(this).parent().hasClass("error")) errorcount = -1;

			urls += $(this).parent().attr("data-url") + "||" + errorcount + "||" + $(this).parent().find(".options a img").attr("alt");
		});
		return urls;
	},
	"errors":function() {
		var errs = "";
		$(".pages li span:not(.options):not(.errorcount)").each(function(event) {
			if ("" !== errs) { errs += ";;"; }
			if (0 < $(this).parent().find(".errorcount").size()) {
				errs += $(this).parent().find(".errorcount").html();
			} else if ($(this).hasClass("error")) {
				errs += "-1";
			} else {
				errs += "0";
			}
		});
		return errs;
	},
	"types":function() {
		var tps = "";
		$(".pages li span:not(.options):not(.errorcount)").each(function(event) {
			if ("" !== tps) { tps += ";;"; }
			tps += $(this).parent().find(".options .view-source img").attr("alt");
		});
		return tps;
	},
	"setStatus":function() {
		var status = "0";
		if (0 < $(".pages li span.invalid").size()) { status = "1"; }
		if (0 < $(".pages li span.error").size()) { status = "-1"; }
		if (0 < $(".pages li span.working").size()) { status = "null"; }
		return status;
	},
	"setTotals":function() {
		var errorCount = 0;
		$(".pages li span.errorcount").each(function(event) {
			errorCount += parseInt(this.innerHTML, 10);
		});
		var totalCount = $(".pages > li:not(.detail)").size();
		var invalidCount = $(".pages li span.invalid, .pages li span.error").size();
		if (0 < totalCount) {
			if (0 === invalidCount) {
				if (0 === $(".pages li span:not(.errorcount):not(.error):not(.valid):not(.invalid):not(.error):not(.options)").size()) {
					$(".total").html("Nice! No errors.");
				}
			} else {
				$(".total").html(Math.abs(errorCount) + " errors" + ", " + invalidCount + " invalid items out of " + totalCount);
			}
		} else {
			$(".total").html("no items ...");
		}

		if (0 < $(".pages li span.valid, .pages li span.invalid, .pages li span.error").size()) {
			var score = Math.round(10 * (1 - (invalidCount / totalCount)));
			dbgr.writeln("score " + score);
			$("body").addClass("score" + score);

			for (i = 0; i <= 10; i++) {
				if (score != i) { $("body").removeClass("score" + i); }
			}
		}
	},
	"onchange":function() {
		if (0 < $(".pages li span:not(.errorcount):not(.error):not(.valid):not(.invalid):not(.error):not(.options)").size()) {
			favicon.reset();
		} else {
			if (0 < $(".pages li span.error").size()) {
				favicon.set(-1);
			} else if (0 < $(".pages li span.invalid").size()) {
				favicon.set(0);
			} else {
				favicon.set(1);
			}
		}
		$("#savedUrl").addClass("help");
		$("#savedUrl").html("[click \"Save as New List\" to store your list and get a unique url]");
		url_cookie.update();
		validateUnvalidated();
	},
	"onvalidated":function() {
		dbgr.writeln("onvalidated");

		url_list.setTotals();

		bindItemEvents();

		// done validating ?
		if (0 === $(".pages li span:not(.errorcount):not(.error):not(.valid):not(.invalid):not(.options)").size()) {
			if ("0" === $(".total").html().substr(0, 1)) {
				var invalidCount = $(".pages li span.invalid, .pages li span.error").size();
				if (0 === invalidCount) {
					$(".total").html("Nice! No errors.");
				} else {
					$(".total").html("No errors, but something isn't right.");
				}
			}

			// has a key ? 
			if (0 < $("#savedUrl a").size()) {

				var status = "0";
				if (0 < $(".pages li span.invalid").size()) { status = "1"; }
				if (0 < $(".pages li span.error").size()) { status = "-1"; }
				var key = $("#savedUrl a").html().substr($("#savedUrl a").html().indexOf("=") + 1);
				var params = "key=" + escape(key) + "&status=" + status;

				$.ajax({
					type: "GET",
					data: params,
					url: "listvalidated.php",
					dataType: "json"
				});
			}

			if (0 < $(".pages li span.error").size()) {
				favicon.set(-1);
			} else if (0 < $(".pages li span.invalid").size()) {
				favicon.set(0);
			} else {
				favicon.set(1);
			}
			url_cookie.update();
		}
	},
	"ondeleteclick":function() {
//todo?
	}
};

var set_list = {
	"init":function() {
		$(".sets .deleteButton").unbind("click").bind("click", deleteButton_click);
	},
	"rename":function($link, newkey) {
		$link.html(newkey);
		$link.attr("href", $link.attr("href").substr(0, $link.attr("href").indexOf("=") +1) + newkey);
		$link.parent().parent().highlightFade({color:'#fffc7c',speed:1500,iterator:'exponential',complete: function() {$link.parent().parent().removeAttr("style");}});
	},
	"remove":function($link) {
		$link.parent().parent().slideUp("normal", function() { $(this).remove(); } );
	}
};

var unsaved_list = {
	"init":function() {
		$(".sets_unsaved .saveButton").unbind("click").bind("click", saveUnsavedButton_click);
	},
	"remove":function($link) {
		set_cookie.remove($link.html());
		$link.parent().parent().slideUp("normal", function() { 
			$(this).remove();
			if (0 === $(".sets_unsaved a").size()) { unsaved_list.hide(); }
		});		
	},
	"hide":function() {
		$(".sets_unsaved").parent().parent().slideUp("normal", function() { $(this).remove(); } );
	}
};

var dashboard = {
	"init":function() {
		$(".tabs span").unbind("click").bind("click", function() { 

			$(".tabs span").removeClass("selected");
			$(this).addClass("selected");

			if ($(this).hasClass("tab-all")) {
				$(".sets li").animate( { height:"35" } , 500 ).animate( { opacity:"1"}, 500 );
			} else if ($(this).hasClass("tab-valid")) {
				$(".sets li:has(span.valid)").animate( { height:"35" } , 500 ).animate( { opacity:"1"}, 500 );
				$(".sets li:not(:has(span.valid))").animate( { opacity:"0"}, 500 ).animate( { height:"0" } , 500, function() { $(this).css("display", "none"); } )

			} else if ($(this).hasClass("tab-invalid")) {
				$(".sets li:has(span.invalid)").animate( { height:"35" } , 500 ).animate( { opacity:"1"}, 500 );
				$(".sets li:not(:has(span.invalid))").animate( { opacity:"0"}, 500 ).animate( { height:"0" } , 500, function() { $(this).css("display", "none"); } );
			} else if ($(this).hasClass("tab-error")) {
				$(".sets li:has(span.error)").animate( { height:"35" } , 500 ).animate( { opacity:"1"}, 500 );
				$(".sets li:not(:has(span.error))").animate( { opacity:"0"}, 500 ).animate( { height:"0" } , 500, function() { $(this).css("display", "none"); } );
			}
		});
	},
};


$(function()
{
	url_list.init();
	set_list.init();
	unsaved_list.init();
	dashboard.init();

	$('#addText').watermark({html:'Type or paste your url in here ...',cls:'watermark'});
	$("#addText").unbind("keypress").bind("keypress", addText_keypress);
	$(".expand").unbind("click").bind("click", expand_click);
	$(".contract").unbind("click").bind("click", contract_click);
	$('#addList').watermark({html:'Enter one url per line.',cls:'watermark'});
	$("#addButton").unbind("click").bind("click", addButton_click);
	$("#addListButton").unbind("click").bind("click", addListButton_click);
	$("#clearButton").unbind("click").bind("click", clearButton_click);
	$("#revalidateAllButton").unbind("click").bind("click", revalidateAllButton_click);
	$(".revalidateButton").unbind("click").bind("click", revalidateButton_click);
	$("#saveButton").unbind("click").bind("click", saveButton_click);
	$("#updateButton").unbind("click").bind("click", updateButton_click);
	$("#discoverButton").unbind("click").bind("click", discoverButton_click);
	bindItemEvents();

	$("form input:first").focus();
});

function setDetailText($details, txt) {
	if ($details.is(":visible")) {
		$details.slideUp("normal", function() {
			$details.html(txt);
			$details.slideDown();
			$details.find(".scrollable").scrollable({ size: 1 });
			bindItemEvents();
		});
	} else {
		$details.html(txt);
		$details.find(".scrollable").scrollable();
	}
}

function bindItemEvents() {
	$(".deleteButton").unbind("click").bind("click", deleteButton_click);
	$(".detailButton").unbind("click").bind("click", detailButton_click);
	$(".renameButton").unbind("click").bind("click", renameButton_click);
	$(".revalidateButton").unbind("click").bind("click", revalidateButton_click);
}

function validateUnvalidated() {
	url_list.setTotals();

	$(".pages li span:not(.errorcount):not(.working):not(.error):not(.valid):not(.invalid):not(.options):first").each(function(event) {
		dbgr.writeln("validate " + $(this).parent().attr("data-url"));
		validate(this);
		// play nice with w3
		setTimeout(validateUnvalidated, 1000);
	});
}

function validate(obj) {
	obj.className = "working";
	$(obj).parent().addClass("working");
	var validator = "validate/";
	var params = "url=" + escape($(obj).parent().attr("data-url"));
	$.ajax({
		type: "GET",
		data: params,
		url: validator,
		dataType: "json",
		success: function(response) {
			url_list.setValues(obj, response.valid, response.validator, response.type, response.errors.length, response.errors);
		},
		error: function(XMLHttpRequest, textStatus, errorThrown) {
			url_list.addOptions(obj, -1);
			//alert(textStatus);
		}
	});
}

function addText_keypress(event) {
	if (13 === event.which || 10 === event.which) { addButton_click(event); }
}

function contract_click(event) {
//todo : animate
	$(".new-page").show();
	$(".new-list").hide();
}

function expand_click(event) {
//todo : animate
//	$(".new-list #addList").val($(".new-page #addText").val());
	$(".new-page").hide();
	$(".new-list").show();
}

function addButton_click(event) {
	var url = $("#addText").attr("value");
	if ("" !== url) {
		url_list.add(url);
		$("#addText").attr("value", "");
	} else {
		$("#addText").focus();
		//$("#addText").highlightFade({color:'#fffc7c',speed:1500,iterator:'exponential',complete: function() {$("#addText").removeAttr("style");}});
	}
}

function addListButton_click(event) {
	var urls = $("#addList").val().split("\n");

	for (j = 0; j < urls.length; j++) {
		if ("" !== urls[j]) {
			url_list.add(urls[j]);
		}
	}
	$("#addList").val("");
}

function clearButton_click(event) {
	url_list.clear();
}

function deleteButton_click(event) {
	$list = $(this).parent().parent().parent();

	if ($list.hasClass("pages")) {
		url_list.remove($(this).parent().parent());
	} else if ($list.hasClass("sets")) {
		var $link = $(this).parent().parent().find("a");
		var params = "key=" + escape($link.html());
		$.ajax({
			type: "GET",
			data: params,
			url: "../setdelete.php",
			dataType: "json",
			success: function(response) {
				if ("0" === response.status) {
					set_list.remove($link);
				}
			},
			error: function(XMLHttpRequest, textStatus, errorThrown) {
				alert(textStatus);
			}
		});
	} else if ($list.hasClass("sets_unsaved")) {
		var $link_u = $(this).parent().parent().find("a");
		unsaved_list.remove($link_u);
	}
}

function detailButton_click(event) {
	var $details = $(this).parent().parent().find(".details");
	if ("none" == $details.css("display")) {
		$details.slideDown();
	} else {
		$details.slideUp();
	}
}

function discoverButton_click(event) {
	var url = $("#addText").attr("value");

	if ("" !== url) {
		$("body").addClass("working");
		$("#discoverButton").attr("disabled", true);

		url_list.add(url, false, true);
		discoverList(0);
	} else {
		$("#addText").focus();
		//$("#addText").highlightFade({color:'#fffc7c',speed:1500,iterator:'exponential',complete: function() {$("#addText").removeAttr("style");}});
	}
	validateUnvalidated();
}

function discoverList(index) {
	var items = url_list.items();
	if (index < items.size()) {
		var url = items[index].innerHTML;
		var params = "output=json&follow=0&url=" + escape(url);

		$(items[index]).highlightFade({color:'#dff5f6',speed:1500,iterator:'exponential',complete: function() {$(this).removeAttr("style");}});

		if (0 === $("#dscvrProgress").size()) { $("body").append("<div id='dscvrProgress'><img src='_style/progress.gif' /></div>"); }
		$("#dscvrProgress").css("position", "absolute").css("left", $("#discoverButton")[0].offsetLeft + 3).css("top", $("#discoverButton")[0].offsetTop - 9 + $("#discoverButton")[0].offsetHeight/2);

		index++;
		$.ajax({
			type: "GET",
			data: params,
			url: "dscvr/",
			dataType: "json",
			success: function(response) {
				$("#addText").attr("value", "");

				$.each(response.pages,function(i,item) {
					url_list.add(item, true, true);
				});
				$.each(response.stylesheets,function(i,item) {
					url_list.add(item, true, true);
				});
				$.each(response.feeds,function(i,item) {
					url_list.add(item, true, true);
				});

				if (1 === index) validateUnvalidated();
				discoverList(index);
			},
			error: function(XMLHttpRequest, textStatus, errorThrown) {
				alert(textStatus);
			}
		});
	} else {
		$("body").removeClass("working");
		$("#dscvrProgress").remove();
		$("#discoverButton").removeAttr("disabled");
	}
}

function renameButton_click(event) {
	var $link = $(this).parent().parent().find("a");
	var newkey = prompt("new name", $link.html());

	if (null !== newkey && "" !== newkey) {
		var params = "key=" + escape($link.html()) + "&new=" + escape(newkey);
		$.ajax({
			type: "GET",
			data: params,
			url: "../setrename.php",
			dataType: "json",
			success: function(response) {
				if ("0" === response.status) {
					set_list.rename($link, newkey);
				}
			},
			error: function(XMLHttpRequest, textStatus, errorThrown) {
				alert(textStatus);
			}
		});
	}
}

function revalidateButton_click(event) {
	$item = $(this).parent().parent().parent();
	if (!$item.parent().hasClass("pages")) $item = $item.parent().parent().parent();

	url_list.reset($item);
	validateUnvalidated();
}

function revalidateAllButton_click(event) {
	url_list.reset();
	validateUnvalidated();
}

function saveButton_click(event) {
	var urls = url_list.toString();
	var status = url_list.setStatus();
	var errors = url_list.errors();
	var types = url_list.types();

	var params = "urls=" + escape(urls) + "&status=" + status + "&errors=" + errors + "&types=" + types;
	$.ajax({
		type: "GET",
		data: params,
		url: "save.php",
		dataType: "json",
		success: function(response) {
			var $output = $("#savedUrl");
			if ("" === response.url) {
				$output.html("[error]");
				$output.addClass("error");
			} else {
				var url = response.url;
				$output.html("Come back soon using <a href='" + url + "'>" + url + "</a><br /><a href='login/'>Log in</a> to change the name of your list or get an overview of all your lists.");
			}
			$output.removeClass("help");
			$output.highlightFade({color:'#fffc7c',speed:1500,iterator:'exponential'});

			if (0 === response.user) {
				set_cookie.add(response.key);
			}
		},
		error: function(XMLHttpRequest, textStatus, errorThrown) {
			alert(textStatus);
		}
	});
}

function updateButton_click(event) {
	var urls = url_list.toString();
	var status = url_list.setStatus();
	var key = $(".pages").attr("data-key");
	if ("" == key) {
		alert("Invalid key. List was not updated!");
		return;
	}

	var params = "key=" + key + "&urls=" + escape(urls) + "&status=" + status;
	$.ajax({
		type: "GET",
		data: params,
		url: "update.php",
		dataType: "json",
		success: function(response) {
			var $output = $("#savedUrl");
			if ("" === response.url) {
				$output.html("[error]");
				$output.addClass("error");
			} else {
				var url = response.url;
				$output.html("Come back soon using <a href='" + url + "'>" + url + "</a><br /><a href='login/'>Log in</a> to change the name of your list or get an overview of all your lists.");
			}
			$output.removeClass("help");
			$output.highlightFade({color:'#fffc7c',speed:1500,iterator:'exponential'});

			if (0 === response.user) {
				set_cookie.add(response.key);
			}
		},
		error: function(XMLHttpRequest, textStatus, errorThrown) {
			alert(textStatus);
		}
	});
}

function saveUnsavedButton_click(event) {
	var $link = $(this).parent().parent().find("a");
	var params = "key=" + escape($link.html());
	$.ajax({
		type: "GET",
		data: params,
		url: "../setsave.php",
		dataType: "json",
		success: function(response) {
			if ("0" === response.status) {
				unsaved_list.remove($link);
				document.location.reload();
			}
		},
		error: function(XMLHttpRequest, textStatus, errorThrown) {
			alert(textStatus);
		}
	});
}