// 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 );
};

/**/
/* $Id: link.js 40440 2009-01-27 12:01:33Z bas $ */
/*  */



// 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_link( container, context )
{
	context = context || document;
	
	$( container, context ).each(
		function () 
		{
			var options		= jQuery.attrOptions( this , 'link' );
			options.ajax	= jQuery.attrOptions( this , 'link_ajax' );
			options.extra	= jQuery.attrOptions( this , 'link_extra' );
			new Link( this, options );
		}
	);
}



// External callback for the documentation functions
// @return  object      Structure containing 'examples' of usage, 'about' message, default 'opts', and 'classes' (never used...)
function docs_link()
{
	var a = 'Link things';
	var e = [];
	var c = [];
	var o = 
	[[ "id", 			null,				"Id of the element to hide/show during linking - defaults to id of container extracted from the element" ]
	,[ "url",			null,				"Used for connection via url (eg. module)" ]
	,[ "silent",		false,				"Don't jQuery.log on ajax error" ]
	,[ "service",		"anymeta.edge.add",	"Name of REST service" ]
	,[ "dropDown",		null,				"jQuery selector of autocomplete dom element -- used with the imputless dropdow menu" ]
	,[ "showonload",	false,				"Start widget immediatly as page loads" ]
	,[ "toggle",		false,				"Link element switches between two views"]
	,[ "toggle_hide",	null, 				"DOM id of object to hide on link_show"]
	,[ "toggle_show",	null, 				"DOM id of object to show on link_show"]
	,[ "ajax",			{},					"Extra ajax parameters" ]
	,[ "extra",			{},					"Parameters passed to the scomp when creating an instance of the newly-linked element" ]
	,[ "notification",	'',					"The notification the system should give after succes of the ajax call" ]
	];
	
	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_link( o )
{
	window.link_opts = window.link_opts || docs_link().opts;
	return apply_options( window.link_opts, o );
}



function Link( container, options )
{
	options			= opts_link( options, container );
	options.id		= options.id 	|| container.id 	|| null;
	options.url		= options.url	|| container.href	|| null;
	this.options	= options;
	this.container	= container;
	var self = this;
	$( container ).click( 
		function (event) 
		{
			window.lastEvent = event;
			self.show();
			return false;
		}
	);

	this.options.showonload && this.show();
}



// Hide the widget
// @return undefined
Link.prototype.hide = function ()
{
	if ( this.options.ajax.link_autocomplete )
	{
		$( "#link_overlay" ).remove();
		this.showObjects();
	}
	$( "#link_" + this.options.id ).remove();
	$( "#" + this.options.id ).show();		


	// could be an automplete box
	$( "#autocomplete_" + this.options.id ).remove();
};


// Static hook to hide the current widget
// @return undefined
function link_hide()
{
	window.widget_manager && window.widget_manager.stop( 'link' );
}



// 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
Link.prototype.widgetBlur = function ( name, obj )
{
	if ( obj != this )
	{
		this.hide();
	}
	return false;
};



// Show the widget
// @return undefined
Link.prototype.show = function ()
{
	window.widget_manager && window.widget_manager.widgetFocus( 'link', this );

	if ( this.options.id && document.getElementById( 'link_' + this.options.id ) )
	{
		return false;
	}

	if ( this.options.url != null )
	{
		this.linkByURL();
	} 
	else if ( this.options.dropDown )
	{
		var auto = $( this.options.dropDown ).get( 0 ).obj_autocomplete;
		auto.show();
	} 
	else 
	{
		this.linkDirectly();
	}
};



// Connect by loading a given scomp first
// @return undefined
Link.prototype.linkByURL = function ()
{
	// connection via url (eg. module)
	var arrowOffset	= this.container.offsetWidth;
	var pos 		= jQuery.elemInWindow( this.container );

	if ( this.options.ajax.link_autocomplete )
	{
		// create overlay link holder 9 i this case for the autocomplete 
		$( "#link_overlay" ).get( 0 ) || this.domCreate( );
		this.overlaySize();

		var ac 		 = document.createElement( 'div' );
		ac.id 		 = 'autocomplete_' + this.options.id;
		ac.className = 'autocomplete-holder';
		$( document.body ).append( ac );

		$( ac )
			.css( { position: "absolute", "z-index": 102, left: 0, top: pos.t + 30 + "px" } )
			.load( jQuery.addToQuery( this.options.url , jQuery.param( this.options.ajax ) ) );
		
		var self 	= this;
		setTimeout(
			function ()
			{
				self.hideObjects();
			},
			20
		);
	}
	else
	{
		var link = document.createElement( 'div' );
		link.id = 'link_' + this.options.id;
		link.className = 'link-holder';
		$( document.body ).append( link );

		$( link )
			.css( { position: "absolute", left: pos.l + "px", top: pos.t + "px" } )
			.load( jQuery.addToQuery( this.options.url , jQuery.param( this.options.ajax ) ) );
	}
};

// Create the dom structure necessary to display lightbox
// @param container optional container where to create the lightbox
// @return  undefined
Link.prototype.domCreate = function ( container )
{
	var lo	= document.createElement( "div" );
	
	lo.id	= "link_overlay";

	if (container)
	{
		$( container ).append( lo );
	}
	else
	{
		$( "body" ).append( lo );
	}
	
};

// Determine size of dark mask 
// @return  undefined
Link.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;
  	}

	$( "#link_overlay" ).css( "height" , yScroll + "px" );
};


// Connect directly
// @return undefined
Link.prototype.linkDirectly = function ()
{
	// direct connection
	$( this.container ).append(
		'<span id="throbberwait"><img src="http://fast.mediamatic.nl/f/dxdx/image/throbberwait.gif" alt="" /></span>'
	);
	
	var self 	= this;
	anyRest.service( this.options.service, this.options.ajax, 
		function ( xml ) 
		{
			if ( anyRest.aux.error( xml ) )
			{
				// on error
				var errorMsg = anyRest.aux.errorMsg( xml ) || 'Something went wrong, try again later.';
				$( "#throbberwait" ).remove();
				jQuery.warn( "Link" , null , errorMsg );
			}
			else
			{
				self.options.callback && self.options.callback( xml );
				// on ok
				if ( self.options.writetxt != null ) 
				{
					$( self.container ).remove();
					$( "#throbberwait" ).empty();
					$( "#throbberwait" ).prepend( self.options.writetxt );
				} 
				else if ( self.options.rebuild_scp ) 
				{
					$( "#throbberwait" ).remove();

					rebuild_scp( self.options );
				}
				else if ( self.options.toggle && self.options.toggle_hide != null && self.options.toggle_show != null) 
				{
					$( "#throbberwait" ).remove();
					$( "#" + self.options.toggle_show ).show();
					$( "#" + self.options.toggle_hide ).hide();
				}
				else
				{
					$( "#throbberwait" ).remove();
				}
				
				//if(self.options.notification)
				//{
				//	anyRest.notifications.add(self.options.notification,'success');	
				//}
			}
		}
	);
};

Link.prototype.hideObjects = function ()
{
	$( 'embed,select' ).each(
		function ()
		{
			this.preLinkVisibility = $( this ).css( 'visibility' );
			this.style.visibility = 'hidden';
		}
	);
};

Link.prototype.showObjects = function ()
{
	$( 'embed,select' ).each(
		function ()
		{
			this.style.visibility = this.preLinkVisibility;
		}
	);
};


// STATIC FUNCTIONS


// Static callback for injecting the link results on the page
// Removed from template file because of scoping problems
function edge_clone( oldid , xmlsubs , xml )
{
	var o = document.getElementById( oldid );
	var x = o.cloneNode(true);
	var newid = x.getAttribute( 'newid' );
	
	$( x )
		.attr( 'id', newid )
		.addClass( 'sortable-item' )
		.removeClass( 'link_new' );
	$( o ).before( x );

	for ( var i = 0 ; i < xmlsubs.length ; i++ )
	{
		var r = new RegExp( '\\$' + xmlsubs[i] , 'g' );
		x.innerHTML = x.innerHTML.replace( r , $( xmlsubs[i] , xml ).text() );
		x.id 		= x.id.replace( r , $( xmlsubs[i] , xml ).text() );
	}

	$( x ).show();
	init_unlink( x );
	$( x ).SortableAdd();
}



// Static callback for injecting the link results on the page
function edge_inject( ops, xml )
{
	var dom_id		 = ops.dom_id;
	var file		 = ops.file;
	var manipulation = ops.manipulation || '';
	var new_base	 = ops.new_base 	|| 'list_item';

	if ( jQuery.isObject( dom_id ) )
	{
		for ( var i = 0 ; i < dom_id.length ; i++ )
		{
			fl = jQuery.isObject( file ) 		 ? file[i] 		   : file;
			nb = jQuery.isObject( new_base ) 	 ? new_base[i] 	   : new_base;
			mp = jQuery.isObject( manipulation ) ? manipulation[i] : manipulation;

			_edge_inject( ops, xml, dom_id[i], fl, nb, mp );
		}	
	}
	else
	{
		_edge_inject( ops, xml, dom_id, file, new_base, manipulation );
	}
}

function _edge_inject ( ops, xml, dom_id, file, new_base, manipulation )
{
	var rsp			 = xml.getElementsByTagName( 'rsp' 			)[0];
	var edg_id		 = rsp.getElementsByTagName( 'edg_id' 		)[0].firstChild.nodeValue;
	var predicate_id = rsp.getElementsByTagName( 'predicate_id' )[0].firstChild.nodeValue;
	var thg_id		 = rsp.getElementsByTagName( 'subject' 		)[0].firstChild.nodeValue;
	var list_id		 = rsp.getElementsByTagName( 'object' 		)[0].firstChild.nodeValue;

	// Renamed the thg_id paramater to subject_id
	// thg_id is stronger then list_id in template fetching
	// so the subject list item tempalte is used
	// When a person is added to an article the article list item is fetched
	// TODO: lets see if this works out

	var scomp_options = jQuery.assocMerge( 
		ops.extra,
		{ 	'name': 		file, 
			'edg_id':		edg_id,
			'predicate_id':	predicate_id,
			'list_id':		list_id,
			'new_base':		new_base,
			'subject_id':	thg_id,
			'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 );
					//var v = ts.firstChild.nodeValue;
					if ( manipulation == 'replace' )
					{
						$( x ).after ( v ).remove();
					}
					else if ( manipulation == 'inner' )
					{
						$( x ).innerHTML( v );
					}
					else if ( manipulation == 'before' )
					{
						$( x ).before( v );
					}
					else if ( manipulation == 'after' )
					{
						$( x ).after( v );
					}
					else if ( manipulation == 'prepend' )
					{
						$( x ).prepend( v );
					}
					else if ( manipulation == 'append' )
					{
						$( x ).append( v );
					}
					else
					{
						$( x ).before( v );
					}
			
					y = document.getElementById( new_base + '' + edg_id );
			
					init_widgets( y );
					//init_unlink( '.do_unlink' , y );
					//init_lightbox( '.do_lightbox' , y );
					//$( y ).SortableAdd();
			
				}
				else
				{
					jQuery.log('Internal error: did not get a template from the server or no dom id. Sorry...');
				}
			}
		}
	);
}


// Static callback for injecting the link results on the page
// Give access rights to a user
function auth_add( usr_id )
{
	var current = window.widget_manager && window.widget_manager.current( 'link' );
	opts = {};
	opts['usr_id']		= usr_id;
	opts['aco_section']	= current.options.ajax.aco_section;
	opts['aco_value']	= current.options.ajax.aco_value;
	opts['id']			= current.options.ajax.id;

	anyRest.service( 'accesscontrol.user.thing.add', opts,
		function ( xml ) 
		{
			if ( anyRest.aux.error( xml ) )
			{
				jQuery.warn( "Link", null, anyRest.aux.errorMsg( xml ) );
			}
			else
			{
				auth_inject( xml );	
			}
		}
	);
	link_hide();
}



// Inject a scomp for a user that as access rights 
// Static callback for injecting the link results on the page
function auth_inject( xml )
{
	var current		 = window.widget_manager && window.widget_manager.current( 'link' );
	var dom_id		 = current.options.dom_id;
	var manipulation = current.options.manipulation;
	var new_base	 = current.options.new_base 	|| 'list_item';

	var rsp		= xml.getElementsByTagName( 'rsp' 	 )[0];
	var usr_id	= rsp.getElementsByTagName( 'usr_id' )[0].firstChild.nodeValue;
	var gut_id 	= rsp.getElementsByTagName( 'gut_id' )[0].firstChild.nodeValue;

	var scomp_options = jQuery.assocMerge(
		current.options.extra,
		{ 'gut_id': gut_id,
		  'usr_id': usr_id
		}
	);	

	anyRest.service( 'anymeta.html.scomp', scomp_options, 
		function ( xml ) 
		{
			if ( anyRest.aux.error( xml ) )
			{
				jQuery.warn( "Link", null, anyRest.aux.errorMsg( xml ) );
			}
			else 
			{
				//var html = xml.getElementsByTagName( 'html' )[0];
				//$( '#' + dom_id ).append( html.firstChild.nodeValue );
				//init_unlink( '.do_unlink' );

				var ts = xml.getElementsByTagName( 'html' )[0];
				x = document.getElementById( dom_id );
			
				if ( ts && x )
				{
					var v = anyRest.aux.concatChildNodes( ts );
					//var v = ts.firstChild.nodeValue;
					if ( manipulation == 'replace' )
					{
						$( x ).after ( v ).remove();
					}
					else if ( manipulation == 'inner' )
					{
						$( x ).innerHTML( v );
					}
					else if ( manipulation == 'before' )
					{
						$( x ).before( v );
					}
					else if ( manipulation == 'after' )
					{
						$( x ).after( v );
					}
					else if ( manipulation == 'prepend' )
					{
						$( x ).prepend( v );
					}
					else if ( manipulation == 'append' )
					{
						$( x ).append( v );
					}
					else
					{
						$( x ).before( v );
					}
			
					y = document.getElementById( new_base + '' + gut_id );
			
					init_widgets( y );
				}
				else
				{
					jQuery.log('Internal error: did not get a template from the server or no dom id. Sorry...');
				}
			}
		}
	);
}



// Static callback for injecting the link results on the page
function edge_add( ajax )
{
	var current = window.widget_manager && window.widget_manager.current( 'link' );
	extra = current.options;
	ajax['id']		  = extra.ajax.id;
	ajax['predicate'] = extra.ajax.predicate;
	ajaxNotice = new AjaxNotice();

	ajaxNotice.start( '...' );
	anyRest.edge.add( ajax , 
		function ( xml ) 
		{
			ajaxNotice.stop();

			if ( anyRest.aux.error( xml ) ) 
			{
				jQuery.warn( "Link" , null , anyRest.aux.errorMsg( xml ) );
			}
			else
			{
				edge_inject( extra , xml  );
	 		}

		}
	);

	link_hide();
}

function edge_add_lb ( ajax )
{
	var current = window.widget_manager && window.widget_manager.current( 'link' );
	var extra   = current.options;

	var id	  = extra.ajax.id;
	var pred  = extra.ajax.predicate;
	var title = ajax.object_title;

	//var kind  = ajax.object_kind;
	//var type  = ajax.object_type;

	url = '/add/object/ID/PRED';
	
	url = url.replace(/ID/,   escape(id));
	url = url.replace(/PRED/, escape(pred));
	
	b = document.createElement( 'button' );
	b.style.display = 'none';
	
	jQuery( document.body ).append( b );
	
	var l_opt 	 = {};
	l_opt.freeze = true;
	l_opt.width  = '60%';
	l_opt.height = '80%';
	l_opt.url	 = url;
	
	if (title)
	{
		l_opt.url += '?title='+escape(title);
	}

/*	
	if (kind)
	{
		l_opt.url += '&amp;kind=' + escape(kind);
	}
	if (type)
	{
		l_opt.url += '&amp;type=' + escape(type);
	}
*/

	l = new Lightbox( b , l_opt );
	l.show();
}

// Static callback for injecting the link results on the page
function add_tag ( opts , addexisting )
{
	var rest = addexisting ? anyRest.edge.add : anyRest.tags.add;

	var current = window.widget_manager && window.widget_manager.current( 'link' );
	opts['id']  = current.options.ajax.id;

	rest( opts , 
		function ( xml ) 
		{
			if ( anyRest.aux.error( xml ) ) 
			{
				jQuery.warn( "Add tag" , null , anyRest.aux.errorMsg( xml ) );
			}
			else
			{
				edge_inject_tag();
			}
		}
	);

	link_hide();
}



// Static callback for injecting the link results on the page
function edge_inject_tag ()
{
	var current = window.widget_manager && window.widget_manager.current( 'link' );
	var id		= current.options.ajax.id;
	var file	= current.options.file;
	var dom_id	= current.options.dom_id;

	anyRest.html.scomp ( 
		{ 'name': file, 'thg_id': id } , function (xml) { edge_inject_aux ( xml , dom_id ) }
	);
}



// Static callback for injecting the link results on the page
function edge_inject_aux ( xml , dom_id ) 
{
	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 )
		{
			$( x ).after( ts.firstChild.nodeValue ).remove();
			
			y = document.getElementById( dom_id );
			init_widgets( y );
			//init_link( '.do_link' , y );
			//init_unlink( '.do_unlink' , y );
			//init_lightbox( '.do_lightbox' , y );
		}
		else
		{
			jQuery.log('Internal error: did not get a template from the server or no dom id. Sorry...');
		}
	}
} 


// Static callback for injecting the link results on the page
function edit_trust ( id, dom_id, file, lang, rem_id, val, field )
{
	fld = field || 'trust.view';
	var options = {
		'id':	 id, 
		'field': fld,
		'value': val,
		'lang':	 lang
	};

	anyRest.predicates.set( 
		options , 
		function ( xml ) 
		{
			if ( anyRest.aux.error( xml ) )
			{
				// on error
				jQuery.log(anyRest.aux.errorMsg(xml));
			}
			else
			{
				// create new scomp with the date
				var scomp_options = {
					'name':	  file, 
					'thg_id': id,
					'list_id': id,
					'dom_id': dom_id,
					'trust': val,
					'field': fld
				};
			
				anyRest.html.scomp ( scomp_options , 
					function ( xml ) 
					{
						if (anyRest.aux.error(xml)) 
						{
							jQuery.showHide(onfail);
							jQuery.log(anyRest.aux.errorMsg(xml));
						}
						else
						{
							var ts = xml.getElementsByTagName( 'html' )[0];

							x = document.getElementById( dom_id );
							if ( ts && x )
							{
								$( x ).after( ts.firstChild.nodeValue ).remove();
								
								y = document.getElementById( dom_id );
								$( "#" + rem_id ).remove();
								init_widgets( y );
							}
							else
							{
								jQuery.log('Internal error: did not get a template from the server or no dom id. Sorry...');
							}
						}
					}
				);
			}
		}
	);

}

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



// 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_unlink( container , context )
{
	context = context || document;
	
	$( container, context ).each(
		function () 
		{
			var options		= jQuery.attrOptions( this , 'unlink' );
			options.ajax	= jQuery.attrOptions( this , 'unlink_ajax' );

			new Unlink( this, options );
		}
	);
}



// External callback for the documentation functions
// @return  object      Structure containing 'examples' of usage, 'about' message, default 'opts', and 'classes' (never used...)
function docs_unlink()
{
	var a = 'Unlink things';
	var e = [];
	var c = [];
	var o = 
	[[ "unlink", 	jQuery.label( 'unlink.title'	, 'Unlink this item'),	"Label - html 'title' attribute of [x]" ]
	,[ "confirm",	jQuery.label( 'unlink.confirm'	, 'Remove'			),	"Label - confirmation text" ]
	,[ "yes",		jQuery.label( 'unlink.yes'		, 'Yes'				),	"Label - confirmation button" ]
	,[ "cancel",	jQuery.label( 'unlink.cancel'	, 'Cancel'			),	"Label - cancelation button" ]
	,[ "service",	"anymeta.edge.remove",	"Name of REST service" ]
	,[ "use_cross",	true,						"use a cross?" ]
	,[ "ajax",		{},						"Extra ajax parameters" ]
	];
	
	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_unlink( o )
{
	window.unlink_opts = window.unlink_opts || docs_unlink().opts;
	return apply_options( window.unlink_opts, o );
}



function Unlink( container, options )
{
	options			= opts_unlink( options );
	this.options	= options;
	this.container	= container;

    this.show();
}



// 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
Unlink.prototype.widgetBlur = function ( name, obj )
{
	if ( obj != this )
	{
		this.selector_dont();
	}
	return false;
};



// Callback for when the [x] is clicked
// @param   event   ev      Click event
// @return  boolean         (Always) false to stop the event propagation
Unlink.prototype.x_fun = function ( ev, obj )
{
	window.widget_manager.widgetFocus( 'unlink', this );
	
    // this.marker.style.display 	= "none";
	// this.selector.style.display = "block";
	
	$(this.selector).fadeIn(300);
	
	var pos = jQuery.elemInWindow( obj );

	$(this.container).addClass('unlinking');

	$( this.selector ).css({
		position: 	"absolute",
		left: 		pos.l,
		top: 		pos.t
	});

	return false;
};



// Make the yes / no confirmation dialog
// @param   string      id      The suffix used in the dom ids of the different elements
// @return  undefined
Unlink.prototype.mk_selector = function ( id )
{
	var div = document.createElement( 'div' );
		div.id = 'unlink_confirm_' + id;
		div.className = 'unlink-confirm';
		div.innerHTML = '<span class="unlink-confirm-text">' + this.options.confirm + '</span>';
		div.style.display = 'none';
		div.style.zIndex = 200;
		$( document.body ).append( div );

	var buty = document.createElement( 'button' );
		buty.className = 'unlink-confirm-yes cancel';
		buty.innerHTML = this.options.yes;
		$( div ).append( buty );

	var butn = document.createElement( 'button' );
		butn.id = 'unlink-confirm-cancel' + id;
		butn.className = 'cancel';
		butn.innerHTML = this.options.cancel;
		$( div ).append( butn );
		
	this.selector = div;
	this.selector_y = buty;
	this.selector_n = butn;
};



// Make the [x] marker
// @param   string      id      The suffix used in the dom ids of the different elements
// @return  undefined
Unlink.prototype.mk_marker = function ( id )
{
	var del = document.createElement( 'span' );
		del.id = 'unlink_' + id;
		del.className = "unlink-marker";

	var axx = document.createElement( 'a' );
		axx.title 			= this.options.unlink;
		axx.href  			= 'javascript:void(0);';
		
		if(this.options.use_cross)
		{
			$(axx).html('<img src="http://fast.mediamatic.nl/f/dxdx/image/cross.gif" alt="" />');
		}
		
		$(axx).css({cursor: 'pointer'});
		$(axx).addClass('do_tooltip');
		$(axx).attr('tooltip', 'width: \'auto\'');

    $( del ).append( axx );
    $( this.container ).append( del );
    this.marker     = del;
    this.marker_x   = axx;
};

// Show the [x] symbol and initialize other structures
// @return  undefined
Unlink.prototype.show = function ()
{
	$( this.container ).addClass('unlink-hover');

	// the object which should be removed could be anither one of the 
	this.id = this.options.id || this.container.id;
	
	this.marker || this.mk_marker( this.id );
	// this.marker.style.display = 'block';
		
	this.selector || this.mk_selector( this.id );

	if ( this.container.tagName.toLowerCase() == 'button' ) 
	{
		var mdown = this.container;
	}
	else
	{
		var mdown = this.marker_x;
	}
	
	var self = this;
	
	var fdown = function ( ev )
	{ 
		ev.preventDefault();
		// $( mdown ).unbind( 'mousedown' , fdown );
		self.x_fun( ev, this );
		return false;
	} 
	
	$( mdown ).bind( 'mousedown' , fdown );
	
	$( this.selector_y ).click( 
		function ( ev )
		{ 
			self.selector_do();
			ev.preventDefault();
			return false;
		}
	);

	$( this.selector_n ).click( 
		function ( ev )
		{ 
			self.selector_dont();
			ev.preventDefault();
			return false;
		}
	);	
};



// Callback for when 'yes' is press on the confirmation dialog
// @return  undefined
Unlink.prototype.selector_do = function ()
{
	if ( this.is_unlinked && !this.options.toggle)
	{
		return;
	}

	this.is_unlinked = true;

	this.selector && $( this.selector ).remove();
	this.selector = null;

	var self = this;
	anyRest.service( this.options.service, this.options.ajax , 
		function ( xml ) 
		{
			self.options.callback && self.options.callback( xml );

			if ( self.options.rebuild_scp ) 
			{
				rebuild_scp( self.options );
			}
			else if ( self.options.toggle && self.options.toggle_hide != null && self.options.toggle_show != null )
			{
				$( ".unlink-confirm" , self.container ).remove();

				$( "#" + self.options.toggle_show ).show();
				$( "#" + self.options.toggle_hide ).hide();
			} 
			else 
			{
				$( "#" + self.id ).fadeOut('slow');
			}
		}
	);
};



// Callback for when 'no' is pressed in the confirmation dialog
// @return  undefined
Unlink.prototype.selector_dont = function ()
{
    if ( this.selector )
    {
        $(this.selector).slideUp(100);
        // this.selector.style.display = 'none';
    }
	$( this.container ).removeClass('unlink-hover').removeClass('unlinking');
};


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


// External callback, close all widgets
// @return  undefined
function end_editinplace()
{
	var all = window.widget_manager.widgets( 'editinplace' );
	var fls = window.required_fields;
    
	if ( all && fls )
	{
		for ( var i in all )
		{
			if ( fls[all[i].options.ajax.field] )
			{
				all[i].addRequiredClass();
			}
		}
	}
} 


// 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_editinplace( container, context )
{
	context = context || document.body;
	
	$( container, context ).each( 
		function ()
		{
			var opts		= jQuery.attrOptions( this, 'editinplace' );
			opts.ajax		= jQuery.attrOptions( this, 'editinplace_ajax' );
			opts.validate	= jQuery.attrOptions( this, 'editinplace_validate' );
			opts.data		= jQuery.attrOptions( this, 'editinplace_data' );
    
			window.widget_manager.add( "editinplace", new EditInPlace( this, opts ) );
		}
	);
}


// External callback for the documentation functions
// @return  undefined
function docs_editinplace()
{
	var a = 'Edit in place implementation';
	var e = 
	[['<div id="ed_body" style=\'display: none;\'>\n<span id="wc_body"></span> words\n</div>\n<div class="do_editinplace" editinplace=\'invisibleId: "ed_body", wordCountId: "wc_body"\' ...>\nmy text\n</div>',
		"Using invisible and word count"]
	,['<div  ... editinplace_validate=\'name: "integer"\' >', 
		"Validation -- must be an integer"]
	,['<span ... editinplace_validate=\'name: "float", min: 0\' >', 
		"Validation -- must be a positive number"]
	,['<p ... editinplace_validate=\'regexp: /[a-z]\w*/, msg: "Please insert a proper variable name"\' >', 
		"Validation -- must be a variable name"]
	];
    
	var c = [];
	
	var le = jQuery.label( 'editinplace.onempty'		, 'Add value'	 );
	var la = jQuery.label( 'editinplace.save'			, 'Save'		 );
	var lc = jQuery.label( 'editinplace.cancel'			, 'Cancel'		 );
	var lm = jQuery.label( 'editinplace.charcountmax'	, 'Too many characters'		 );
	
	var o = 
	[[ "onEmpty",			le,			"Text displayed when field is empty" ]
	,[ "save",				la,			"Label for save button" ]
	,[ "cancel",			lc,			"Label for cancel button" ]
	,[ "paramName",			'value',	"Parameter name in which to send value in ajax request" ]
	,[ "callback",			null,		"Function that gets called after a value is saved" ]
	,[ "type",				'text',		"Type of editinplace [select,text,textarea]" ]
	,[ "minHeight",			400,		"Minimum height of textarea" ]
	,[ "maxHeight",			2000,		"Maximum height of textarea" ]
	,[ "extraHeight",		150,		"Fixed height padding (not added to minimum height)" ]
	,[ "width",				null,		"CSS width for element. Ignores other width options. Use '90%' or '400px'" ]
	,[ "minWidth",			80,			"Minimum width of textarea" ]
	,[ "remWidth",			0,			"Fixed width negative padding (not removed from minimum width)" ]
	,[ "editClass",			null,		"Css class added to container element when in edit mode" ]
	,[ "invisibleId",		null,		"Id of element to show only when in edit mode" ]
	,[ "wordCountId",		null,		"Id of element where the number of words type by the user is to be displayed" ]
	,[ "charCountId",		null,		"Id of element where the number of chars type by the user is to be displayed" ]
	,[ "charCountMax",		null,		"Maximum characters which can be used inside a textarea" ]
	,[ "charCountMaxText",	lm,			"text displayed when too many characters are typed" ]
	,[ "countMaxClass",		'count_max_notice', "Class added when more then the maximum characters is used inside a textarea" ]
	,[ "scompEdit",			null,		"Name of the scomp to fetch and inject as a form" ]
	,[ "scompView",			null,		"Name of the scomp used to display after save" ]
	,[ "ajax",				{},			"Extra ajax parameters" ]
	,[ "data",				{},			"Data for select menu (and later, checkboxes)" ]
	];
	
	o = o.concat( docs_validate().opts );
	
	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_editinplace( o )
{
	window.editinplace_opts = window.editinplace_opts || docs_editinplace().opts;
	return apply_options( window.editinplace_opts, o );
}


// CLASS EditInPlace
function EditInPlace( container, options )
{
	options			= opts_editinplace( options );
	this.options	= options;
	this.throbber	= '<img src="http://fast.mediamatic.nl/f/dxdx/image/throbberdots.gif" />';
	
	this.container	= container;
	this.invisible	= document.getElementById( options.invisibleId );
	this.wordCount	= document.getElementById( options.wordCountId );
	this.charCount	= document.getElementById( options.charCountId );
	this.validate	= new Validate( options.validate );
    
	this.container.editinplace_widget	= this;
	this.ajaxNotice = new AjaxNotice( this.container );
    
	this.spaceHolder();
	this.orgHTML 		= this.container.innerHTML; 
    
	var self = this;

	$(self.container).addClass('editable-area');

	$(container).bind('mouseover', function (e) 
	{
		if(!self.editing)
		{
			$(self.container).addClass('editinplace-hover');
		}
	})
	.bind('mouseout', function(e) 
	{
		if(!self.editing)
		{
			$(self.container).removeClass('editinplace-hover');
		}
	})
	.bind('mousedown', function(e) 
	{
		$(self.container).removeClass('editinplace-hover').removeClass('editable-area').addClass('editinplace-active');
		self.show();
	});
	
	options.showonload && this.show();
}


// 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	boolean	        True if ajax request made, false otherwise
EditInPlace.prototype.widgetBlur = function( name, obj )
{
	var s = false;
	if ( obj != this && this.editing )
	{
		s = this.save( true );
		s || this.cancel();
	}
	return s;
};


// Reset the html (close widget)
// @return  undefined
EditInPlace.prototype.reset = function( html )
{
	$(this.container)
		.html(html != null ? html : this.orgHTML)
		.removeClass('editinplace-active');
	this.spaceHolder();
};


// When an empty string was saved we use a space holder with some text
// @return  undefined
EditInPlace.prototype.spaceHolder = function ()
{
	if ( this.container.innerHTML.match( /^(\s*)$/ ) ) 
	{
		this.container.innerHTML = '<span class="editinplace-onempty">' + this.options.onEmpty + '</span>';
		this.required && this.addRequiredClass();
	}
};


// Element is a required field,
// Add css class
// @return undefined
EditInPlace.prototype.addRequiredClass = function ()
{
	this.required = true;
	$( this.container ).addClass( 'required_field' );
};


// Remove css class of required field
// @return  undefined
EditInPlace.prototype.remRequiredClass = function ()
{
	$( this.container ).removeClass( 'required_field' );
};


// Go into edit mode
// @return  undefined
EditInPlace.prototype.editable = function ()
{
	window.widget_manager.widgetFocus( 'editinplace', this );
    
	this.required && this.remRequiredClass();
    
	this.editing 		= true;
	this.orgHeight		= jQuery.getSize( this.container ).h;
	this.orgWidth		= jQuery.getSize( this.container ).w;
	//this.orgTop			= jQuery.getPosition( this.container ).y;
	//this.orgLeft		= jQuery.getPosition( this.container ).x;
	
	$(this.container).html(this.throbber);
	
	//var loader = $('<div></div>').addClass('loading').css({color: '#8c8c8c', opacity: .60, position: 'absolute', top: this.orgTop, left: this.orgLeft, height: this.orgHeight, width: this.orgWidth}).html(this.throbber);
	//$(document.body).append(loader);
	
	this.invisible && $( this.invisible ).show();
		
	if ( this.options.editClass )
	{
		$( this.container ).addClass( this.options.editClass );
	}
    
	if ( this.options.type == 'scomp' )
	{
		var ajax = jQuery.assocMerge( this.options.ajax, { name: this.options.scompEdit } );
		var rest = anyRest.html.scomp;
	}
	else
	{
		var ajax = this.options.ajax;
		var rest = this.options.ajax.edge ? anyRest.edge.attribute.get : anyRest.predicates.get;
	}
    
	var self = this;
	var call = function ( xml ) 
	{ 
		self.ajaxNotice.stop();
		self.processGet( xml );
		//loader.remove();
	} 
	
	this.ajaxNotice.start( '...' );
	rest( ajax, call );
};


// Go into edit mode if not there alreaady
// @return  undefined
EditInPlace.prototype.show = function ()
{
	if ( this.editing )
	{
		return;
	}
    
	this.saving = false;
	this.editable();
};


// Copy relevant css styles from the one element to another
// @param   dom         o   Node to copy style from 
// @param   dom         n   Node to copy style to
// @return  undefined
EditInPlace.prototype.copyStyle = function( o , n )
{
	var fs = anyGetComputedStyle( o , 'font-size' );
	
	if ( fs.search( '%' ) < 0 && fs.search( 'em' ) < 0 )
	{
		// Set the font size and the line height
		n.style.fontSize	= fs;
		n.style.lineHeight	= anyGetComputedStyle( o , 'line-height' );
	}
	else
	{
		// If the font size is given in percentage, set it 
		// to 100% and don't set the line height 
		n.style.fontSize 	= '100%';
		n.style.lineHeight	= anyGetComputedStyle( o , 'line-height' );
	}
	
	// Copy other relevant css values
	n.style.fontFamily		= anyGetComputedStyle( o , 'font-family' );
	n.style.fontWeight		= anyGetComputedStyle( o , 'font-weight' );
	n.style.textTransform	= anyGetComputedStyle( o , 'text-transform' );
	n.style.color			= anyGetComputedStyle( o , 'color' );
	n.style.fontStyle	 	= anyGetComputedStyle( o , 'font-style' );
	n.style.backgroundColor = copyStyleWhile( o , 'background-color', 'rgba(0, 0, 0, 0)' , 'grey' );
};


// Do not react to Escape and Enter keyboard commands
// @param   dom         i       Input node to which events are attached
// @param   boolean     enter   True if return key should be used for submiting
// @return  undefined
EditInPlace.prototype.applyInputEvents = function ( i, enter )
{
	var self = this;
	$( i ).keydown( 
		function( e ) 
		{
			if ( e.keyCode == 27 ) 
			{ 
				// ESC
				e.preventDefault();
				$(self.container).addClass('editable-area');
				self.cancel();
			} 
			else if ( e.keyCode == 13 && enter )
			{ 
				// Enter
				e.preventDefault();
				$(self.container).addClass('editable-area');
				self.save();
			}
		}
	);
};


// Get edited-in-place value
// @return  string      Value the user entered
EditInPlace.prototype.getValue = function ()
{
	var ret = {};
	ret[this.options.paramName] = this.mydiv.i.value;
	return ret;
};


// Append Save and Cancel buttons
// @return  undefined
EditInPlace.prototype.appendButtons = function ()
{
	var bSave	= this.addButton( this.options.save   , 'submit' );
	var bCancel	= this.addButton( this.options.cancel , 'cancel' );
    
	var self = this;
	$( bSave ).click( 
		function ( e ) 
		{
			e.preventDefault();
			$(self.container).addClass('editable-area');
			self.save();
			return false;
		}
	);
	
	$( bCancel ).click( 
		function ( e ) 
		{
			e.preventDefault();
			$(self.container).addClass('editable-area');
			self.cancel();
			return false;
		}
	);
};


// Add a button for the editinplace form (used for submit/cancel buttons)
// @param   string  value   Button text
// @param   string  c       Suffix of the classname of the button ('editinplace_<c>')
// @param   string  t       Type of button (optional)
// @return  dom             New input field
EditInPlace.prototype.addButton = function ( value , c , t )
{
	var b		= document.createElement("button");
//	b.type		= t || 'submit';
	b.innerHTML	= value;
	b.className	= 'editinplace-' + c ;
	this.mydiv.appendChild( b );
	return b;
};


// When edit mode form elements come from a server scomp instead
// of being generated with js, this function is used to save the 
// (indeterminate number of) values
// @param   boolean    silent  Not used in this function. Here for consistency  
// @return  object             Data to be sent to server 
EditInPlace.prototype.saveScomp = function ( silent )
{
	var self = this;
	var vals = {};
	vals['field'] = [];
	vals['value'] = [];
	var func = function () 
	{
		if ( this.type != 'button' && this.type != 'submit' )
		{
			vals['field'].push( this.name  );
			vals['value'].push( this.value );
		}
	};
	
	$( 'input',		this.scomp ).each( func );
	$( 'textarea',	this.scomp ).each( func );
	$( 'select',	this.scomp ).each( func );
	
	return jQuery.assocMerge( this.options.ajax, vals );
};


// Save the edit mode values when the form was generated using js
// @param   boolean    silent  Don't notify the user about errors
// @return  object             Data to be sent to server 
EditInPlace.prototype.saveInline = function ( silent )
{
	var v	= this.getValue();
	var x	= v[this.options.paramName];
	
	if ( !this.validate.value( x ) )
	{
		silent || alert( this.validate.opts.msg );
		return false;
	}
    
	if ( !this.validate.range( x ) )
	{
		silent || alert( this.validate.opts.out );
		return false;
	}
	
	return jQuery.assocMerge( this.options.ajax, v );
};


// Get the edit mode form values, send them to server
// @param   boolean    silent   Don't notify the user about errors
// @return  boolean             False if save action was not executed 
EditInPlace.prototype.save = function ( silent )
{
	if ( !this.saving )
	{
		var o = this.options.type == 'scomp' ? this.saveScomp( silent ) : this.saveInline( silent );
		if ( o )
		{
			// if too many characters are typed show a message 
			var v = false;				
			if ( $( 'textarea', this.container ).get(0)
			  && $( 'textarea', this.container ).get(0).value )
			{
				v = this.countChars( $( 'textarea', this.container ).get(0).value );
			}
			else if ( $( 'input', this.container ).get(0)
			  	  && $( 'input', this.container ).get(0).value )
			{
				v = this.countChars( $( 'input', this.container ).get(0).value );
			}

			$(this.container).html(this.throbber).removeClass('editinplace-active');

			if ( v
			  && this.options.charCountMax 
			  && v > this.options.charCountMax )
			{
				this.invisible && $( this.invisible ).show();
			}
			else
			{
				this.invisible && $( this.invisible ).hide();
			}
			this.saving = true;
    
			if ( this.options.scompView )
			{
				o.name = this.options.scompView;
			}
    
			var self = this;		
			var m = this.options.ajax.edge ? "anymeta.edge.attribute.set" : "anymeta.predicates.set";
			var f = function ( xml )
			{
				self.ajaxNotice.stop();
				self.processSet( xml ); 
			};
    		
			this.ajaxNotice.start( '...' );
			anyRest.post( m, o, f );

			if (EditInPlace.prototype.helptext)
			{
				$(this.container).attr('title',EditInPlace.prototype.helptext);
			}
			
			return true;
		}
	}
	return false;
};


// Cancel the edit-in-place, put the form back where it was
// @return  undefined
EditInPlace.prototype.cancel = function ()
{
	$( this.container ).removeClass( 'editinplace-hover' );
	this.reset();
	this.options.editClass  &&  $( this.container ).removeClass( this.options.editClass );
	this.editing 		= false;
	this.invisible && $( this.invisible ).hide();
	
	if (EditInPlace.prototype.helptext)
	{
		$(this.container).attr('title',EditInPlace.prototype.helptext);
	}
};


// Callback for when the scomp comes from the server. 
// Insert it into the page
// @param   xml     xml     Return from the ajax request
// @return  null    Always returns null
EditInPlace.prototype.processGetScomp = function ( xml )
{
	var ts	= xml.getElementsByTagName( 'html' )[0];
	if ( ts )
	{
		this.scomp = document.createElement( 'span' );
		var str = anyRest.aux.concatChildNodes( ts );
    
		$( this.scomp ).append( str ); 
		$( this.mydiv ).append( this.scomp ); 
		var self = this;
		$( 'input', this.scomp ).each(
			function () 
			{
				self.applyInputEvents( this, true );
			}
		);
	}
	
	return null;
};

// Callback for when the server replies with the editable value
// Create a form and inject the value into it
// @param   xml     xml     Return from ajax request
// @return  dom             The first input field (to call focus() on)            
EditInPlace.prototype.processGetInline = function ( xml )
{
	var r = $( 'result' , xml ).text();
	var firstInput = this.appendInputs( r );
//	this.mydiv.appendChild( document.createElement( 'br' ) );

	return firstInput;
};

// Callback for ajax request that gets the value from the server
// @param   xml         xml     Return from ajax request
// @return  undefined
EditInPlace.prototype.processGet = function ( xml )
{
	if ( anyRest.aux.error( xml ) )
	{
		jQuery.warn( "EditInPlace" , anyRest.aux.errorMsg( xml ) );
		this.reset();
		this.editing = false;
		return;
	}
    
	this.container.innerHTML = '';
	
	this.mydiv = document.createElement( 'div' );
	this.mydiv.style.display = 'inline';
    
	if ( this.options.type == 'scomp' )
	{
		var firstInput = this.processGetScomp( xml );
	}
	else
	{
		var firstInput = this.processGetInline( xml );
	}
	
	this.appendButtons();
	
	// For some reason this has to be done after the 
	// me.mydiv.innerHTML is altered, otherwise IE fails
	this.container.appendChild( this.mydiv );
	
	// initialize the editorial editor
	$(firstInput).wysiwyg({
		field: this.options.ajax.field
	});

	this.addhelptexts(firstInput);

	$(firstInput).focus();
};

EditInPlace.prototype.addhelptexts = function(obj)
{
	EditInPlace.prototype.helptext = $(this.container).attr('title');
	
	if(EditInPlace.prototype.helptext)
	{
		var el = $('<div></div>').addClass('help-text').html('<p>'+EditInPlace.prototype.helptext+'</p>');
		$(obj).parent().before(el);
		$(this.container).attr('title','');
	}
}

// Callback for ajax request that sets the value from the server
// @param   xml     xml     The value return by the ajax request
// @return  boolean         True on success
EditInPlace.prototype.processSet = function ( xml )
{
	if ( anyRest.aux.error( xml ) )
	{
		jQuery.warn( "EditInPlace" , anyRest.aux.errorMsg( xml ) );
		this.reset();
		this.editing = false;
		return false;		
	}
    
	var r = $( 'result' , xml ).text();
	this.orgHTML 				= r;
	this.container.innerHTML 	= r; // r.replace(/<\s*script\s*.*>.*<\/\s*script\s*.*>/gi, "");
	
	// Insert the space holder if blank
	this.spaceHolder();
	
	// Callback if necessary
	this.options.callback && this.options.callback( this.container ); 
	
	// Release
	this.editing = false;
    
    return false;
};


// Add an input field
// @param   string  tag     Html tag name (which kind of html node to create)
// @param   string  value   The value
// @param   string  cla     The suffix for the classname 
// @param   string  type    The type
// @return  dom             The created input element
EditInPlace.prototype.addInput = function ( tag , value , cla , type )
{
	var i = document.createElement( tag );
	this.mydiv.appendChild( i );
	this.mydiv.i	= i;
	i.value			= value;
	xtr_class = '';
	if ( type == 'text' )
	{
		xtr_class = 'text';
	}
	i.className 	= '' + xtr_class + ' editinplace-input editinplace-' + cla;
	
	if ( type )
	{
		i.type = type;
	}
	
	this.copyStyle( this.container , i );
	this.applyInputEvents( i, this.options.type == 'text'  );
	
	return i;
};


// Count the words in a string. Used when the wordCountId options is set.
// @param   string      str     Text
// @return  integer             Number of words
EditInPlace.prototype.countWords = function ( str )
{
	words = str.split( /[^\w\-]+/ );
	var n = words.length;
 	n -= words[0] 				== '' ? 1 : 0;
 	n -= words[words.length-1]	== '' ? 1 : 0;
	return n < 0 ? 0 : n;
};

// Count the characters in a string. Used when the charCountId options is set.
// @param   string      str     Text
// @return  integer             Number of chars
EditInPlace.prototype.countChars = function ( str )
{
	chars = str.length;
	return chars <= 0 ? 0 : chars;
};

// Event handler for the word counting feature
// @param   dom         elem   Input element that will contain the text 
// @return  undefined 
EditInPlace.prototype.addWordCount = function ( elem )
{
	if ( this.wordCount )
	{
		var self = this;
		$( elem ).keyup( 
			function ( e ) 
			{
				var key = e.charCode || e.keyCode || -1;
				switch( key ) {
					case 8: 	// backspace
					case 13: 	// enter
					case 32: 	// space
						self.lastup = true;
						break;
					case 27: 	// escape
					case 35: 	// end
					case 36: 	// home
					case 38: 	// up
					case 40: 	// down
					case 63232: // up 
					case 63233: // down
					case 63273: // home
					case 63275: // end 
						var exit = self.lastup;
						self.lastup = true;
						break;
					default:
						var exit = !self.lastup;
						self.lastup = false;
						break;
				}
				
				if ( !exit )
				{
					$( self.wordCount ).html( self.countWords( elem.value ) );
				}
			}
		);
	}
};

// Event handler for the char counting feature
// @param   dom         elem   Input element that will contain the text 
// @return  undefined 
EditInPlace.prototype.addCharCount = function ( elem )
{
	if ( this.charCount )
	{
		var self = this;
		$( elem ).keyup( 
			function ( e ) 
			{
				var key = e.charCode || e.keyCode || -1;
				switch( key ) {
					case 13: 	// enter
					case 27: 	// escape
					case 35: 	// end
					case 36: 	// home
					case 38: 	// up
					case 40: 	// down
					case 63232: // up 
					case 63233: // down
					case 63273: // home
					case 63275: // end 
						var exit = false;
						break;
					default:
						var exit = true;
						break;
				}
				
				if ( exit )
				{
					v = self.countChars( elem.value );
					self.addCharCountShow( elem, v );
				}
			}
		);
		v = this.countChars( elem.value );
		this.addCharCountShow( elem, v );
	}
};

// @param   elem, v
// @return  null    
EditInPlace.prototype.addCharCountShow = function ( elem, v )
{
	if ( this.options.charCountMax 
	  && v > this.options.charCountMax )
	{
		$( elem ).css('background-color', '');
		$( elem ).addClass( this.options.countMaxClass );
		$( this.charCount ).html( '<span class="' + this.options.countMaxClass + '">' + this.options.charCountMaxText + '</span> ' + v );
	}
	else
	{
		$( elem ).css('background-color', 'transparent');
		$( elem ).removeClass( this.options.countMaxClass );
		$( this.charCount ).html( '' + v + '' );
	}
};

// Generate the editinplace for a select type of value 
// Not sure if it is being used anywhere at the moment!
// @param   
// @return  null    
EditInPlace.prototype.dropdown = function ( v )
{
	var d = this.options.data;
	var r = document.createElement( 'select' );
	var o = null;
	for ( var i in d )
	{
		o = document.createElement( 'option' );
		o.innerHTML = d[i];
		o.value		= i;
		if ( i == v )
		{
			o.selected = 'yes';
		}
		r.appendChild( o );
	}
	this.mydiv.appendChild( r );
	this.mydiv.i = r;
	return null;
};


// Generate the editinplace for a checkbox type of value 
// Not sure if it is being used anywhere at the moment!
// @return  null
EditInPlace.prototype.checkbox = function ()
{
	var d = this.options.data;
	var o = null;
	for ( var i in d )
	{
		o = document.createElement( 'input' );
		o.type		= 'checkbox';
		o.value		= i;
		if ( d[i] )
		{
			o.checked = "yes";
		}
		this.mydiv.appendChild( o );
	}
	this.mydiv.i = null;
	return null;
};


// Add the correct input elements in the page
// @param   string  v   Editable value as requested from server
// @return  dom         Node to append
EditInPlace.prototype.appendInputs = function ( v )
{
	var r = null;
	
	if ( this.options.type == "textarea" ) 
	{
		r = this.addInput( 'textarea' , v , 'multiline' );
		
		if ( this.wordCount )
		{
			$( this.wordCount ).html( this.countWords( v ) );
		}
		this.addWordCount( r );

		if ( this.charCount )
		{
			$( this.charCount ).html( this.countChars( v ) );
		}
		this.addCharCount( r );

		r.style.height	= Math.min( Math.max( this.orgHeight + this.options.extraHeight , this.options.minHeight ) , this.options.maxHeight ) + 'px';
		if ( this.options.width )
		{
			r.style.width	= this.options.width;
		}
		else
		{
			r.style.width	= Math.max( this.orgWidth - this.options.remWidth , this.options.minWidth ) + 'px';
		}
	}
	else if ( this.options.type == "text" )
	{
		var r 		= this.addInput( 'input' , v , 'oneline' , 'text' );
		if ( this.charCount )
		{
			$( this.charCount ).html( this.countChars( v ) );
		}
		this.addCharCount( r );

		r.style.width	= '96%';
	}
	else if ( this.options.type == 'select' )
	{
		r = this.dropdown( v );
	}
	else if ( this.options.type == 'checkbox' )
	{
		r = this.checkbox();
	}
	
	return r;
};


// Left-pad a string
// @param   object  value   Value to pad 
// @param   integer size    Number of characters to pad string to
// @param   string  c       Character to pad with
// @return  string          Padded string with minimum length 'size'
function pad( value, size, c )
{
	c = c || '0';
	value = '' + value;
	size  = Math.max( size - value.length , 0 );
	while ( size-- )
	{
		value = c + value;
	}
	return value;
}


// Copy entire style definition of a node into another
// @param   dom         target     Node to copy style to 
// @param   dom         source     Node to copy style from
// @return  undefined
function copyAllStyle ( target , source ) 
{
	// work around for opera which doesn't have cssText, and for IE which fails on setAttribute 
	if ( !source.style.cssText )
	{
		target.setAttribute( "style", source.getAttribute( "style" ) );
	}
	else
	{
		target.style.cssText = source.style.cssText; 
	}
    
	$( target ).addClass( source.className );
}


// Get the css value for an attribute of a node, using a value as stop condition
// @param   dom     e       Node to start from
// @param   string  name    Css attribute name
// @param   object  value   Stop value
// @param   object  def     Default value to return in case of failure
// @return  object          Css value
function copyStyleWhile( e , name , value , def )
{
	var x = '';
    
	do 
	{
		x = anyGetComputedStyle( e , name );
		e = e.parentNode;
	}
	while ( ( !x || x == value ) && e );
	
	if ( !x || x == value )
	{
		x = def;
	}
	return x;
}

/*  */
(function($) 
 { 	/*
	*	wysiwyg
	*	Has the default values, and initiates the editor
	*
	*	@param	options		Array of all options
	*/
	$.fn.wysiwyg = function(options) 
	 { 		var opts = $.extend({}, $.fn.wysiwyg.defaults, options);
		
		$.fn.wysiwyg.label_add_link 	= jQuery.label('wysiwyg.add_link' 		, 'add the link');
		$.fn.wysiwyg.label_add_youtube	= jQuery.label('wysiwyg.add_youtube'	, 'add youtube');
		$.fn.wysiwyg.label_add_title 	= jQuery.label('wysiwyg.add_title' 		, 'add the title of the link');
		$.fn.wysiwyg.label_ok		 	= jQuery.label('wysiwyg.ok' 			, 'ok');
		$.fn.wysiwyg.label_cancel 		= jQuery.label('wysiwyg.cancel' 		, 'cancel');
		$.fn.wysiwyg.label_youtube_expl = jQuery.label('wysiwyg.youtube_expl' 	, 'You can find the YouTube code in the link, for example http://www.youtube.com/watch?v=nogDHP0gl9M, gives the code nogDHP0gl9M. Youhave to copy the movie code between = and before the &.');
 				
		return this.each( function() 
		 { 			$.fn.wysiwyg.initEditor(this, opts);
		});
 } 	
	/*
	*	wysiwyg.initEditor
	*	creates wapper div, reads the options array and calls the activate buttons function
	*	
	*	@param	object		The object you are working on
	*	@param	opts		The array of options
	*/
	$.fn.wysiwyg.initEditor = function(object, opts) 
	 { 		if(!!document.selection)
		 { 			$(object)
			.keyup(function()
			 { 				$.fn.wysiwyg.range = document.selection.createRange();
			})
			.mouseup(function()
			 { 				$.fn.wysiwyg.range = document.selection.createRange();
			});
	 } 
		var ctr = opts.controls;
		var fld	= opts.field;
	
		if(ctr[fld])
		 { 			$.fn.wysiwyg.toolbar = $('<div></div>', object).addClass('toolbar clearfix');
			$(object).before(this.toolbar);
			
			$(object).resizable( { 
				handles: "s",
				preventDefault: true
			});

			for(i = 0; i < ctr[fld].length; i++)
			 { 				$.fn.wysiwyg.addButton(object, ctr[fld][i][0], ctr[fld][i][0], ctr[fld][i][1], ctr[fld][i][2], ctr[fld][i][0]);		
		 } 	 }  } 
	
	/*
	*	wysiwyg.addButton
	*	creates the buttons, is there an array key that says own_function = true, then it does special stuff
	*
	*	@param	object			The object you are working on
	*	@param	classname		Classname of the button you are adding	
	*	@param	html			Html to write in the button
	*	@param	prefix			The prefix of the wiki tag	
	*	@param	suffix			The suffix of the wiki tag	
	*/
	$.fn.wysiwyg.addButton = function(object, classname, html, prefix, suffix, extra)
	 { 		classname = classname.replace(' ','');
		
		$('<button></button>')
				.addClass('toolbar-button-' + classname)
				.html(html)
				.mouseup(function() 
				 { 					if(extra == 'youtube')
					 { 						$.fn.wysiwyg.doYoutube(object);
				 } 					else if(extra == 'link')
					 { 						$.fn.wysiwyg.doLink(object);
				 } 					else
					 { 						$.fn.wysiwyg.wrapSelection(object, prefix, suffix);
				 } 				})
				.appendTo(this.toolbar);
 } 	
	/*
	*	wysiwyg.getSelection
	*	returns the selection
	*
	*	@param	object			The object you are working on
	*/	
	$.fn.wysiwyg.getSelection = function(object)
	 { 		if(!!document.selection)
		 { 			return document.selection.createRange().text;
	 } 		else if(!!object.setSelectionRange)
		 { 			return object.value.substring(object.selectionStart,object.selectionEnd);
	 } 		else
		 {  
			return false;
	 }  } 
	/*
	*	wysiwyg.replaceSelection
	*	replaces the selection
	*
	*	@param	object			The object you are working on
	*	@param	string			string of thext to replace	
	*/	
	$.fn.wysiwyg.replaceSelection = function(object, text)
	 { 		var scroll_top = object.scrollTop;
				
		if(!!document.selection)
		 { 			$(object).focus();
			var range = (this.range) ? this.range : document.selection.createRange();
			
			range.text = text;
				
			range.select();
	 } 		else if(!!object.setSelectionRange)
		 { 			var selection_start = object.selectionStart;
			object.value 		= object.value.substring(0, selection_start) + text + object.value.substring(object.selectionEnd);
			
			object.setSelectionRange(selection_start + text.length, selection_start + text.length);
	 } 		
		$(object).focus();
		object.scrollTop = scroll_top;
 } 	
	/*
	*	wysiwyg.wrapSelection
	*	wraps the selection with tags
	*
	*	@param	object			The object you are working on
	*	@param	before			string of thext	
	*	@param	after			string of thext	
	*/
	$.fn.wysiwyg.wrapSelection = function(object, before, after)
	 { 		this.replaceSelection(object, before + this.getSelection(object) + after);
 } 	
  	/* special functions */
  	
  	$.fn.wysiwyg.youtubeForm = function(object, what)
	 { 	  	if(what == 'create') 
	  	 { 
	  		$.fn.wysiwyg.youtube_form = $('<div class="youtube_form_wrapper"><p style="font-size: .9em;">'+$.fn.wysiwyg.label_youtube_expl+'</p><input type="text" value="'+$.fn.wysiwyg.label_add_youtube+'" class="youtube_code" /><button class="youtube_button_ok">'+$.fn.wysiwyg.label_ok+'</button><button class="youtube_button_cancel">'+$.fn.wysiwyg.label_cancel+'</button></div>', object);
	   		
	   		this.youtube_form.hide();
	   		this.youtube_form.appendTo(this.toolbar);
	    } 	   	
	   	if(what == 'show')  { $.fn.wysiwyg.youtube_form.slideDown('fast'); } 	   	if(what == 'hide')  { $.fn.wysiwyg.youtube_form.slideUp('fast'); } 	   	if(what == 'remove')  { $.fn.wysiwyg.youtube_form.remove(); }  } 	
	$.fn.wysiwyg.linkForm = function(object, what)
	 { 	  if(what == 'create_all') 
	   	 { 
	   		$.fn.wysiwyg.link_form = $('<div class="link_form_wrapper"><input type="text" class="link_url" value="'+$.fn.wysiwyg.label_add_link+'" />&nbsp;<input type="text" class="link_title" value="'+$.fn.wysiwyg.label_add_title+'" /><button class="link_button_ok">'+$.fn.wysiwyg.label_ok+'</button><button class="link_button_cancel">cancel</button></div>', object);
	   		
	   		this.link_form.hide();
	   		this.link_form.appendTo(this.toolbar);
	    } 	   	
	   	if(what == 'create_url') 
	   	 { 
	   		$.fn.wysiwyg.link_form = $('<div class="link_form_wrapper"><input type="text" class="link_url" value="'+$.fn.wysiwyg.label_add_link+'" /><button class="link_button_ok">'+$.fn.wysiwyg.label_ok+'</button><button class="link_button_cancel">'+$.fn.wysiwyg.label_cancel+'</button></div>', object);
	   		
	   		this.link_form.hide();
	   		this.link_form.appendTo(this.toolbar);
	    } 	   	
	   	if(what == 'show')  { $.fn.wysiwyg.link_form.slideDown('fast'); } 	   	if(what == 'hide')  { $.fn.wysiwyg.link_form.slideUp('fast'); } 	   	if(what == 'remove')  { $.fn.wysiwyg.link_form.remove(); }  }   	
  	$.fn.wysiwyg.doYoutube = function(object)
  	 { 		if ( $('.youtube_form_wrapper').length )
		 {   			return;
	 } 		else
		 { 			var selection = this.getSelection(object);  
			
			if(selection)
			 { 				this.replaceSelection(object, '[[youtube ' + selection +']]');  
		 } 			else
			 { 				this.youtubeForm(object, 'create');
				this.youtubeForm(object, 'show');
				
				$('.youtube_button_ok').click(function()
				 { 					var youtube_code = $('.youtube_code').val();
					
					$.fn.wysiwyg.replaceSelection(object, '[[youtube ' + youtube_code +']]');  
					$.fn.wysiwyg.youtubeForm(object, 'remove');
				});
				
				$('.youtube_button_cancel').click(function() 
				 { 					$.fn.wysiwyg.youtubeForm(object, 'remove');
					$(object).focus();
				});
		 } 	 }    }   	
  	$.fn.wysiwyg.doLink = function(object)
  	 { 		if ( $('.link_form_wrapper').length )
		 {   			return;
	 } 		else
		 { 		
			var selection = this.getSelection(object);  
			
			if(selection)
			 { 				this.linkForm(object, 'create_url');
				this.linkForm(object, 'show');
				
				$('.link_button_ok').click(function()
				 { 					var url = $('.link_url').val();
					$.fn.wysiwyg.replaceSelection(object, '[' + url + ' ' + selection + ']');
					$.fn.wysiwyg.linkForm(object, 'remove');
				});
		 } 			else
			 { 				this.linkForm(object, 'create_all');
				this.linkForm(object, 'show');		
					
				$('.link_button_ok').click(function()
				 { 					var url 	= $('.link_url').val();
					var title 	= $('.link_title').val();
					
					$.fn.wysiwyg.replaceSelection(object, '[' + url + ' ' + title + ']');
					$.fn.wysiwyg.linkForm(object, 'remove');
				});
		 } 			
			$('.link_button_cancel').click(function() 
			 { 				$.fn.wysiwyg.linkForm(object, 'remove');
				$(object).focus();
			});
	 }    }   	
  	$.fn.wysiwyg.wikidefaults = 
	 { 		h3: 	 ['subhead','+++ ',''],
		h4: 	 ['small subhead','++++ ',''],
		bold: 	 ['bold','**','**'],
		italic:  ['italic','//','//'],
		link: 	 ['link','',''],
		youtube: ['youtube','','']
	};
	
	$.fn.wysiwyg.defaults =  { 		controls:
		 { 			'text.intro':
			[
				$.fn.wysiwyg.wikidefaults.italic,
				$.fn.wysiwyg.wikidefaults.link
			],
			'text.body':
			[
				$.fn.wysiwyg.wikidefaults.h3,
				$.fn.wysiwyg.wikidefaults.italic,
				$.fn.wysiwyg.wikidefaults.link,
				$.fn.wysiwyg.wikidefaults.youtube
			]
	 } 	};	
})(jQuery);

$.fn.wysiwyg.label_buttons_bold 	= jQuery.label('wysiwyg.buttons.bold', 		'bold');
$.fn.wysiwyg.label_buttons_italic 	= jQuery.label('wysiwyg.buttons.italic', 	'italic');
$.fn.wysiwyg.label_buttons_link 	= jQuery.label('wysiwyg.buttons.link', 		'link');
$.fn.wysiwyg.label_buttons_youtube 	= jQuery.label('wysiwyg.buttons.youtube', 	'youtube');
$.fn.wysiwyg.label_buttons_h1	 	= jQuery.label('wysiwyg.buttons.h1', 		'hige head');
$.fn.wysiwyg.label_buttons_h2	 	= jQuery.label('wysiwyg.buttons.h2', 		'medium head');
$.fn.wysiwyg.label_buttons_h3	 	= jQuery.label('wysiwyg.buttons.h3', 		'subhead');
$.fn.wysiwyg.label_buttons_h4	 	= jQuery.label('wysiwyg.buttons.h4', 		'small subhead');

$.fn.wysiwyg.wikidefaults = 
{
	h1:		[$.fn.wysiwyg.label_buttons_h1,'+ ',''],
	h2:		[$.fn.wysiwyg.label_buttons_h2,'++ ',''],
	h3: 	[$.fn.wysiwyg.label_buttons_h3,'+++ ',''],
	h4: 	[$.fn.wysiwyg.label_buttons_h4,'++++ ',''],
	bold: 	[$.fn.wysiwyg.label_buttons_bold,'**','**'],
	italic: [$.fn.wysiwyg.label_buttons_italic,'//','//'],
	link: 	[$.fn.wysiwyg.label_buttons_link,'',''],
	youtube:[$.fn.wysiwyg.label_buttons_youtube,'','']
};

$.fn.wysiwyg.defaults = {
	controls:
	{
		}
}
//
// 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);
//
/*  */

/*  */

// 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);

/*  */
// 
//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);
//