
	

/*  */

// 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="/image/throbberwait.gif" />');
				}
				else
				{
					// Outside of simple linking
		    		button.parents('.form-button').append('<img class="form-throbber" src="/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="/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.zwarts.jansma.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);
//
// 

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

