
	


/*  */

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

/*  */

if (!$.any) $.any = {};

/* global notification code - Tim Benniks */
$.extend($.any,
{
	/**
	* Notification of messages resulting from interactions with AnyMeta.
	*/
	notification:
	{
		notice: function(msg, element)
		{
			$.any.notification._showNotice(msg, 'notice', element);
		},

		error: function(msg, element)
		{
			$.any.notification._showNotice(msg, 'error', element);
		},

		success: function(msg, element)
		{
			$.any.notification._showNotice(msg, 'success', element);
		},

		formError: function(msg, element)
		{
			$.any.notification._showNoticeForm(msg, 'form-error', element);
		},

        /**
         * Find the first element of class 'notification' that is
         * within the parents of the given element.
         */
        getNotificationElement: function(element)
        {
            var el = $(element).parents().prev('.notification:first');
            return el;
        },

		/**
		* Show the notice and calculate the timeout
		*/
		_showNotice: function(msg, kind, element)
		{
			$.any.notification.time  = 4000;
			$.any.notification.timer = null;

            if (element)
            {
                var notification = element;
            }
			else if($.any.dialog.opened())
			{
				var notification = $('.dialog .notification:first');
			}
			else
			{
				var notification = $('.notification:first');
			}

			if($('ul', notification).length)
			{
				$('ul', notification).append('<li class="'+ kind +'">'+ msg +'</li>');

				$('li', notification).each(function()
				{
					$.any.notification.time += 700;
				});

				$.any.notification._hideNotice(notification);
			}
			else
			{
				$('<ul></ul>').append('<li class="'+ kind +'">'+ msg +'</li>').appendTo(notification);

				setTimeout(function()
				{
					notification.animate({opacity: 'show', height: 'show'}, 300, function()
					{
						$('li', notification).each(function()
						{
							$.any.notification.time += 700;
						});

						$.any.notification._hideNotice(notification);
					});
				},

				10);
			}
		},

		/**
		* Hide all form-error notices and show the form submit notice
		*/
		_showNoticeForm: function(msg, kind, element)
		{
            if (element)
            {
				if($('span.'+kind, element).length)
				{
					$('span.'+kind, element).parents(".notification:eq(0)").remove();
				}
				$('<div></div>').addClass("notification").append('<span class="'+ kind +'">'+ msg +'</span>').prependTo(element).show();
			}
		},
		
		/*
		* After the timeout, remove the notices
		*/
		_hideNotice: function(notification)
		{
			clearTimeout($.any.notification.timer);

			$.any.notification.timer = setTimeout(function()
			{
				notification.animate({opacity: 'hide', height: 'hide'}, 300, function()
				{
					$(this).empty().hide();
				});
			},

			$.any.notification.time);
		},

		/**
		* Ask Anymeta explicitly if there are any outstanding notifications which I should show in the interface.
		*/
		notifyCheck: function(callback)
		{
			$.any.rest.get('anymeta.notifications.get', {}, function(data)
			{
				for(var i = 0; i < data.length; i++)
				{
					var n = data[i];

					if(typeof $.any.notification[n['class']] == 'function')
					{
						$.any.notification[n['class']](n['message']);
					}
					else
					{
						throw('wrong notification kind used.');
					}
				}
                if (typeof callback == "function")
                {
                    callback();
                }
			});
		},

		loader:
		{
			options:
			{
				count: 0,
				time: null,
				dialogAnimation: false
			},

			start: function()
			{
				$.any.notification.loader.options.count += 1;
				$.any.notification.loader.options.time = (new Date()).getTime();

				if($.any.dialog.opened())
				{
					$('.dialog .dialog-loader').fadeIn(150);
				}
			},

			stop: function()
			{
				$.any.notification.loader.options.count -= 1;

				if($.any.notification.loader.options.count > 0)
				{
					return;
				}
				else
				{
					$.any.notification.loader.options.count = 0;
				}

				var now = (new Date()).getTime();
				var diff = now - $.any.notification.loader.options.time;

				var remove = function()
				{
					$('.dialog .dialog-loader').fadeOut(150);
				};

				if(diff > 500)
				{
					remove();
				}
				else
				{
					setTimeout(remove, diff);
				}
			},
			
			addLoader: function(button)
		    {
		    	var self = this;
				
		        if(button.next('.action-expl').length)
		        {
					// As simple linking
		        	button.next('.action-expl').show().html('<img class="form-throbber" src="http://fast.mediamatic.nl/f/pgcq/image/throbberwait.gif" />');
				}
				else
				{
					// Outside of simple linking
		    		button.parents('.form-button').append('<img class="form-throbber" src="http://fast.mediamatic.nl/f/pgcq/image/throbberwait.gif" />');
		    	}
		    },
		    
		    removeLoader: function()
		    {
		    	$('.form-throbber').each(function()
		    	{
		    		$(this).remove();
		    	});
		    }

		}
	},

    /**
     * Parse a string in 'choose' format. Returns an array of objects,
       each object containing a kind, type and variant element (of
       which kind and variant can be false).
     */
    parseChoose: function(choose)
    {
        choose = jQuery.trim(choose);
        if (!choose.length)
        {
            return [];
        }
        var result = [];
        choose = choose.split(",");
        var tr = function(s) { return s ? jQuery.trim(s.toLowerCase()) : false; };

        for (var i=0; i<choose.length; i++)
        {
            var item = jQuery.trim(choose[i]);
            if (!item.length) continue;
            var l = item.split(":");
            result.push({kind: tr(l[0]), type: tr(l[1]), variant: tr(l[2])});
        }
        return result;
    }
});

//
// START Global Initialization code
//
window.widget_manager = null;
$(function()
{
	window.widget_manager = new WidgetManager( window.widget_names );

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

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

// 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 widgets = [];
	var names_do = {};
	for ( var i in names )
	{
		names_do['do_' + names[i]] = names[i];
	}
	
	// Cufon
	if (typeof Cufon != 'undefined')
	{
		Cufon.refresh();
	}

	while ( stack.length > 0 )
	{
        // Old-style widget
		var e = stack.pop();
		
		if (e.className && typeof e.className === "string" && 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 )");
				}
			}
        }

        // New-style widget ($.any.ui)

        var objectClass;
		if(e.className && (objectClass = new RegExp("ui_([\\w_]+)", "g").exec(e.className)))
		{
			var base = objectClass[1];
            
                        var w = $.any.ui._instantiate(e, base);
                        
                        if (w)
                        {
                            widgets.push(w);
                        }
                }

		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] + '()' );
		}
	}

    $.each(widgets, function(i,w) { w._postInit(); });
};

// 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(arguments.length > 0) {
			
			// join for graceful degradation
			var args = (arguments.length > 1) ? Array.prototype.join.call(arguments, " ") : arguments[0];
			
			// standard console.log
			try {
			
				if (window.location.host.split('.')[0] != 'www') {
					console.log(args);
				}
				
				return true;
			} catch(e) {		
				// opera's posterror support
				try { 
					opera.postError(args); 
					return true;
				} catch(e) { }
			}
			
			// alert box if there is no other solution
			// alert(args);
			return false;
		}
	},

	warn: function(where, what)
	{
		$.any.notification.notice(where);
	},

	error: function ( msg , url , line )
	{
		$.any.notification.error(msg + " @ " + url + " @ " + line);
	},

	// 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 )
	{
		return eval( "({" + ((e.getAttribute( name ) || '').replace( /,\s*$/ , '' )) + "})" );
	},

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

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

	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/pgcq/image/throbberdots.gif" />';
	}

	$( this.sel ).addClass( 'ajax_notice' );
}

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

// 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
	$.any.rest.post('anymeta.error.log',
                        params ,
		        function ( result )
		        {
			    jQuery.log( 'Error was reported!' );
			    // Unlock (informal mutex)
			    window.handlingOnError = false;
		        }
	);

	// Pass error onto browser
	return false;
}

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

// caret plugin (http://examplet.buss.hk/jquery/caret.php)
(function(k,e,i,j){k.fn.caret=function(b,l){var a,c,f=this[0],d=k.browser.msie;if(typeof b==="object"&&typeof b.start==="number"&&typeof b.end==="number"){a=b.start;c=b.end}else if(typeof b==="number"&&typeof l==="number"){a=b;c=l}else if(typeof b==="string")if((a=f.value.indexOf(b))>-1)c=a+b[e];else a=null;else if(Object.prototype.toString.call(b)==="[object RegExp]"){b=b.exec(f.value);if(b!=null){a=b.index;c=a+b[0][e]}}if(typeof a!="undefined"){if(d){d=this[0].createTextRange();d.collapse(true);
d.moveStart("character",a);d.moveEnd("character",c-a);d.select()}else{this[0].selectionStart=a;this[0].selectionEnd=c}this[0].focus();return this}else{if(d){c=document.selection;if(this[0].tagName.toLowerCase()!="textarea"){d=this.val();a=c[i]()[j]();a.moveEnd("character",d[e]);var g=a.text==""?d[e]:d.lastIndexOf(a.text);a=c[i]()[j]();a.moveStart("character",-d[e]);var h=a.text[e]}else{a=c[i]();c=a[j]();c.moveToElementText(this[0]);c.setEndPoint("EndToEnd",a);g=c.text[e]-a.text[e];h=g+a.text[e]}}else{g=
f.selectionStart;h=f.selectionEnd}a=f.value.substring(g,h);return{start:g,end:h,text:a,replace:function(m){return f.value.substring(0,g)+m+f.value.substring(h,f.value[e])}}}}})(jQuery,"length","createRange","duplicate");
// no queue in animation plugin
;(function($)
{
	$.fn.animnoq = function(type, prop, speed, easing, callback)
	{
		if($.inArray(type, ['mouseover', 'mouseenter', 'mouseout', 'mouseleave']) == -1) { return this; }

		var opt = typeof speed === 'object' ? speed : { complete: callback || !callback && easing || $.isFunction(speed) && speed, duration: speed, easing: callback && easing || easing && !$.isFunction(easing) && easing }
		opt.queue = false;
		var origCallback = opt.complete;

		opt.complete = function()
		{
			$(this).dequeue();
			if($.isFunction(origCallback)) origCallback.call(this);
		}

		return this.each(function()
		{
			var $this = $(this);

			if(type == 'mouseover' || type == 'mouseenter')
			{
				$this.data('jQuery.animnoq', true);
			}
			else
			{
				$this.removeData('jQuery.animnoq');
			}

			$this.queue(function()
			{
				var condition = (type == 'mouseover' || type == 'mouseenter') ? $this.data('jQuery.animnoq') !== undefined : $this.data('jQuery.animnoq') === undefined;
				if(condition) { $this.animate(prop, opt); } else { $this.queue([]); }
			});
		});
	};
})(jQuery);

// Empty value on click
// Return value when blur and no value
(function($)
{
    /**
     * Returns the value of the field element.
     */
    $.fieldValue = function(el, successful) {
        var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
        if (typeof successful == 'undefined') successful = true;

        if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
                           (t == 'checkbox' || t == 'radio') && !el.checked ||
                           (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
                           tag == 'select' && el.selectedIndex == -1))
            return null;

        if (tag == 'select') {
            var index = el.selectedIndex;
            if (index < 0) return null;
            var a = [], ops = el.options;
            var one = (t == 'select-one');
            var max = (one ? index+1 : ops.length);
            for(var i=(one ? index : 0); i < max; i++) {
                var op = ops[i];
                if (op.selected) {
				    var v = op.value;
				    if (!v) // extra pain for IE...
                	    v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
                    if (one) return v;
                    a.push(v);
                }
            }
            return a;
        }
        return el.value;
    };

})(jQuery);

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

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

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

// fixes problem in IE6,7 as explained by anymeta:#7873:
$(window).unload(function() 
{
	window["__flash__removeCallback"] = function (instance, name) 
	{
		return;
	};
});
/*  */
//
// Depends: Ajax/js/any/utils.js
//
// any.rest
/** Start of the newstyle rest functions */
;(function($)
{
    if (!$.any) $.any = {};
    $.extend($.any,
    {
        rest:
        {
            user: 
            {
                id: 0
            },

            init: function (userId)
            {
                $.any.rest.user.id = userId;

                $(window).bind('ajaxStart', $.any.notification.loader.start);
                $(window).bind('ajaxStop', $.any.notification.loader.stop);
            },
            
            /**
             * jQuery.param(), but filters out empty values first.
             */
            param: function (data)
            {
                var filterEmpty = function(data)
                {
                    var result;
                    switch (typeof data)
                    {
                    case "object":
                        result = {};
                        for (var k in data) {
                            if ((typeof data[k] == "undefined") || (data[k] === null)) 
                            {
                                continue;
                            }
                            result[k] = filterEmpty(data[k]);
                        }
                        break;
                    default:
                        result = data;
                    }
                    return result;
                };
                return $.param(filterEmpty(data));
            },

            /**
             * Require the user to be logged on. If this is not the case,
             * we redirect to the logon URI; appending the current URI as
             * the 'goto' uri + extra arguments that are passed into this
             * function.
             * @return true if we are redirecting, otherwise false.
             */
            requireLogon: function( ret_args )
            {
                if ($.any.rest.user.id == 0)
                {
                    var uri = "/logon";
                    var go = document.location.href + '';
                    // Only keep the URL path part
                    go = go.replace(new RegExp('^.*?://.*?(/.*)$'), '$1');

				    go += go.match(/\?/) ? '&' : '?';
                    if (ret_args)
                    {
                        go += $.any.rest.param(ret_args);
                    }

                    uri += "?goto=" + escape(go);
                    $.any.rest.redirect(uri);
                    return true;
                }
                return false;
            },
            
            
            /**
             * Logoff the current user.
             */
            logoff: function (goto, notifycallback)
            {
                var arg = {doLogoff: true};
                $.any.ui.publish("logoff", arg, null);
                if (arg.doLogoff)
                {
                    $.any.rest.performLogoff(goto, notifycallback);
                }
            },

            /**
             * Actually perform the Anymeta logoff.
             */
            performLogoff: function (goto, notifycallback)
            {
                if (typeof notifycallback == 'function')
                {
                	$.any.rest.post("authentication.user.logoff", {}, notifycallback);
                }
                else
                {
	                $.any.rest.post("authentication.user.logoff", {}, function() {
	                                    if (goto!=null)
	                                    {
	                                        window.location.href = goto;
	                                    }
	                                    else
	                                    {
	                                        // Reload current page, but strip request arguments
	                                        var l = document.location;
	                                        document.location = l.protocol + '//' + l.host + l.pathname;
	                                    }
	                                });
                }
            },


            /**
             * Call the api using HTTP POST. The callback function gets a
             * JSON object with the result; or, when an (API or HTTP)
             * error has been detected, the errback function is called.
             */
            post: function (method, params, callback, errback, notifycallback)
            {
                $.any.rest._doMethod('POST', method, params, callback, errback, notifycallback);
            },


            /**
             * Call the api using HTTP GET. The callback function gets a
             * JSON object with the result; or, when an (API or HTTP)
             * error has been detected, the errback function is called.
             */
            get: function (method, params, callback, errback, notifycallback)
            {
                $.any.rest._doMethod('GET', method, params, callback, errback, notifycallback);
            },


            /**
             * Internal function for handling the API calls.
             */
          _doMethod: function(verb, api_method, params, callback, errback, notifycallback)
            {
			    var opts = {
				    "type": verb.toUpperCase(),
				    "url": 	"http://www.womeninc.nl/services/rest/"
			    };

                params['format'] = 'json';
                params['method'] = api_method;
			    params['_n'] = '1'; // we want notifications
                
                try 
                {
                    if ( opts.type == 'GET' )
			        {
				        opts.url += '?' + $.any.rest.param( params );
			        }
                
			        if ( opts.type == 'POST' )
			        {
				        opts.data = $.any.rest.param( params );
			        }
                }
                catch (err)
                {
                    throw 'Encoding params for ' + api_method + ' failed: ' + err; 
                }

			    opts.success = function ( data )
			    {
                    //data = eval('(' + data + ')');

                    if (data && data['_n'])
                    {
                        // We got notifications
                        for (var i=0; i<data['_n'].length; i++)
                        {
                            var notice = data['_n'][i];

                            if (typeof notifycallback == 'function')
                            {
                                notifycallback(notice);
                            }
                        }
                    }

                    if (data && data.err && data.err['-attrib-'])
                    {
                        var error = {code:    data.err['-attrib-'].code,
                                     message: data.err['-attrib-'].msg};
                        if (typeof errback == 'function')
                        {
                            errback(error);
                            return;
                        }

                        // No errback, shout out to the world this was not right.
                        $.log("unhandled.... in data.err");
                        $.any.rest.unhandledError(error);
			        }

                    if (typeof callback == 'function')
                    {
			            callback( data );
                    }
			    };

			    opts.error = function ( request, text, errorThrown )
			    {
                    var error = {'code': request.status, 'type': 'http', 'message': text};
                    if (typeof errback == 'function')
                    {
                        errback(error);
                        return;
                    }
					$.log(text);
					$.log(errorThrown);
					$.log(request);

                    $.any.rest.unhandledError(error);
			    };

			    $.ajax( opts );
            },

            /**
             * Redirect to a URL.
             */
            redirect: function ( url )
            {
                $.any.notification.loader.start();
                document.location.href = url;
            },


            /**
             * This function is called when a REST error has not been
             * handled by the caller (e.g., no error callback was
             * given).
             */
            unhandledError: function ( error )
            {
                $.any.notification.error(error.message);
                $.any.notification.loader.stop();
                throw error.code + ": " + error.message;
            }
	    }
    });
})(jQuery);
//

/*  */
/*  */



// External callback to initialize the widget
// @param   mixed   container   jQuery selector for the object to be used
// @param   dom     context     DOM object used as jQuery context. Defaults to document.body
// @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' );
            
            if ($(this).data("do_unlink"))
            {
                return;
            }
            $(this).data("do_unlink", true);
			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" ]
	,[ "handle",	'<img src="http://fast.mediamatic.nl/f/pgcq/image/cross.gif" alt="" />', "specify link content" ]
	,[ "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 = $( obj ).offset();

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

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

	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 = 50000;
		$( 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);';
		
		$(axx).html( this.options.handle );	
		$(axx).css({cursor: 'pointer'});
		$(axx).attr('title', $.label('unlink.confirm'));

    $( 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;
	
	if ($("#" + self.id).length) {
		el = $("#" + self.id);
	}
	else {
		el = $(self.container);
	}
	
	$.any.rest.post(this.options.service, this.options.ajax, function (result) {
		el.fadeOut('slow', function() {
			el.remove();
		});
	});
};


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


/*  */
// 

(function() {

	$.any.ui('menu', {
	
		_init: function() {
		
			var self = this,
				menu_items = self.element.find("> li"),
				submenu_zindex = 1000,
				timeout_duration_over = 0,
				timeout_duration_out = 300;
				
			if (self.element.attr("id") == "menu-auth") {
				submenu_zindex = 3000;
			}
			
			else if (self.element.attr("id") == "menu-nav") {
				submenu_zindex = 2000;
			}
			
			function removeSiblingHelpers() {
				self.element.find(".next-sibling-hover").removeClass("next-sibling-hover");
				self.element.find(".prev-sibling-hover").removeClass("prev-sibling-hover");
				self.element.find(".next-sibling-selected").removeClass("next-sibling-selected");
				self.element.find(".prev-sibling-selected").removeClass("prev-sibling-selected");
			}
			
			function turnOffMenuItem(el) {
				el.css('zIndex', 0);
				el.find("a").removeClass("hover");
				el.find("ul").css('zIndex', 0).hide();
			}
			
			function addSiblingHelpers(menu_item) {
			
				if (menu_item.next().length) {
					menu_item.next().addClass("next-sibling-hover");
						
					if (menu_item.next().find("> a").hasClass("selected")) {
						menu_item.addClass("next-sibling-selected");
					}
				}
				
				if (menu_item.prev().length) {
					menu_item.prev().addClass("prev-sibling-hover");
					
					if (menu_item.prev().find("> a").hasClass("selected")) {
						menu_item.addClass("prev-sibling-selected");
					}
				}
			}
			
			menu_items.each(function() {
			
				// the current menu item
				var menu_item = $(this);
				
				menu_item.hover(
				
					// mouse over
					function(e) {
					
						var el = $(this),
							anchor = el.find("a").not(".ul-lvl-1 a"), // do not select the submenu anchors
							submenu = el.find("> ul");
						
						removeSiblingHelpers();
						addSiblingHelpers(menu_item);
						
						anchor.addClass("hover");
						
						// clear all mouseout timeouts from all menu items in this menu, except for the current menu item
						menu_items.each(function() {
						
							var timeout_out = $(this).data("timeout_out");
							
							if (timeout_out) {
								window.clearTimeout(timeout_out)
							}
							
							// do not deactivate the current menu item, comparison of raw dom elements is required
							if ($(this).get(0) != menu_item.get(0)) {
								turnOffMenuItem($(this));
							}
							
						});
						
						if (submenu.length) {
						
							el.data("timeout_over", window.setTimeout(function() {
							
								// align the top of the submenu to the anchor element and increase the z-index so the latest popped up menu will always be on top
								submenu.css({
									top: anchor.outerHeight(),
									zIndex: submenu_zindex++
								}).show();
								
								// IE7 z-index fix: the parent of the submenu (the LI-element in this case) should get a higher z-index
								menu_item.css('zIndex', submenu_zindex++);
								
								submenu.find("li a").hover(
									function() {
										$(this).addClass("hover");
									},
									function() {
										$(this).removeClass("hover");
									}
								);
								
							}, timeout_duration_over));
						}
					},
					
					// mouse out
					function(e) {
						
						var el = $(this),
							timeout_over = el.data("timeout_over");
						
						if (timeout_over) {
							window.clearTimeout(timeout_over)
						}
						
						el.data("timeout_out", window.setTimeout(function() {
						
							turnOffMenuItem(el);
							removeSiblingHelpers();
							
						}, el.find("> ul").length ? timeout_duration_out : 0));
					}
				);			
			});
		}
	})
	
})(jQuery);

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

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

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

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

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

			obj.bind(options.inevent, function(e) 
			{
				this.tip = this.title;
				
				if(options.html)
				{
					var tip_content = options.html;
				}
				else if(options.dom_id)
				{
					var tip_content = $('#' + options.dom_id).html();
				}
				else
				{
					var tip_content = this.tip;
				}
				
				this.title = "";
				
				tip = jQuery('<div></div>')
						.addClass('tooltip')
						.html(options.extra_image + tip_content)
						.css({top: e.pageY + options.offsetY, left: e.pageX + options.offsetX, width: options.width, maxWidth: options.maxwidth });
				
				jQuery(document.body).append(tip);
								
				if(options.follow)
				{
					jQuery(document).bind('mousemove', function(e)
					{ 
						tip.css({top: e.pageY + options.offsetY});
						
						if(e.pageX + tip.width() > jQuery.windowSize().w)
						{
							tip.css({left: e.pageX - options.offsetX - tip.width() - 30});
						}
						else if(e.pageX + tip.width() < jQuery.windowSize().w)
						{
							tip.css({left: e.pageX + options.offsetX });
						}
						else
						{
							tip.css({left: e.pageX + options.offsetX });
						}
					});
				}
				else
				{
					var left = jQuery.getPosition(this).x;
					var top  = jQuery.getPosition(this).y;
					
					tip.css({top: top - tip.height() - 10});
					
					if(left + tip.width() > jQuery.windowSize().w)
					{
						tip.css({left: left - (obj.width() / 2) - (tip.width() / 2 )});
					}
					else
					{
						tip.css({left: left + (obj.width() / 2) - (tip.width() / 2 )});
					}
									
					if(options.type == 'input')
					{
						tip.css({ left: left + obj.width() + 15, top: top});
					}
				}
								
				tip.oneTime(options.timein, function()
				{
					jQuery(this).stop().animate(options.ineffect, options.ineffectduration);
				});
			});
			
			obj.bind(options.outevent, function(e) 
			{
				if (typeof tip == "undefined") return;

				tip.stop().animate(options.outeffect, options.outeffectduration, function()
				{
					jQuery(this).remove();
				});
				
				jQuery.fn.tooltip.destroy();
				this.title = this.tip;
			});
			
			jQuery.fn.tooltip.destroy = function()
			{
				obj.unbind(options.inevent, options.outevent);
				jQuery(document).unbind('mousemove');
			}
		});			
	}
})(jQuery);
//
// 
//function init_imgzoomer(container, context)
//{
//	context = context || document.body;
//
//	$(container, context).each(function() 
//	{
//		var opts = "";
//		opts = jQuery.attrOptions(this, "imgzoomer");
//		new Imgzoomer(this, opts);
//	});
//}
//
//function docs_imgzoomer()
//{
//	var a = "An image zoomer.";
//	var e = [ ['<img class="do_imgzoomer" src="" />', "Image zoomer"] ];
//	var c = [];
//	
//	var o = 
//	[
//		[ "showZoomer", 	'true',		"Show the zoomer in the bar" ],
//		[ "showMakeBig", 	'true',		"Show the button to make the image big" ]
//	];
//	
//	return { 'examples': e, 'about': a, 'opts': o, 'classes': c };
//}
//
//function opts_imgzoomer(o)
//{
//	return apply_options(docs_imgzoomer().opts, o);
//}
//
//function Imgzoomer(container, opts)
//{
//	opts			= opts_imgzoomer( opts );
//	this.container	= container;
//	this.opts		= opts;
//	
//	$(container).img_zoomer(
//	{
//		showZoomer: this.opts.showZoomer,
//		showMakeBig: this.opts.showMakeBig	
//	});	
//}

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

		return this.each(function() 
		{
			var origImg 		= $(this);	
			var imgWidth 		= origImg.width();
			var imgHeight 		= origImg.height();
			var img_id 			= origImg.attr('usemap').replace('#',''); // find the <map> that belongs to this image
			
		
			var usemap, imgVars, imgSrc, originalWidth, originalHeight;
			
			if($('map[name='+ img_id +']').length)
			{
				usemap 			= $('map[name='+ img_id +']');
				imgVars 		= usemap.attr('title').split(",");
				imgSrc 			= imgVars[2];
				originalWidth 	= parseInt(imgVars[0]);
				originalHeight 	= parseInt(imgVars[1]);
			}
			
			var perc = 1;
		
			if(imgWidth != originalWidth)
			{			
				perc = (imgWidth / originalWidth);
			}
	

			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 image does not need zoomer, finish here, and skip to the next iteration
			var zoomImg = true;
			if(!perc || perc > .85 || imgWidth < 280)
			{
				wrapper.addClass('img-no-zoom');
				zoomImg = false; 
			}
		
			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');			
					toolbar.show();
				})
				.mouseout(function()
				{	
					$(this).removeClass('img_hovered');

					toolbar = $('.img-toolbar', this);
					toolbar.hide();
				});
		
			toolbar.prependTo(wrapper)
		
			// add tags button, set binds and title	
			var tagText = $('.edit-img-tags', toolbar).text().split(',')[0];

			$('.edit-img-tags', toolbar).html( '<span class="icon-background"><span class="icon"></span></span><span class="caption">' + tagText + '</span>' );

			var img 	= $('<img />').addClass('img-zoomer-hi-res').css({position: 'absolute'}).hide().insertAfter(origImg);	
/*
			if (zoomImg)
			{
				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').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 class="img-zoomer-tool-fullscreen"><span class="caption">Vergroot</span><span class="icon-background"><span class="icon"></span></span></span>')
					.click(function(e)
					{
						imgViewerFunctions.imageGoFullsize(img, wrapper, origImg, toolbar);
						e.stopPropagation();
					})
					.prependTo(zoomer);
			}
		});
	}
})(jQuery);
	
imgViewerFunctions = 
{
	loadBigImage: function(img)
	{
		var wrapper = img.parents('div');
		var toolbar = $('.img-toolbar', wrapper);
		// var zoomer 	= $('.img-zoomer-tools', wrapper).show();
		// 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
			.css({position:'absolute',left:-999999})
			.load(function()
			{
				// loader.hide();
				
			
				$(this)
					.attr({rel: $(this).width()+','+$(this).height()})
					.css({width: wrapper.width(),height: wrapper.height()})
					.unbind('load')
					.css({position:'static',left:'auto'});
			})
			.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);
				}
			});
		img.attr({src: imgSrc})
	},
	
	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);
			});
			
		$(document)
			.unbind('keypress.imageZoomer')
			.bind('keypress.imageZoomer', function(e)
			{
				var key;
				if($.browser.msie) 	{ key = e.which } else { key = e.keyCode }
				if(key == 27) { $('.popup-overlay').click(); }
			});	
	},
	
	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');
			}
	
			if($('map[name='+imgId+']').length)
			{
				// 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;
			}
			else
			{
				return;
			}
					
			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').fadeOut();
			objParent.siblings('.temp-actor-list').remove()
			objParent.removeClass('in_edit');
			
			actorList = $('.list-actor .list-view-simple');
			
			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(',');
				
						$('.list-view-simple-thing:contains('+text[0]+')',actorList).css({textDecoration: 'underline'});		
					},
					function()
					{
						hide_label();
						$('.list-view-simple-thing',actorList).css({textDecoration: 'none'});
					})
				});
			
			obj.removeAttr('alt');
			
			objParent
				.bind('mouseenter', function()
				{
					annotations = $('.annotation', this);
					toolbar 	= $('.img-toolbar', this);
					
					$(this).addClass('img_hovered');
					
					if(!objParent.hasClass('zooming-img'))
					{
						annotations.show();
					}
					
					// 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(toolbar.length && toolbar.html() != '') 
					{
						// toolbar.stop().css({opacity: 1});
					}
				})
				.bind('mouseleave', function()
				{	
					annotations = $('.annotation',this);
					toolbar 	= $('.img-toolbar',this);
					
					$(this).removeClass('img_hovered');
					
					annotations.hide();
					// toolbar.css({opacity: 0});
				});
	
			// Fix for IE, image-link wasn't clickable because usemap was in the way. Parent attribute href being called to still get it to work!
			if($.browser.msie && obj.parents('a:first').length) 
			{
				$('.edit-img-tags', objParent).click(function(e)
				{
					e.stopPropagation();
				});
			
				objParent.click(function()
				{
					location.href = obj.parents('a:first').attr("href");
				})
			}
					
			// 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)
{
	$('.list-view-simple-thing', actorList).unbind('mouseover', 'mouseout');
	
	$('.list-view-simple-thing', actorList).hover(function()
	{
		if(!$(".annotation", objParent).length)
		{
			return false;
		}

		annotations.animate({opacity: 1});
		
		var search_text = $.trim( $(this).removeAttr('title').text() );
		
		$(".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});
	});
}

//

// 

if (!$.any) $.any = {};


$.any.dialogVariant = function(namespace, extended)
{
	var shared =
	{
		/**
		 * Default settings.
		 */
		defaults: {
			// Display
			width			: '',		// Dialog width
			height			: 'auto',		// Dialog initial height
			overlay			: true,			// Show overlay over page while dialog is displayed
			className		: '',			// Extra classnames for .dialog

			// Positioning
			draggable		: true,			// Draggable
			underObj		: false,		// Position below parent
			offsetX			: 11,			// Left offset calculated from top left of parent
			offsetY			: 11,			// Top offset calculated from top left of parent
			dialogLeft		: false,		// Left fixed position (offset and underObj are ignored)
			dialogTop		: false,		// Top fixed position (offset and underObj are ignored)

			// Content
			title			: '',			// Dialog title
			url				: '',			// Fetch URL using AJAX and display this in dialog
			html			: '',			// Put HTML in dialog
			dom_id			: '',			// Copy dom_id inner HTML to dialog

			// Animations
			ineffect		: {opacity: 1},	// Animation when opening
			ineffectTime	: 250,			// Opening animation duration
			outeffect		: {opacity: 0},	// Animation when closing
			outeffectTime	: 10,			// Closing animation duration

			// Opening, Closing
			initWidgets		: false,		// Initialize widgets and Cufon
			closeClick		: true,			// Close when something else than dialog is clicked
			callback		: null,			// Callback on close
			openCallback	: null			// Callback on open
		},

		/**
		 * Settings for the currently opened dialog.
		 */
		currentOptions: {},

		/**
		 * Open a dialog.
		 */
		open: function(options, parent)
		{
			var self = this;
			
			if (self.opened()) {
				// Remove the dialog.
				self.close(function() {
					self._showDialog(options, parent);
				});
			}
			else
			{
				self._showDialog(options, parent);
			}
			if (typeof window.widget_manager.currentWidgets.editinplace == 'object')
			{
				window.widget_manager.currentWidgets.editinplace.save();
			}
		},

		/**
		 * Return whether a dialog is opened.
		 */
		opened: function()
		{
			var self = this;
			return !!self.dialogElement().length;
		},

		/**
		 * Close the dialog.
		 */
		close: function(callback)
		{
			var self = this;

			// We're still opening the dialog
			if (self.opening) {
				return;
			}

			// We're not open					
			if (!self.opened()) {
				return false;
			}

			var options = self.currentOptions;

			self.dialogElement().animate(options.outeffect, options.outeffectTime, function()
			{
				self._removeDialog(callback);
			});

			return true;
		},

		/**
		 * Fetch the dialog, returns jQuery object.
		 */
		dialogElement: function()
		{
			return $('.dialog.dialog-'+namespace);
		},

		/**
		 * Fetch the page overlay element, returns jQuery object.
		 */
		overlayElement: function()
		{
			return $('.dialog-overlay.dialog-overlay-'+namespace);
		},

		/**
		 * Return the content area element of the dialog.
		 */
		contentArea: function(container)
		{
			var self = this;
			container = container || self.dialogElement();
			return container.find('.dialog-content');
		},

		/**
		 * Get the title of the current dialog.
		 */
		getTitle: function()
		{
			var self = this;
			return self.dialogElement().find('.dialog-drag-handle-text').html();
		},

		/**
		 * Set the title of the current dialog.
		 */
		setTitle: function(title)
		{
			var self = this;
			return self.dialogElement().find('.dialog-drag-handle-text').html(title);
		},

		/**
		 * Set initial dialog content and show the dialog
		 */
		setContent: function(html)
		{
			var self = this;
			var dialogStructure = self.dialogStructure;
			var options = self.currentOptions;

			// Add HTML
			self.contentArea(dialogStructure)
				.empty()
				.append('<div class="notification">')
				.append(html);
			
			// Put in body
			dialogStructure
				.appendTo(document.body)
				.css({
					opacity: 0,
					display: 'block'
				});

			// Reposition dialog if it's partly outside viewport
			var offset = dialogStructure.offset();
			self._correctPosition(dialogStructure, offset.left, offset.top);
			
			// Callbacks, triggers
			dialogStructure.animate(options.ineffect, options.ineffectTime, function()
			{
				self._addCloseTriggers(options);
				self._executeCallbacks([options.openCallback]);

				// Draggable
				if (options.draggable)
				{
					$(this).draggable(
					{
						handle		: dialogStructure.find('.dialog-drag-handle:first'),
						containment	: document.documentElement
					});
				}

				// Widgets + Cufon
				if (options.initWidgets)
				{
					self.initWidgets();
				}
				
				// IE7,8 fix. Prevent clipping of nested divs when they exceed the parent div (dialog). Happens with the autocompleter dropdown.
				$(this).css('filter', '');
				
				// Mark the fact that we're done
				self.opening = false;
			});
		},
		
		/**
		 * Initializes widgets and Cufon on dialog 
		 */
		initWidgets: function()
		{
			var self = this;
			var dialog = self.dialogElement();
			
			$.any.ui.scan(dialog);
			
			if (typeof Cufon != 'undefined')
			{
				Cufon.refresh();
			}
		},

		/**
		 * Get position of the dialog, returns position in jQuery.css() compatible format.
		 */
		_getPosition: function(options, parent)
		{
			var self = this;
			var left, top;

			// Called with explicit coordinates
			if (options.dialogLeft && options.dialogTop)
			{
				left = options.dialogLeft;
				top = options.dialogTop;
			}

			// Position below clicked button
			else if (parent && parent[0] != document.body)
			{
				left = parseInt(parent.offset().left + options.offsetX, 10);
				top = parseInt(parent.offset().top + options.offsetY, 10);

				if (options.underObj)
				{
					top += parent.height();
				}
			}

			// Position in the middle of the window
			else
			{
				left = ($(document).width() / 2) - ((options.width || 500) / 2);
				top = $(window).scrollTop() + Math.floor($(window).height() / 3);
			}

			return {left: Math.round(left), top: Math.round(top)};
		},

		/**
		 * Get the default title for the dialog
		 */
		_getTitle: function(options, parent)
		{
			if (options.title)
			{
				return options.title;
			} else if (parent && parent[0] != document.body)
			{
				return parent[0].text();
			}
		},

		/**
		 * Create dialog overlay and add to body.
		 */
		_createOverlay: function()
		{
			return $('<div>')
				.addClass('dialog-overlay')
				.addClass('dialog-overlay-'+namespace)
				.css({
					zIndex: 35000,
					height: $(document).height()
				})
				.appendTo(document.body);
		},

		/**
		 * Create dialog DOM structure
		 */
		_createDialogStructure: function(options, title, position)
		{
			var self = this;
			title = title || '';

			var dialog = $('<div>')
							.addClass('dialog')
							.addClass('dialog-'+namespace)
							.addClass(options.className)
							.css(position)
							.css({
								position	: 'absolute',
								width		: options.width,
								height		: options.height
							});

			var titleBar = $('<div>')
							.addClass('dialog-drag-handle')
							.mousedown(function() { 
								$(this).addClass('pressed');
								// Hide all wizard confirms
								$(".dialog-confirm").remove();
							})
							.append()
							.appendTo(dialog);

			var titleEl = $('<span>')
							.addClass('dialog-drag-handle-text')
							.html(title)
							.appendTo(titleBar);

			var close = $('<span>')
							.addClass('dialog-close')
							.appendTo(titleBar)
							.text('x')
							.click(function()
							{
								var isComplex = self._isComplex();
								
								if (isComplex)
							    {
							    	$("body").append($("<div></div>")
											    	.addClass("dialog-confirm")
											    	.html('<span class="dialog-confirm-text">Weet je het zeker?</span>')
						    						.append($("<button></button>")
																.addClass("cancel")
																.text("Ja")
																.click( function() {
																	self._trackDialog({action: "dialog close by cross"});
																	$(".dialog-confirm").remove();
																	$.any.dialog.close();

																	
																}))
													.append($("<button></button>")
																.addClass("cancel")
																.text("Nee")
																.click( function() {
																	$(".dialog-confirm").remove();
																	return false;
																}))
													.css({position: "absolute", top: $(this).offset().top, left: $(this).offset().left})
													.animate({opacity: 1}, 300)
												);
								}
								else
								{
									self._trackDialog({action: "dialog close by cross"});
									self.close();
								}

							});

			var content = $('<div>')
							.addClass('dialog-content')
							.appendTo(dialog);

			return dialog;
		},

		/**
		 * Fetch content for the dialog
		 */
		_fetchContent: function(options)
		{
			var self = this;

			// Fetch content using AJAX
			if(options.scomp)
			{
				// Used from within actiondialog_trigger
	            $.any.rest.get('anymeta.html.scomp',
                {
                    'name': options.scomp,
                    'thg_id': options.thg_id
                },
                function (json)
                {
					self.setContent(json.html);
                });
			} 

			// Still used in modules/GoogleMaps/templates/gmaps_viewonmap_link.tpl
			else if(options.url)
			{
				$.get(options.url, function(content)
				{
					self.setContent(content);
				});
			} 

			// Clone a DOM node
			else if (options.dom_id)
			{
				self.setContent($(options.dom_id).clone(true));
			}

			// HTML
			else if (options.html)
			{
				self.setContent(options.html);
			}
		},

		/**
		 * Correct position of the dialog.
		 */
		_correctPosition: function(dialogElement, left, top)
		{
			var windowObj	= $(window);
			var buffer		= 30;

			if (left + dialogElement.width() > windowObj.width())
			{
				left = Math.max(windowObj.scrollLeft(), windowObj.width() - dialogElement.width() - buffer);
			}

			if (top + dialogElement.height() + buffer - windowObj.scrollTop() > windowObj.height())
			{
				top = Math.max(buffer + windowObj.scrollTop(), top - dialogElement.height());
			}

			left= Math.max(0, left);
			top = Math.max(0, top);

			dialogElement.css({left: left, top: top});
		},

		/**
		 * Dialog closing triggers.
		 * (For dialog haters)
		 */
		_addCloseTriggers: function(options)
		{
			var self = this;
			
			// Destroy on Esc key.
			// Unless an input field has focus, user may have wanted to close browser autocompletion.
			$(document).keyup(function(e)
			{
				if (e.keyCode == $.ui.keyCode.ESCAPE && !$('input:focus').length)
				{
					self._trackDialog({action: "dialog close by escape"});
					self.implicitClose();
				}
			});

			// Destroy on outside click
			if (options.closeClick)
			{
				if (!options.overlay)
				{
					$(document).click(function(e)
					{		
						if (!$(e.target).hasClass('dialog') 
						 && !$(e.target).parents('.dialog').length 
						 && !$(e.target).parents('#ui-datepicker-div').length 
						 && !$(e.target).hasClass('unlink-confirm')
						 && !$(e.target).parents('.unlink-confirm').length
						){
							self.implicitClose();
						}
					});
				}
				else
				{
					self.overlayElement().click(function()
					{
						self.implicitClose();
					});
				}
			}
		},

		/**
		 * Implicit close (by shortcuts like Esc button and outside clicks)
		 * Default is close immediately without any checks
		 */
		implicitClose: function()
		{
			var self = this;
			self.close();
		},

		/**
		 * Execute an array of callbacks.
		 */
		_executeCallbacks: function(callbackArray)
		{
			for (var i=0, m=callbackArray.length; i<m; i++)
			{
				var callback = callbackArray[i];
				if(typeof callback == 'function')
				{
					callback();
				}
				else if (typeof callback == 'string' && window[callback])
				{
					window[callback]();
				}
			}
		},

		/**
		 * Physically remove the dialog from the DOM.
		 */
		_removeDialog: function(callback)
		{
			var self = this;

			self.overlayElement().remove();
			self.dialogElement().remove();

			self._executeCallbacks([callback, self.currentOptions.callback]);

			self.currentOptions = {};
		},

		/**
		 * Private function for showing the dialog.
		 */
		_showDialog: function(options, parent)
		{
			var self = this;
			
			self.opening = true;

			self._trackDialog({action: "dialog open"});
			
			options = $.extend({}, self.defaults, options);

			var title = self._getTitle(options, parent);
			var position = self._getPosition(options, parent);

			self.dialogStructure = self._createDialogStructure(options, title, position);
			self.currentOptions = options;

			if (options.overlay)
			{
				self._createOverlay();
			}

			self._fetchContent(options);
		},
		
		/**
		 * Returns if a dialog is complex
		 * A complex dialog has a ui form with the isComplex option
		 */
		_isComplex: function()
		{
			var el = $.any.dialog.contentArea();
			var isComplex = false;

			$(".ui_form", el).each(function() {
				var form = $(this).data("ui");
				if (form && form.options.isComplex)
				{
					isComplex = true;
				}
			});
			
			return isComplex;
		},

		/**
		 * Trac this click with google analytics
		 */
		_trackDialog: function(trackOptions)
		{
			var opts = $.any.dialog.currentOptions;

			if (opts.dialogTrack)
			{
				var optsAction = opts.dialogTrack.action || "";
				var optsLabel = opts.dialogTrack.label || "";
				var optsValue = opts.dialogTrack.value || "";
			
				$.any.ui.trackEvent.track({
					category: trackOptions.category ? $.trim(trackOptions.category + " " + opts.dialogTrack.category) : opts.dialogTrack.category,
					action: trackOptions.action ? $.trim(trackOptions.action + " " + optsAction) : optsAction,
					label: trackOptions.label ? $.trim(trackOptions.label + " " + optsLabel) : optsLabel,
					value: trackOptions.value ? $.trim(trackOptions.value + " " + optsValue) : optsValue
				});
			}
		},

		/**
		 * Set the width of a dialog. Can be reset to the old value.
		 */
		setWidth: function(obj, width)
		{
			if (width == 'reset')
			{
				// Use the 'old' width
				width = $(obj).data("width");
				var dialogLeft = ($(window).width() / 2) - (parseInt(width) / 2);
				if (width && dialogLeft)
				{
					$(obj).css({"width": width+"px", "left": dialogLeft});
				}

				// Reset data
				$(obj).data("width","");
			}
			else if (!$(obj).data("width"))
			{
				// Remember the actual width
				$(obj).data("width",$(obj).width());

				// Set the new width
				var dialogLeft = ($(window).width() / 2) - (parseInt(width) / 2);
				$(obj).css({"width": width+"px", "left": dialogLeft});
			}
		}
	};

	$.any[namespace] = $.extend(true, shared, extended);
};


// Regular dialog
$.any.dialogVariant('dialog',
{
	defaults:
	{
		helptexts: [],
		initWidgets	: true
	},

	/**
	 * Closes the dialog by pressing esc or clicking outside the dialog.
	 * Only allowed when the dialog does not contain complex forms.
	 */
	implicitClose: function(callback)
	{
		var self = this;
		var container = $.any.dialog.contentArea();
		var performClose = true;

		$('.ui_form', container).each(function()
		{
			var form = $(this).data('ui');
			if (form && form.options.isComplex)
			{
				performClose = false;
			}
		});

		if (performClose)
		{
			$.any.dialog._trackDialog({action: "dialog close by clicking outside"});
			return self.close(callback);
		}
		return false;
	},

	/**
	 * Add helptext to the current dialog
	 */
	setHelpText: function(text)
	{
		var self = this;

		if (!self.opened()) return;

		var helpEl = self.dialogElement().find('.action-help');

		if (!helpEl.length && text)
		{
			helpEl = $('<div>')
				.addClass('action-help')
				.appendTo(self.contentArea());
		}

		if (!text)
		{
			helpEl.remove();
		} else
		{
			helpEl.html(text);
		}
	},

	/**
	 * ???
	 */
	pushHelpText: function()
	{
		var self = this;

		if (!self.opened()) return;

		var helpEl = self.dialogElement().find('.action-help');
		self.currentOptions.helptexts.push( helpEl.html() );
	},

	/**
	 * Add helptext to the current dialog
	 */
	popHelpText: function()
	{
		var self = this;

		if (!self.opened()) return;

		var helpEl = self.dialogElement().find('.action-help');
		var text = self.currentOptions.helptexts.pop();
		helpEl.html(text);
	},

	/**
	 * Remove helptext from the current dialog
	 */
	removeHelpText: function()
	{
		var self = this;
		var helpEl = self.dialogElement().find('.action-help');
		helpEl.remove();
	},

    /**
     * Always return to the top of the dialog if the top
     * of the dialog is not visible (page scrolled down).
     */
    gotoDialogTop: function(modifierFunction) 
    {
        if (!$.any.dialog.opened())
        {
        	modifierFunction();
            return;
        }
        var dc = $.any.dialog.contentArea();

        modifierFunction();

        $.any.dialog._moveOffstageContent();
		
        // Scroll up to the input box
        if($('html,body').scrollTop() > dc.offset().top)
	    {
			$('html,body').animate({scrollTop: dc.offset().top-72}, 100);
		}
	},

	/**
	 * Move offstage content if it's not a direct child of .dialog-content
	 */
	_moveOffstageContent: function()
	{
		var self = this;
		var dc = self.contentArea();

		var offstage = dc.find('.action-offstage');
		if (!offstage.length) return;

		// Remove already moved contentareas
		dc.find('.action-offstage-moved').remove();

		if (offstage.parents('.action-stage-wrapper').length)
		{
			if (dc.find('.action-help').length)
			{
				offstage.insertBefore('.action-help:last');
			} else
			{
				offstage.insertAfter( dc.find('.action-stage-wrapper:first') );
			}
			offstage.addClass('action-offstage-moved');
		}
	}

});



// List item viewer dialog
$.any.dialogVariant('liviewer',
{
	defaults:
	{
		initWidgets		: true,
		overlay			: false,
		removeLinks		: false,
		buttons			: [],
		closeClick		: false
	},

	/**
	 * Cache object for LI html
	 */
	cache: {},

	/**
	 * Show things in the LI viewer
	 */
	showThings: function(ids, options)
	{
		var self = this;
		var cacheKey = ids.join('_');
		var cache = self.cache[cacheKey];

		if (!cache)
		{
			var data =
			{
				name		: 'listsection',
				query_list	: ids
			};

			$.any.rest.get('anymeta.html.scomp', data, function(json)
			{
				options = options || {};
				options.html = self.cache[cacheKey] = json.html;
				self.open(options);
				self._modifiers(ids);
			});
		} else
		{
			options.html = cache;
			self.open(options);
			self._modifiers(ids);
		}
	},
	
	/**
	 * Run content modifiers
	 */
	_modifiers: function(ids)
	{
		var self = this;
		var options = self.currentOptions;
		
		if (typeof options.buttons != 'undefined' && options.buttons.length)
		{
			self._addButtons(ids, options);
		}
		
		if (options.removeLinks)
		{
			self._removeLinks();
		}
	},
	
	/**
	 * Add buttons below the content
	 */
	_addButtons: function(ids, options)
	{
		var self = this;
		var dc = self.contentArea();
		
		var wrapper = $('<div>')
			.addClass('dialog-liviewer-buttons')
			.addClass('clearfix')
			.appendTo(dc);

		for (var i in options.buttons)
		{
			var button = options.buttons[i];
			$('<button>')
				.text(button.title)
				.click(function(){ button.callback(ids); })
				.appendTo(wrapper);
		}		
	},

	/**
	 * Remove hyperlink behaviour on links. LI's normally link to their own page.
	 */	
	_removeLinks: function()
	{
		var self = this;
		self.contentArea().find('a')
			.removeAttr('href')
			.unbind('click')
			.css({cursor: 'default'});
	}
});



/**
 * DialogTrigger ui element
 */
$.any.ui('dialogtrigger',
{
	_init: function()
	{
		var self = this;

		if (self.options.direct)
		{
			$.any.dialog._trackDialog({action: "dialog open direct"});
			$.any.dialog.open(self.options, self.element);
		}

		if (self.options.click)
		{
			self.element.click(function(e)
			{
				self.open();
			});
		}
	},

	open: function(callback)
	{
		var opts = $.extend({}, this.options);
		if (callback)
		{
			opts.openCallback = callback;
		}
		$.any.dialog.open(opts, this.element);
	}
},
{
	click: true,
	direct: false
});


// 
