// Labels for Ajax javascripts
// $Id: labels.js 41776 2009-04-29 09:10:45Z bas $
//

window.labels								= new Object();

window.labels["main"] 						= new Object();
window.labels["main"]["loading"]			= "loading...";

/* autocomplete */
window.labels["autocomplete"] 				= new Object();
window.labels["autocomplete"]["cancel"]		= "Cancel";

/* annotations */
window.labels["annotations"] 				= new Object();
window.labels["annotations"]["makeactor"]	= "Make a new person";
window.labels["annotations"]["popuptitle"]	= "Tag person";
window.labels["annotations"]["needtologin"]	= "You need to log in";
window.labels["annotations"]["addingactor"]	= "Adding person";
window.labels["annotations"]["actoralreadyadded"]	= "Person is already tagged.";
window.labels["annotations"]["wasaproblem"]	= "There was a problem, please try again";

/* sortable */
window.labels["sortable"]	 				= new Object();

/* lightbox */
window.labels["lightbox"]					= new Object();
window.labels["lightbox"]["cancel"]			= "Cancel";
window.labels["lightbox"]["save"]			= "OK";

/* editatonce */
window.labels["editatonce"] 				= new Object();
window.labels["editatonce"]["saving"]		= "saving ...";

/* editinplace */
window.labels["editinplace"] 				= new Object();
window.labels["editinplace"]["cancel"]		= "Cancel";
window.labels["editinplace"]["save"]		= "OK";
window.labels["editinplace"]["onempty"]		= "Add value";
window.labels["editinplace"]["saving"]		= "saving ...";
window.labels["editinplace"]["fetching"]	= "fetching ...";
window.labels["editinplace"]["validate"]			= new Object();
window.labels["editinplace"]["validate"]["msg"]		= "Please enter valid information";
window.labels["editinplace"]["validate"]["out"]		= "Value out of range";
window.labels["editinplace"]["validate"]["integer"]	= "Enter numbers between 0-9 only";
window.labels["editinplace"]["validate"]["float"]	= "Enter numbers between 0-9 only";

/* unlink */
window.labels["unlink"] 					= new Object();
window.labels["unlink"]["cancel"]			= "No";
window.labels["unlink"]["yes"]				= "Yes";
window.labels["unlink"]["confirm"]			= "Remove";
window.labels["unlink"]["title"]			= "Remove";

/* shoutbox */
window.labels['shoutbox'] 					= new Object();
window.labels['shoutbox']['away']	  		= "You seem to be away from this window. Shutting down shoutbox.";
window.labels['shoutbox']['changed']		= "The default conversation for this page has changed. Refreshing in a few seconds.";
window.labels['shoutbox']['noconv']			= "No conversation exists for this thing yet.";
window.labels['shoutbox']['emptytext']		= "Text is empty. You need to type something";
window.labels['shoutbox']['votedthis']		= "Voted this:";
window.labels['shoutbox']['showsms']		= "Show SMS";
window.labels['shoutbox']['hidesms']		= "Hide SMS";


/* wysiwyg */
window.labels['wysiwyg'] 					= new Object();
window.labels['wysiwyg']['add_link']		= "Add the link";
window.labels['wysiwyg']['add_youtube']		= "Add YouTube code";
window.labels['wysiwyg']['add_title']		= "Add the title of the link";
window.labels["wysiwyg"]['cancel']			= "Cancel";
window.labels["wysiwyg"]['ok']				= "OK";
window.labels["wysiwyg"]['youtube_expl']	= "You can find the YouTube code in the url on YouTube, for example http://www.youtube.com/watch?v=<strong>nogDHP0gl9M</strong>";

window.labels['wysiwyg']['buttons']				= new Object();
window.labels['wysiwyg']['buttons']['italic'] 	= "italic";
window.labels['wysiwyg']['buttons']['bold'] 	= "bold";
window.labels['wysiwyg']['buttons']['link']	 	= "link";
window.labels['wysiwyg']['buttons']['youtube']	= "youtube";
window.labels['wysiwyg']['buttons']['h1'] 		= "huge subhead";
window.labels['wysiwyg']['buttons']['h2'] 		= "medium subhead";
window.labels['wysiwyg']['buttons']['h3'] 		= "subhead";
window.labels['wysiwyg']['buttons']['h4'] 		= "small subhead";

/**/

// class for status messages on user pages
// by Tim Benniks
var statusMessages = 
{

	// this function initializes all events for the status messages.
	// it needs the thg_id so we have that when we save a message.
	init: function(thg_id)
	{
		// make the keyboard trigger save and cancle events in the keypress.statusMessages namespace.		
		$(document)
			.unbind('keypress.statusMessages')
			.bind('keypress.statusMessages', function(e)
			{
				var key;
				if($.browser.msie) 	{ key = e.which } else { key = e.keyCode }
				if(key == 27) { $('.cancel-status-message').click(); }
				if(key == 13) { $('.save-status-message').click(); }
			});
		
		$('#add-status-massage, .status-editable').click(function()
		{
			statusMessages.addMsg($(this));
		});
				
		$('.save-status-message').click(function()
		{
			if(
				$('.add-status-message-wrapper input').val() != '' 
			 || $('.add-status-message-wrapper input').val() != 'is ' 
			 || $('.add-status-message-wrapper input').val() != 'is'
			)
			{
				statusMessages.saveMsg(thg_id, $('.add-status-message-wrapper input').val());
			}
			else
			{
				return false;
			}
		});
		
		$('.cancel-status-message').click(function()
		{
			statusMessages.cancelMsg();
		});
		
		statusMessages.applyStyle('h1', $('.add-status-message-wrapper input'));
	},
	
	// show the form, hide the button.
	addMsg: function(obj)
	{
		obj.fadeOut(150, function()
		{
			$('.add-status-message-wrapper').fadeIn();
			$('.add-status-message-wrapper input').focus().css({width: '100%'});
		});	
	},

	// hide the form, show the old status message / new message button.
	cancelMsg: function()
	{
		$('.add-status-message-wrapper').fadeOut(function()
		{
			$('#the-status-msg').fadeIn();
		});
	},

	// apply the style from the header to the inputfield.
	applyStyle: function(from, to)
	{
		var inheritStyle = ['font-family', 'font-size', 'font-weight', 'color', 'line-height', 'font-style'];

		for(i in inheritStyle)
		{
			to.css(inheritStyle[i], $(from).css(inheritStyle[i]));
		}
	},

	// Save the status mesage.
	saveMsg: function(thg_id, msg)
	{
		$('.add-status-message-wrapper input').addClass('loading');
		
		anyRest.service('status.set', 
		{
			id: 		thg_id,
			message: 	msg
		},
		function(xml)
		{
			$('.add-status-message-wrapper input').removeClass('loading');
			
			// on error show the error div and add the status message in there.
			if(anyRest.aux.error(xml))
			{
				return false;
			}
			else
			{
				statusMessages.saveMsgCallBack(thg_id);
			}
		});
	},
	
	// after saving the message, reload the template with the right content
	saveMsgCallBack: function(thg_id)
	{
		anyRest.html.scomp(
		{
			'name':		'scomp_status_message_wrapper', 
			'thg_id':	thg_id
		}, 
		function(xml)
		{
			if(anyRest.aux.error(xml)) 
			{
				$.warn($('rsp msg', xml).attr('msg'), '');
			}
			else
			{
				var scompHtml 	= $('html', xml).text();
				var domID 		= $('#status-message-wrapper');

				if(scompHtml && domID)
				{
					$(domID).fadeOut(300, function()
					{
						$(domID).after(scompHtml).remove();
						return false;
					});
				}
				else
				{
					$.log('Internal error: did not get a template from the server or no dom id. Sorry...');
				}
			}
		});
	}
}
/* */
/*  */

// Main library file 
// Contains all functions used by two or more widgets,
// Contains the main initialization and widget management code

/*  */

//
// START Global Initialization code
//

window.widget_manager = null;
$(document).ready(
	function ()
	{
		window.widget_manager = new WidgetManager( window.widget_names );
		if ( window.widget_roots && window.widget_roots.length > 0 )
		{
			for ( var i in window.widget_roots )
			{
				// Initialize widgets at one or more specific root points
				// This option is here for perfomance reasons: it can make
				// a huge difference on a big page with few localized widgets,
				// e.g. admin interface
				init_widgets( window.widget_roots[i] );
			}
		}
		else
		{
			// Initialize the widgets starting from the root
			init_widgets( document.body );
		}
		$( window ).unload( 
			function ()
			{
				// There might be things to save at 
				// the moment a user clicks a link
				window.widget_manager.stopAll();
			}
		);		

		// check if modifier keys influence a link
		modifier_keys();

		// #4825: ready function for edit_required_fields template
		if ( window.required_fields )
		{
			validate_update();
		}
	}
);

// check if modifier keys influence a link
function modifier_keys( url )
{
	if ($('#editpage'))
	{
		// check if editpage button is clicked
		// if shift key is clicked, use the admin page in new window
		$('#editpage').click(
			function(e)
			{
				if (e.shiftKey && $('#editpage').attr('rel')) 
				{
					uri = $('#editpage').attr('rel');
					win = window.open('','_blank');
					win.location.href = uri;
					return false;
				}
			}
		); 
	}
}

// External callback for the widget 
function widget_save( submit_form )
{
	var n = window.widget_manager.stopAll();
	var t = n > 0 ? 2000 : 1; 
	setTimeout( 
		function ()
		{
			submit_form.submit();
		},
		t
	);
	return false;
}

// Start widgets inside a given element/css selector
function init_widgets( context )
{                    
	var x = $( context ).get(0);
	x && window.widget_manager.startWidgets( x );
}

// CLASS WidgetManager
// Handles the inter-widget communication
function WidgetManager( widget_names )
{
	this.widgetNames 	= widget_names;
	this.currentWidgets = {};
	this.allWidgets 	= {};
}

// Return all widgets of a given type
WidgetManager.prototype.widgets = function ( name )
{
	return this.allWidgets[name];
};

// Make the Widget Manager aware of a new widget instance
WidgetManager.prototype.add = function ( name, w )
{
	this.allWidgets[name] = this.allWidgets[name] || [];
	this.allWidgets[name].push( w );
};

// Select all the widget instances of types defined in ws
// that match predicate function f
WidgetManager.prototype.find_all = function ( f, ws )
{
	ws = ws || ['editinplace','editatonce'];
	var rt = [];
	for ( var i in ws )
	{
		var a = this.allWidgets[ws[i]] || [];
		
		for ( var j in a )
		{
			if ( f( a[j] ) )
			{
				rt.push( a[j] );
			}
		}
	}
	return rt;
};

// Finds the first element belonging to one of the types 
// defined in ws that matches predicate function f
WidgetManager.prototype.find = function ( f, ws )
{
	ws = ws || ['editinplace','editatonce'];
	for ( var i in ws )
	{
		var a = this.allWidgets[ws[i]] || [];
		
		for ( var j in a )
		{
			if ( f( a[j] ) )
			{
				return a[j];
			}
		}
	}
	return null;
};

// Returns the current/active widget of type name
WidgetManager.prototype.current = function ( name )
{
	return this.currentWidgets[name];
};

// Stop (blur) all widgets. Used for e.g when the page is unloading
WidgetManager.prototype.stopAll = function ()
{
	var count = 0;
	for ( var name in this.currentWidgets )
	{
		var w = this.currentWidgets[name];
		if ( w )
		{
			if ( w.widgetBlur( name, null ) )
			{
				count += 1;
			}
		}
	}
	return count;
};

// Stop the current/active widget instance of type name
WidgetManager.prototype.stop = function ( name )
{
	var w = this.currentWidgets[name];
	w && w.widgetBlur( name, null );
};

// Defined a widget instance as the new current/active element
// fot the widget type name
WidgetManager.prototype.widgetFocus = function ( name, obj )
{
	for ( i in this.currentWidgets )
	{
		var w = this.currentWidgets[i];
		w.widgetBlur && w.widgetBlur( name, obj );
	}
	
	this.currentWidgets[name] = obj;
};

// Main widget initialization function
// Does a traversal of the tree that starts at root, checking
// the css classes of all elements for the do_<widgetname> pattern
// that matches one of the allowed types defined in window.widgetNames
WidgetManager.prototype.startWidgets = function ( root )
{
	var stack = [root];
	var names = this.widgetNames;
	var names_do = {};
	for ( var i in names )
	{
		names_do['do_' + names[i]] = names[i];
	}
	
	while ( stack.length > 0 )
	{
		var e = stack.pop();
		if ( e.className && e.className.match( /do_[a-z_]+/ ) )
		{
			// Note: IE6 does not recognize the \b escape char 
			// (whitespace in js regexps)
			var clsss = e.className.match( /do_[a-z_]+/g );
			for ( var i = 0; i < clsss.length; i++ )
			{
				var w = names_do[clsss[i]];
				if ( w )
				{
					eval("init_" + w + "( e )");
				}
			}
		}
		
		if ( e.childNodes ) 
		{
			for ( var i = 0 ; i < e.childNodes.length ; i++ )
			{
				if ( e.childNodes[i].nodeType != 3 )
				{
					// Only process if not a text node
					stack.unshift( e.childNodes[i] );
				}
			}
		}
	}
	
	var ends = ['editinplace','editatonce'];
	for ( var i in ends )
	{
		if ( jQuery.inArray( ends[i], names ) > -1)
		{
			eval( 'end_' + ends[i] + '()' );
		}
	}
};

// Convert the name of a validated field into a 
// string usable as a dom id attribute
function validate_field2id ( name )
{
	name = name.replace( /\./g, "_" );
	return 'required_fields_li_' + name;
}

function validate_cmpfield ( l )
{
	return function ( x ) 
	{
		return jQuery.inArray( x.options.ajax.field, l ) > -1; 
	}
}

// Scroll down/up the window to make visible the edit place of 
// a given validate field that was incorrect or missing
function validate_goto ( i )
{
	var e = window.widget_manager.find( validate_cmpfield( [i] ) );
	if ( e )
	{
		var p = jQuery.getPosition( e.container );
		scroll( 0, p.y - 100 );
	}
}

// Update the validate fields error information
function validate_update ()
{
	if ( jQuery.isEmptyObject( window.required_fields ) )
	{
		$( "#required_fields_main"  ).hide();
		$( "#required_fields_other" ).show();
		return;
	}
	
	$( "#required_fields_main" ).show();
	$( "#required_fields_other" ).hide();
	
	var fs = window.required_fields;
	var ul = $( "#required_fields_list" ).get( 0 );
	ul.innerHTML = '';
	var bind_click = function ( li, i )
	{
		var fu = function ( x ) 
		{ 
			return x.options.ajax.field == i; 
		};

		var e = window.widget_manager.find( fu );

		if ( e && e.options )
		{
			var c = e.options.callback;
			c = c || function () {};

			var fc = function ( d )
			{
				delete( window.required_fields[i] );
				validate_update();
				c && c( d );
			}

			e.options.callback = fc;
			
			$( li ).bind( "click",
				function () 
				{
					validate_goto( i );
					return false;
				}
			);
		}
	};
	
	var gs = jQuery.objKeys( fs );
	var fu = validate_cmpfield( gs );
	var ws = window.widget_manager.find_all( fu );
	for ( var i in ws )
	{
		ws[i].addRequiredClass();
	}
	
	for ( var i in fs )
	{
		var li = document.createElement( "li" );
		var an = document.createElement( "a" );
		li.id = validate_field2id( i );
		li.className = "required_field_title";
		an.href='#';
		$( an  ).html( fs[i] );
		$( li ).append( an );
		$( ul ).append( li );
		bind_click( an, i );
	}
}

// Used by all widgets. Override the default widget 'docs' 
// options with those 'o' specific for this instance
function apply_options( docs, o )
{
	for ( var i in docs )
	{
		var d	= docs[i];
		var ds	= d[0].split('.');
		if ( ds.length > 1 )
		{
			o[ds[0]][ds[1]]	= (typeof o[ds[0]][ds[1]] != 'undefined') ? o[ds[0]][ds[1]]	: d[1];
		}
		else
		{
			o[d[0]]			= (typeof o[d[0]] != 'undefined') 		  ? o[d[0]] 		: d[1];
		}
	}
	return o;
}


//
// END Global Initialization code
//

// Identity function
function identity ( x )
{
	return x;
}

jQuery.fn.opacity = function ( v )
{
	this.css('opacity', v );
	if ( jQuery.browser.msie ) 
	{
		this.css('filter', 'alpha(opacity=' + v * 100 + ')');
	}
}

jQuery.extend( {
	
	objKeys: function ( obj )
	{
		var ret = [];
		for ( var i in obj )
		{
			ret.push( i );
		}
		return ret;
	},
	
	label: function ( path, def )
	{
		var names 	= path.split('.');
		var tmp 	= window.labels;

		while ( names.length > 0 && tmp )
		{
			tmp = tmp[names.shift()];
		}

		return tmp || def;
	},
	
	// Create a new cookie
	// @param	name	string		name of the cookie
	// @param	value	string		value to be stored
	// @param	days	int			expiry time in days
	createCookie: function ( name, value, days )
	{
		if ( days ) 
		{
			var date = new Date();
			date.setTime( date.getTime() + ( days * 24 * 60 * 60 * 1000 ) );
			var expires = "; expires=" + date.toGMTString();
		}
		else
		{
			var expires = "";
		}
		document.cookie = name + "=" + value+expires + "; path=/";
	},

	// Read the value of a cookie
	readCookie: function ( name )
	{
		var nameEQ = name + "=";
		var ca = document.cookie.split( ';' );
		for ( var i = 0 ; i < ca.length ; i++ )
		{
			var c = ca[i];
			while ( c.charAt(0) == ' ' )
			{
				c = c.substring( 1, c.length );
			}
			
			if ( c.indexOf( nameEQ ) == 0 ) 
			{
				return c.substring( nameEQ.length, c.length );
			}
		}
		return null;
	},

	// Delete a cookie
	eraseCookie: function( name )
	{
		jQuery.createCookie( name, "", -1 );
	},
	
	// Use the log function of firebug without messing with other browsers 
	log: function () 
	{
		if ( window.console && window.console.log && window.console.assert )
		{
			window.console.log( arguments );
		}
	},
	
	warn: function(where, what)
	{
		if(!what)
		{
			what = "You cannot edit this now.";
		}
		
		anyRest.notifications.add(where+': '+what, 'notice');
	},
	
	error: function ( msg , url , line ) 
	{
		alert( msg + " @ " + url + " @ " + line );
		return false;
	},
	
	// Encode object for get/post request: works with arbitrarily-leveled-nested 
	// arrays and objects. Adapted from PHP (any_functions' http_build_query)
	//
	xparam: function (params)
	{
		var encoder_f = function (f, name, value, sep, uri)
		{
			if (value.constructor == Array)
			{
				for ( var i = 0 ; i < value.length ; i++ )
				{
					f(f, name + '[' + i + ']', value[i], sep, uri);
				}
			}
			else if ( value.constructor == Object )
			{
				for ( var i in value )
				{
					f(f, name + '[' + i + ']', value[i], sep, uri);
				}
			}
			else
			{
				uri.uri += sep.sep + name + '=' + encodeURIComponent(value);
				sep.sep = '&';
			}
		};

		// Hack for simulating call-by-reference in JavaScript
		var sep = {sep:''};
		var uri = {uri:''};

		for ( var i in params )
		{
			encoder_f(encoder_f, i, params[i], sep, uri);
		}

		return uri.uri;
	},
	
	// Get the window size
	windowSize: function ()
	{
		var p = { w: 0, h: 0 };
        var e = document.documentElement;
        var b = document.body;

		if ( typeof( window.innerWidth ) == 'number' )
		{
		  //Non-IE
		  p.w = window.innerWidth;
		  p.h = window.innerHeight;
		}
		else if( e && ( e.clientWidth || e.clientHeight ) )
		{
		  //IE 6+ in 'standards compliant mode'
		  p.w = e.clientWidth;
		  p.h = e.clientHeight;
		}
		else if( b && ( b.clientWidth || b.clientHeight ) )
		{
		  //IE 4 compatible
		  p.w = b.clientWidth;
		  p.h = b.clientHeight;
		}	
		
		return p;
	},
	
	mousePosition: function ( ev )
	{
		ev = ev || window.event;

		var x = 0, y = 0;

		if ( ev.pageX || ev.pageY )
		{
			x = ev.pageX;
			y = ev.pageY;
		}
		else if ( ev.clientX || ev.clientY )
		{
			x = ev.clientX + document.body.scrollLeft;
			y = ev.clientY + jQuery.pageScrollTop(); //document.body.scrollTop;
		}

		return { "x": x, "y": y };
	},
	
	// Coordinates of mouse in relation to the window 
	mouseInWindow: function ( e )
	{
		var ret = {};
		if ( !e )
		{
			var e = window.event;
		}
		
		if ( e.clientX || e.clientY )
		{
			ret.x = e.clientX - Math.max( 0 , document.body.scrollLeft );
			ret.y = e.clientY - Math.max( 0 , document.body.scrollTop ); // document.body.scrollTop*/  + document.documentElement.scrollTop;
		}
		return ret;
	},
	
	// Override r with properties from object a
	assocMergeInto: function ( r , a ) 
	{
		if ( a )
		{
			for( var i in a )
			{
				r[i] = a[i];
			}
		}
		return r;
	},
	
	// Merge two objects (properties of the second 
	// argument override those of the first), result is a new 
	// object in memory
	assocMerge: function ( a , b ) 
	{
	 	return jQuery.assocMergeInto( jQuery.assocMergeInto( {}, a ),  b );
	},
	
	// Output a datetime 
	dateFormat: function ( format , date )
	{
		format = format.replace( /\%Y/ , date.getFullYear() );
		format = format.replace( /\%m/ , date.getMonth() + 1 );
		format = format.replace( /\%d/ , date.getDate() );
		format = format.replace( /\%H/ , date.getHours() );
		format = format.replace( /\%M/ , date.getMinutes() );
		return format;
	},

	// Remove a parameter from the url
	remFromQuery: function ( url , name ) 
	{
		var reg = new RegExp( '&?' + name + '=[^&]*' );
		url = url.replace( reg, '' );
		return url;
	},
	
		
	// Add some query data to a url
	addToQuery: function ( url , data ) 
	{
		return url + ( url.match( /\?/ ) ? '&' : '?' ) + data;
	},
	
	// Parse options given inside the attribute of an element
	attrOptions: function ( e , name )
	{
		var a = e.getAttribute( name ) || '';
		a = a.replace( /,\s*$/ , '' );
		eval( "var tmp = {" + a + "}" );
		return tmp;
	},

	// DEPRECATED: Vertical scroll offset in pixels 
	pageScrollTop: function ()
	{
        var pos = jQuery.getScroll();
        return pos.y;
	},
    
    getScroll: function ()
    {
        var p = {x: 0, y: 0};
        var e = document.documentElement;
        var b = document.body;

        if ( e && ( e.scrollLeft || e.scrollTop ) ) 
        {
            p.x = e.scrollLeft;
            p.y = e.scrollTop;
        
        }
        else if ( b && ( b.scrollLeft || b.scrollTop ) ) 
        {
            p.x = b.scrollLeft;
            p.y = b.scrollTop;
        }
        else if ( window.pageXOffset || window.pageYOffset )
        {
            p.x = window.pageXOffset;
            p.y = window.pageYOffset;
        }
        else if( window.scrollX || window.scrollY ) 
        {
            p.x = window.scrollX;
            p.y = window.scrollY;
        }
        return p;
    },


	// Calculate x,y position of a given dom element
	getPosition: function ( obj, ignoreRelative )
	{
        ignoreRelative = ignoreRelative || false;

		var curleft = curtop = 0;

		// Hack for IE not needed ?
		// Not sure but this check for IE gave problems in the end
		// Keep the if in until we're sure that it is not needed
		if ( false && jQuery.browser.msie )
		{
			// Hack for IE, naturally
			var childImg = false;
			for ( var i in obj.childNodes )
			{
				childImg = childImg || obj.childNodes[i].tagName == 'IMG';
			}

			if ( childImg )
			{
				curleft -= obj.offsetLeft;
			}
		}

		while ( obj )
		{                                        
			if ( !ignoreRelative || jQuery( obj ).css('position') != "relative" )
			{
				curleft += obj.offsetLeft;
				curtop  += obj.offsetTop;
			}
			obj = obj.offsetParent;
		}
		return { x: curleft, y: curtop };
	},
	
	// Calculate dimensions of a dom element
	getSize : function(e)
	{
		var w = jQuery.css(e,'width');
		var h = jQuery.css(e,'height');
		var wb = 0;
		var hb = 0;
		es = e.style;
		
		if ( jQuery(e).css('display') != 'none' ) 
		{
			wb = e.offsetWidth;
			hb = e.offsetHeight;
		}
		else
		{
			oldVisibility = es.visibility;
			oldPosition = es.position;
			es.visibility = 'hidden';
			es.display = 'block';
			es.position = 'absolute';
			wb = e.offsetWidth;
			hb = e.offsetHeight;
			es.display = 'none';
			es.position = oldPosition;
			es.visibility = oldVisibility;
		}
		return { w:w, h:h, wb:wb, hb:hb };
	},
	
	// Get an object position from the upper left viewport corner
	elemInWindow: function ( o )
	{
		// Get the positions from the parent object
		var oLeft = o.offsetLeft;            
		var oTop  = o.offsetTop;            

		// Parse the parent hierarchy up to the document element
		while( o.offsetParent != null )
		{   
			oParent	 = 	o.offsetParent;		// Get parent object reference
			oLeft	+=	oParent.offsetLeft;	// Add parent left position
			oTop	+=	oParent.offsetTop;	// Add parent top position
			o = oParent;
		}
		
		return { l: oLeft , t: oTop };
	},

	validateForm: function ( frm , alrt )
	{
		if ( document.getElementById( frm ) )
		{
			var f = document.getElementById( frm );
		}
		else if ( document.forms[ frm ] )
		{
			var f = document.forms[ frm ];
		}
		else
		{
			f = frm;
		}

		if ( alrt == 'null' )
		{
			alrt = 'All fields are required';
		}

		var isValid		= true;
		var	vFormElems	= new Array();

		for ( i = 0 ; i < f.elements.length ; i++ ) 
		{
			elem  = f.elements[i]; 


			vCode = ( elem['alt'] ) ? elem['alt'] : elem.getAttribute('alt');

			if ( !( typeof vCode == 'undefined' || vCode == null || vCode == "" ) )	
			{
				if ( jQuery.inArray( elem.name, vFormElems ) == -1)
				{
					v = jQuery.getValue( f.elements[elem.name] );

					if ( vCode == 'required' && v.length < 1 ) 
					{
						isValid = false;
					}
					vFormElems[i] = elem.name;
				}
			}
		}
		if (!isValid) 
		{
			alert( alrt );
		}
		return isValid
	},

	// Returns value of a formfield
	getValue: function ( ffield , def )
	{
		v = '';
		t = ffield.type;
		
		if( !t ) 
		{
			t = ffield[0].type;
		}

		if( t == 'checkbox' )
		{
			if( ffield.length )
			{
				for( gv_i=0 ; gv_i < ffield.length ; gv_i++ )
				{
					if ( ffield[gv_i].checked ) 
					{
						v = ffield[gv_i].value;
					}
				}
			}
			else
			{
				if ( ffield.checked ) 
				{
					v = ffield.value;
				}
			}	
		} 
		else if ( t == 'file' )		v = ffield.value;
		else if ( t == 'hidden' )	v = ffield.value;
		else if ( t == 'password' ) v = ffield.value;
		else if ( t == 'radio' ) 
		{
			if ( ffield.length ) 
			{
				for ( gv_i=0 ; gv_i < ffield.length ; gv_i++) 
				{
					if ( ffield[gv_i].checked ) v = ffield[gv_i].value;
				}
			}
			else
			{
				if ( ffield.checked ) v = ffield.value;
			}	
		} 
		else if (t == 'select-multiple' ) v = ffield.options[ffield.selectedIndex].value;
		else if (t == 'select-one' )	  v = ffield.options[ffield.selectedIndex].value;
		else if (t == 'text' )			  v = ffield.value;
		else if (t == 'textarea' )		  v = ffield.value;
		
		if (def != null && v == '')
		{
			return def;
		}
		return v;
	},
	
	byId: function ( id , doc ) 
	{
		if ( typeof id == "string" || id instanceof String ) 
		{
			if ( !doc )
			{
				doc = document;
			}
			return doc.getElementById ( id );
		}
		// assume it's a node
		return id; 
	},

	textContent: function ( node ) 
	{
		var _result = "";
		if (node == null)
		{ 
			return _result;
		}
		
		for (var i = 0; i < node.childNodes.length; i++) 
		{
			switch (node.childNodes[i].nodeType) 
			{
				case 1: // ELEMENT_NODE
				case 5: // ENTITY_REFERENCE_NODE
					_result += jQuery.textContent(node.childNodes[i]);
					break;
				case 3: // TEXT_NODE
				case 2: // ATTRIBUTE_NODE
				case 4: // CDATA_SECTION_NODE
					_result += node.childNodes[i].nodeValue;
					break;
				default:
					break;
			}
		}
		return _result;
	},

	// used in form validator
	// maybe change in the future
	isEmailAddress: function ( v ) 
	{
		mailvalid = true;
		if ( v.indexOf ( '@' ) < 1 )
		{
			mailvalid = false;
		}
		else if ( v.lastIndexOf ( '.' ) < v.indexOf ( '@' ) ) 
		{
			isvalid = false;
		}
		aapje  = v.indexOf( '@' ); 
		puntje = v.indexOf( '.' );  
		if ( puntje - aapje == 1 ) 
		{
			mailvalid = false;
		}
		return mailvalid;
	},

	// used in form validator
	// maybe change in the future
	isSizeSmaller: function ( v , size ) 
	{
		if ( v.length < size )
		{
			return true;
		}
		else
		{
			return false;
		}
	},

	// used in form validator
	// maybe change in the future
	isSizeGreater: function ( v , size ) 
	{
		if ( v.length > size )
		{
			return true;
		}
		else
		{
			return false;
		}
	},

	// showhide functions
	// used while rest call state is failed/ok/wait
	showHide: function (str)
	{
		a = str.split(',');
		for ( var i = 0 ; i < a.length ; i++ ) 
		{
			s = a[i];
			c = s.charAt(0);
			v = 'block';
			
			if (c == '-') 
			{
				v = 'none';
				s = s.substr( 1 , s.length-1);
			}
			else if ( c == '+') 
			{
				s = s.substr( 1 , s.length - 1 );
			}
			
			if ( $( "#" + s ) ) 
			{
				$("#" + s).css( 'display', v );	
			}
		}
	},

	// Hide all obj in an array or string and optionally show obj b (can be an array)
	// Parameters:	a	which obj to hide (can be an array) 				
	//				b	which obj should openend afterwards (hide all but ...)(can be an array)
	//				s	if s = true search for objects starting with the string 'a'
	hideAllBut: function ( a, b ) 
	{
		if ( jQuery.isObject( a ) )
		{
			for ( var p in a ) 
			{
				$( "#" + a[p] ).hide(); 
			}
		}
		else if ( jQuery.isArray(a) )
		{
			for ( i = 0 ; i < a.length ; i++ )
			{
				$( "#" + a[i] ).hide(); 
			}
		}
		else if ( jQuery.isString( a ) )
		{
			var d = document.getElementsByTagName( 'div' );
			for( i = 0 ; i < d.length ; i++ )
			{
				var l = a.length;
				var id = d[i].id;
				if ( id.substring( 0 , l ) == a )
				{
					$( "#" + d[i].id ).hide(); 
				}
			}
		}
		
		if ( jQuery.isArray( b ) )
		{
			for ( i = 0 ; i < a.length ; i++) 
			{
				$( "#" + b[i] ).show();
			}
		}
		else if ( jQuery.isString( b ) ) 
		{
			$( "#" + b ).show();
		}
	},
		
	
	popup: function ( thg_id, o, l, t, w, h ) 
	{ 
		if ( l == null ) l = 100;
		if ( t == null ) t = 100;
		if ( w == null ) w = 400;
		if ( h == null ) h = 400;

		var aPopupWin = window.open( '', 'PopupWin', 'scrollbars=yes,menubar=no,location=no,directories=no,statusbar=no,toolbar=no,resizable=yes,width=' + w + ',height=' + h + ',top=' + t + ',left=' + l );
		aPopupWin.document.open(); 
		aPopupWin.document.close();
		order = '';
		if ( o != null ) order = '&order='+o;
		var p = 'popup.php?id=' + thg_id + order;

		aPopupWin.document.location.href = p;
		if( aPopupWin && !aPopupWin.closed )
		{
			aPopupWin.focus();
		}	
	},

	resizePopup: function()
	{
		var mw = 400;

		if ( parseInt( navigator.appVersion.charAt( 0 ) ) >= 4 )
		{
			NN = ( navigator.appName=="Netscape" ) ? 1 : 0;
		}

		if ( document.images[0] )
		{
			ww = ( screen.width  );
			wh = ( screen.height );

			if (NN)
			{
				rw = document.images[0].width + 80;
				rh = document.images[0].height;
				if ( ww < rw ) rw = ww;
				if ( rw < mw ) rw = mw;
				if ( wh < rh ) rh = wh;
				if ( rh < wh - 140 ) rh = rh + 160;
				window.top.innerWidth  = rw;
				window.top.innerHeight = rh;
			}
			else
			{
				rw = document.images[0].width + 80;
				rh = document.images[0].height;
				if ( ww < rw ) rw = ww;
				if ( rw < mw ) rw = mw;
				if ( wh < rh ) rh = wh;
				if ( rh < wh - 140 ) rh = rh + 160;
				window.top.resizeTo( rw, rh );
			}
			parent.window.moveTo( ( screen.width - rw ) / 2, ( screen.height - rh ) / 2 );
		}
	},

	// helper function
	isAlien: function ( a ) 
	{
		return jQuery.isObject(a) && typeof a.constructor != 'function';
	},

	isArray: function ( a ) 
	{
		return jQuery.isObject(a) && a.constructor == Array;
	},

	isBoolean: function ( a ) 
	{
		return typeof a == 'boolean';
	},
	
	isEmptyObject: function ( o )
	{
		for ( var i in o )
		{
			return false;
		}
		return true;
	},

	isEmpty: function(o) 
	{
		var i, v;
		if (jQuery.isObject(o)) 
		{
			for (i in o) 
			{
				v = o[i];
				if (jQuery.isUndefined(v) && jQuery.isFunction(v)) 
				{
					return false;
				}
			}
		}
		return true;
	},

	isFunction: function ( a ) 
	{
		return typeof a == 'function';
	},

	isNull: function ( a ) 
	{
		return typeof a == 'object' && !a;
	},

	isNumber: function ( a ) 
	{
		return typeof a == 'number' && isFinite(a);
	},

	isObject: function ( a ) 
	{
		return (a && typeof a == 'object') || jQuery.isFunction(a);
	},

	isString: function ( a ) 
	{
		return typeof a == 'string';
	},

	isUndefined: function ( a ) 
	{
		return typeof a == 'undefined';
	},

	isDomElement: function ( a ) 
	{
		return a && typeof a == 'object' && !jQuery.isUndefined(a.childNodes);
	},
	
	truncate: function(string, chars, after)
	{
		if (string.length <= chars)
		{
			return string;
		}
		
		if (typeof after == 'undefined')
		{
			after = '&hellip;';
		}
		
		string = string.substring(0, chars);
		string = string.replace(/\w+$/, '');
		 
		var re  = new RegExp('[ 	\.\,\:\;\!\?]+$', 'g');
		string  = string.replace(re, '');
		string += ' ' + after;
		
		return string;
	}
});

// CLASS AjaxNotice
function AjaxNotice( sel )
{
	this.sel = sel;
}

// Start the notice
AjaxNotice.prototype.start = function ( text )
{
	if ( text == '...' )
	{
		text = '<img src="http://fast.mediamatic.nl/f/dxdx/image/throbberdots.gif" />';
	}
	
	text && notice_corner_show( text );
	$( this.sel ).addClass( 'ajax_notice' );	
}

// Stop the notice
AjaxNotice.prototype.stop = function ()
{
	notice_corner_hide();
	$( this.sel ).removeClass( 'ajax_notice' );
}

// Make corner notices have unique ids so that timeout events
// only act if the instance that triggered them is still active
window.notice_corner_count = 0;

// Show the notice in the corner of window
function notice_corner_show ( text )
{
	window.notice_corner_count += 1;
	var x = document.getElementById( 'corner_notice' );
	if ( !x )
	{
		x = document.createElement( 'div' );
		x.id = 'corner_notice';
		$( document.body ).append( x );
	}
	$( x ).html( text );
	$( x ).fadeIn(400);
	
	window.notice_corner_time = (new Date()).getTime();
}

// Hide the notice in the corner of window
function notice_corner_hide ()
{
	window.notice_corner_count -= 1
	if ( window.notice_corner_count > 0 )
	{
		return;
	}
	else
	{
		window.notice_corner_count = 0;
	}
	var now = (new Date()).getTime();
	var dif = now - window.notice_corner_time;
	if ( dif > 1000 )
	{
		$( '#corner_notice' ).fadeOut(400);
	}
	else
	{
		setTimeout(
			function () 
			{
				notice_corner_hide();
			},
			dif
		);
	}
}


// External callback for the documentation functions
function docs_validate()
{
	var lm = jQuery.label( 'editinplace.validate.msg'	, 'Please input a valid value' );
	var lo = jQuery.label( 'editinplace.validate.out'	, 'Value out of range'  );
	
	return { opts:	
		[[ "validate",			{},			"Input format validation" ]
		,[ "validate.regexp",	null,		"Regular expression to validate format" ]
		,[ "validate.name",		null,		"Name of predefined regular expression [integer|float]" ]
		,[ "validate.msg",		lm,			"Alert message to be displayed in case of wrong format" ]
		,[ "validate.min",		null,		"Minimum allowed value" ]
		,[ "validate.max",		null,		"Maximum allowed value" ]
		,[ "validate.empty",	false,		"Input value is allowed to be empty" ]
		,[ "validate.blank",	false,		"Input value is allowed to be empty/whitespace" ]
		,[ "validate.out",		lo,			"Alert message to be displayed in case of out of range number" ]
		]
	};
}

// CLASS Validate
// Used by EditAtOnce and EditInPlace to validate 
// the presence or format of some fields
function Validate( opts )
{
	this.opts = opts;
	if ( "integer" == this.opts.name )
	{
		this.opts.msg		= jQuery.label( 'editinplace.validate.integer'	, 'Please input an integer number' );
		this.opts.regexp	= /^\s*[-+]?\d+\s*$/;
	} 
	else if ( "float" == this.opts.name )
	{
		this.opts.msg		= jQuery.label( 'editinplace.validate.float'	, 'Please input a number' );
		this.opts.regexp	= /^\s*[-+]?\d+(\.\d+)?\s*$/;
	}
}

// Is the value inside this range?
Validate.prototype.range = function ( value )
{
	var fs 	= {'integer': parseInt, 'float': parseFloat};
	var f 	= fs[this.opts.name] || identity;
	value = f( value );
	
	return 	!(	this.opts.min && this.opts.min > value  
			||	this.opts.max && this.opts.max < value );
}

// Is the value not empty?
Validate.prototype.value = function ( value )
{	
	return ( this.opts.blank && value.match( /^\s*$/ ) )
		|| ( this.opts.empty && value == '' )
		|| ( this.opts.regexp && this.opts.regexp.exec( value ) )
		|| ( !this.opts.regexp );
}


// Comment this line to stop server-side reporting of javascript errors
//window.onerror = anyOnError;

window.handlingOnError = false;
window.handledErrors   = [];

// Function used for server-side reporting of javascript errors 
function anyOnError( msg , url , line )
{
	var exclude = [ "uncaught exception: Permission denied to get property HTMLInputElement.parentNode"
				  , "uncaught exception: Permission denied to get property HTMLDivElement.parentNode" ];

	// Don't report errors in black list
	for ( var i in exclude )
	{
		if ( exclude[i] == msg )
		{
			jQuery.log( "Error belongs to blacklist, won't report it." );

			// Stop browser from declaring this error
			return true;
		}
	}

	// Don't report same error twice
	for ( var i in window.handledErrors )
	{
		if ( window.handledErrors[i] == msg )
		{
			jQuery.log( "Error already reported once on this session" );
			
			// Stop browser from declaring same error twice
			return true;
		}
	}
	
	// Don't report errors that may have happened 
	// during the error-processing phase 
	// (danger of infinite recursivity)
	if ( window.handlingOnError )
	{
		jQuery.log( "Trying to report an error while already busy reporting another one" );
		
		// Pass error onto browser
		return false;
	}
	
	// Lock (informal mutex)
	window.handlingOnError = true;

	jQuery.log( "Reporting error: " + msg ); 
	
	// Remember this error
	window.handledErrors.push( msg ); 

	// Values to send to the server
	var params = {};
	params.msg			= msg;
	params.pageurl		= location.href;
	params.scripturl	= url;
	params.linenumber	= line;
	params.referrer		= document.referrer;
	
	// Send error report o server
	anyRest.error.log( params , 
		function ( xml ) 
		{ 
			jQuery.log( 'Error was reported!' ); 
			// Unlock (informal mutex)
			window.handlingOnError = false;
		}
	);
	
	// Pass error onto browser
	return false;
}

// rebuild a scomp ( file ) with a specific dom_id
function rebuild_scp ( ops, xml )
{
	var dom_id	= ops.dom_id;
	var file	= ops.file;
	var thg_id 	= ops.rebuild_scp_thg_id || self.options.id; 

	if ( dom_id == null || file == null || thg_id == null )
	{
		jQuery.log('rebuild_scp - please use all parameters: dom_id, file, thg_id');
	}
	else
	{
		var scomp_options = jQuery.assocMerge( 
			ops.extra,
			{ 	'name': 		file, 
				'thg_id':		thg_id
			}
		);
	
		anyRest.html.scomp ( scomp_options , 
			function ( xml ) 
			{
				if (anyRest.aux.error(xml)) 
				{
					jQuery.log(anyRest.aux.errorMsg(xml));
				}
				else
				{
					var ts = xml.getElementsByTagName( 'html' )[0];
					x = document.getElementById( dom_id );
				
					if ( ts && x )
					{
						var v = anyRest.aux.concatChildNodes( ts );
	
						$( x ).after ( v ).remove();
	
						y = document.getElementById( dom_id );

						init_widgets( y );
					}
					else
					{
						jQuery.log('Internal error: did not get a template from the server or no dom id. Sorry...');
					}
				}
			}
		);
	}
}

// Get a css style attribute for a html node
function anyGetComputedStyle( node , cssSelector , inValue ) 
{
	var cssSelector = toSelectorCase( cssSelector );
	var property = toCamelCase( cssSelector );
	if( !node || !node.style ) 
	{
		return inValue;
	} 
	
	else if ( document.defaultView &&
			// mozilla segfaults when margin-* and node is removed from doc
			// FIXME: need to figure out a if there is quicker workaround
			isDescendantOf( node, node.ownerDocument ) ) 
	{ // W3, gecko, KHTML
		try 
		{
			var cs = document.defaultView.getComputedStyle( node , "" );
			if ( cs ) 
			{
				return cs.getPropertyValue( cssSelector );
			}
		} 
		catch( e )
		{ // reports are that Safari can throw an exception above
			if ( node.style.getPropertyValue ) 
			{ // W3
				return node.style.getPropertyValue( cssSelector );
			} 
			else 
			{
				return inValue;
			}
		}
	} 
	else if ( node.currentStyle ) 
	{ // IE
		return node.currentStyle[property];
	} 
	if ( node.style.getPropertyValue ) 
	{ // W3
		return node.style.getPropertyValue( cssSelector );
	}
	else
	{
		return inValue;
	}
}



// Change from fontSize to font-size
function toSelectorCase( selector )
{
	return selector.replace(/([A-Z])/g, "-$1" ).toLowerCase();
}


// Change from font-size to fontSize
function toCamelCase ( selector ) {
	var arr = selector.split('-'), cc = arr[0];
	for ( var i = 1 ; i < arr.length ; i++ )
	{
		cc += arr[i].charAt( 0 ).toUpperCase() + arr[i].substring( 1 );
	}
	return cc;
}


// Test if node is descendant of a given ancestor
function isDescendantOf( node, ancestor, guaranteeDescendant )
{
	// guaranteeDescendant allows us to be a "true" isDescendantOf function
	if( guaranteeDescendant && node ) 
	{
		node = node.parentNode; 
	}
	
	while( node ) 
	{
		if ( node == ancestor ) 
		{	
			return true;
		}
		node = node.parentNode;
	}
	return false;
}

// Convert these strings '#abc', '#aabbcc', 'abc', etc
// into this string 'aabbcc'
// TODO: Find better name
function validate_hex( s )
{
	s = s.replace( /#/, '' );
	var h3 = s.match(/^[a-fA-F0-9]{3}$/);
	if ( h3 )
	{
		var a = "";
		for ( var i = 0; i < 3; i++ )
		{
			var c = s.slice(i,i+1);
			a += c + c;
		}
		s = a;
	}
	return s;
}

// Get the background color of a dom node and convert it 
// into an array of decimal numbers [255,255,255]
// TODO: Find better name
function parse_dec_a( elem, stl )
{
	var stl = stl || 'background-color';
	var color = anyGetComputedStyle( elem, stl );
	
	if ( color == 'transparent' )
	{
		color = '';	
	}
	else
	{
		var ff_fr = ffrgb_s2dec_a( color );
	
		if ( ff_fr || ff_fr == 'transparent' )
		{
			color = ff_fr;
		}
		else
		{
			color = validate_hex( color );
			color = hex_s2dec_a( color );
		}
	}
	return color;
}

// Convert from firefox-returned color format 'rgb(12,34,12)'
// into an array of decimal numbers [12,34,12]
// TODO: Find better name
function ffrgb_s2dec_a( x )
{
	var a = null;
	var m = x.match(/rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/);
	
	if ( m )
	{
		a = [];
		for ( var i = 1; i < 4 ; i++ )
		{
			a.push( parseInt( m[i] ) );
		}
	}
	return a;
}

// Make the node in the first argument throb its background color 
// using the bkg color of the second argument as the intermediate
function throb( selector, color_example )
{
	var elem = $( selector      ).get( 0 );
	var exmp = $( color_example ).get( 0 );

	if ( !elem || !exmp )
	{
		jQuery.log( 'throb: did not receive the correct arguments' );
		return;
	}

	// Get the base color
	var frcolor = parse_dec_a( elem );
	var tocolor = parse_dec_a( exmp );
	
	if ( !frcolor || !tocolor )
	{
		jQuery.log( 'throb: color values not parseable' );
		return;
	}
	
	
	// Get the start time for animation
	var start = (new Date()).getTime();
	
	// Total duration of animation
	var total = 5000;
	
	var anim = function () 
	{
		var diff = (new Date()).getTime() - start;
		if ( diff > total )
		{
			elem.style.backgroundColor = '#' + dec_a2hex_s( frcolor );
		}
		else
		{
			var x = [];
			var f = diff / total;
			var g = ( Math.cos(6 * Math.PI * f + Math.PI) + 1 ) / 2;
			for ( var i = 0; i < 3; i++ )
			{
				x[i] = Math.floor( g * tocolor[i] + (1 - g) * frcolor[i] )
			}
			elem.style.backgroundColor = '#' + dec_a2hex_s( x );
		}
	};
	setInterval( anim, 20 );
}



// Parse a mysql datetime field
// @param	String		datetime	The string containing the datetime value "YYYY-MM-DD HH-MM-SS"
// @return	Date					The parsed value
function mysqlDatetimeToDate ( datetime )
{
   var regex = /^([0-9]{2,4})-([0-1][0-9])-([0-3][0-9]) (?:([0-2][0-9]):([0-5][0-9]):([0-5][0-9]))?$/;
   var parts = datetime.replace( regex, "$1 $2 $3 $4 $5 $6" ).split( ' ' );
   return new Date( parts[0], parts[1] - 1, parts[2], parts[3], parts[4], parts[5] );
}



// Show a date relative to a given moment
// @param	Date		date		The date to represent
// @param	Date		now			Optional, used as reference, default new Date()
// @return	String					The formatted string
function showDateRelative( date, now )
{
	now = now || new Date();
	var prefix = '';
	if ( date.getFullYear() != now.getFullYear() || date.getMonth() != now.getMonth() || date.getDay() != now.getDay() )
	{
		prefix = '' + date.getFullYear() + '/' + ( date.getMonth() + 1 ) + '/' + date.getDate() + ' ';
	}
	return prefix + lpad( date.getHours(), 2, '0' ) + ':' + lpad( date.getMinutes(), 2, '0' );
}



// Left-pad a string to a desired length
// @param	String		s		The string to pad
// @param	Number		n		Final length of resulting string
// @param	String		c 		The character to repeat
// @return	String				The padded string
function lpad( s, n, c )
{
	// Make sure it is a string
	s = '' + s; 
	
	while ( s.length < n )
	{
		s = c + s;
	}
	return s
}

// Convert [255,255,255] into 'ffffff'
// TODO: Find better name
function dec_a2hex_s( a )
{
	var x = '';
	for ( var i in a )
	{
		x += lpad( dec2hex( a[i] ), 2, '0' );
	}
	return x;
}

// Convert 'ffffff' into [255,255,255]
// TODO: Find better name
function hex_s2dec_a( s )
{
	var r = [];	
	for ( var i = 0; i <= 4; i += 2 )
	{
		r.push( hex2dec( s.slice( i, i + 2 ) ) );
	}
	return r;
}

// Convert a decimal number/string into a hex string
function dec2hex( x )
{
	x = parseInt( x );
	var m = 16;
	var s = '';
	while ( x > 0 )
	{
		r = x % m;
		r = r > 9 ? "abcdef".slice( r-10, r-9 ) : '' + r;
		s = r + s;
		x = Math.floor( x / m );
	}
	return s;
}

// Convert a hex string into a decimal number
function hex2dec( x )
{
	x = '' + x;
	x = x.toLowerCase();
	s = "0123456789abcdef";
	n = 0;
	for ( var i = 0; i < x.length; i++ )
	{
		n = n * 16 + s.indexOf( x.slice(i,i+1) );
	}
	return n;
}

// Convert an array to string using a separator in between
function implode( arr, sep )
{
    var ret = ''
    for ( var i = 0; i < arr.length; i++ )
    {
        ret += ( i > 0 ? sep : '' ) + arr[i];
    }
    return ret;
}

function strip( str )
{
	str = str || '';
	str = str.replace( /^\s+/g, "" );
	str = str.replace( /\s+$/g, "" );
	return str;
}

jQuery.fn.extend({
	everyTime: function(interval, label, fn, times, belay) {
		return this.each(function() {
			jQuery.timer.add(this, interval, label, fn, times, belay);
		});
	},
	oneTime: function(interval, label, fn) {
		return this.each(function() {
			jQuery.timer.add(this, interval, label, fn, 1);
		});
	},
	stopTime: function(label, fn) {
		return this.each(function() {
			jQuery.timer.remove(this, label, fn);
		});
	}
});

jQuery.extend({
	timer: {
		guid: 1,
		global: {},
		regex: /^([0-9]+)\s*(.*s)?$/,
		powers: {
			// Yeah this is major overkill...
			'ms': 1,
			'cs': 10,
			'ds': 100,
			's': 1000,
			'das': 10000,
			'hs': 100000,
			'ks': 1000000
		},
		timeParse: function(value) {
			if (value == undefined || value == null)
				return null;
			var result = this.regex.exec(jQuery.trim(value.toString()));
			if (result && result[2]) {
				var num = parseInt(result[1], 10);
				var mult = this.powers[result[2]] || 1;
				return num * mult;
			} else {
				return value;
			}
		},
		add: function(element, interval, label, fn, times, belay) {
			var counter = 0;
			
			if (jQuery.isFunction(label)) {
				if (!times) 
					times = fn;
				fn = label;
				label = interval;
			}
			
			interval = jQuery.timer.timeParse(interval);

			if (typeof interval != 'number' || isNaN(interval) || interval <= 0)
				return;

			if (times && times.constructor != Number) {
				belay = !!times;
				times = 0;
			}
			
			times = times || 0;
			belay = belay || false;
			
			if (!element.$timers) 
				element.$timers = {};
			
			if (!element.$timers[label])
				element.$timers[label] = {};
			
			fn.$timerID = fn.$timerID || this.guid++;
			
			var handler = function() {
				if (belay && this.inProgress) 
					return;
				this.inProgress = true;
				if ((++counter > times && times !== 0) || fn.call(element, counter) === false)
					jQuery.timer.remove(element, label, fn);
				this.inProgress = false;
			};
			
			handler.$timerID = fn.$timerID;
			
			if (!element.$timers[label][fn.$timerID]) 
				element.$timers[label][fn.$timerID] = window.setInterval(handler,interval);
			
			if ( !this.global[label] )
				this.global[label] = [];
			this.global[label].push( element );
			
		},
		remove: function(element, label, fn) {
			var timers = element.$timers, ret;
			
			if ( timers ) {
				
				if (!label) {
					for ( label in timers )
						this.remove(element, label, fn);
				} else if ( timers[label] ) {
					if ( fn ) {
						if ( fn.$timerID ) {
							window.clearInterval(timers[label][fn.$timerID]);
							delete timers[label][fn.$timerID];
						}
					} else {
						for ( var fn in timers[label] ) {
							window.clearInterval(timers[label][fn]);
							delete timers[label][fn];
						}
					}
					
					for ( ret in timers[label] ) break;
					if ( !ret ) {
						ret = null;
						delete timers[label];
					}
				}
				
				for ( ret in timers ) break;
				if ( !ret ) 
					element.$timers = null;
			}
		}
	}
});

if (jQuery.browser.msie)
	jQuery(window).one("unload", function() {
		var global = jQuery.timer.global;
		for ( var label in global ) {
			var els = global[label], i = els.length;
			while ( --i )
				jQuery.timer.remove(els[i], label);
		}
	});


// rounded corners
	
	// adds 4 spans to element, positions using css in utils.css
	/*  EXAMPLE 
	
	if( $.browser.msie){	
		// add rounded corners to these elements for IE
		$('.list-context li').rounded_corners();
	};
	// add rounded corners to these elements for all browsers
	$('.fig li').rounded_corners();
	
	*/
	
	$.fn.rounded_corners = function(){
		if (!$(this).length)
		return false;
		
		$(this)
			.css({position: 'relative'})
			.hover(function(){
				$(this).addClass('cnrs-hovered');
			},function(){
				$(this).removeClass('cnrs-hovered');
			});
			
		$('<span class="cnrs cnr_nw"></span><span class="cnrs cnr_ne"></span><span class="cnrs cnr_sw"></span><span class="cnrs cnr_se"></span>')
		.css({ // will only accept numeric border widths
	 		marginTop: 		'-'+(parseInt($(this).css('border-top-width'))||0),
	 		marginRight: 	'-'+(parseInt($(this).css('border-right-width'))||0),
	 		marginBottom: 	'-'+(parseInt($(this).css('border-bottom-width'))||0),
	 		marginLeft: 	'-'+(parseInt($(this).css('border-left-width'))||0)
	 	})
		.appendTo(this);
		// ie odd values bug fix
		if( $.browser.msie && $.browser.version < 7){
			$(this).each(function(){	
				var h = $(this).height();
				if (h%2){ $('.cnr_sw',this).css({bottom: '-1px'});$('.cnr_se',this).css({bottom: '-1px'}); };
				var w = $(this).width();
				if (w%2){ $('.cnr_ne',this).css({right: '-1px'});$('.cnr_se',this).css({right: '-1px'}); };
			});
		};
	};	

// hoverintent plugin
(function($){$.fn.hoverIntent=function(f,g){var cfg={sensitivity:7,interval:100,timeout:0};cfg=$.extend(cfg,g?{over:f,out:g}:f);var cX,cY,pX,pY;var track=function(ev){cX=ev.pageX;cY=ev.pageY;};var compare=function(ev,ob){ob.hoverIntent_t=clearTimeout(ob.hoverIntent_t);if((Math.abs(pX-cX)+Math.abs(pY-cY))<cfg.sensitivity){$(ob).unbind("mousemove",track);ob.hoverIntent_s=1;return cfg.over.apply(ob,[ev]);}else{pX=cX;pY=cY;ob.hoverIntent_t=setTimeout(function(){compare(ev,ob);},cfg.interval);}};var delay=function(ev,ob){ob.hoverIntent_t=clearTimeout(ob.hoverIntent_t);ob.hoverIntent_s=0;return cfg.out.apply(ob,[ev]);};var handleHover=function(e){var p=(e.type=="mouseover"?e.fromElement:e.toElement)||e.relatedTarget;while(p&&p!=this){try{p=p.parentNode;}catch(e){p=this;}}if(p==this){return false;}var ev=jQuery.extend({},e);var ob=this;if(ob.hoverIntent_t){ob.hoverIntent_t=clearTimeout(ob.hoverIntent_t);}if(e.type=="mouseover"){pX=ev.pageX;pY=ev.pageY;$(ob).bind("mousemove",track);if(ob.hoverIntent_s!=1){ob.hoverIntent_t=setTimeout(function(){compare(ev,ob);},cfg.interval);}}else{$(ob).unbind("mousemove",track);if(ob.hoverIntent_s==1){ob.hoverIntent_t=setTimeout(function(){delay(ev,ob);},cfg.timeout);}}};return this.mouseover(handleHover).mouseout(handleHover);};})(jQuery);

/*  */
/*  */
/*  */

// Methods that do not fall inside the standard anyrest call style
var anyRest = {
	'aux':
	{
		'items': function ( xml )
		{
			var rsp = xml.getElementsByTagName( 'rsp' )[0];
			var rst = [];
			for ( var i = 0 ; i < rsp.childNodes.length ; i++ )
			{
				if ( rsp.childNodes[i].tagName == 'item' )
				{
					rst.push( rsp.childNodes[i] );
				}
			}
			return rst;
		},
		
		'status': function ( xml )
		{
			var rsp = xml.getElementsByTagName( 'rsp' );
			return rsp[0].getAttribute( 'stat' );
		},
		
		'error': function ( xml )
		{
			return ( anyRest.aux.status( xml ) != 'ok' );
		},

		'errorMsg': function ( xml )
		{
			var es = xml.getElementsByTagName( 'msg' );
			return es[0] ? es[0].getAttribute( 'msg' ) : '';
		},
		
		'skeleton': function ( type , params , callback )
		{
			params.format = params.format || "xml";
			callback = callback || function ( _ ) {};

			var no_notification_check = false;
			if (params.no_notification_check)
			{
				var no_notification_check = true;
				delete params.no_notification_check;
			}

			var opts = {
				"type": type,
				"url": 	"http://www.picnicnetwork.org/services/rest/"
			};
			
			if ( type == 'GET' ) 
			{
				opts.url += '?' + jQuery.xparam( params );
			}

			if ( type == 'POST' )
			{
				opts.data = jQuery.xparam( params );
			}

			opts.success = function restSuccess ( data )
			{
			    if (type == 'POST' && !no_notification_check) 
			    {
				anyRest.notifications.check();
			    }
			    
			    callback( data );
			}
			opts.error = function restError ( request, text, errorThrown )
			{
				if (request.status == 401)
				{
					var loc = request.getResponseHeader('X-Authorize-Location');
					if (!loc)
					{
						return false;
					}

					$( "#throbberwait" ).remove();

					var target = document;
					if (LightboxHelper.isLightbox())
					{
						target = window.parent.document;
					}

					// Append return_to to callback URI
					if (loc.match(/\?/))
					{
						loc = loc + '&';
					}
					else
					{
						loc = loc + '?';
					}
					loc = loc + 'return_to=' + target.location.href;
					target.location = loc;

				}
			}

			jQuery.ajax( opts );
		}
	}
};

anyRest.aux.concatChildNodes = function ( xmldom )
{
	var str = '';
	for ( var i = 0; i < xmldom.childNodes.length ; i++ )
	{
		str += xmldom.childNodes[i].nodeValue;
	}
	return str;
};

anyRest.aux.checkNotifications = function ( xml, target )
{
	if (!target && LightboxHelper.isLightbox())
	{
		target = window.parent.document;
	}
	else if (!target)
	{
		target = document;
	}

	$(xml).find('item').each( function() {
		var msg = $(this).find('message').text();
		var cls = $(this).find('class').text();
		var el = $('<div></div>', target).html(msg).addClass(cls).hide();
		$('.notification', target).append(el);
		el.slideDown('fast');
	});
	
};

anyRest.notifications = 
{
	check: function ()
	{
		if(LightboxHelper.isLightbox())
		{
			return window.parent.anyRest.notifications.check();
		}
		
		anyRest.get( 'anymeta.notifications.get', [], anyRest.notifications.checkCallback );
	},

	checkCallback: function (xml)
	{

		$(xml).find('item').each( function() {
			var msg = $(this).find('message').text();
			var cls = $(this).find('class').text();
			anyRest.notifications.add(msg, cls);
		});
	},

	add: function (message, cls)
	{
		if(LightboxHelper.isLightbox())
		{
			return window.parent.anyRest.notifications.add(message, cls);
		}
		
		var notification = $('.notification');
		
		$('<div></div>')
			.html(message)
			.addClass(cls)
			.appendTo(notification)
			.parent()
				.hide()
				.animate({height: 'show', opacity: 'show'}, 600, function() 
				{
					message = message.split(' ');
					var notification_time = ((message.length * .9) + 1) * 1000;
					
					$(this).oneTime(notification_time, function() 
					{ 
						notification.animate({height: 'hide', opacity: 'hide'}, 600, function()
						{
							$(this).empty();					
						});
					});
				});
	}
};

// POST
// List of anymeta REST methods, indexed by their name prefix and http verbs
var batch = [
	['anymeta.','POST',
		['edge.attribute.get'
		,'edge.attribute.set'
		,'edge.add'
		,'edge.remove'
		,'edge.order'
		,'edge.addList'
		,'error.log'
		,'tags.add'
		,'predicates.get'
		,'predicates.set'
		,'messages.send'
		,'user.check'
		,'thing.insert'
		]],
	['','POST',
		['contact.add'
		,'contact.change'
		,'contact.remove'
		]],
	['','POST',
		['jury.accuse'
		]],
	['anymeta.','GET',
		['html.template'
		,'html.scomp'
		,'search.live'
		]]]

for ( var i in batch )
{
	// For each of the REST methods, generate a javascript method with the same name. 
	// Mostly here for backwards compatibility, this additional style freedom is not
	// particularly welcome
	var prefix = batch[i][0];
	var http_method = batch[i][1];
	var lis = batch[i][2];

	for ( var j in lis )
	{
		var words = lis[j].split('.');
		var tmp = anyRest;
		for ( var k = 0; k < words.length - 1; k++ )
		{
			if ( !tmp[words[k]] )
			{
				tmp[words[k]] = {};
			}
			tmp = tmp[words[k]];
		}
		
		tmp[words[words.length-1]] = makeRestMethod( http_method, prefix + lis[j] );
		
	}
}

// Produces a ajax method to communicate with anymeta REST services
function makeRestMethod( http_method, rest_method )
{
	return function ( params, callback )
	{
		params.method = rest_method;
		anyRest.aux.skeleton( http_method, params, callback );
	}
}

anyRest.service = function ( name, opts, callback )
{
	opts.method = name;
	anyRest.aux.skeleton( 'POST' , opts , callback );
};

anyRest.call = function ( opts, callback )
{
	anyRest.aux.skeleton( 'POST' , opts , callback );
};

anyRest.post = function ( name, opts, callback )
{
	opts.method = name;
	anyRest.aux.skeleton( 'POST' , opts , callback );
};

anyRest.get = function ( name, opts, callback )
{
	opts.method = name;
	anyRest.aux.skeleton( 'GET' , opts , callback );
};

/**/
/*  */

/*  */

// Notes
// ULs and LIs margin values are set to 0
// LIs get display: block
// Style the menu and get it working first with CSS, then add this JS to make it nicer

// External callback to initialize the widget
// @param	mixed	container	jQuery selector for the object to be used
// @param	dom		context		DOM object used as jQuery context. Defaults to document.body
function init_menu( container, context )
{
	context = context || document.body;
	
	$( container, context ).each( 
		function () 
		{
			var opts = "";
			opts = jQuery.attrOptions( this , "menu" );
			new Menu( this, opts );
		}
	);
}

// External callback for the documentation functions
// @return  object Structure containing 'examples' of usage, 'about' message, default 'opts', and 'classes' (never used...)
function docs_menu()
{
	var a = "Dynamic cascading menu item. Notes: Get it working with CSS only first, then use this file to make it nice.";
	var e = [
		['<ul class="do_menu" menu="animationOver: {ldelim}height:\'show\',opacity: .9{rdelim}, speedOver: 400, dropShadows: true">\n<li>A.root\n\t<ul>\n\t<li>A.1</li>\n\t<li>A.2</li>\n\t</ul>\n</li>\n<li>B.root\n\t<ul>\n\t<li>B.1</li>\n\t</ul>\n</li>\n</ul>',
		"Superfish menu"]
	];
	var c = [];
	
	var o = 
	[[ "hoverClass", 	'sfHover', 		 				"the class applied to hovered list items" ]
	,[ "pathClass", 	'overideThisToUse',				"the class you have applied to list items that lead to the current page" ]
	,[ "pathLevels", 	1,								"the number of levels of submenus that remain open or are restored using pathClass" ]
	,[ "delay",  		400,							"the delay in milliseconds that the mouse can remain outside a submenu without it closing" ]
	,[ 'animationOver',	{height:'show'},				"exmp: {opacity:.85}, {height:'show'}, Animation on mouse over. See jQueryÍs .animate() method" ]
	,[ "speedOver",		300, 			 				"speed of the mouse over animation (in milliseconds). Equivalent to second parameter of jQueryÍs .animate() method" ]
	,[ 'animationOut',	{opacity:'hide',height:'hide'},	"{opacity:'hide'}, {height:'hide'}, Animation on mouse out" ]
	,[ "speedOut", 		200, 			 				"speed of the mouse out animation (in milliseconds). Equivalent to second parameter of jQueryÍs .animate() method" ]
	,[ "autoArrows",	false, 			 				"if true, arrow mark-up generated automatically. Will add .sf-with-ul to anchor of parent. " ]
	,[ "dropShadows",	false, 			 				"completely disable drop shadows by setting this to false" ]
	];
	
	return { 'examples': e, 'about': a, 'opts': o, 'classes': c };
}


// Process the options for this widget (cached)
// @param   object  o   Widget options extracted from the html
// @return  object      Default options overriden by html options
function opts_menu( o )
{
	return apply_options( docs_menu().opts, o );
}



function Menu( container, opts )
{
	opts			= opts_menu( opts );
	this.container	= container;
	this.opts		= opts;
	
	
	$(container).superfish(
	{
		hoverClass: 	this.opts.hoverClass,
		pathClass: 		this.opts.pathClass,
		pathLevels: 	this.opts.pathLevels,
		delay: 			this.opts.delay,
		animationOver:	this.opts.animationOver,
		speedOver:		this.opts.speedOver,
		animationOut:	this.opts.animationOut,
		speedOut:		this.opts.speedOut,
		autoArrows: 	this.opts.autoArrows,
		dropShadows:	this.opts.dropShadows
	}	
	);	
	
}

;(function($){
	$.fn.superfish = function(op){

		var sf = $.fn.superfish,
			c = sf.c,
			$arrow = $(['<span class="',c.arrowClass,'"> &#187;</span>'].join('')),
			over = function(){
				var $$ = $(this), menu = getMenu($$);
				clearTimeout(menu.sfTimer);
				$$.showSuperfishUl().siblings().hideSuperfishUl();
			},
			out = function(){
				var $$ = $(this), menu = getMenu($$), o = sf.op;
				clearTimeout(menu.sfTimer);
				menu.sfTimer=setTimeout(function(){
					o.retainPath=($.inArray($$[0],o.$path)>-1);
					$$.hideSuperfishUl();
					if (o.$path.length && $$.parents(['li.',o.hoverClass].join('')).length<1){over.call(o.$path);}
				},o.delay);	
			},
			getMenu = function($menu){
				var menu = $menu.parents(['ul.',c.menuClass,':first'].join(''))[0];
				sf.op = sf.o[menu.serial];
				return menu;
			},
			addArrow = function($a){ $a.addClass(c.anchorClass).append($arrow.clone()); };
			
		return this.each(function() {
			var s = this.serial = sf.o.length;
			var o = $.extend({},sf.defaults,op);
			o.$path = $('li.'+o.pathClass,this).slice(0,o.pathLevels).each(function(){
				$(this).addClass([o.hoverClass,c.bcClass].join(' '))
					.filter('li:has(ul)').removeClass(o.pathClass);
			});
			sf.o[s] = sf.op = o;
			
			$('li:has(ul)',this)[($.fn.hoverIntent && !o.disableHI) ? 'hoverIntent' : 'hover'](over,out).each(function() {
				if (o.autoArrows) addArrow( $('>a:first-child',this) );
			})
			.not('.'+c.bcClass)
				.hideSuperfishUl();
			
			var $a = $('a',this);
			$a.each(function(i){
				var $li = $a.eq(i).parents('li');
				$a.eq(i).focus(function(){over.call($li);}).blur(function(){out.call($li);});
			});
			o.onInit.call(this);
			
		}).each(function() {
			menuClasses = [c.menuClass];
			if (sf.op.dropShadows  && !($.browser.msie && $.browser.version < 7)) menuClasses.push(c.shadowClass);
			$(this).addClass(menuClasses.join(' '));
		});
	};

	var sf = $.fn.superfish;
	sf.o = [];
	sf.op = {};
	sf.IE7fixAdd = function(){
		var o = sf.op;
		if ($.browser.msie && $.browser.version > 6 && o.dropShadows )
			this.addClass(sf.c.shadowClass+'-off');
		};
	sf.IE7fixRemove = function(){
		var o = sf.op;
		if ($.browser.msie && $.browser.version > 6 && o.dropShadows )
			this.removeClass(sf.c.shadowClass+'-off');
		};
	sf.c = {
		bcClass     : 'sf-breadcrumb',
		menuClass   : 'sf-js-enabled',
		anchorClass : 'sf-with-ul',
		arrowClass  : 'sf-sub-indicator',
		shadowClass : 'sf-shadow'
	};
	sf.defaults = {
		disableHI	: true,		// true disables hoverIntent detection
		onInit		: function(){}, // callback functions
		onBeforeShow: function(){},
		onShow		: function(){},
		onHide		: function(){}
	};
	$.fn.extend({
		hideSuperfishUl : function(){
			var o = sf.op,
			not = (o.retainPath===true) ? o.$path : '';
			o.retainPath = false;
			
			var $ul = $(['li.',o.hoverClass].join(''),this).add(this).not(not).find('>ul:visible')
			sf.IE7fixAdd.call($ul);		
			
			// important fix for ff2 opacity issues on a mac
			if (navigator.appVersion.indexOf("Mac")!=-1 && ($.browser.mozilla && $.browser.version.substr(0,3) == 1.8)) o.animationOut.opacity = 1;
			
			$ul.animate(o.animationOut,o.speedOut,function(){$(this).parent().removeClass(o.hoverClass); sf.IE7fixRemove.call($ul);});
			
			o.onHide.call($ul);
			return this;
		},
		showSuperfishUl : function(){
			var o = sf.op,
				sh = sf.c.shadowClass+'-off',
				$ul = this.addClass(o.hoverClass)
					.find('>ul:hidden').css('visibility','visible');
					
			sf.IE7fixAdd.call($ul);
			o.onBeforeShow.call($ul);
			
			// important fix for ff2 opacity issues on a mac
			if (navigator.appVersion.indexOf("Mac")!=-1 && ($.browser.mozilla && $.browser.version.substr(0,3) == 1.8)) o.animationOut.opacity = 1;
						
			$ul.animate(o.animationOver,o.speedOver,function(){ sf.IE7fixRemove.call($ul); o.onShow.call($ul); });
			return this;
		}
	});

})(jQuery);

/*  */
//
// External callback to initialize the widget
// @param	mixed	container	jQuery selector for the object to be used
// @param	dom		context		DOM object used as jQuery context. Defaults to document.body
function init_tooltip ( container, context )
{
	context = context || document.body;
	
	$(container, context).each(function()
	{
		var opts = jQuery.attrOptions( this , 'tooltip' );
		new Tooltip( this, opts );
	});
}

// External callback for the documentation functions
// @return  object      Structure containing 'examples' of usage, 'about' message, default 'opts', and 'classes' (never used...)
function docs_tooltip()
{
	var a = 'Mouse-pivoted tooltip that behaves intelligently against screen limits';
	var e = 
	[
		[ '<a class="do_tooltip" title="my tooltip text"', "simple popup using a custom div id" ],
	];
	
	var c = [];
	
	var o = 
	[ 
	 	[ 'timeover'			,50,				  					'Number of milliseconds before tooltip appears' ],
		[ 'effectover'			,{opacity: "show"}, 				 	'Action when hovering over. {opacity: \'toggle\', height: \'toggle\'}' ],
	 	[ 'effectover_duration'	,150,	  								'Duration of out effect' ],
	 	[ 'effectout' 			,{opacity: "hide"},  				  	'Action when hovering out. Other examples can be hide(), fadeOut(\'slow\')' ],
	 	[ 'effectout_duration'	,100,	  								'Duration of hover effect' ],
	 	[ 'offy'				,0, 				  					'Vertical distance from the mouse pointer' ],
	 	[ 'offx'				,5, 				  					'Horizontal distance from the mouse pointer' ],
	 	[ 'follow'				,true,	  								'Follow the mouse cursor' ],
		[ 'extra_image'			,'',									'Add an extra image to the ' ],
	 	[ 'inevent'				,'mouseover', 	  	  					'Bind this action when hovering over, choose mousemove when tooltip should move with mouse' ],
	 	[ 'outevent' 			,'mouseout', 		  					'Bind this action when hovering out' ],
	 	[ 'width'				,'auto', 			  					'Width of the tooltip' ],
	 	[ 'selectable'			,1, 	  	  	  					    'Set to true when it should be possible to select the text in the tooltip' ],
	 	[ 'cursor'				,'', 	  	  	  					    'tooltip parent cursor' ],
	 	[ 'maxwidth'			,'300px',								'the maximum width of the tooltip' ],
	 	[ 'type'				,'',									'is the type an input?' ],
	 	[ 'html'				,'',									'The html to be injected into the tooltip div' ],
	 	[ 'dom_id'				,'',									'what dom id to use for filling the tooltip' ]
	];
	
	return { 'examples': e, 'about': a, 'opts': o, 'classes': c };
}

// Process the options for this widget
// @param   object  o   Widget options extracted from the html
// @return  object      Default options overriden by html options
function opts_tooltip( o )
{
	return apply_options( docs_tooltip().opts, o );
}

function Tooltip( container, options )
{
	options			= opts_tooltip( options );
	this.options	= options;
    this.container  = container;
        
	$(container).tooltip(
    {
    	timein: 			options.timeover,
    	ineffect: 			options.effectover,
    	ineffectduratrion: 	options.effectover_duration, 
    	outeffect: 			options.effectout,
    	outeffectduratrion: options.effectout_duration, 
    	offsetY:			options.offy,
    	offsetX: 			options.offx,
    	follow: 			options.follow,
    	extra_image: 		options.extra_image,
	   	inevent: 			options.inevent,
    	outevent: 			options.outevent,
    	width: 				options.width,
    	cursor:				options.cursor,
    	maxwidth:			options.maxwidth,
    	type:				options.type,
    	html:				options.html,
    	dom_id:				options.dom_id
    });
}

(function(jQuery) 
{
	jQuery.fn.tooltip = function(options)
	{
		var defaults = {
			timein: 			300, 
			ineffect: 			{opacity: 'show'},
			ineffectduration: 	200, 
			outeffect: 			{opacity: 'hide'},
			outeffectduration: 	300, 
			offsetY: 			-15,
			offsetX: 			15,
			follow: 			true,
			extra_image: 		'<span class="tooltip-arrow"></span>',
			inevent: 			'mouseover',
			outevent: 			'mouseout',
			width: 				'auto',
			cursor: 			'',
			maxwidth: 			'330px',
			type: 				'',
			html: 				'',
			dom_id:				''
  		};
  		
  		var options = jQuery.extend(defaults, options);
  			
  		return this.each(function() 
		{
			obj = jQuery(this);
		
			obj.css({cursor: options.cursor});			
			
			if(this.title == '' && !options.dom_id && !options.html)
			{
				obj.unbind(options.inevent, options.outevent);
				return false;
			}

			obj.bind(options.inevent, function(e) 
			{
				this.tip = this.title;
				
				if(options.html)
				{
					var tip_content = options.html;
				}
				else if(options.dom_id)
				{
					var tip_content = $('#' + options.dom_id).html();
				}
				else
				{
					var tip_content = this.tip;
				}
				
				this.title = "";
				
				tip = jQuery('<div></div>')
						.addClass('tooltip')
						.html(options.extra_image + tip_content)
						.css({top: e.pageY + options.offsetY, left: e.pageX + options.offsetX, width: options.width, maxWidth: options.maxwidth });
				
				jQuery(document.body).append(tip);
								
				if(options.follow)
				{
					jQuery(document).bind('mousemove', function(e)
					{ 
						tip.css({top: e.pageY + options.offsetY});
						
						if(e.pageX + tip.width() > jQuery.windowSize().w)
						{
							tip.css({left: e.pageX - options.offsetX - tip.width() - 30});
						}
						else if(e.pageX + tip.width() < jQuery.windowSize().w)
						{
							tip.css({left: e.pageX + options.offsetX });
						}
						else
						{
							tip.css({left: e.pageX + options.offsetX });
						}
					});
				}
				else
				{
					var left = jQuery.getPosition(this).x;
					var top  = jQuery.getPosition(this).y;
					
					tip.css({top: top - tip.height() - 10});
					
					if(left + tip.width() > jQuery.windowSize().w)
					{
						tip.css({left: left - (obj.width() / 2) - (tip.width() / 2 )});
					}
					else
					{
						tip.css({left: left + (obj.width() / 2) - (tip.width() / 2 )});
					}
									
					if(options.type == 'input')
					{
						tip.css({ left: left + obj.width() + 15, top: top});
					}
				}
								
				tip.oneTime(options.timein, function()
				{
					jQuery(this).stop().animate(options.ineffect, options.ineffectduration);
				});
			});
			
			obj.bind(options.outevent, function(e) 
			{
				tip.stop().animate(options.outeffect, options.outeffectduration, function()
				{
					jQuery(this).remove();
				});
				
				jQuery.fn.tooltip.destroy();
				this.title = this.tip;
			});
			
			jQuery.fn.tooltip.destroy = function()
			{
				obj.unbind(options.inevent, options.outevent);
				jQuery(document).unbind('mousemove');
			}
		});			
	}
})(jQuery);
//
// 
//function init_imgzoomer(container, context)
//{
//	context = context || document.body;
//
//	$(container, context).each(function() 
//	{
//		var opts = "";
//		opts = jQuery.attrOptions(this, "imgzoomer");
//		new Imgzoomer(this, opts);
//	});
//}
//
//function docs_imgzoomer()
//{
//	var a = "An image zoomer.";
//	var e = [ ['<img class="do_imgzoomer" src="" />', "Image zoomer"] ];
//	var c = [];
//	
//	var o = 
//	[
//		[ "showZoomer", 	'true',		"Show the zoomer in the bar" ],
//		[ "showMakeBig", 	'true',		"Show the button to make the image big" ]
//	];
//	
//	return { 'examples': e, 'about': a, 'opts': o, 'classes': c };
//}
//
//function opts_imgzoomer(o)
//{
//	return apply_options(docs_imgzoomer().opts, o);
//}
//
//function Imgzoomer(container, opts)
//{
//	opts			= opts_imgzoomer( opts );
//	this.container	= container;
//	this.opts		= opts;
//	
//	$(container).img_zoomer(
//	{
//		showZoomer: this.opts.showZoomer,
//		showMakeBig: this.opts.showMakeBig	
//	});	
//}

;(function($)
{
	$.fn.img_zoomer = function(options) 
	{
		//if($.browser.msie) 
		//{
		//	try { document.execCommand("BackgroundImageCache", false, true); } catch(err) { }
		//}
		
		var defaults = {
			showZoomer: false,
			showMakeBig: true
  		}
  		
  		var options = jQuery.extend({}, defaults, options);

		return this.each(function() 
		{
			var origImg 		= $(this);	
			var imgWidth 		= origImg.width();
			var imgHeight 		= origImg.height();
			var img_id 			= origImg.attr('usemap').replace('#',''); // find the <map> that belongs to this image
			var usemap 			= $('map[name='+ img_id +']');
			var imgVars 		= usemap.attr('title').split(",");
			var imgSrc 			= imgVars[2];
			var originalWidth 	= parseInt(imgVars[0]);
			var originalHeight 	= parseInt(imgVars[1]);
			var perc 			= 1;
		
			if(imgWidth != originalWidth)
			{			
				perc = (imgWidth / originalWidth);
			}
	
			// if image does not need zoomer, finish here, and skip to the next iteration
			if(!perc || perc > .85 || imgWidth < 220)
			{
				return true;
			}
			
			var a = false;
		
			// create wrapper div if it not in the template
			if(!origImg.parents('.img-wrapper').length) 
			{
				// wrap the image in a div for annotation positioning		
				wrapper = $('<div class="img-wrapper"></div>')
							.attr({rel: img_id})
							.css({width: imgWidth, height: imgHeight});
							
				if(origImg.parents('a').length)
				{
					a = origImg.parents('a');
					origImg.parents('a').wrap(wrapper);
				}
				else
				{
					origImg.wrap(wrapper);
				}
			}
			else
			{
	 			if(origImg.parents('a').length)
				{
					a = origImg.parents('a');
				}
			}
		
		
			var wrapper = origImg.parents('.img-wrapper');
		
			if($('.img-toolbar', wrapper).length)
			{
				var toolbar = $('.img-toolbar', wrapper);
			}
			else
			{
				// find toolbar div, add it to the wrapper
				var toolbar = wrapper.siblings('.img-toolbar').prependTo(wrapper);
			}
		
			// bind hovers
			wrapper
				.mouseover(function()
				{
					// load hi res image if present
					if(!$('.img-zoomer-hi-res',this).attr('src') && perc < .85)
					{
						imgViewerFunctions.loadBigImage($('.img-zoomer-hi-res', this));
					}
												
					$(this).addClass('img_hovered');			
					
					if($.browser.msie) 
					{
						toolbar.show();
						return false;
					}
					
					if(toolbar.length && toolbar.html() != '') 
					{
						toolbar.slideDown(200);
						
						if(toolbar.css('display') == 'none') 
						{
							toolbar.show();
						}
					}			
				})
				.mouseout(function()
				{	
					$(this).removeClass('img_hovered');
				
					if($.browser.msie) 
					{
						toolbar.animate({opacity: 1}, 800, function()
						{
							if(toolbar.length && !objParent.hasClass('img_hovered'))
							{
								toolbar.slideUp(200);
							}
						});
					
						return false;
					}
				
					$(this).animate({opacity: 1}, 800, function()
					{
						toolbar = $('.img-toolbar', this);
						if(!$(this).hasClass('img_hovered')) 
						{
							if(toolbar.length)
							{
								toolbar.slideUp(200);
							}
						}
					});
				});
		
			toolbar
				.prependTo(wrapper)
				.mouseover(function()
				{
					wrapper.addClass('img_hovered');
					if(options.showZoomer)
					{
						wrapper.addClass('zooming-img');
					}
				})
				.mouseout(function()
				{
					if(options.showZoomer)
					{
						if($('.ui-slider', this).slider('value') <= 1)
						{
							wrapper.removeClass('zooming-img');
						}
					}
				});

			toolbar = $(toolbar, wrapper);
		
			// add tags button, set binds and title	
			$('.edit-img-tags', toolbar).text($('.edit-img-tags', toolbar).text().split(',')[0]);
		
			var img 	= $('<img />').addClass('img-zoomer-hi-res').css({position: 'absolute'}).hide().insertAfter(origImg);	
			var loader 	= $('<span></span>', origImg).addClass('loading-toolbar').hide().prependTo(toolbar);
		
			// if zooming tools don't exist, add them to the toolbar
			if(!$('.img-zoomer-tools',toolbar).length)
			{
				var zoomer = $('<div></div>').addClass('img-zoomer-tools').hide().prependTo(toolbar);
			}
			else 
			{
				var zoomer = $('.img-zoomer-tools', toolbar);
			}
		
			// build zoom slider and add to zoomer
			if(options.showZoomer)
			{			
				$('<div></div>').slider({
					max: (perc * 100)+100,
					start: function()
					{
						origImg.hide();
						img.show();
								
						if(!wrapper.hasClass('zooming-img'))
						{
							wrapper.addClass('zooming-img');
						}
					},
					slide: function()
					{	
						if($('.ui-slider',toolbar).slider('value') > 1)
						{
							$('.img-zoomer-tool-reset', toolbar).animate({opacity: 1}, 200);
							$('.annotation', wrapper).hide();
						}
					
						var val 		= $(this).slider('value');
						var newPerc 	= (val / 100 ) + 1;			
						var percWidth 	= (imgWidth * newPerc);
						var percHeight 	= (imgHeight * newPerc);
						var left 		= (imgWidth - percWidth) / 2;
						var top 		= (imgHeight - percHeight) / 2;
					
						img.css({width: percWidth,height: percHeight,top: top,left: left});			
					},
					stop: function()
					{									
						if(a)
						{
							a.click(function()
							{ 
								return false 
							});
						}
					
						if($(this).slider('value') < 1)
						{
						 	imgViewerFunctions.resetImage($(this), img, origImg, toolbar, wrapper, a);
						}
					}		
				})
				.appendTo(zoomer);
			
				$('<span>+</span>').addClass('img-zoomer-tool-add').mousedown(function() { $(this).siblings('.ui-slider').slider("moveTo", "+=15"); }).appendTo(zoomer);
				$('<span>-</span>').addClass('img-zoomer-tool-less').mousedown(function() { if($(this).siblings('.ui-slider').slider('value') < 1) { imgViewerFunctions.resetImage($(this).siblings('.ui-slider'), img, origImg, toolbar, wrapper, a); } else { $(this).siblings('.ui-slider').slider("moveTo", "-=15"); } }).prependTo(zoomer);	
				$('<span>r</span>').addClass('img-zoomer-tool-reset').mouseup(function() { imgViewerFunctions.resetImage($(this).siblings('.ui-slider'), img, origImg, toolbar, wrapper, a); }).css({opacity: .4}).prependTo(zoomer);
			}
			
			if(options.showMakeBig)
			{
				$('<span></span>').addClass('img-zoomer-tool-fullscreen').click(function() { imgViewerFunctions.imageGoFullsize(img, wrapper, origImg, toolbar); }).prependTo(zoomer);
			}
		});
	}
})(jQuery);
	
imgViewerFunctions = 
{
	loadBigImage: function(img)
	{
		var wrapper = img.parents('div');
		var toolbar = $('.img-toolbar', wrapper);
		var zoomer 	= $('.img-zoomer-tools', wrapper).hide();
		var loader  = $('.loading-toolbar', wrapper).show();
		var imgVars	= $('map[name='+ wrapper.attr('rel') +']').attr('title').split(",");
		var imgSrc 	= imgVars[2];
		
		// load hi-res image	
		img
			.attr({src: imgSrc})
			.load(function()
			{
				loader.hide();
				zoomer.fadeIn(300);
			
				$(this)
					.attr({rel: $(this).width()+','+$(this).height()})
					.css({width: wrapper.width(),height: wrapper.height()})
					.unbind('load');
			})
			.draggable(
			{
				start: function(e, ui)
				{
					uiWidth  = - (ui.helper.width() - wrapper.width());
					uiHeight = - (ui.helper.height() - wrapper.height());
				},
				
				drag: function(e, ui)
				{
					if(ui.position.left >= 0) 		ui.position.left = 1;
					if(ui.position.top >= 0) 		ui.position.top = 1;
					if(ui.position.left <= uiWidth) ui.position.left = (uiWidth + 1);
					if(ui.position.top <= uiHeight)	ui.position.top = (uiHeight + 1);
				}
			});
	},
	
	resetImage: function(slider, img, origImg, toolbar, wrapper, a)
	{
		a = a || false;
		
		if(slider.slider('value') > 1)
		{
			slider.slider("moveTo", "1");
		}
		
		img.hide()
	 	origImg.show()
	 	
	 	$('.img_zoomer_tool_reset', toolbar).css({opacity: .4});
		$('.annotation', wrapper).show().animate({opacity: 1},300,function()
		{
			wrapper.removeClass('zooming-img');
		});
		
		if(a)
		{
			a.unbind('click');
		}
	},
	
	imageGoFullsize: function(img, wrapper, origImg, toolbar)
	{
		var zoomDimen 		= img.attr('rel').split(',');
		var zoomImgWidth 	= zoomDimen[0];
		var zoomImgHeight 	= zoomDimen[1];
		var fullWidth 		= zoomImgWidth;
		var fullHeight 		= zoomImgHeight;

		// never wider than window
		if(zoomImgWidth > $(window).width())
		{
			fullWidth  = $(window).width() - 40;
			fullHeight = zoomImgHeight * (fullWidth / zoomImgWidth);	
		}		
		
		// re-position when window resizes
		$(window).resize(function()
		{
			if($(window).width() < zoomImgWidth)
			{
				$('.full-screen-img').animate({ 
					width: 	($(window).width() - 40),
					height: zoomImgHeight * (($(window).width() - 40) / zoomImgWidth),
					left: 	($(window).width() / 2) - (($(window).width() - 40) / 2)
				}, 200);
			} 
			else
			{
				$('.full-screen-img').animate({ 
					width: 	zoomImgWidth,
					height: zoomImgHeight,
					left: 	($(window).width() / 2) - (zoomImgWidth / 2)
				}, 200);
			}			
		});
 		
		leftPos = ($(window).width() / 2) - (fullWidth / 2);
		topPos  = $(window).scrollTop() + 10;
				
		// overlay
		$('<span class="popup-overlay"></span>')
			.css({opacity: .5, height: $(document).height(), backgroundColor: '#000000'})
			.click(function()
			{
				imgViewerFunctions.closeBigImage(toolbar, origImg, wrapper);	
			})
			.appendTo('body');
			
		// clone the large image, and animate to full screen
		img
			.clone()
			.show()
			.addClass('full-screen-img')
			.css({left: wrapper.offset().left, top: wrapper.offset().top})
			.appendTo('body')
			.animate({width: fullWidth, height: fullHeight, left: leftPos, top: topPos}, 300, 'easeOutCirc')
			.click(function()
			{
				imgViewerFunctions.closeBigImage(toolbar, origImg, wrapper);
			});
	},
	
	closeBigImage: function(toolbar, origImg, wrapper)
	{
		$('.popup-overlay').fadeOut(300,function()
		{
			$(this).remove() 
		});
		
		$('.full-screen-img').animate({width: origImg.width(),height: origImg.height(), left: wrapper.offset().left,top: wrapper.offset().top}, 200, function()
		{ 
			$(this).remove(); 
			toolbar.show();
		})	
	}
}
// 
// 
//function init_imgannotations(container, context)
//{
//	context = context || document.body;
//
//	$(container, context).each(function() 
//	{
//		var opts = "";
//		opts = jQuery.attrOptions(this, "imgannotations");
//		new Imgannotations(this, opts);
//	});
//}
//
//function docs_imgannotations()
//{
//	var a = "An image annotator.";
//	var e = [ ['<img class="do_imgannotations" src="" />', "Image Annotator"] ];
//	var c = [];
//	var o = [];
//	return { 'examples': e, 'about': a, 'opts': o, 'classes': c };
//}
//
//function opts_imgannotations(o)
//{
//	return apply_options(docs_imgannotations().opts, o);
//}
//
//function Imgannotations(container, opts)
//{
//	opts			= opts_imgannotations( opts );
//	this.container	= container;
//	this.opts		= opts;
//	
//	$(container).img_annotations();
//}

;(function($)
{
	$.fn.img_annotations = function() 
	{
		return this.each(function() 
		{
			var obj	  	= $(this);
			var imgId 	= $(this).attr('usemap').replace("#","");
			objParent 	= obj.parent();
				
			// create label for future use
			if(!$('.annotation-label').length)
			{
				$('<span class="annotation-label"></span>').css({opacity: .9}).appendTo('body');
			}
	
			// remove usemap
			var imgWidth 		= $(this).width();
			var imgHeight 		= $(this).height();
			var usemap 			= $('map[name='+imgId+']');			// find the <map> that belongs to this image
			var imgVars			= usemap.attr('title').split(",");
			var originalWidth	= parseInt(imgVars[0]);
			var originalHeight	= parseInt(imgVars[1]);
			var percWidth 		= 1;
			var percHeight 		= 1;
			
			if(imgWidth	 != originalWidth){ percWidth = (imgWidth / originalWidth) }
			if(imgHeight != originalHeight){ percHeight = (imgHeight / originalHeight) }
			
			// remove <a> from around image if in edit mode
			if(obj.parents('a:first').length) 
			{
				attachmentLink = obj.parents('a:first').clone();
			}
			
			// create wrapper div if it not in the template
			if(!obj.parents('.img-wrapper').length) 
			{
				// wrap the image in a div for annotation positioning		
				img_wrapper = $('<div class="img-wrapper '+imgId+'_wrap"></div>').addClass('img-with-annotations').attr({rel: imgId}).css({width: imgWidth,height: imgHeight});
				
				if(obj.parents('a:first').length)
				{
					obj.parents('a:first').wrap(img_wrapper);
				}
				else
				{
					obj.wrap(img_wrapper);
				}
			}
			else 
			{
				img_wrapper = obj.parents('.img-wrapper').addClass('img-with-annotations '+imgId+'_wrap').attr({rel: imgId});
			}
			
			objParent = obj.parents('.img-wrapper');
			
			// find and add toolbar
			if($('.img-toolbar',objParent).length)
			{
				toolbar = $('.img-toolbar',objParent);
			}
			else
			{
				// find toolbar div, add it to the wrapper
				toolbar = objParent.siblings('.img-toolbar:first').prependTo(objParent);
			}
			
			// Set up image 
			objParent.siblings('.help-text:first').slideUp();
			objParent.siblings('.temp-actor-list').remove()
			objParent.removeClass('in_edit');
			
			actorList = $('.list-actor ul');
			
			highlight_annotation_from_li(actorList, objParent, toolbar);
			
			// Create annotations from <map><area>'s
			if(obj.siblings('.annotation').length < 1)
			{
				$("area", usemap).each(function(i)
				{	
					// post ids = thing_id , edge_id, actor id
					var postIds 	= $(this).attr('rel').split(",");
					var thing_id 	= postIds[0];
					var actor_id 	= postIds[1];
					var edge_id 	= postIds[2];
					var coords 		= $(this).attr('coords').split(",");			
					var width 		= (coords[2] - coords[0]) * percWidth; // find the saved dimensions for the annotation
					var height 		= (coords[3] - coords[1]) * percHeight;
					var left 		= parseInt(coords[0]) * percHeight;
					var top 		= parseInt(coords[1]) * percHeight;
					var full_title  = $(this).attr("title").split(",");
			
					// create annotation
					annotation = $('<a id="annot'+actor_id+'" rel="'+postIds+'" href="'+$(this).attr("href")+'"><span class="drag">'+full_title+'</span></a>');
		
					$('span.drag',annotation).css({width: width, height: height, zIndex: ( 9998 - ( width + height ) ) });
					
					// IE fix
					if($.browser.msie) 
					{
						$('span.drag',annotation).css({backgroundColor: '#ccc',opacity: 0});
						$('body').addClass('isIE');
					}
					
					// make outer wrapper wider for border
					width = (width + 2);
							
					$(annotation)
						.addClass('annotation')
						.css({width: width, top: top, left: left, zIndex: ( 9998 - ( width + height ) ) })
						.prependTo('.'+imgId+'_wrap');
				});
			}
		
			// Bind image and annotation hover states
			annotations = $('.annotation', objParent);
			annotations.css({opacity: 1})
				// pause before fading
				.animate({opacity: 1},1500, function()
				{
					// fade annotations out
					$(this).animate({opacity: 0},500,function()
					{
						// if image has gone into edit, and show annotations
						if(img_wrapper.hasClass('in_edit'))
						{
							$(this).css({opacity: 1});
						}
					});
				})
				.each(function()
				{
					// show label on hover
					$(this).hover(function()
					{
						annotations.css({opacity: 1});
									
						if(!labelText) 
						{
							var labelText = $(this).text();
							$('span.annotation-label').text(labelText);
						}
				
						$(this).mousemove(function(e){
							$('span.annotation-label').css({ left: e.pageX, top: e.pageY, zIndex: 9999999 });
						});
						
						// underline li in actor list that matches annotation title
						var text = $('span:first',this).text().split(',');
				
						$('li:contains('+text[0]+') a',actorList).css({textDecoration: 'underline'});		
					},
					function()
					{
						hide_label();
						$('li a',actorList).css({textDecoration: 'none'});
					})
				});
			
			obj.removeAttr('alt');
			
			objParent
				.mouseover(function()
				{
					annotations = $('.annotation', this);
					toolbar 	= $('.img-toolbar', this);
					
					$(this).addClass('img_hovered');
					
					if(!objParent.hasClass('zooming-img'))
					{
						annotations.css({opacity: 1});
					}
					
					// load hi res image if present and has not been loaded yet
					if($('.img-zoomer-hi-res',this).length && !$('.img-zoomer-hi-res',this).attr('src'))
					{
						imgViewerFunctions.loadBigImage($('.img-zoomer-hi-res',this));
					}
					
					if($.browser.msie && !objParent.hasClass('zooming-img')) 
					{
						annotations.show();
						toolbar.show();
						return false;
					}
					
					if(toolbar.length && toolbar.html() != '') 
					{
						toolbar.slideDown(200);
						if(annotations.css('opacity') < 1 && toolbar.css('display') == 'none') 
						{
							toolbar.show();
						}
					}
				})
				.mouseout(function()
				{	
					annotations = $('.annotation',this);
					toolbar 	= $('.img-toolbar',this);
					
					$(this).removeClass('img_hovered');
					
					if($.browser.msie) 
					{
						annotations.hide();
						toolbar.animate({opacity: 1},800,function()
						{
							if(toolbar.length && !objParent.hasClass('img_hovered'))
							{
								toolbar.slideUp(200);
							}
						});
						
						return false;
					}
					
					$(this).animate({opacity: 1},800,function()
					{
						annotations = $('.annotation', this);
						toolbar 	= $('.img-toolbar', this);
	
						if(!$(this).hasClass('img_hovered')) 
						{
							annotations.animate({opacity: 0},200);	
							if(toolbar.length)
							{
								toolbar.slideUp(200);
							}
						}
					});
				});
					
			// remove areas for edit mode - needed for IE
			$("area", usemap).remove();
		});
	}
})(jQuery);

function hide_label()
{
	$('span.annotation-label').css({top: -99999});
}

// highlight annotation when actor li is hovered
function highlight_annotation_from_li(actorList, objParent, toolbar)
{
	$('li', actorList).unbind('mouseover', 'mouseout');
	
	$('li', actorList).hover(function()
	{
		if(!$(".annotation", objParent).length)
		{
			return false;
		}
		
		$('a', this).removeAttr('title');
		
		var li_text = $('span', this).text().replace('\n',' ').split(' ');
		
		if(li_text.length <= 1)
		{
			var search_text = li_text[0]
		}
		else
		{
			var search_text = li_text[0]+" "+li_text[1]
		}
		
		$(".annotation:contains("+search_text+")",objParent).addClass('hovered');
		objParent.trigger('mouseover');
		
		toolbar.css({top: -9999});	
	}, 
	function()
	{
		if( !$(".annotation",objParent).length )
		return false;
		
		$(".annotation",objParent).removeClass('hovered');
		objParent.trigger('mouseout');
		
		toolbar.hide().css({top: 0});
	});
}

//
/*  $Id: lightbox.js 43279 2009-08-24 09:37:35Z blaise $ */


// STATIC 

window.lightbox_pointer = null;


// Static hook to kill the currently active lightbox
// @return  undefined
function lightbox_remove()
{
	window.widget_manager.stop( 'lightbox' );
}


// Static hook to add an item to the top bar of the currently active lightbox
// @return  undefined
function lightbox_bar_add( x )
{
	$( "#lightbox-wrap-buttons" ).append( x );
}

// Static hook to add an button to the top bar of the currently active lightbox
// @return  undefined
function lightbox_bar_add_button( value, functionName )
{
	var str =  "<button onclick='lightbox_pointer." + functionName + "();'>" + value + "</button>";
	lightbox_bar_add( str );
}

// External callback to initialize the widget
// @param   mixed   container   jQuery selector for the object to be used
// @param   dom     context     DOM object used as jQuery context. Defaults to document.body
// @return  undefined
function init_lightbox( container, context )
{
	context = context || document;

	$( container, context ).each(
		function ()
		{
			var opts	= jQuery.attrOptions( this , 'lightbox' );
			var ajax	= jQuery.attrOptions( this , 'lightbox_ajax' );
			
			opts.ajax = ajax;
			
			new Lightbox( this, opts );
		}
	);
}


// External callback for the documentation functions
// @return  object      Structure containing 'examples' of usage, 'about' message, default 'opts', and 'classes' (never used...)
function docs_lightbox()
{
	var a = "A slightly too versatile lightbox implementation...";
	var e = [];
	var c = [];
	var s = jQuery.label( 'lightbox.cancel', 'Cancel' );
	var o = 
	[[ 'url', 			null, 	"Url to be loaded. Defaults to the href attribute in the element, and then to null" ]
	,[ 'lbl_cancel',	s, 		"Cancel button name" ]
	,[ 'width',			"60%", 	"Css width of the lightbox" ]
	,[ 'height',		"60%", 	"Css height of the lightbox" ]
	,[ 'freeze',		false, 	"Whether the lightbox size should be fixed instead of adapting to content"]
	,[ 'ajax',			{}, 	"Parameters to be added to lightbox url" ]
	,[ 'className',		"", 	"Class that will be added to the lightbox frame" ]
	];
	
	return { 'examples': e, 'about': a, 'opts': o, 'classes': c };
}


// Process the options for this widget (cached)
// // @param   object  o   Widget options extracted from the html
// // @return  object      Default options overriden by html options
function opts_lightbox( o )
{
	return apply_options( docs_lightbox().opts, o );	
}


function Lightbox( container, options )
{
	options			= opts_lightbox( options, container );
	options.url		= options.url || container.href || null;
	options.width	= ( "" + options.width  ).replace( 'px' , '' );
	options.height	= ( "" + options.height ).replace( 'px' , '' );

	this.options	= options;
	
	container = $( container ).get(0);
	container.obj_lightbox = this;

	var self = this;
	$( container ).click( 
		function (event)
		{
			window.lastEvent = event;
			self.show();
			return false;
		}
	);

	if ( options.showonload )
	{
		// bug#1732
		//
		// http://support.microsoft.com/default.aspx/kb/927917
		// http://clientside.cnet.com/code-snippets/manipulating-the-dom/ie-and-operation-aborted/
		//
		// The "operation is aborted" when we generate DOM content in the parent of a container of a script.
		// It is also aborted when we try to add DOM content to a container that is not yet ready.
		// We fix this IE problem by generating the lightbox inside the container that has the showonload parameter.
		//
		if ($.browser.msie)
		{
			this.show(container);
		}
		else
		{
			this.show();
		}
		container.blur();	
	}
}


// Callback for when the widget gets out of focus
// // @param   string  name    The name of the widget type
// // @param   object  obj     The object replacing this one in the widget focus
// // @return  bool            True if ajax request made, false otherwise
Lightbox.prototype.widgetBlur = function ( name, obj )
{
	if ( obj == null )
	{
		this.remove();
	}
	return false;
};



// Create the dom structure necessary to display lightbox
// @param container optional container where to create the lightbox
// @return  undefined
Lightbox.prototype.domCreate = function ( container )
{
	var self = this;
	
	var lo	= document.createElement( "div" );
	var lf	= document.createElement( "div" );
	var lw	= document.createElement( "div" );
	var lc	= document.createElement( "div" );
	var wb	= document.createElement( "div" );
	
	var cb	= $('<div></div>')
					.attr({id: 'lightbox-cancel', title: this.options.lbl_cancel})
					.click(function() 
					{ 
						self.remove();
					});
	
	lo.id	= "lightbox-overlay";
	lf.id	= "lightbox-frame";
	lw.id	= "lightbox-window";
	lc.id	= "lightbox-bar";
	wb.id	= "lightbox-wrap-buttons";
	
	lf.className = this.options.className;
	
	lf.style.position	= "absolute";
	lw.style.width		= "100%";
	lw.style.height		= "100%";
	
	if (container)
	{
		$( container ).append( lo );
		$( container ).append( lf );
	}
	else
	{
		$( "body" ).append( lo );
		$( "body" ).append( lf );
	}
		
	$( lf ).append( lc );
	$( lf ).append( lw );
	$( wb ).append( cb );
	$( lc ).append( wb );
	$( lc ).addClass('clearfix');
	
};


// Clear the timer used to resize the lightbox
// @return  undefined
Lightbox.prototype.clearResizeInterval = function()
{
	if ( window.lightbox_pointer && window.lightbox_pointer.interval )
	{
		clearInterval( window.lightbox_pointer.interval );
		window.lightbox_pointer.interval = null;
	}
};


// Remove this lightbox
// @return  undefined
Lightbox.prototype.remove = function ()
{
	this.clearResizeInterval();
	
	// $( document ).unkeyup();

	// Timeout is needed when this function is called from within the lightbox itself
	var self = this;
	setTimeout(
		function () 
		{
			$('#lightbox-window,#lightbox-frame,#lightbox-overlay').remove();
			self.showObjects();
		},
		500
	);
};


// Get the height of the page inside the lightbox 
// @return  undefined
Lightbox.prototype.size_of_content = function ()
{
	var fr = document.getElementById("lightbox-iframeContent").contentWindow;
	var h1 = 0;
	var h2 = 0;
	
	if ( fr && fr.document )
	{
		if ( fr.document.documentElement && fr.document.documentElement.scrollHeight )
		{
			h1 = fr.document.documentElement.scrollHeight;
		}

		if ( fr.document.body && fr.document.body.scrollHeight )
		{
			h2 = fr.document.body.scrollHeight;
		}
	}

	var h = Math.max( h1, h2 );
	var s = jQuery.getSize( document.getElementById( 'lightbox-frame'  ) );

	h = s.h < h ? h + 60 : 0;
	
	return h;
};


// Resize the lightbox in accordance to its contents
Lightbox.prototype.size = function ()
{
	if ( document.getElementById( "lightbox-iframeContent" ) )
	{
		var h = this.size_of_content();
		h > 0 && $( "#lightbox-frame" ).css( 'height', h + 'px' );
	}
};


// Convert a size in "%" into a value in "px"
// @param   string      x   Percentage value
// @param   integer     y   Absolute value
// @return  integer         Resulting pixel value
Lightbox.prototype.pixelizeSize = function ( x, y )
{
	x = parseInt( x.match( /\d+/ ) );
	x = Math.max( x , 0 );
	x = Math.min( x , 100 );
	x = Math.round( y * x / 100 );
	return x;
};


// Determine the width (horizontal position)
// @return  undefined
Lightbox.prototype.h_position = function ()
{
	var l = 0;
	var w = (typeof this.options != 'undefined') ? this.options.width : '60%';
	var d = jQuery.windowSize();

	if ( w.match( /\%/ ) )
	{
		w = this.pixelizeSize( w, d.w );
	}

	l = ( d.w - w ) / 2;

	$( "#lightbox-frame"  ).css( 'left',  l + 'px' );	
	$( "#lightbox-bar" ).css( 'width', w  - 2 + 'px' );
	$( "#lightbox-window" ).css( 'width', w + 'px' );
	
	this.overlaySize();	
};


// Determine the position  (vertical position)
// @return  undefined
Lightbox.prototype.v_position = function ()
{
	var pagesize   = jQuery.windowSize();	
	var pageScroll = jQuery.pageScrollTop();
	
	var t = 0;
	var h = (typeof this.options != 'undefined') ? this.options.height : '60%';
	var d = jQuery.windowSize();

	if ( h.match( /\%/ ) )
	{
		h = this.pixelizeSize( h, d.h );
	}

	t = ( d.h - h ) / 2;
	
	t = jQuery.pageScrollTop() + t;

	$( "#lightbox-frame" ).css({
		top:    t + 'px',
		height: h + 'px'
	});
};


// Determine size of dark mask 
// @return  undefined
Lightbox.prototype.overlaySize = function ()
{
	if ( window.innerHeight && window.scrollMaxY )
	{	
		yScroll = window.innerHeight + window.scrollMaxY;
	}
	else if ( document.body.scrollHeight > document.body.offsetHeight )
	{
		 // all but Explorer Mac
		yScroll = document.body.scrollHeight;
	}
	else
	{ 
		// Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
		yScroll = document.body.offsetHeight;
  	}

	$( "#lightbox-overlay" ).css( {height: yScroll + "px", cursor: 'pointer' })
							.bind('click', function()
							{
								Lightbox.prototype.remove();
							});
};


// Update de size of the lightbox at a regular time interval
// @return  undefined
Lightbox.prototype.setRegularUpdate = function ()
{
	var self 	= this;
	var fr 		= document.getElementById("lightbox-iframeContent");
	fr.interval = setInterval( 
		function () 
		{ 
			self.size() 
		}, 
		800 
	);
};

Lightbox.prototype.hideObjects = function ()
{
	var self = this;
	$( 'embed,select' ).each(
		function ()
		{
			this.preLightboxVisibility = $( this ).css( 'visibility' );
			this.style.visibility = 'hidden';
		}
	);
};

Lightbox.prototype.showObjects = function ()
{
	var self = this;
	$( 'embed,select' ).each(
		function ()
		{
			this.style.visibility = this.preLightboxVisibility;
		}
	);
};


// Show the lightbox
// @param container, optional container where to generate the lightbox
// @return  undefined
Lightbox.prototype.show = function ( container )
{
	try 
	{
		window.widget_manager.widgetFocus( 'lightbox', this );
		
		$( "#lightbox-overlay" ).get( 0 ) || this.domCreate(container);
		
		var scroll = jQuery.pageScrollTop();

/*		window.scrollTo( 0, scroll );*/

		this.overlaySize();

		this.options.before_ajax && this.options.before_ajax( this.options );
		
		// Let the request know it is inside a lightbox
		this.options.ajax.in_lightbox = '1';
		
		var url		= jQuery.addToQuery( this.options.url , jQuery.param( this.options.ajax ) );
		var rand	= Math.floor( Math.random() * 100000 );
		
		if ( document.getElementById( "lightbox-iframeContent" ) == null )
		{
			$("#lightbox-window").append(
				"<iframe src='" + url + "' name='frame" + rand + "' id='lightbox-iframeContent' frameborder='0' border='0' style='width: 100%; height: 100%;'></iframe>" );
		}
		else
		{
			$( "#lightbox-iframeContent").get(0).src = url;
		}
		
		this.v_position();
		this.h_position();
		
 		$( "#lightbox-window,#lightbox-frame" ).css( 'display' , 'block' );

		this.options.freeze || this.setRegularUpdate();
		
		window.lightbox_pointer = window.frames['frame'+rand];
		
		var self = this;
		$( window ).resize(function () 
		{
			self.h_position();
			self.v_position();
		});	
		
		setTimeout(
			function ()
			{
				self.hideObjects();
			},
			20
		)
	}
	catch( e ) 
	{
		jQuery.warn( "Lightbox" , "An error occurred when opening the lightbox." , e );
	}
};

var LightboxHelper = 
{
	isLightbox: function(uri) 
	{
		if (!window.parent || window.parent.document.location.href == document.location.href)
		{
			return false;
		}
		if (!uri) uri = document.location.href;
		if (uri.match(/\bin_lightbox=1\b/))
		{
			return true;
		}
		
		return false;
	},

	openLightbox: function(url, container, options)
	{
		options['url'] = url;
		var lb = new Lightbox (container, options);
		lb.show();
	}
};

/**/
//
// External callback to initialize the widget
// @param	mixed	container	jQuery selector for the object to be used
// @param	dom		context		DOM object used as jQuery context. Defaults to document.body
function init_dialog ( container, context )
{
	context = context || document.body;
	
	$(container, context).each(function()
	{
		var opts = jQuery.attrOptions( this , 'dialog' );
		new Dialog( this, opts );
	});
}

// External callback for the documentation functions
// @return  object      Structure containing 'examples' of usage, 'about' message, default 'opts', and 'classes' (never used...)
function docs_dialog()
{
	var a = 'dialog is a lightweight version of lightbox';
	var e = 
	[
		[ '<a class="do_dialog" dialog="url: ..."', "simple popup using a custom div id" ],
	];
	
	var c = [];
	
	var o = 
	[ 
	 	[ 'width'			,'500px',  							'The width of the dialog' ],
		[ 'height'			,'auto',				 			'The height of the dialog' ],
	 	[ 'url'				,'',	  							'Url to load' ],
	 	[ 'dom_id'			,'',	  							'DOM id to load' ],
	 	[ 'ineffect' 		,{opacity: 'show', height: 'show'}, 'Action when clicked on the launch button. {opacity: \'show\', height: \'show\'}' ],
	 	[ 'ineffectTime'	,250,	  							'Duration of the startup effect' ],
	 	[ 'outeffect'		,{opacity: 'hide'}, 'Action when clicked on the close button. {opacity: \'hide\', height: \'hide\'}' ],
	 	[ 'outeffectTime'	,100, 				  				'Duration of the close effect' ],
	 	[ 'dialog'			,true,	  							'Is this box a dialog? It will become draggable and you get an overlay div, like a real lightbox.' ],
	 	[ 'direct'			,false,	  							'Fire the dialog without a click event'],
	 	[ 'title'			,'',	  							'The title of the dialog if there is no object specified'],
	 	[ 'overlay'			,true,	  							'Show the overlay' ],
	 	[ 'callback'		,'',	  							'Function that gets called after the dialog is destroyed' ],
	 	[ 'click'			,true,  							'Does the dialog show up on click?' ],
	 	[ 'hover'			,false,  							'Does the dialog show up on hover?' ],
	 	[ 'underObj'		,false,  							'Show the dialog under the object you click or hover' ],
	 ];
	
	return { 'examples': e, 'about': a, 'opts': o, 'classes': c };
}

// Process the options for this widget
// @param   object  o   Widget options extracted from the html
// @return  object      Default options overriden by html options
function opts_dialog( o )
{
	return apply_options( docs_dialog().opts, o );
}

function Dialog( container, options )
{
	options			= opts_dialog( options );
	this.options	= options;
    this.container  = container;
        
	jQuery(container).any_dialog(
    {
    	width: 			options.width,
		height: 		options.height,
		url:			options.url,
		dom_id:			options.dom_id,
		ineffect:		options.ineffect,
		ineffectTime:	options.ineffectTime,
		outeffect:		options.outEffect,
		outeffectTime:	options.outEffectTime,
		dialog:			options.dialog,
		direct:			options.direct,
		title:			options.title,
		callback:		options.callback,
		click:			options.click,
		hover:			options.hover,
		overlay:		options.overlay,
		underObj:		options.underObj
	});
}

;(function(jQuery)
{
	jQuery.fn.any_dialog = function(options)
	{
  		var options = jQuery.extend({}, jQuery.fn.any_dialog.defaults, options);

		return this.each(function() 
		{			
			if(options.direct)
			{
				jQuery.addDialog(options, jQuery(this));
			}
			
			if(options.hover)
			{
				jQuery(this).hoverIntent(
				{
					sensitivity: 	7, 											 // number = sensitivity threshold (must be 1 or higher)    
					interval: 		100, 										 // number = milliseconds for onMouseOver polling interval    
					over: 			function()
									{
										jQuery.addDialog(options, jQuery(this)); // function = onMouseOver callback (REQUIRED)
									}, 				    
					timeout: 		500, 										 // number = milliseconds delay before onMouseOut    
					out: 			function()
									{
																	 			 // function = onMouseOut callback (REQUIRED) 
									}    
				});
			}
			
			if(options.click)
			{
				jQuery(this).click(function()
				{
					jQuery.addDialog(options, jQuery(this))
				});
			}			
		});
	}
	
	jQuery.extend(
	{                 
    	addDialog: function(options, obj)
		{
			var theBox, closeButton, templateWrapper, dialogContent, overlayDiv, clickedText, dragHandle, dialogTop, dialogLeft, boxHeight, buttonText, obj = obj || '';
			
			opts = options;
			
			if(!jQuery('.dialog').length)
			{
				clickedText		= jQuery('<div></div>').addClass('dialog-loader-text loading');
				closeButton		= jQuery('<span></span>').addClass('dialog-close').html('x');
				templateWrapper	= jQuery('<div></div>').addClass('dialog-template-wrapper');
				overlayDiv		= jQuery('<div></div>').addClass('dialog-overlay').css({zIndex: 35000, opacity: .75, height: $(document).height() });
			
				if(typeof opts.height == 'string')
				{
					boxHeight = 'auto';
				}
				else
				{
					boxHeight = opts.height.replace('px', '') - 52;
				}
			
				if(obj != '')
				{
					buttonText	= obj.text();
					
					if(opts.underObj)
					{
						dialogTop = parseInt(obj.position().top) + parseInt(obj.height());
					}
					else
					{
						dialogTop = obj.position().top;
					}
					
					dialogLeft	= obj.position().left;

				}
				else
				{
					dialogTop	= jQuery(window).scrollTop() + Math.floor(jQuery(window).height() / 5);
					dialogLeft	= (jQuery(window).width() / 2) - (parseInt(opts.width) / 2);
				}
				
				if (opts.title)
				{
					buttonText	= opts.title;
				}

				theBox 			= jQuery('<div></div>')
									.addClass('dialog')
									.css({position: "absolute", top: dialogTop, left: dialogLeft, width: opts.width, height: opts.height});
				
				dialogContent 	= jQuery('<div></div>')
									.addClass('dialog-content')
									.css({height: boxHeight})
									.append(clickedText)
									.appendTo(theBox);
				
				document.onkeypress = function(e)
				{
					if($.browser.msie) 
					{
						var key = e.which
					}
					else 
					{
						key = e.keyCode
					} 
					
					if(key == 27)
					{
						jQuery.fn.any_dialog.destroy(theBox);
					}
				}
				
				if(opts.dialog)
				{
					dragHandle	= jQuery('<div></div>')
									.addClass('dialog-drag-handle')
									.html('<span class="dialog-drag-handle-text">'+buttonText+'</span>')
									.mousedown(function()
									{
										$(this).addClass('pressed');
									})
									.mouseup(function()
									{
										$(this).removeClass('pressed');
									});
				
					theBox
						.draggable({
							handle: dragHandle
						})
						.prepend(dragHandle);
				}
					
				if(opts.overlay)
				{
					jQuery(document.body).append(overlayDiv);
				}				
				
				if(jQuery.browser.msie) 
				{
					theBox.css({background: 'none', padding: 0});
					
					if(jQuery.browser.version < 7)
					{
						jQuery('select').each(function()
						{
							jQuery(this).css({visibility: 'hidden'});
						});
					}
				}
				
				jQuery(document.body).append(theBox);
				
				theBox.animate(opts.ineffect, opts.ineffectTime, function()
				{
					var setContent = function(html)
					{
						dialogContent.append(templateWrapper);										
						
						clickedText.animate({opacity: 'hide'}, opts.outeffectTime, function()
						{
							templateWrapper
								.html(html)
								.animate(opts.ineffect, opts.ineffectTime, function()
								{
									var left = jQuery(theBox).position().left;
									var top  = jQuery(theBox).position().top;
									
									if(top + jQuery(this).height() > jQuery(window).height() && top > (jQuery(window).height() / 2))
									{
										var bodyTop = jQuery(document.body).scrollTop() + Math.floor(jQuery(window).height() / 5);
										theBox.animate({top: bodyTop}, 300);
										$('html, body').animate({scrollTop: bodyTop}, 300);
									}
									
									if(left + jQuery(this).width() > jQuery(window).width())
									{
										theBox.animate({left: left - (jQuery(this).width() / 2) - 20}, 150);
									}
								});
								
							if(!opts.showMarginLastParagraph) 
	                            { 
                                	jQuery('.dialog-template-wrapper p:last', theBox).css({margin: 0}); 
                                } 
							
							jQuery('.dialog-template-wrapper input:first', theBox).focus();
								
							if(opts.dialog)
							{
								dragHandle.append(closeButton);
							}
							else
							{
								templateWrapper.prepend(closeButton);
							}
						});
					};
				
					if (opts.url)
					{
						jQuery.ajax({
							type: 'GET',
							url: opts.url,
							success: setContent
						});
					} else if (opts.dom_id)
					{
						setContent( $(opts.dom_id).clone(true) );
					} else 
					{
						setContent('<p>Specify content using dom_id or url</p>');
					}
				});

				
				closeButton.click(function()
				{
					jQuery.fn.any_dialog.destroy(theBox);	
				});
			}
		}
	});
	
	jQuery.fn.any_dialog.destroy = function(theBox, callback)
	{
		theBox = theBox || $('.dialog');
		callback = callback || '';
		
		var closeButton 	= jQuery('.dialog-close', theBox);
		var templateWrapper = jQuery('.dialog-template-wrapper', theBox);
		var overlayDiv		= jQuery('.dialog-overlay');
		
		templateWrapper.animate(opts.outeffect, opts.outeffectTime, function()
		{
			theBox.animate(opts.outeffect, opts.outeffectTime, function()
			{
				theBox.remove();
				overlayDiv.fadeOut(300, function()
				{
					overlayDiv.remove();
				});
				
				if(jQuery.browser.msie && jQuery.browser.version < 7) 
				{
					jQuery('select').each(function()
					{
						jQuery(this).css({visibility: 'visible'});
					})
				}
				
				if(callback)
				{
					callback();
				}
				
				if(opts.callback)
				{
					opts.callback();
				}
			});
		});
	}
	
	jQuery.fn.any_dialog.defaults = {
		width: 			'500px',
		height: 		'auto',
		url:			'',
		dom_id:			'',
		ineffect:		{opacity: 'show', height: 'show'},
		ineffectTime:	100,
		outeffect:		{opacity: 'hide'},
		outeffectTime:	100,
		dialog:			false,
		direct:			false,
		title:			'',
		callback:		''
	}

})(jQuery);
//