/*extern dojo, date_str_to_i18n_date, gettext, copy_an_object, is_array, Option, add_option_to_select_box */


    
/**
 * Shows or hides an element
 * @param {Object} element
 * @param {Boolean} bool Whether to show the element
 */
function showElement(element, bool)
{
    if (bool === true) {
        element.style.display = '';
    }
    else {
        element.style.display = 'none';
    }
}

/*
    Function: date_to_str
        Date to yyyy-mm-dd conversion
    
    Parameters:
        a_date_obj - Date object
    
    Returns:
        String, date in format yyyy-mm-dd
*/
function date_to_str(a_date_obj)
{
    var yyyy = a_date_obj.getFullYear();
    var mm = a_date_obj.getMonth() + 1;
    var dd = a_date_obj.getDate();
    
    var a_date_str = yyyy + '-';
    if (mm < 10)
    {
        a_date_str += '0';
    }
    a_date_str += mm + '-';
    if (dd < 10) 
    {
        a_date_str += '0';
    }
    a_date_str += dd;
    
    return a_date_str;
}

/*
    Function: parse_date_str
        Creates a date object from a yyyy-mm-dd string
        Beware that JS uses the range 0-11 for months
    
    Parameters:
        a_date_str - String, date in the format yyyy-mm-dd
    
    Returns:
        Date, object
*/
function parse_date_str(a_date_str)
{
    var date_parts = a_date_str.split ('-');
    var a_date = new Date(date_parts[0], date_parts[1]-1, date_parts[2]);
    return a_date;
}

/*
    Function: date_to_i18n_date
        Date to international date conversion
    
    Parameters:
        a_date - date object
        format_string - string, optional specifies format
*/
function date_to_i18n_date( a_date, format_string )
{
    // js month range is [0-11]!
    var date_str = a_date.getFullYear() + '-' + (a_date.getMonth()+1) + '-' + a_date.getDate();
    return date_str_to_i18n_date(date_str, format_string);
}

/*
    Function: date_str_to_i18n_date
        String date to international date conversion.
    
    Parameters:
        a_str - date string in the format YYYY-mm-dd
        format_string - string, optional specifies format
*/
function date_str_to_i18n_date ( a_str, format_string )
{
    /*
        String date to international date conversion.

        It is assumed that string has the following format: YYYY-mm-dd  
        
        Django admin interface will be used as source for gettext.

        If format_string is provided, date will be displayed as desired.

        Format:
            d - Day of month, 2 digits
            j - Day of month, 1 or 2 digits (if needed)

            F - Month name
            m - month in digits, always 2 digits
            n - month in digits, 1 or 2 digits (if needed)
            M - month, three letter abbreviation

            Y - year in 4 digits
            y - year in 2 digits
    */
    var result = '';
    var a_list_result = [];
    
    var yyyy, mm, dd;
    var a_date_splitted = a_str.split ( '-' );
    
    yyyy = a_date_splitted [ 0 ];
    mm   = a_date_splitted [ 1 ];
    dd   = a_date_splitted [ 2 ];
    
    var tmp_date = new Date(yyyy, mm-1, dd);
    var days = gettext('Sonntag Montag Dienstag Mittwoch Donnerstag Freitag Samstag').split(' ');
    
    var months = gettext('Januar Februar März April Mai Juni Juli August September Oktober November Dezember').split(' ');
    var month_name = months [ mm - 1 ];

    /*
        Do the replacements:
    */
    if ( format_string )
    {   
        for ( var i = 0; i < format_string.length; i ++ )
        {
            /*
                test each character and build join list:
            */
            var a_char = format_string .charAt ( i );

            if ( a_char === 'd' )
            {
                /*
                    d - Day of month, 2 digits    
                */                
                a_list_result.push ( dd );
            }
            else if ( a_char === 'l' )
            {
                /*
                    l - Day of week, full
                */
                var d = tmp_date.getDay();
                var dayofweek = days[d % 7];
                a_list_result.push(dayofweek);
            }
            else if ( a_char === 'j' )
            {
                /*                
                    j - Day of month, 1 or 2 digits (if needed)
                */
                if ( dd [ 0 ] === '0' )
                {
                    a_list_result.push ( dd [ 1 ] );
                }
                else
                {
                    a_list_result.push ( dd );
                }
            }
            else if ( a_char === 'F' )
            {
                /*
                    F - Month name, full
                */
                a_list_result .push ( month_name );
            }
            else if ( a_char === 'm' )
            {
                /*            
                    m - month in digits, always 2 digits
                */
                a_list_result .push ( mm );
            }
            else if ( a_char === 'n' )
            {
                /*
                    n - month in digits, 1 or 2 digits (if needed)
                */
                if ( mm [ 0 ] === '0' )
                {
                    a_list_result .push ( mm [ 1 ] );
                }
                else
                {
                    a_list_result .push ( mm );
                }
            }
            else if ( a_char === 'M' )
            {
                /*
                    M - month, three letter abbreviation
                */
                if ( month_name .length > 3 )
                {
                    a_list_result .push ( month_name . slice ( 0, 3 ) );
                    a_list_result .push ( '.' );
                }
                else
                {
                    a_list_result .push ( month_name );
                }
            }
            else if ( a_char === 'Y' )
            {
                /*
                    Y - year in 4 digits
                */                    
                a_list_result .push ( yyyy );
            }
            else if ( a_char === 'y' )
            {
                /*
                    y - year in 2 digits
                */
                a_list_result .push ( yyyy .slice ( 2, 4 ) );
            }                
            else
            {
                a_list_result .push ( a_char );
            }
        }
        
        result = a_list_result .join ( '' );
    }
    else
    {
        /*
            Just assume default format:
        */
        a_list_result = [ dd, '. ', month_name, ' ', yyyy ];

        result = a_list_result .join ( '' );
    }
    
    return result;
}

/*
    Function: date_str_to_dd_mm
*/
function date_str_to_dd_mm ( a_str )
{
    var a_list = a_str.split ( '-' );
    
    var dd = a_list [ 2 ];
    var mm = a_list [ 1 ];
    
    var result = [ dd, '.', mm, '.' ].join ('');
    return result;
}

/*
    Function: min
*/
function min ( an_array )
{
    var a_min = an_array[0];
    
    for ( var i = 1; i < an_array.length; i ++ )
    {
        if ( an_array[i] < a_min ) 
        {
            a_min = an_array[i];
        }
    }
    
    return a_min;
}

/*
    Function: max
*/
function max ( an_array )
{
    var a_max = an_array [ 0 ];
    
    for ( var i = 1; i < an_array.length; i ++ )
    {
        if ( an_array[i] < a_max ) 
        {
            a_max = an_array[i];
        }
    }
}

/*
    Function: is_in
*/
function is_in ( an_obj, an_array )
{
    /*
        This function traverses
        an array and checks if 
        the object is in it.        
    */            
    var result = false;
       
    for ( var i = 0; i < an_array.length; i ++ )
    {
        if ( an_obj === an_array[i] )
        {
            result = true;
            break;
        }
    }
    
    return result;
}

/*
    Function: filter
*/
function filter ( an_array, a_func )
{   
    /*
        This function applies the given
        a_func function on the array
        and returns filtered array.
    */
    var result = [];
    
    for ( var i = 0; i < an_array.length; i ++ )
    {
        var an_item = an_array[i];
        result.push( a_func( an_item ) );
    }
    
    return result;
}                

/*
    Function: copy_shallow
*/
function copy_shallow ( an_obj )
{
	/*
		This function returns a
		shallow copy of the object.
	*/
	return copy_an_object ( an_obj, true /* only_shallow */ );
}

/*
    Function: copy_an_object
        Creates a copy of an object.
        
        If only_shallow is true, it makes only a shallow copy, 
        
        otherwise it returns a deep copy.
*/
function copy_an_object ( an_obj, only_shallow )
{
	var result = null;
	
    if ( ! an_obj )
	{
		result = an_obj;	
	} 
	else if ( is_array ( an_obj ) )
	{
		if ( only_shallow )
		{
			/*
				clone it using build in function:
			*/
			result = an_obj .slice ();				
		}
		else
		{
			/*
				Do it recursevly:
			*/
            result = [];
			
			for ( var i = 0; i < an_obj .length; i ++ )
			{
                result.push( copy_an_object( an_obj[i] ) );
			}
		}
	}
	else if ( typeof ( an_obj ) === 'object' )
	{
		result = {};
		
	    for ( var a_property in an_obj )
    	{
			if ( only_shallow )
			{
				result [ a_property ] = an_obj [ a_property ];
			}
			else
			{
				result [ a_property ] = copy_an_object ( an_obj [ a_property ] );
			}
	    }
	}
	else
	{
		/*
			We don't have or don't know
			how to make copy of this
			thing:
		*/
		result = an_obj;
	}
	
    return result;
}	

/*
    Function: is_function
        Checks if the given object is a function.
        
        In that case it returns true,
        otherwise false.
*/
function is_function ( an_obj )
{
    return typeof ( an_obj ) === 'function';
}

/*
    Function: is_array
        Checks if the given object is an array.
*/
function is_array ( an_obj ) 
{
    var result = false;
    
    if ( !an_obj || !an_obj.constructor )
    {
        result = false;
    }        
    else
    {                      
        result = typeof(an_obj) === 'array' || (an_obj.constructor.toString().indexOf("Array") !== -1 );           
    }
               
    return result;
}    

/*
    Function: is_a
        Checks the type of an object
    
    Arguments:
        obj - Object
        className - String, class name
        
    Source:
        http://webreflection.blogspot.com
*/
function is_a ( obj, className )
{ 
    className = className.replace(/[^\w\$_]+/, ""); // paranoia
    return get_class(obj) === className && obj instanceof eval( className );
}

/*
    Function: create_and_add_option_to_select_box
        This function creates an option via provided strings
        and add it to the select box taking care of
        cross-browser compatibility.
    
    Returns:
        The created option element.
*/
function create_and_add_option_to_select_box ( a_select_box,
                                               a_caption,
                                               a_value,
                                               value_to_be_selected )
{
    var a_new_option = new Option ( a_caption, a_value );
    
    add_option_to_select_box( a_select_box, a_new_option, value_to_be_selected );
    return a_new_option;
}

/*
    Function: is_string
*/
function is_string ( an_obj )
{
    /*
        This function checks if the given
        object is a string;
        
        in that case it returns true,
        otherwise false.
    */
    return typeof( an_obj ) === 'string' || is_a( an_obj, 'String' );    
}

/*
    Function: is_number
*/
function is_number ( an_obj )
{
    /*
        This function checks if the given
        object is a number;
        
        in that case it returns true,
        otherwise false.
    */
    return typeof( an_obj ) === 'number' || is_a( an_obj, 'Number' );    
}

/*
    Function: is_boolean
        checks if the given object is a boolean;
        
    Returns:
        True, if it is a boolen,
        False, otherwise
*/
function is_boolean ( an_obj )
{
    return typeof( an_obj ) === 'boolean' || is_a ( an_obj, 'Boolean' );    
}

/*
    Function: is_date
        checks if the given object is a date;
        
    Returns:
        True, if it is a date,
        False, otherwise
*/
function is_date ( an_obj )
{
    return typeof(an_obj) === 'date' || is_a(an_obj, 'Date');
}

/*
    Function: is_form_element
        Checks if a_node has a form attached to it.
*/
function is_form_element ( a_node, a_form )
{
    var result = false;
    if ( a_node.form !== undefined )
    {
        result = true;
    }
    return result;
}

/*
    Function: clear_all_values_in
        Sets the value of the given node if it's a form element to null or
        the value of all his children form elements if it has any.
        
        If a node has clear_all function, it will be prefered.
        
        Elements with check property will be unchecked. 
*/
function clear_all_values_in ( a_node )
{             
    if ( is_form_element ( a_node ) )
    {
        /*
            Prefer the clear_all function if element has it:            
        */
        if ( a_node.clear_all )
        {
            a_node.clear_all();
        }
        else 
        {
            a_node.value = '';
            /*
                Uncheck it:                
            */
            if ( a_node.checked ) 
            {
                a_node.checked = false;
            }
        }
    }
    else if ( a_node !== a_node.TEXT_NODE )
    {        
        var kids = a_node.childNodes;
        for(var i = 0; i < kids.length; i++ ) 
        {      
            clear_all_values_in ( kids[i] );
        }            
    }        
}    

/*
	Function: clear_and_hide_on_switch
        Shows the node if the switch is set to the on value  
        
		Hides a node and sets values of all input elements inside to '' if a_switch is not set to the on value.
    
    Parameters:
    	a_node - Node element or String (node id)
    	a_switch - 
    	an_on_value - 
*/
function clear_and_hide_on_switch ( a_node, a_switch, an_on_value )
{     
    var condition = null;
    
    if ( is_string ( a_node ) )
    {
        a_node = dojoWrapper.getById ( a_node );
    } 
    
    if ( is_array ( an_on_value ) )
    {
        /*
            if an_on_value is an array,
            go trough all values and or them.            
        */
        condition = false;
        for ( var i = 0; i < an_on_value.length; i ++ )
        {
            condition = condition || ( a_switch === an_on_value[i] ); 
        }
    }
    else
    {                
        /*
            an_on_value is a single variable:            
        */
        if ( an_on_value )
        {
            condition = a_switch === an_on_value;
        }
        else
        {
            /*
                an_on_value is void and therefore condition is true, 
                if a_switch is not void.                
            */
            condition = a_switch;
        }    
    }    
    
    if ( condition )
    {
        /*
            The switch is on, we should show it:            
        */
        a_node.style.display = '';        
    }
    else
    {
        /*
            The switch is off, we should hide it and clear it:
        */
        clear_all_values_in ( a_node );
        a_node.style.display = 'none';
    }        
}

function attach_hidden_field_to_form ( a_form, 
                                       a_field_name, 
                                       a_field_value )
{
    /*
        This function attaches a hidden input
        field to the given form.
    */
    var an_input_field = document.createElement ( 'input' );
    
    an_input_field.type  = 'hidden';
    an_input_field.name  = a_field_name;
    an_input_field.value = a_field_value;
    
    a_form .appendChild ( an_input_field );
    
}

/*
    Function: form_fields_to_package
        Traverses form elements and packs their values into a package.
        If an element is a checkbox, it will be included only if it's checked.
        If an element is a radio box, it will be included only if it's checked and its value is not 'false'.
    
    Parameters:
        a_form - Form element to traverse
    
*/
function form_fields_to_package ( a_form )
{
    var a_package = {};
    
    for ( var i = 0; i < a_form.elements.length; i ++ )
    {
        var an_element = a_form.elements[i];
            
        var a_field_name = ( an_element.name ) ? an_element.name : an_element.id;
        
        if (a_field_name && an_element.value)
        {
            if ( an_element.type === 'checkbox' )
            {
                if ( an_element.checked )
                {
                    a_package[a_field_name] = an_element.value;
                }
            }
            else if ( an_element.type === 'radio' )
            {
                if ( an_element.checked && an_element.value !== 'false' )
                {
                    a_package[a_field_name] = an_element.value;
                }
            }
            else
            {
                a_package[a_field_name] = an_element.value;
            }
        }
    }
    
    return a_package;    
}

/*
    Function: find_period_with_date
        traverses all the items of the given
        array with periods (which should all have from_date
        and to_date property) and returns the first period
        which includes the given date.
        
        to_date day is excluded.  
    
    Parameters:
        a_date - String in the format 'yyyy-mm-dd'
        periods - Array of objects { from_date, to_date, ... }
    
    Returns:
        Date period, if one is found
        
        Null, otherwise
*/
function find_period_with_date( a_date, periods )
{
    var result = null;

    
    for (var i = 0; i < periods.length; i++)
    {
        var a_period = periods[i];
        
        if (a_date >= a_period.from_date && 
            a_date < a_period.to_date)
        {
            result = a_period;
            break;
        }
    }
    
    return result;
}

/*
    Function: intersect_periods_of_owners
        Returns the intersection of all periods such that:
        - Each owner covers all the periods of the intersection.
        - Each owner is given as an array of periods (objects with from_date and to_date properties)
        
    Parameters:
    	periods - Array of 
    	ignore_past - Boolean, whether periods with a to_date was before 'today' should be considered.
    	today - String, date in the format "yyyy-mm-dd"

    Returns:
        Intersections
*/
function intersect_periods_of_owners(array_of_owners_with_periods, ignore_past, today)
{   
    /*
        Make owners:
    */
    var owner_list = [];
    
    /*
        Timepoints is a timeline stack:
        ( scan line pattern !)
    */
    var timepoints = [];
    
    for ( var i = 0; i < array_of_owners_with_periods.length; i ++ )
    {                      
        /*
            Get the owner :           
        */
        var periods_of_an_owner = array_of_owners_with_periods [ i ];
        
        /*
            If there are no periods or
            owner doesn't exist,
            don't do anything!
        */
        if ( !periods_of_an_owner )
        {
            continue;
        }
        
        /*
            Construct an owner 
            and add it to the owner list:
        */        
        var an_owner = {};
        an_owner.active = false;
        an_owner.id     = i;
        owner_list.push( an_owner );
    
        for ( var j = 0; j < periods_of_an_owner.length; j ++ )
        {            
            var a_period = periods_of_an_owner [ j ];
            
            if ( implies( ignore_past, a_period.to_date > today ) )
            {
                /*
                    Add both from_date and to_date
                    as timepoints:
                */
                var a_timepoint = {};
                a_timepoint.owner = an_owner;                            
                a_timepoint.date = a_period.from_date;
                a_timepoint.type = 'from_date';                                                                                                              
                
                timepoints.push( a_timepoint );
                
                a_timepoint = {};
                a_timepoint.owner = an_owner;            
                a_timepoint.date = a_period.to_date;
                a_timepoint.type = 'to_date';
                
                timepoints.push( a_timepoint );
            }                
        }                    
    }                    

    /*
        We have all timepoints now, 
        let's intersect!
    */
    timepoints.sort (  function ( a, b )
                        {
                            if ( a.date === b.date )
                            {
                                /*
                                    To_dates come before
                                    from_dates!
                                */
                                if ( a .type === 'to_date' &&
                                     b .type === 'from_date' )
                                {
                                    return -1;
                                }
                                else if ( a .type === 'from_date' &&
                                          b .type === 'to_date' )
                                {
                                    return 1;
                                }
                                else
                                {
                                    return 0;
                                }
                            }
                            if ( a.date <  b.date ) 
                            {
                                return -1;
                            }
                            if ( a.date >  b.date ) 
                            {
                                return  1;
                            }
                        } 
                     );


    /*
        Go through all timepoints:
    */
    var result = [];
    
    var current_from_date = null;
    
    for ( var i = 0; i < timepoints.length; i ++ )
    {
        var a_timepoint = timepoints[i];
        
        if ( a_timepoint.type === 'from_date' )
        {            
            a_timepoint.owner.active = true;
        }
        else if ( a_timepoint.type === 'to_date' )
        {
            a_timepoint.owner.active = false;
        }
        
        /*
            Check if all owners active:
        */
        var all_owners_active = true;
        for ( var j = 0; j < owner_list.length; j ++ )
        {
            var an_owner = owner_list [ j ];            
            all_owners_active = all_owners_active && an_owner.active;
        }
        
        if ( !all_owners_active )
        {    
            /*
                We have to stop with a period,
                if there were any:
            */
            if ( current_from_date )
            {
                var a_new_intersected_period = {};
                
                a_new_intersected_period.from_date      = current_from_date;
                a_new_intersected_period.to_date        = a_timepoint.date;
                               
                result.push(a_new_intersected_period );
                
                current_from_date = null;
            }
        }
        else
        {
            /*
                All owners are active,
                start a new period,
                if we haven't already 
                started one:
            */
            if ( !current_from_date )
            {
                current_from_date = a_timepoint.date;
            }
        }
    }
    
    return result;
}
/*
    Function: xor
        Exclusive OR
*/
function xor ( a, b )
{
    return ( !a && b ) || ( a && !b );
}

/*
    Function: implies
        =>
*/
function implies ( a, b )
{
    return ( !a || b );
}

/*
    Function: get_mouse_position
        returns an object
        with mouse position:
        
        result.x = x position relative to the screen
        result.y = y position relative to the screen
    
    Returns:
        Mouse positions in an Array [x, y]
*/                  
function get_mouse_position ( evt )
{                            
    var result = { x: null, y: null };
                 
    /*
        If we don't have an event, take 
        the window:
    */                 
    if ( !evt )
    {
        evt = window.event;
    }
    
    if ( evt.pageX || evt.pageY )
    {
        result.x = evt.pageX;
        result.y = evt.pageY;
    }
    else if (evt.clientX || evt.clientY) 	
    {
        result.x = evt.clientX                        + 
                   document.body.scrollLeft           +
                   document.documentElement.scrollLeft;
                   
        result.y = evt.clientY                        + 
                   document.body.scrollTop            +
                   document.documentElement.scrollTop;
    }
    
    return result;                 
}

/*
    Function: get_value_of_obj
        returns the value property of the given object.
*/
function get_value_of_obj ( an_obj )
{
    return get_property_of_obj ( an_obj, 'value' );
}

/*
    Function: get_property_of_obj
        Analyses the property string which should be of form:
        
        It doesn't work with indices!
        
        It supports only functions without arguments.
        (example: a_test.test_func1().test_func2().id )
    
    Parameters:
        an_obj - Object
        a_property_str - Property String of the format 'a_property.some_other_property'
    
    Returns:
        Property
*/
function get_property_of_obj ( an_obj, a_property_str )
{
    /*
        This function 
    */
    /*    
        var regexp_function_without_arguments = 
                            /
                            ^
                            ( [_A-Za-z][_A-Za-z0-9]* )
                            ( \s* \( \s* \) )
                            $
                            /;
    */
    var regexp_function_without_arguments = /^([_A-Za-z][_A-Za-z0-9]*)(\s*\(\s*\))$/;
    var result;
    var parent_obj = an_obj;
    var property_line = a_property_str.replace(' ', '').split('.');
    
    for (var i = 0; i < property_line.length; i ++ )
    {
        var a_property = property_line[i];
            
        if ( regexp_function_without_arguments.test(a_property) )
        {
            /*
                Remove brackets:
            */
            a_property = a_property.replace(/\s*\(\s*\)/, '');
            
            /*
                Invoke the function:
            */
            result = parent_obj[a_property]();
        }
        else
        {
            if (parent_obj.hasOwnProperty(a_property))
            {
                result = parent_obj[a_property];
            }
            else
            {
                return parent_obj + ':' + a_property + ' does not exist!';
            }
        }
        
        parent_obj = result;
    }
    
    return result;
}                             

/*
    Function: format_string
    
*/
function format_string ( an_object, a_string )
{
        
    /*
        This function goes through the string.
        
        It replaces the following tokens:
        
        %string ( property name )         
            -> will be replaced by the 
               property of the object
                                     
        %plural_string ( property name, singular string, plural string )
            -> will be replaced by singular string if property name
               is exactly 1 and plural string
               if it is greater than 1
               
               will be replaced by empty string otherwise.
               
        %boolen ( property name, true string, false string )
            -> will be replaced by true string if property 
               is true, 
               by false string otherwise     
                         
        %% 
            -> will be replaced by '%'                         
            
        Regular expression (strip white spaces and \n's):
    */
    
    /*
        Regular expressions:
    */
    var regexp_tokens              = /(\%(\%|(string)\s*\(\s*([_A-Za-z][_A-Za-z0-9]*)(\s*\.\s*[_A-Za-z][_A-Za-z0-9]*)*(\s*\(\s*\))?\s*\)|(plural_string)\s*\(\s*([_A-Za-z][_A-Za-z0-9]*)(\s*\.\s*[_A-Za-z][_A-Za-z0-9]*)*(\s*\(\s*\))?\s*,\s*(([_A-Za-z][_A-Za-z0-9]*)(\s*\.\s*[_A-Za-z][_A-Za-z0-9]*)*(\s*\(\s*\))?|([1-9][0-9]*|[0-9])|('([^']|\\')*'|"([^"]|\\")*"))\s*,\s*(([_A-Za-z][_A-Za-z0-9]*)(\s*\.\s*[_A-Za-z][_A-Za-z0-9]*)*(\s*\(\s*\))?|([1-9][0-9]*|[0-9])|('([^']|\\')*'|"([^"]|\\")*"))\s*\)|(boolean)\s*\(\s*([_A-Za-z][_A-Za-z0-9]*)(\s*\.\s*[_A-Za-z][_A-Za-z0-9]*)*(\s*\(\s*\))?\s*,\s*(([_A-Za-z][_A-Za-z0-9]*)(\s*\.\s*[_A-Za-z][_A-Za-z0-9]*)*(\s*\(\s*\))?|([1-9][0-9]*|[0-9])|('([^']|\\')*'|"([^"]|\\")*"))\s*,\s*(([_A-Za-z][_A-Za-z0-9]*)(\s*\.\s*[_A-Za-z][_A-Za-z0-9]*)*(\s*\(\s*\))?|([1-9][0-9]*|[0-9])|('([^']|\\')*'|"([^"]|\\")*"))\s*\))|[^\%]*)/g;
    var regexp_command             = /\%(\%|[_A-Za-z][_A-Za-z0-9]*)\s*\(\s*((?:[_A-Za-z][_A-Za-z0-9]*)(?:\s*\.\s*[_A-Za-z][_A-Za-z0-9]*)*(?:\s*\(\s*\))?)\s*([^)]*)\s*\)/;
    var regexp_parameter           = /\s*,\s*(([_A-Za-z][_A-Za-z0-9]*)(\s*\.\s*[_A-Za-z][_A-Za-z0-9]*)*(\s*\(\s*\))?|([1-9][0-9]*|[0-9])|('([^']|\\')*'|"([^"]|\\")*"))/g;
    
    var regexp_identifier          = /^([_A-Za-z][_A-Za-z0-9]*)(\s*\.\s*[_A-Za-z][_A-Za-z0-9]*)*(\s*\(\s*\))?$/;
    var regexp_string_with_quotes  = /^('([^']|\\')*'|"([^"]|\\")*")$/;
    var regexp_decimal_number      = /^(-)?([1-9][0-9]*|[0-9])(\.[0-9]*)?$/;
    
    
    function process_a_parameter ( an_obj, a_parameter_str )
    {
        if ( regexp_identifier.test ( a_parameter_str ) )
        {
            return get_property_of_obj( an_obj, a_parameter_str );
        }
        else if ( regexp_string_with_quotes.test ( a_parameter_str ) )
        {
            return a_parameter_str.substring ( 1, a_parameter_str.length - 1 );
        }
        else if ( regexp_decimal_number.test ( a_parameter_str ) ) 
        {
            return Number( a_parameter_str );
        }
        else
        {
            return 'Parameter: ' + a_parameter_str + ' could not be identified.'; 
        }
    }                                                                            

    var tokens = a_string.match ( regexp_tokens );
    
    /*
        Process tokens:
    */
    for ( var i = 0; i < tokens.length; i ++ )
    {		
		var a_token = tokens [ i ];

        if ( a_token.length < 2 )
        {
            continue;
        }
		
        if ( a_token.charAt ( 0 ) === '%' )
        {
			/*
                we have a job to do!
            */
            if ( a_token [ 1 ] === '%' )
            {
				/*
                    Just substitute '%%' with '%':
                */
                tokens [ i ] = '%';
            }
            else 
            {				
				/*
                    Extract command name:
                    
                    ( RegExp.$1 = command name,
                      RegExp.$2 = property name of the object,
                      RegExp.$3 = paramters
                    )
                */
                a_token.match ( regexp_command );
                var command_name = RegExp.$1;

                if ( command_name === 'string' )
                {
					/*
                        Replace the token with 
                        the property:
                    */    
                    var the_property_str = RegExp.$2;
                    
                    var result = get_property_of_obj ( an_object, the_property_str );
                    tokens [ i ] = result;                                                     
                }
                else if ( command_name === 'plural_string' )
                {
                    var the_property_str     = RegExp.$2;                                        
                    
                    var a_parameter_list_str = RegExp.$3;
                                       
                    /*
                        Define true and false string:
                    */
                    var singular_string = ( a_parameter_list_str.match(regexp_parameter)[0].match( regexp_parameter ) 
                                          ) ? RegExp.$1 : '!!! error with singular string !!!';                                                                            
                                                                                                
                    singular_string = process_a_parameter ( an_object, singular_string);
                                                                                                                                                            
                                      
                    var plural_string = ( a_parameter_list_str.match( regexp_parameter )[1].match( regexp_parameter ) 
                                        ) ? RegExp.$1 : '!!! error with plural string !!!';                                      
                                      
                    plural_string = process_a_parameter( an_object, plural_string );                                      
                                      
                    var switch_obj = get_property_of_obj( an_object, the_property_str );        
                                                                                                                                                                              
                    if ( switch_obj > 1 )
                    {
                        tokens[i] = plural_string;
                    }
                    else if ( switch_obj === 1 )
                    {
                        tokens[i] = singular_string;
                    }
                    else if (switch_obj === 0)
                    {
                        tokens[i] = '';
                    }
                    else
                    {
                        tokens[i] = '!!! Format string error: plural/singular string is not defined !!!';
                    }
                }                
                else if ( command_name === 'boolean' )
                {
                    var the_property_str     = RegExp.$2;                                        
                    
                    var a_parameter_list_str = RegExp.$3;
                    
                    /*
                        Define true and false string:
                    */
                    var true_string = ( a_parameter_list_str.match ( regexp_parameter )[0].match ( regexp_parameter ) 
                                      ) ? RegExp.$1 : '!!! error in true string!!!';
                                      
                    true_string = process_a_parameter ( an_object, true_string );                                      
                                      
                    var false_string = ( a_parameter_list_str.match ( regexp_parameter )[1].match ( regexp_parameter ) 
                                       ) ? RegExp.$1 : '!!! error in false string!!!';                                      
                                      
                    false_string = process_a_parameter ( an_object, false_string );                                      
                                                                            
                    var switch_obj = get_property_of_obj ( an_object, the_property_str );                                                                                               
                    if ( switch_obj )
                    {
                        tokens [ i ] = true_string;
                    }
                    else
                    {
                        tokens [ i ] = false_string;
                    }
                }
            }            
        }
    }
    
    /*
        Join the tokens:
    */
    var result = tokens .join ( '' );
	
    return result;
}

/*
    Function: get_class
        Class determination
    
    Source:
        http://webreflection.blogspot.com
*/
function get_class(obj)
{    
    function get_class ( obj )
    {
        return "".concat(obj).replace(/^.*function\s+([^\s]*|[^\(]*)\([^\x00]+$/, "$1") || "anonymous";
    };
    
    var result = "";
    
    if(obj === null)
    {
        result = "null";
    }
    else if(obj === undefined)
    {
        result = "undefined";
    }
    else {
        result = get_class ( obj.constructor );
        
        if ( result === "Object" &&  obj.constructor.prototype ) 
        {
            for ( result in this ) 
            {
                if ( typeof ( this [ result ] ) === "function" && 
                     obj instanceof this [result ] ) 
                {
                    result = get_class ( this [ result ] );
                    break;
                }
            }
        }
    }
    
    return result;
}

/*
    Function: add_option_to_select_box
        Adds an option to the given select box. 
	
	    It takes care to make it cross browser compatible.
*/	
function add_option_to_select_box ( a_select_box,
                                    a_new_option,
                                    value_to_be_selected )
{
	a_select_box [ a_select_box.options.length ] = a_new_option;
	
	if ( a_new_option.value )
	{
    	a_new_option.selected = (a_new_option.value === value_to_be_selected);
	}	
}

/*
    Function: clear_all_rows
        This function clears all rows of the given row array.
    
    Example:
        clear_all_rows ( a_tbody.rows );
    
        clear_all_rows ( a_table.rows );        
*/  
function clear_all_rows(a_row_array)
{
    for (var i = a_row_array.length - 1; i > -1 ; i--)
    {
        var a_row = a_row_array[i];
        a_row .parentNode.removeChild ( a_row );
    }
}

/*
    Function: get_class_name
        This function returns the class name of an obj. 
        
        If it has widget_class property, it will be prefered.
        
        Especially used when handling dojo widgets.
*/
function get_class_name ( an_obj )
{   
    var result = null;
    
    if ( an_obj .widget_class )
    {
        result = an_obj .widget_class;
    }
    else
    {
        result = an_obj .className;
    }
    
    return result;
}

/*
    Function: set_class_name
        Sets the className property of the object.
        
        If the object has set_widget_class function, it will be prefered.
        
        Especially used when handling dojo widgets. 
*/
function set_class_name ( an_obj, a_class_name )
{

    if ( an_obj. set_widget_class )
    {
        an_obj .set_widget_class ( a_class_name );
    }
    else
    {
        an_obj .className = a_class_name;
    }
}

/*
    Function: find_position
        Finds the absolute position of a HTML element
    
    Returns:
        [left, top]
        
    Source:
        http://www.quirksmode.org/js/findpos.html
*/
function find_position(obj) 
{
	var curleft = 0;
    var curtop = 0;
	if (obj.offsetParent) 
    {
		curleft = obj.offsetLeft;
		curtop = obj.offsetTop;
		
		while (obj = obj.offsetParent)
        {
			curleft += obj.offsetLeft;
			curtop += obj.offsetTop;
		}
	}
	return [curleft,curtop];
}

/**
 * Converts to lowercase, removes
 * 	non-alpha chars and converts spaces to hyphens
 * @param {Object} value
 */
function slugify(value){
      value = value.replace(/[^\w\s-]/g, "").toLowerCase();
      return value.replace(/[\-\s]+/g, "-");
}
