/*
    .------------------------------.
    |    Price calculator class    |
    *------------------------------*         
    
    Class: Price calculator
        Takes care of displaying and managing price calculator (Controller & View)
        
        Moreover, it sends and receives JSON packages.
        
    Requires:
        - <utility_functions.js> (needed for find_period_with_date)
        - <ware_class.js>
        - <school_demonstrator_class.js>
        
    Author: 
        Marko Ristin
        Andreas Hermann
*/

/**
 * 
 * @param {Object} a_school_demonstrator
 * @param {Object} prefetched_packages
 * @param {Int} country_id
 * @param {Int} city_id
 * @param {Int} school_id
 * @param {Date} today
 * @param {Object} url_fetch_obj
 */                                               
function Price_calculator ( a_school_demonstrator, 
                            prefetched_packages, 
                            country_id,  
                            city_id,      
                            school_id,
                            today,
                            url_fetch_obj )
{
    /*extern dojo, gettext */
    /*extern find_period_with_date, is_function, attach_hidden_field_to_form, xor, show_errors */
    /*extern copy_an_object */
    /*extern date_str_to_i18n_date, intersect_periods_of_owners, add_option_to_select_box, create_and_add_option_to_select_box */
    /*extern a_money_exchange, a_cart, a_wait_cursor_manager */
    /*extern Ware, Option */
    
    /* 
        Variable: url_fetch_obj
            URL for fetching objects
    */
    this.url_fetch_obj = null;

    /*    Properties:    */
    
    /*
        Variable: school_demonstrator
            Reference to the school_demonstrator
    */
    this.school_demonstrator = a_school_demonstrator;

    this.country = null;
    this.city = null;
    this.school = null;
    this.course = null;
    this.accommodation_form = null;
    
    this.school_set = null;
    this.course_set = null;
    this.accommodation_form_set = null;
    
    this.course_price_set = null;
    this.accommodation_price_set = null;
    this.school_inscription_fee_set = null;
    this.school_transfer_set = null;
    this.school_material_fee_set = null;
    
    this.level = null;
    this.course_start = null;
    this.course_weeks = null;
    this.price_periods = null;
    this.transfer = null;
    this.accommodation_weeks = null;
    
    this.total_price = null; // number
    this.price_details = null; // object

    this.form = dojo.byId('id_cart_form');

    /*    Interface elements:    */
    this.country_select_box = dojo.byId('id_price_calculator_select_country');
    this.city_select_box = dojo.byId('id_price_calculator_select_city');
    this.school_select_box = dojo.byId('id_price_calculator_select_school');
    this.level_select_box = dojo.byId('id_price_calculator_select_level');
    this.course_select_box = dojo.byId('id_price_calculator_select_course');
    this.accommodation_form_select_box = dojo.byId('id_price_calculator_select_accommodation_form');
    this.transfer_select_box = dojo.byId('id_price_calculator_select_transfer');
    this.course_start_select_box = dojo.byId('id_price_calculator_select_course_start');
    this.course_weeks_select_box = dojo.byId('id_price_calculator_select_course_weeks');
    this.accommodation_weeks_select_box = dojo.byId('id_price_calculator_select_accommodation_weeks');
    this.price_period_span = dojo.byId('id_price_calculator_price_period_span');
    this.price_period_number_span = dojo.byId('id_price_calculator_price_period_number_span');
    this.locale_currency_select_box = dojo.byId('id_price_calculator_select_locale_currency');
    
    this.add_to_cart_button = dojo.byId('id_price_calculator_button_add_to_cart');
    this.book_button = dojo.byId('id_price_calculator_button_book');
    

    /*
        Prices fields:
    */
    this.span_course_price_amount = dojo.byId('course_price_amount');
    this.span_course_price_currency = dojo.byId('course_price_currency');
                
    this.span_course_inscription_fee_amount = dojo.byId('course_inscription_fee_amount');
    this.span_course_inscription_fee_currency = dojo.byId('course_inscription_fee_currency');
    this.course_inscription_fee_row = dojo.byId('accommodation_price_row');
    
    this.span_accommodation_price_amount = dojo.byId('accommodation_price_amount');
    this.span_accommodation_price_currency = dojo.byId('accommodation_price_currency');
    this.accommodation_price_row = dojo.byId('accommodation_price_row');
    
    this.span_accommodation_inscription_fee_amount = dojo.byId('accommodation_inscription_fee_amount');
    this.span_accommodation_inscription_fee_currency = dojo.byId('accommodation_inscription_fee_currency');
    this.accommodation_inscription_fee_row = dojo.byId('accommodation_inscription_fee_row');
    
    this.span_transfer = dojo.byId('transfer_cost_amount');
    this.span_transfer_currency = dojo.byId('transfer_cost_currency');
    this.transfer_cost_row = dojo.byId('transfer_cost_row');
    
    this.span_school_material_fee_amount = dojo.byId('school_material_fee_amount');
    this.span_school_material_fee_currency = dojo.byId('school_material_fee_currency');
    this.school_material_fee_row = dojo.byId('school_material_fee_row');
    
    this.span_course_exam_fee_amount = dojo.byId('course_exam_fee_amount');
    this.span_course_exam_fee_currency = dojo.byId('course_exam_fee_currency');
    this.course_exam_fee_row = dojo.byId('course_exam_fee_row');
    
    this.span_total_price = dojo.byId('total_price');
    this.span_total_price_comment = dojo.byId('total_price_comment');
    this.span_total_price_currency = dojo.byId('total_price_currency');
    
    this.span_total_price_school_curr = dojo.byId('total_price_school_curr');
    this.span_total_price_school_curr_comment = dojo.byId('total_price_school_curr_comment');
    this.span_total_price_school_curr_currency = dojo.byId('total_price_school_curr_currency');
    
    this.span_exchange_rate = dojo.byId('exchange_rate');
    this.row_exchange_rate = dojo.byId('exchange_rate_row');
    
    this.price_table = dojo.byId('id_price_calculator_price_table');
    this.buttons_div = dojo.byId('id_price_calculator_buttons');

	/**
	 *	Waiting dialog widget
	 *      Used for displaying a white overlay when the form is sent
	 */
	this.waiting_widget = dijit.byId('id_waiting_div');
    
    /**
     * Step_num: defines the logic order of the select boxes
     */
    this.country_select_box.step_num = 1;
    this.city_select_box.step_num = 2;
    this.school_select_box.step_num = 3;
    this.level_select_box.step_num = 4;
    this.course_select_box.step_num = 5;
    this.accommodation_form_select_box.step_num = 6;
    this.transfer_select_box.step_num = 7;
    this.course_start_select_box.step_num = 8;
    this.course_weeks_select_box.step_num = 9;
    this.accommodation_weeks_select_box.step_num = 10;
    this.locale_currency_select_box.step_num = 11;
    
    /**
     *   Variable: select_boxes
     *       All select boxes in the form, the order is important!
     *       Is used for marking the active select box depending
     *       on the current step
     */
    this.select_boxes = [ 
        '',
        this.country_select_box, 
        this.city_select_box, 
        this.school_select_box, 
        this.level_select_box, 
        this.course_select_box, 
        this.accommodation_form_select_box,
        this.transfer_select_box,
        this.course_start_select_box,
        this.course_weeks_select_box,
        this.accommodation_weeks_select_box,
        this.locale_currency_select_box
        ];
    
    /* 
        Variable: current_step
            the currently active step 
    */
    this.current_step = 1;

    /**
     *   Corresponding set functions:
     *   Used for setting selected index of the select box artificially;
     *
     *   This function will be invoked:
     *       base_object [ function_str ] ( select box value )
     */
    this.city_select_box.set_function = { base_object: this, function_str: 'set_city' };
    this.school_select_box.set_function = { base_object: this, function_str: 'set_school' };
    this.level_select_box.set_function = { base_object: this,  function_str: 'set_level' };
    this.course_select_box.set_function = { base_object: this,  function_str: 'set_course' };
    this.accommodation_form_select_box.set_function = { base_object: this, function_str: 'set_accommodation_form' };
    this.course_weeks_select_box.set_function = { base_object: this, function_str: 'set_course_weeks' };
    this.accommodation_weeks_select_box.set_function = { base_object: this, function_str: 'set_accommodation_weeks' };

    /**  
     *   Variable: today
     *       Today's date:   
     */
    this.today = today;

    /**  
     *   Variable: void_accommodation_form
     *       Option 'No accommodation form required' is inserted
     *       as first option into each select box
     */
    this.void_accommodation_form = {
        id: gettext('no_accommodation_required_value'),
        name: gettext('no_accommodation_required_text')
    };

    /**
     *   Variable: cached_responses
     *       Hash array, as index serves the request string
     */
    this.cached_responses = {};
    
    /**
     *   Ajax Request URL
     */
    this.url_fetch_obj = url_fetch_obj;
    
    /**
     *   Variable: properties
     *       Are set to null if the element is disabled
     */
    this.country_select_box.properties = ['country'];
    this.city_select_box.properties = ['city', 'school_set'];
    this.school_select_box.properties = ['school', 'course_set', 'accommodation_form_set', 'school_transfer_set', 'school_inscription_fee_set'];
    
    this.level_select_box.properties = ['level'];
    this.course_select_box.properties = ['course'];
    this.accommodation_form_select_box.properties = ['accommodation_form'];
    this.transfer_select_box.properties = ['transfer'];
    this.course_start_select_box.properties  = ['course_season', 'price_periods','course_start'];
    this.course_weeks_select_box.properties = ['course_weeks'];
    this.accommodation_weeks_select_box.properties = ['accommodation_weeks'];
    
    this.span_course_inscription_fee_amount.properties = ['current_course_inscription_fee'];
    this.span_course_price_amount.properties = ['current_course_price'];
    this.span_accommodation_price_amount.properties = ['current_accommodation_price'];
    this.span_accommodation_inscription_fee_amount.properties = ['current_accommodation_inscription_fee'];
    this.span_transfer.properties = ['current_transfer_cost'];
    this.span_total_price.properties = ['total_price'];
    
    /**
     * Variable: price_fields
     *       These fields remain hidden/disabled if not
     *       all select boxes have an option selected
     */
    this.price_fields = [ 
        this.span_course_inscription_fee_amount, 
        this.span_course_inscription_fee_comment,
        
        this.span_course_price_amount, 
        this.span_course_price_currency,
        
        this.span_accommodation_inscription_fee_amount, 
        this.span_accommodation_inscription_fee_currency,
        
        this.span_accommodation_price_amount, 
        this.span_accommodation_price_currency,
        
        this.span_transfer, 
        this.span_transfer_currency,
        
        this.span_school_material_fee_amount,
        this.span_school_material_fee_currency,
        
        this.span_total_price, 
        this.span_total_price_currency,
        /* this.span_total_price_comment, */
        
        this.span_total_price_school_curr, 
        this.span_total_price_school_curr_currency,
        /* this.span_total_price_school_curr_comment ,*/
       
        this.exchange_rate_row,

        this.add_to_cart_button, 
        this.book_button
    ];
    
    /** 
     * Special attributes
     */
    this.course_start_select_box.isWidget = true;
    
    /**
     *   Select Box Children Elements:
     *       User interface elements that are directly related,
     *       and should be disabled when a new item is selected
     *       
     *       e.g. when a country is selected from the country select box
     *       the city select box is disabled until the related cities are filled in
     */        
    this.country_select_box.children_elements = [this.city_select_box];
    this.city_select_box.children_elements    = [this.school_select_box];
    this.school_select_box.children_elements  = [this.level_select_box];
    
    this.level_select_box.children_elements = [this.course_select_box];
    this.course_select_box.children_elements  = [this.accommodation_form_select_box];
    this.accommodation_form_select_box.children_elements = [this.transfer_select_box, this.course_start_select_box];
    this.transfer_select_box.children_elements = [];
    
    this.course_start_select_box.children_elements = [this.course_weeks_select_box, this.accommodation_weeks_select_box];
    this.course_weeks_select_box.children_elements = [this.accommodation_weeks_select_box];
    this.accommodation_weeks_select_box.children_elements = [];
    
    this.locale_currency_select_box.children_elements = [];

    /**
     * Calculates the price for the given start date and duration in weeks
     * The result is a compound price, if the period spans across multiple prices.
     *
     * @param a_school {Object} School object
     * @param a_price_list {Array} list of prices
     * @param a_start {String} isoDate
     * @param a_weeks {Number} number of weeks
     */
    this.get_week_price = function(a_school, a_price_list, a_start, a_weeks)
    {
        var result = {
            total: 0, // total price
            multiseason: false, // is this a multi season booking
            prices: [], // price figures
            factors: [], // factors of prices in total
            weeks: [], // weeks for prices
            objects: [], // references to original price objects
            start_dates: [], // start of a price
            end_dates: [], // end of a price
            
            price_start: a_start,
            price_end: null
        };
        
        var cur_date = dojo.date.stamp.fromISOString(a_start);
        var end_date = dojo.date.stamp.fromISOString(a_start);
        
        // calculate the end date by adding 'a_weeks' weeks
        // ensure the end date is the day of week specified by course.last_day
        end_date = dojo.date.add(end_date, 'week', a_weeks);
        var last_day = 5; // friday
        while (end_date.getDay() !== last_day) {
            end_date = dojo.date.add(end_date, 'day', -1);
        }
        result.price_end = end_date;
        console.info('get_week_price: start='+cur_date+', weeks='+a_weeks+', end='+end_date+' mode='+a_school.calculation_mode);

        
        if (a_school.calculation_mode == "first") {
            // simple price calculation mode
            var a_price = find_period_with_date(a_start, a_price_list);
            var cur_price = a_price['week' + a_weeks];
            result.objects.push(a_price);
            result.prices.push(cur_price);
            result.factors.push(1.0);
            result.start_dates.push(a_start);
            result.end_dates.push(end_date);
            result.weeks.push(a_weeks);
            result.total = cur_price;
            
            return result;
        }
        else {
            // composite price calculation mode
        
            // sort prices
            var prices = [];
            for (var i = 0; i < a_price_list.length; i++) {
                prices.push(a_price_list[i]);
            }
            prices.sort(function(a,b) {return a.from_date > b.from_date;});
            
            for (var i = 0; i < prices.length; i++)
            {
                var a_price = prices[i];
                var price_start = dojo.date.stamp.fromISOString(a_price.from_date);
                var price_end = dojo.date.stamp.fromISOString(a_price.to_date);
                if (cur_date >= price_start && cur_date < price_end)
                {
                    var cur_price = a_price['week' + a_weeks];
                    
                    if (end_date <= price_end && result.prices.length === 0) {
                        // a single price is sufficient
                        result.objects.push(a_price);
                        result.prices.push(cur_price);
                        result.factors.push(1.0);
                        result.start_dates.push(cur_date);
                        result.end_dates.push(end_date);
                        result.weeks.push(a_weeks);
                        result.total = cur_price;
                        return result;
                    } else if (end_date <= price_end && result.prices.length > 0) {
                        // this is the last required price of a multiseason booking
                        // dojo.difference(weeks) does only truncate!
                        // --> instead use diff(day) and round result/7
                        var diff = dojo.date.difference(cur_date, end_date, 'day');
                        var weeks = +(diff/7).toFixed(0);
                        if (weeks > 0) {
                            var factor = weeks/a_weeks;
                            result.objects.push(a_price);
                            result.prices.push(cur_price);
                            result.factors.push(factor);
                            result.start_dates.push(cur_date);
                            result.end_dates.push(end_date);
                            result.weeks.push(weeks);
                            result.total += factor * cur_price;
                            result.multiseason = true;
                        } else {
                            console.warn("price is already sufficient!");
                        }
                        return result;
                    } else {
                        // a non-sufficient price of a multiseason booking
                        // valid: end_date > price_end
                        var diff = dojo.date.difference(cur_date, price_end, 'day');
                        diff += 1; // add the start day - prevents rounding errors
                        var weeks = +(diff/7).toFixed(0); // round to weeks
                        if (weeks > 0) {
                            var factor = weeks/a_weeks;
                            result.objects.push(a_price);
                            result.prices.push(cur_price);
                            result.factors.push(factor);
                            result.start_dates.push(cur_date);
                            result.end_dates.push(price_end);
                            result.weeks.push(weeks);
                            result.total += factor * cur_price;
                            
                            cur_date = dojo.date.add(price_end, 'day', 1);
                        } else {
                            console.warn("illegal price: price[i].to_date == price[i-1].to_date");
                        }
                    }
                }
            }
            
            if (cur_date < end_date) {
                console.warn("no sufficient price found!: cur_date=" + cur_date + ", end_date=" + end_date);
                console.dir(prices);
            }
            
            result.objects = null; // delete references
            return result;
        }
    };
    
    /*
        Function: get_max_weeks
            Searches all the properties of a_price_list
            and determines the longest week period.
            
            e.g: a_price_list.week24 !== undefined,
            returns 24 (even if a_price_list.week24 == null )
    */
    this.get_max_weeks = function ( a_price_list )
    {
        var field_prefix = 'week';
        var max_weeks = 0;
        var num_weeks = null;
        
        /*
            Go through all the properties:            
        */
        for ( var a_property in a_price_list )
        {                
            /*
                Ignore if it's a function:
            */                
            if ( is_function( a_price_list[a_property] ) )
            {
                continue;
            }
            
            if ( a_property.substr(0, field_prefix.length) === field_prefix )
            {
                num_weeks = a_property.substr(field_prefix.length, a_property.length - field_prefix.length);         
                
                if ( Number(num_weeks) > max_weeks )
                {
                    max_weeks = Number(num_weeks);
                }                                                                
            }
        }
        
        return max_weeks;
    };
          
    /*
        Function: set_active
            change the appearance of the select box to active
    */
    this.set_active = function ( element_id )
    {
        var element = dojo.byId(element_id);
        element.parentNode.className = 'price_calculator_active_element';
        
        var active_image_id = 'id_price_calculator_' + element.name + '_active_img';
        var inactive_image_id = 'id_price_calculator_' + element.name + '_inactive_img';
        
		var active_image = dojo.byId(active_image_id);
		if (active_image !== undefined && active_image !== null) {
			active_image.style.display = 'inline';
		}
        var inactive_image = dojo.byId(inactive_image_id);
		if (inactive_image !== undefined && active_image !== null) {
			inactive_image.style.display = 'none';
		}
    }; 
    
    /*
        Function: set_inactive
            change the appearance of the select box to inactive
    */
    this.set_inactive = function ( element_id )
    {
        var element = dojo.byId(element_id);
        element.parentNode.className = 'price_calculator_inactive_element';
        
        var active_image_id = 'id_price_calculator_' + element.name + '_active_img';
        var inactive_image_id = 'id_price_calculator_' + element.name + '_inactive_img';
        
		var active_image = dojo.byId(active_image_id);
		if (active_image !== undefined && active_image !== null) {
			active_image.style.display = 'none';
		}
        var inactive_image = dojo.byId(inactive_image_id);
		if (inactive_image !== undefined && active_image !== null) {
			inactive_image.style.display = 'inline';
		}
    };
    
    /*
        Function: update_active_step
            update the state of the GUI so that only the select_box is marked active
            which corresponds to the current internal state (step number)
        
        Parameters:
            next_step - Int, the next active step
    */
    this.update_active_step = function (next_step)
    {
        this.current_step = next_step;
		if (this.current_step === null)
		{
			this.current_step = 1;
		}
        
        // check if the country is selected 
        if (this.current_step === this.country_select_box.step_num && this.country)
        {
            this.current_step++;
        }
        
        // check if the city is selected 
        if (this.current_step === this.city_select_box.step_num && this.city)
        {
            this.current_step++;
        }
        
        // check if a school is selected
        if (this.current_step === this.school_select_box.step_num && this.school)
        {
            this.current_step++;
        }
        
        // check if a level is selected
        if (this.current_step === this.level_select_box.step_num && this.level)
        {
            this.current_step++;
        }
        
        // check if a course is selected
        if (this.current_step === this.course_select_box.step_num && this.course)
        {
            this.current_step++;
        }	

        // check if an accommodation form is selected
        if (this.current_step === this.accommodation_form_select_box.step_num)
		{
			if (this.accommodation_form)
			{
				this.current_step++;
			}
			else if (this.accommodation_form_set)
			{
				this.fill_accommodation_forms(this.accommodation_form_set);
			}
            
            this.transfer = null;
		}
        
        // check if transfer is selected
        if (this.current_step === this.transfer_select_box.step_num)
        {
            if (this.transfer === null)
            {
                // Fill transfer
                if (!this.school_transfer_set || this.school_transfer_set.length === 0)
                {
                    this.transfer = false;
                    this.current_step++;
                }
                else if (this.school_transfer_set)
                {
                    this.fill_transfer();
                }
            }
            else
            {
                this.current_step++;
            }
        }
        
        // check if a course start is selected
        if (this.current_step === this.course_start_select_box.step_num) {
            if (!this.course_start) {
                this.fill_course_start();
            }
            
            if (this.course_start && this.course_price_set) {
                // update the price tables
                //this.school_demonstrator.set_course_starting_date(this.course_season);
                this.current_step++;
            }
        }
        
        // check if the course weeks is selected
        if (this.current_step === this.course_weeks_select_box.step_num)
        {
            if (!this.course_weeks)
            {
            	this.fill_course_weeks(this.course_start);
            }
            else 
            {
                // TODO: re-enable yalea sequence (without accommodation weeks select box)
                /*
                if (a_money_exchange.locale_currency === null)
                {
                    a_money_exchange.fill_currency_select_box();
                }
                */
    			
                this.current_step++;
            }
        }
        
        // check if the accommodation weeks is selected
        if (this.current_step === this.accommodation_weeks_select_box.step_num)
        {
            if (!this.accommodation_weeks)
            {
               if (this.accommodation_form !== this.void_accommodation_form)
               {
            	   this.fill_accommodation_weeks(this.course_start);
               }
               else
               {
                   this.accommodation_weeks = 0;
                   this.current_step++;
               }
            }
            else
            {
                this.current_step++;
            }
        }
        
        // check if a locale currency is selected
        if (this.current_step === this.locale_currency_select_box.step_num)
        {
            if (!a_money_exchange.locale_currency)
            {
                if (a_money_exchange.locale_currency === null)
                {
                    a_money_exchange.fill_currency_select_box();
                }
            }
            else
            {
                this.prices = this.calculate_prices(this);              
                
                // show buttons
                this.buttons_div.style.display = '';
                
                this.current_step++;
            }
        }
        if (!a_money_exchange.locale_currency)
        {
            // Hide buttons
            this.buttons_div.style.display = 'none';
        }
        
        // update all select boxes to reflect the current state        
        for (var i = 1; i < this.select_boxes.length; i++)
        {
            if (i === this.current_step)
            {
                if (this.select_boxes[i] === this.course_select_box) {
                    var a = 3;
                }
                this.enable_obj(this.select_boxes[i]);
                this.set_active(this.select_boxes[i]);
            }
            else
            {
                this.set_inactive(this.select_boxes[i]);
            }
        }
    };
    
    /*
        Function: set_country
            sets the country, erases select boxes 
            and fills them when the data is received.
    */
    this.set_country = function(a_country_id)
    {
        // Set the select box to point to the a_country_id
        this.set_select_box(this.country_select_box, a_country_id);
        this.disable_obj_children(this.country_select_box);
        this.clear_properties(this.country_select_box);

        if (a_country_id)            
        {                        
            // Set country property of this instance
            this.country = this.find_obj_with_id(a_country_id, this.country_set);
            this.fill_cities(this.city_set);
        }
        
        // change the appearance of the select_box
        this.update_active_step(this.country_select_box.step_num);
    }; 

    /*
        Function: set_city
            Sets the city, erases the affected select boxes and fills them up when the data is received.  
    */
    this.set_city = function(a_city_id)
    {
        // Set the select box to point to the a_city_id
        this.set_select_box(this.city_select_box, a_city_id);
        this.disable_obj_children(this.city_select_box);     
        this.clear_properties(this.city_select_box);        
        
        if (a_city_id)
        {
            // Set city property of this instance
            this.city = this.find_obj_with_id(a_city_id, this.city_set);
            this.send_request(this.school_set_request(a_city_id), this, 'fill_schools');
        }
        
        this.update_active_step(this.city_select_box.step_num);
    };
    
    /*
        Function: set_school
            Sets the school variable, fetches school information. 
            
            Fills accommodation forms and courses select boxes.     
    */
    this.set_school = function (school_id)
    {
        /*
            Set the select box to point to the a_school_id:            
        */
        this.set_select_box(this.school_select_box, school_id);
        this.disable_obj_children(this.school_select_box);
        this.clear_properties(this.school_select_box);

        if (school_id)
        {           
            /*
                Reset previously used data
            */    
            this.school                      = null;
            this.accommodation_form_set      = null;
            this.course_set                  = null;
            
            this.accommodation_price_set     = null;
            this.course_price_set            = null;

            this.school_transfer_set = null;
            this.school_inscription_fee_set = null;
            
            this.school_material_fee_set     = null;
            
            /*
                Send request for the current school:
            */
            this.send_school_request(school_id);
            
            /*
                Show the school in the school demonstrator:                    
            */
            this.school_demonstrator.show_school(school_id); 
        }
        
        this.update_active_step(this.school_select_box.step_num);
    };
    
    /**
     * Sets the level
     */
    this.set_level = function(a_level_id)
    {
        this.set_select_box(this.level_select_box, a_level_id);
        this.disable_obj_children(this.level_select_box);
        this.clear_properties(this.level_select_box);
        
        this.level = a_level_id;
        
        if (this.level) {
            // Fill courses select box
            this.fill_courses(this.course_set);
        }
        
        // update the state
        this.update_active_step(this.level_select_box.step_num);
    };
    
    /*
        Function: set_course
            Sets the course, sends a request for price lists
    */
    this.set_course = function ( a_course_id )
    {       
        /*
            Set the select box to point to the a_course_id:            
        */
        this.set_select_box(this.course_select_box, a_course_id);
        this.disable_obj_children(this.course_select_box);
        this.clear_properties(this.course_select_box);
                    
        if ( a_course_id )
        {
            /*
                Set the course property of the instance:
            */
            this.course = this.find_obj_with_id(a_course_id, this.course_set);
			
            /*
                Send request for course prices:                
            */
            this.course_price_set = null;
            var request = this.course_price_request(a_course_id);
            this.send_request(request, this, 'save_course_package');
        }
        
        // do not update now, but just when the data is received
    };
    
    /**
     * Saves a course package and updates the user interface
     * @param {Object} a_package
     */
    this.save_course_package = function(a_package)
    {
        this.save_package(a_package);
        this.update_active_step(this.course_select_box.step_num);
    };
    
    /*
        Function: set_accommodation_form
            Sets the accommodation form, sends a request for price lists
    */
    this.set_accommodation_form = function ( an_accommodation_form_id )
    {
		/*
            Set the select box to point to the an_accommodation_form_id:            
        */
        this.set_select_box(this.accommodation_form_select_box, an_accommodation_form_id);
        this.disable_obj_children(this.accommodation_form_select_box);
        this.clear_properties(this.accommodation_form_select_box);
        
        if (an_accommodation_form_id)
        {
            if (an_accommodation_form_id === this.void_accommodation_form.id)
            {
                /*
                    No accommodation form required.
                */
                this.accommodation_form = this.void_accommodation_form;
                this.accommodation_price_set = null;
            }
    		else
    		{
    	        /*
    	            Accommodation form required.
    	        */
    	        this.accommodation_form = this.find_obj_with_id(an_accommodation_form_id, this.accommodation_form_set);
    	        
    			/*
    	            Send request for accommodation prices:                
    	        */ 
    	        this.accommodation_price_set = null;
    			var request = this.accommodation_price_request(an_accommodation_form_id);
    	        this.send_request(request, this, 'save_accommodation_package');
            }
        }
		
        this.update_active_step(this.accommodation_form_select_box.step_num);
    };
    
    /**
     * Saves an accommodation package and updates the user interface
     * @param {Object} a_package
     */
    this.save_accommodation_package = function(a_package)
    {
        this.save_package(a_package);
        this.update_active_step(this.accommodation_form_select_box.step_num);
    };
    
    /*
        Function: set_transfer
            Sets the transfer property of the instance and sets 
            the transfer select box option to the option of the switch.
            This function is called by the html element and by internal code.
    */
    this.set_transfer = function ( transfer_id )
    {
        this.set_select_box( this.transfer_select_box, transfer_id );
        this.disable_obj_children( this.transfer_select_box );
        this.clear_properties( this.transfer_select_box );
        
        this.transfer_price_set = null;
        
        this.enable_only_direct_obj_children(this.transfer_select_box);
        
        if (transfer_id > 0)
        {
            var a_transfer = this.find_obj_with_id(transfer_id, this.school_transfer_set);
            this.transfer = { id: a_transfer.id, name: gettext('Transfer')+': '+a_transfer.name };
            
            // Send a request for transfer prices 
            var request = this.transfer_price_request(transfer_id);
            this.send_request(request, this, 'save_transfer_package');
        }
        else
        {
            this.transfer = { id: 0, name: gettext('Ohne Transfer') };
            this.current_step++;
        }
    };
    
    /**
     * Saves an transfer package and updates the user interface
     * @param {Object} a_package
     */
    this.save_transfer_package = function(a_package)
    {
        this.save_package(a_package);
        this.update_active_step(this.transfer_select_box.step_num);
    };

    /*
     *   Sets course_start property and enables calculate price button.
     *       This function is called by the 'select' element
     */
    this.set_course_start = function(a_date)
    {
        // Set the select box to point to the a_from_date
        //this.set_select_box(this.course_start_select_box, a_date);
        
        /*
        set the select box of the school demonstrator too
        var pricePeriodsSelectBox = dojo.byId('id_school_demonstrator_select_course_season');
        if (pricePeriodsSelectBox !== null)
        {
            this.set_select_box(pricePeriodsSelectBox, a_from_date);
        }
        */
            
        this.disable_obj_children(this.course_start_select_box);
        this.clear_properties(this.course_start_select_box);
        
        if (a_date)
        {
            this.course_start = date_to_str(a_date);
            this.update_active_step(this.course_start_select_box.step_num);
        }
        else
        {
            this.course_start = null;
        }
    };
    
    /*
        Function: set_course_weeks
           sets course weeks and 
           enables calculate button as well as transfer checkbox.       
           
           a_course_weeks is just a number of the weeks the user would like to book. 
    */
    this.set_course_weeks = function ( a_course_weeks )
    {
        this.set_select_box( this.course_weeks_select_box, a_course_weeks );
        this.disable_obj_children( this.course_weeks_select_box );
        this.clear_properties( this.course_weeks_select_box );
        
        if (a_course_weeks)
        {
            this.course_weeks = Number(a_course_weeks);
        }
        else
        {
            this.course_weeks = null;
        }
        
        this.update_active_step(this.course_weeks_select_box.step_num);         
    };
    
    /*
        Function: set_accommodation_weeks
            sets accommodation weeks, enables calculation
            
        Parameters:
            accommodation_weeks - number of weeks
    */
    this.set_accommodation_weeks = function(accommodation_weeks)
    {
        this.set_select_box(this.accommodation_weeks_select_box, accommodation_weeks);
        this.disable_obj_children(this.accommodation_weeks_select_box);
        this.clear_properties(this.accommodation_weeks_select_box);
        
        if (accommodation_weeks)
        {
            this.accommodation_weeks = Number(accommodation_weeks);
        }
        else 
        {
            this.accommodation_weeks = null;
        }
        
        this.update_active_step(this.accommodation_weeks_select_box.step_num);     
    };
    
    /**
     *   Updates the selected locale currency
     */
    this.set_locale_currency = function ( a_locale_currency )
    {
        if (a_locale_currency !== "")
        {
            a_money_exchange.set_locale_currency_with_abb(a_locale_currency);
            this.update_active_step(this.locale_currency_select_box.step_num);
        }
    };
    
    /**
     * Calculates the prices and invokes the display update function
     */
    this.calculate_prices = function(a_self)
    {
        if (a_self.school &&
            a_self.level &&
            a_self.course &&
            a_self.accommodation_form &&
            a_self.transfer &&
            a_self.course_start &&
            a_self.course_weeks &&
            a_self.accommodation_weeks !== null)
        {
            // School currency
            var school_currency = a_money_exchange.find_currency_with_id(a_self.school.currency);
            var school_currency_abb = school_currency.abbreviation.toUpperCase();
            
            // Course price
            var course_total = 0;
            var course_exam_fee = 0;
            var c_price = a_self.get_week_price(a_self.school, a_self.course_price_set, a_self.course_start, a_self.course_weeks);
            var course_price = c_price.total;
            console.dir(c_price);
            
            // Add Course exam fee (if available)
            // use the exam fee of the last season that is intersected by the booking
            if (c_price !== null && c_price.objects.length > 0) {
                course_exam_fee = c_price.objects[c_price.objects.length - 1].exam_fee;
                if (course_exam_fee === null) {
                    course_exam_fee = 0;
                }
            }
            
            // Accommodation price
            var accommodation_price = 0;
            var accommodation_inscription_fee = 0;
            var a_price = null;
            if (a_self.accommodation_form !== a_self.void_accommodation_form) {
                a_price = a_self.get_week_price(a_self.school, a_self.accommodation_price_set, a_self.course_start, a_self.accommodation_weeks);
                accommodation_price = a_price.total;
                console.dir(a_price);
                
                // Add accommodation inscription fee (if available)
                if (a_price !== null && a_price.objects.length > 0) {
                    accommodation_inscription_fee = a_price.objects[0].inscription_fee;
                    if (accommodation_inscription_fee === null) {
                        accommodation_inscription_fee = 0;
                    }
                }
            }
            
            // School inscription fee (if available)
            var school_inscription_fee = 0;
            var an_inscription_fee = find_period_with_date(a_self.course_start, a_self.school_inscription_fee_set);
            if (an_inscription_fee) {
                school_inscription_fee = an_inscription_fee.fee;
            }
            
            // School material fee
            var school_material_fee = 0;
            var material_fee_obj = find_period_with_date(a_self.course_start, a_self.school_material_fee_set);
            if (material_fee_obj)
            {
                if (material_fee_obj['week' + a_self.course_weeks] !== undefined) {
                    school_material_fee = material_fee_obj['week' + a_self.course_weeks];
                }
            }
            
            // Transfer cost
            var transfer_cost = 0;
            if (a_self.transfer && a_self.transfer.id > 0)
            {
                var transfer_price_obj = find_period_with_date(a_self.course_start, a_self.school_transfer_price_set);
                if (transfer_price_obj !== null)
                {
                    transfer_cost = transfer_price_obj.cost;
                }   
            }
            
            // Total price
            var result = {
                school_currency: school_currency_abb,
                school_inscription_fee: school_inscription_fee,
                school_material_fee: school_material_fee,
                course_price: course_price,
                course_price_details: c_price,
                course_exam_fee: course_exam_fee,
                accommodation_inscription_fee: accommodation_inscription_fee,
                accommodation_price: accommodation_price,
                accommodation_price_details: a_price,
                transfer_cost: transfer_cost,
                total_price: 0.0
            };
            
            result.total_price = result.course_price + 
                                 result.course_exam_fee +
                                 result.accommodation_price +
                                 result.accommodation_inscription_fee +
                                 result.school_material_fee +
                                 result.school_inscription_fee +
                                 result.transfer_cost;
            
            // Add exchange rate and convert total to local currency
            if (a_money_exchange.locale_currency && 
                school_currency_abb !== a_money_exchange.locale_currency && 
                a_self.row_exchange_rate !== null)
            {
                result.locale_currency = a_money_exchange.locale_currency.abbreviation.toUpperCase();
                result.exchange_rate = a_money_exchange.to_locale_currency(school_currency_abb, 1.0);
                result.total_price_locale = a_money_exchange.to_locale_currency(school_currency_abb, result.total_price);
            }
            
            a_self.total_price = result.total_price;
            a_self.price_details = result;
            
            a_self.update_price(result, a_self);
        }
        else 
        {
            a_self.total_price = null;
            a_self.price_details = null;
        
            a_self.update_price(null, a_self);
        }
    };
    
    this.is_disabled_date = function(dateObject) {
        return false;
    };
    
    this.update_price = function(a_price, a_self) {
        if (a_price) {
            var school_currency_abb = a_price.school_currency;
            
            // course price
            if (a_price.course_price > 0) {
                var prefix = (a_price.course_price_details.multiseason) ? "*" : "";
                a_self.span_course_price_amount.innerHTML = prefix + a_price.course_price.toFixed(0);
                a_self.span_course_price_currency.innerHTML = a_price.school_currency;
            }
            
            // school inscription fee
            if (a_price.school_inscription_fee > 0) {
                a_self.span_course_inscription_fee_amount.innerHTML = a_price.school_inscription_fee.toFixed(0);
                a_self.span_course_inscription_fee_currency.innerHTML = a_price.school_currency;
                a_self.course_inscription_fee_row.style.display = '';
            }
            else {
                a_self.course_inscription_fee_row.style.display = 'none';   
            }
            
            // exam fee
            if (a_price.course_exam_fee > 0) {
                a_self.span_course_exam_fee_amount.innerHTML = a_price.course_exam_fee.toFixed(0);
                a_self.span_course_exam_fee_currency.innerHTML = a_price.school_currency;
                a_self.course_exam_fee_row.style.display = '';
            } else {
                a_self.course_exam_fee_row.style.display = 'none';
            }
            
            // school material fee
            if (a_price.school_material_fee > 0) {
                a_self.span_school_material_fee_amount.innerHTML = a_price.school_material_fee.toFixed(0);
                a_self.span_school_material_fee_currency.innerHTML = a_price.school_currency;
                a_self.school_material_fee_row.style.display = '';
            }
            else {
                a_self.school_material_fee_row.style.display = 'none';
            }
            
            // accommodation price
            if (a_price.accommodation_price > 0) {
                var prefix = (a_price.accommodation_price_details.multiseason) ? "*" : "";
                a_self.span_accommodation_price_amount.innerHTML = prefix + a_price.accommodation_price.toFixed(0);
                a_self.span_accommodation_price_currency.innerHTML = a_price.school_currency;
                a_self.accommodation_price_row.style.display = '';
            }
            else {
                a_self.accommodation_price_row.style.display = 'none';
            }
            
            // accommodation inscription fee
            if (a_price.accommodation_inscription_fee > 0) {
                a_self.span_accommodation_inscription_fee_amount.innerHTML = a_price.accommodation_inscription_fee.toFixed(0);
                a_self.span_accommodation_inscription_fee_currency.innerHTML = a_price.school_currency;
                a_self.accommodation_inscription_fee_row.style.display = '';
            }
            else {
                a_self.accommodation_inscription_fee_row.style.display = 'none';
            }            
        
            // transfer cost
            if (a_price.transfer_cost > 0) {
                a_self.span_transfer_currency.innerHTML = a_price.school_currency;
                a_self.span_transfer_currency.style.display = '';
                a_self.span_transfer.innerHTML = a_price.transfer_cost.toFixed(0);
                a_self.span_transfer.style.display = '';
                
                a_self.transfer_cost_row.style.display = '';
            } 
            else {
                a_self.transfer_cost_row.style.display = 'none';
            }
            
            // Total price in school currency
            if (a_price.total_price > 0) {
                a_self.span_total_price_school_curr_currency.innerHTML = a_price.school_currency;
                a_self.span_total_price_school_curr_currency.style.display = '';
                a_self.span_total_price_school_curr.innerHTML = a_price.total_price.toFixed(0);
                a_self.span_total_price_school_curr.style.display = '';     
            }                  
            else {
                a_self.span_total_price_school_curr_currency.innerHTML = '';
            }
            
            // Exchange rate
            if (a_price.school_currency !== a_price.locale_currency) {
                var exchange_rate = a_money_exchange.to_locale_currency(a_price.school_currency, 1.0);
                a_self.span_exchange_rate.innerHTML = '1 '+a_price.school_currency+' = '+exchange_rate+' '+a_price.locale_currency;
                a_self.row_exchange_rate.style.display = '';
            }
            else {
                a_self.row_exchange_rate.style.display = 'none';
            }
            
            // Total price in local currency
            if (a_price.total_price > 0) {   
                a_self.span_total_price_currency.innerHTML = a_price.locale_currency;
                a_self.span_total_price_currency.style.display = '';
                a_self.span_total_price.innerHTML = a_price.total_price_locale.toFixed(0);
                a_self.span_total_price.style.display = '';
                a_self.span_total_price_comment.style.display = '';
            }
            else {
                 a_self.span_total_price_currency.innerHTML = '';
                 a_self.span_total_price.innerHTML = '';
            }
            
            // show a comment and a tooltip if the booking is across multiple seasons
            var multiseason_comment = "";
            if (a_price.course_price_details.multiseason === true) {
                multiseason_comment += "<b>Kurs - "+ a_self.course_weeks +" Wochen</b><br/>";
                for (var i=0; i<a_price.course_price_details.objects.length; i++) {
                    var price_details = a_price.course_price_details;
                    var price = (price_details.factors[i] * price_details.prices[i]).toFixed(0);
                    var from = date_to_i18n_date(price_details.start_dates[i], "d.m.Y");
                    var to = date_to_i18n_date(price_details.end_dates[i], "d.m.Y");
                    multiseason_comment += price_details.weeks[i] + " Wochen von " + from + " bis " + to + ": " + price + " " + a_price.school_currency;
                    multiseason_comment += " ("+ a_self.course_weeks+ " Wochen: " + price_details.prices[i] + " " + a_price.school_currency + ") <br/>";
                }
                multiseason_comment += "<i>Kurs Total: " + a_price.course_price.toFixed(0) + " " + a_price.school_currency + "</i><br/>";
            }
            if (a_price.accommodation_price > 0 && a_price.accommodation_price_details.multiseason === true) {
                if (multiseason_comment !== "") {
                    multiseason_comment += "<br/>";
                }
                multiseason_comment += "<b>Unterkunft - "+ a_self.accommodation_weeks +" Wochen</b><br/>";
                for (var i=0; i<a_price.accommodation_price_details.objects.length; i++) {
                    var price_details = a_price.accommodation_price_details;
                    var price = (price_details.factors[i] * price_details.prices[i]).toFixed(0);
                    var from = date_to_i18n_date(price_details.start_dates[i], "d.m.Y");
                    var to = date_to_i18n_date(price_details.end_dates[i], "d.m.Y");
                    multiseason_comment += price_details.weeks[i] + " Wochen von " + from + " bis " + to + ": " + price + " " + a_price.school_currency;
                    multiseason_comment += " ("+ a_self.accommodation_weeks+ " Wochen: " + price_details.prices[i] + " " + a_price.school_currency + ") <br/>";
                }
                multiseason_comment += "<i>Unterkunft Total: " + a_price.accommodation_price.toFixed(0) + " " + a_price.school_currency + "</i><br/>";
            }
                
            if (multiseason_comment !== "") {
                dijit.byId('id_multiseason_comment_tt').srcNodeRef.innerHTML = multiseason_comment;
                dojo.byId('id_multiseason_comment_row').style.display = '';
            } else {
                dojo.byId('id_multiseason_comment_row').style.display = 'none';
            }
            
            // show the table
            if (this.price_table)
            {
                this.price_table.style.display = '';
            }
        }         
        else {
            // There is some data missing,
            // disable all objects in price_cells_and_buttons:
            for ( var i = 0; i < a_self.price_fields.length; i++ ) {
                a_self.disable_obj( a_self.price_fields[i] );
            }
            
            // hide the price table
            if (this.price_table) {
                this.price_table.style.display = 'none';
            }
        }  
    };
    
    /**
     * Creates a Json object that reflects the current state
     */
    this.saveWare = function()
    {
        var ware = {
            'country_id': this.country.id,
            'country_name': this.country.name,
            'city_id': this.city.id,
            'city_name': this.city.name,
            'school_id': this.school.id,
            'school_name': this.school.name,
            'course_id': this.course.id,
            'course_name': this.course.name,
            'course_start': this.course_start,
            'course_weeks': this.course_weeks,
            'accommodation_form_id': this.accommodation_form.id,
            'accommodation_form_name': this.accommodation_form.name,
            'accommodation_weeks': this.accommodation_weeks,
            'transfer_id': this.transfer.id,
            'transfer_name': this.transfer.name,
            'price_details': this.price_details,
            'total_price': this.total_price,
            'school_currency_abb': a_money_exchange.find_currency_with_id(this.school.currency).abbreviation.toUpperCase(),
            'locale_currency_abb' : a_money_exchange.locale_currency.abbreviation.toUpperCase()
        };
        return ware;
    };
    
    /*
        Function: save_to_cart
            Propagates booking information to cart and makes proceed to booking button disapear ( it will apear in the cart ).
    */
    this.save_to_cart = function()
    {
        var ware = this.saveWare();
        a_cart.add_ware(ware);
        a_cart.save();
    };
    
    /*
     *
     */
    this.submit = function(ware_pkg, locale_currency_abb)
    {
        attach_hidden_field_to_form(this.form, 'package', ware_pkg );
        attach_hidden_field_to_form(this.form, 'locale_currency_abb', locale_currency_abb);
    	
    	if (this.waiting_widget)
        {
        	this.waiting_widget.show();
        }
        this.form.submit();
    };

    /*
        Function: show_the_booking
            Redirects the user to the booking page
    */
    this.show_the_booking = function()
    {
        var a_ware = this.saveWare();
        var ware_pkg = dojo.toJson([a_ware]);
        var locale_currency_abb = a_money_exchange.locale_currency.abbreviation;
        
        this.submit(ware_pkg, locale_currency_abb);
    };

    /*
        Function: save_school_sets
            Manages saving the received school information.
            
            It saves the package and then checks if all important packages are received:
            
            - school, course_set, 
            - accommodation_form_set
            - school_inscription_fee_set
            - school_transfer_set
            
            We should wait until we have gathered all of them.
    */
    this.save_school_sets = function ( a_package )
    {
        /*
            We only want to take some action if there is really a package,
            otherwise we do as if nothing happened:            
        */
        	                       
        if (a_package)
        {
            /*
                Save it:                
            */
            this.save_package(a_package);                                                
            
            if ( this.school && 
                 this.course_set && 
                 this.accommodation_form_set &&
                 this.school_transfer_set &&
                 this.school_inscription_fee_set &&
                 this.school_material_fee_set )
            {
                // We received all the important packages!
                
                /*
                    Save some school info
                    needed because school assistant skips country/city selection step
                */
                if (!this.country)
                {
                    this.country = { id: this.school.country_id, name: this.school.country_name };
                }
                if (!this.city)
                {
                    this.city = { id: this.school.city_id, name: this.school.city_name };
                }

                // Add void accommodation form to the accommodation form set
                this.accommodation_form_set.unshift(this.void_accommodation_form);
                
                // update the state
                //this.update_active_step(this.course_select_box.step_num);
                this.update_active_step(this.level_select_box.step_num);
            }
            /*
             * Do nothing until all data was received
            */
        }       
    };

    /**
     * Function: save_package
     *       The entry defined by important_part of the package will be saved in this instance.
     *       
     *       if property is not null or undefined, an error will be raised.
     *
     * @param {Object} a_package
     */
    this.save_package = function ( a_package )
    {  
        if ( !a_package )
        {
            show_errors( ['Price calculator: There is no package specified.'] );
        }
        else if ( !a_package.important_part || a_package.important_part === '' )
        {
            //alert( typeof( a_package ) );
            show_errors( [ 'Price calculator: Important part of the package is not specified.' ] );
        } 
        else
        {
            var important_property = a_package.important_part;
            this[ important_property ] = a_package[ important_property ];
        }
    };
    
    /*
        Function: set_select_box
            Set the active item of a select box
        
        Parameters:
            a_select_box - Select box element
            a_value - String, option to select
    */
    this.set_select_box = function ( a_select_box, a_value )
    {
        if ( a_select_box != a_value ) {
            for ( var i = 0; i < a_select_box.options.length; i ++ ) {
                // intentionally allow comparison between int and string!
                if ( a_value == a_select_box.options[i].value ) {
                    a_select_box.selectedIndex = i;
                    break;
                }
            }
        }
    };                                              
    
    /*
        -------------------------
        User interface functions:
        -------------------------
    */       
    
    /*
        Function: hide_price_period_span
            Hides the price period select box and arranges the layout.
    */
    this.hide_price_period_span = function ()
    {
        /* change the src for "course weeks" */
        var course_weeks_active_image = dojo.byId('id_price_calculator_' + this.course_weeks_select_box.name + '_active_img');
        var course_weeks_inactive_image = dojo.byId('id_price_calculator_' + this.course_weeks_select_box.name + '_inactive_img');
        var locale_currency_active_image = dojo.byId('id_price_calculator_' + this.locale_currency_select_box.name + '_active_img');
        var locale_currency_inactive_image = dojo.byId('id_price_calculator_' + this.locale_currency_select_box.name + '_inactive_img');
        
        var num4_active_image = dojo.byId('id_price_calculator_num_4_active_img');
        var num4_inactive_image = dojo.byId('id_price_calculator_num_4_inactive_img');
        var num5_active_image = dojo.byId('id_price_calculator_num_5_active_img');
        var num5_inactive_image = dojo.byId('id_price_calculator_num_5_inactive_img');
        
        /* change the path to number 4 for "course weeks" */
		if (course_weeks_active_image && course_weeks_inactive_image)
		{
	        course_weeks_active_image.src = num4_active_image.src;
    	    course_weeks_inactive_image.src = num4_inactive_image.src;
		}
		
        /* change the path to number 5 for "local currency" */
		if (locale_currency_active_image && locale_currency_inactive_image)
		{
	        locale_currency_active_image.src = num5_active_image.src;
    	    locale_currency_inactive_image.src = num5_inactive_image.src;
		}
        
        /*
            Hide price period span:
        */
        this.disable_obj(this.price_period_span);
        this.disable_obj(this.price_period_number_span);                                                 
    };
    
    /*
        Function: show_price_period_span
            Shows the price period select box and arranges the layout.
    */
    this.show_price_period_span = function ()
    {
        /* change the src for "course weeks" */
        var course_weeks_active_image = dojo.byId('id_price_calculator_' + this.course_weeks_select_box.name + '_active_img');
        var course_weeks_inactive_image = dojo.byId('id_price_calculator_' + this.course_weeks_select_box.name + '_inactive_img');
        var locale_currency_active_image = dojo.byId('id_price_calculator_' + this.locale_currency_select_box.name + '_active_img');
        var locale_currency_inactive_image = dojo.byId('id_price_calculator_' + this.locale_currency_select_box.name + '_inactive_img');
        
        var num5_active_image = dojo.byId('id_price_calculator_num_5_active_img');
        var num5_inactive_image = dojo.byId('id_price_calculator_num_5_inactive_img');
        var num6_active_image = dojo.byId('id_price_calculator_num_6_active_img');
        var num6_inactive_image = dojo.byId('id_price_calculator_num_6_inactive_img');
        
        /* change the path to number 5 for "course weeks" */
		if (course_weeks_active_image && course_weeks_inactive_image)
		{
        	course_weeks_active_image.src = num5_active_image.src;
        	course_weeks_inactive_image.src = num5_inactive_image.src;
		}
		
        /* change the path to number 6 for "local currency" */
		if (locale_currency_active_image && locale_currency_inactive_image)
		{
        	locale_currency_active_image.src = num6_active_image.src;
        	locale_currency_inactive_image.src = num6_inactive_image.src;
		}

        /*
            Show price period span:
        */
        this.enable_obj(this.price_period_span);    
        this.enable_obj(this.price_period_number_span);                                      
    };
    
    /*
        Function: fill_countries
            Fills the select box with countries received from the server.            
            
            Moreover, it sets country_set variable of this instance.
    */
    this.fill_countries = function ( a_package )
    {
        this.country_set = a_package.country_set;
        var please_choose_a_country_option = new Option(gettext('Please choose a country:'), '');
        
        this.fill_select_box(a_package.country_set,
                             this.country_select_box,
                             'id',
                             'name',
                             [please_choose_a_country_option]);
    };

    /**
     *  Fills the select box with an array of cities
     *  and filters the city set for the current country
     */
    this.fill_cities = function(city_set)
    {
		// filter the city set for the current country
		var filteredCities = [];
		var city;
		for (var i=0; i<city_set.length; i++) {
		    city = city_set[i];
			if (city.country == this.country.id) {
				filteredCities.push(city);
			}
		}
		
        var firstOption = new Option(gettext('Please choose a city:'), '');                
        
        this.fill_select_box(
			filteredCities, 
			this.city_select_box, 
            'id', 
			'name',
            [firstOption],
            true /* set_if_only_one_option */
        );
        
    };
    
    /*
        Function: fill_schools
            fills the select box with schools received from the server. 
    */
    this.fill_schools = function ( a_package )
    {                                          
        this.school_set = a_package.school_set;
        var please_choose_a_school_option = new Option( gettext ( 'Please choose a school:' ), '' );                
        
        this.fill_select_box ( a_package.school_set, 
                               this.school_select_box, 
                               'id', 'name',
                               [ please_choose_a_school_option ],
                               true /* set_if_only_one_option */ );
                               
        this.update_active_step(this.school_select_box.step_num);
    };
    
    /**
     * fills the select box with courses received from the server.   
     */
    this.fill_courses = function(a_course_set)
    {
        // clear options
        this.course_select_box.innerHTML = '';
        
        // insert first option
        var option = new Option(gettext('Please choose a course:'), '');           
        add_option_to_select_box(this.course_select_box, option);             
        
        // insert entries
        // Make option groups for entries which have the same group name
        // 'id', 'name', 'type'
        var option_group;
        var prev_group_str = "";
        for (var j = 0; j < a_course_set.length; j++)
        {
            var course = a_course_set[j];
            if ( (!course.min_level || this.level >= course.min_level) &&
                 (!course.max_level || this.level <= course.max_level) )
            {
                // only insert course if (min_level >= level >= max_level)
                var group_str = gettext(a_course_set[j].type);
                if (group_str != prev_group_str) {
                    option_group = document.createElement('optgroup');
                    option_group.setAttribute('label', group_str);
                    this.course_select_box.appendChild(option_group);
                    prev_group_str = group_str;
                }
                
                var element_str = a_course_set[j].name;
                option = document.createElement("option");
                option.appendChild(document.createTextNode(element_str));
                option.value = a_course_set[j].id;
                option_group.appendChild(option);
            }
        }
        
        // select if single option
        if (a_course_set.length === 1)
        {
            this.course_select_box.selectedIndex = 1;
            this.set_course(this.course_select_box.value);
        }
        
        // disable if zero courses available
        if (a_course_set.length === 0) {
            this.course_select_box.innerHTML = '';
            option = new Option( gettext('No courses found'), '');
            add_option_to_select_box(this.course_select_box, option);
            this.course_select_box.disabled = true;
        }
        else {
            this.course_select_box.disabled = false;
        }
        
        this.update_active_step(this.course_select_box.step_num);
    };
    
    /**
     *   Fills the select box with accommodation forms received from the server.      
     */
    this.fill_accommodation_forms = function ( an_accommodation_form_set )
    {
        // clear options
        this.accommodation_form_select_box.innerHTML = '';
        
        // insert first option
        var option1 = new Option(gettext('Please choose an accommodation:'), '');           
        add_option_to_select_box(this.accommodation_form_select_box, option1);
        
        // insert second option
        var option2 = new Option(an_accommodation_form_set[0].name, an_accommodation_form_set[0].id);           
        add_option_to_select_box(this.accommodation_form_select_box, option2);
        
        // insert entries
        // Make option groups for entries which have the same group name
        // 'id', 'name', 'type'
        var option_group;
        var prev_group_str = "";
        for (var j = 1; j < an_accommodation_form_set.length; j++)
        {
            var group_str = gettext(an_accommodation_form_set[j].type);
            if (group_str != prev_group_str) {
                option_group = document.createElement('optgroup');
                option_group.setAttribute('label', group_str);
                this.accommodation_form_select_box.appendChild(option_group);
                prev_group_str = group_str;
            }
            
            var element_str = an_accommodation_form_set[j].name;
            option = document.createElement("option");
            option.appendChild(document.createTextNode(element_str));
            option.value = an_accommodation_form_set[j].id;
            option_group.appendChild(option);
        }
        
        // select if single option
        if (an_accommodation_form_set.length === 1)
        {
            this.accommodation_form_select_box.selectedIndex = 1;
            this.set_accommodation_form(this.accommodation_form_select_box.value);
        }
        
        // disable if zero courses available
        if (an_accommodation_form_set.length === 0) {
            this.accommodation_form_select_box.innerHTML = '';
            option = new Option( gettext('No accommodations found'), '');
            add_option_to_select_box(this.accommodation_form_select_box, option);
            this.course_select_box.disabled = true;
        }
        else {
            this.course_select_box.disabled = false;
        }
        
        //this.update_active_step(this.accommodation_form_select_box.step_num);
    };                      

    /*
        Function: fill_price_periods
            fills the select box with 
            price periods received from the 
            save_course_accommodation_form_prices function.  
    */
    this.fill_price_periods = function(a_price_periods_set)
    {
        // Get text representation of periods
        for ( var i = 0; i < a_price_periods_set.length; i ++ )
        {
            var a_price_period = a_price_periods_set[i];
            var from_date_text = date_str_to_i18n_date(a_price_period .from_date, 'd.My');
            var to_date_text = date_str_to_i18n_date(a_price_period .to_date, 'd.My');
            
            a_price_period.interval_text= from_date_text + ' - ' + to_date_text;
        }
        
        var preselectSinglePeriod = true; // preselect entry, if num(entries) == 1
        this.fill_select_box(
            a_price_periods_set,
            this.course_season_select_box,
            'from_date', 
            'interval_text',
            [ new Option(gettext('Your course is in:'), '') ],
            preselectSinglePeriod);
        
        // fill the new seasons into school demonstrator
        this.school_demonstrator.fill_seasons(a_price_periods_set);
    };
    
    /**
     *   Checks which weeks are available in the course price list 
     *       and fills course weeks select box with options.   
     */
    this.fill_course_weeks = function ( a_from_date )
    {
        var weeks_available_set = [];
        
        // Limit the course price list
        var current_course_price_list = find_period_with_date(a_from_date, this.course_price_set);
        
        // Are there any suitable course prices?
        if (current_course_price_list) {
            // check which weeks are available
            var max_weeks = this.get_max_weeks(current_course_price_list);
            weeks_available_set = [];
            
            // Check weeks from 1 to max_weeks
            for (var i = 1; i < max_weeks+1; i++ ) {                    
                // Add if there is an entry for 'i' number of weeks
                if (current_course_price_list['week' + i]) {
                    var week_entry = {
                        value: i,
                        text: i + ' ' + (i === 1 ? gettext('Week') : gettext('Weeks'))
                    };
                    
                    weeks_available_set.push(week_entry);
                }
            }                           
        } 
        
        // Fill the weeks available
        var please_choose_course_weeks_option = new Option(gettext('Course - How many weeks?'), '');
        
        this.fill_select_box(weeks_available_set,
                             this.course_weeks_select_box,
                             'value', 'text',
                             [ please_choose_course_weeks_option ],
                             true /* set_if_only_one_option */ );
    };
    
    /**
     * Finds available week numbers in the course price list 
     *       and fills course weeks select box.
     *       Condition: max(accommodation_weeks) <= course_weeks
     * @param {Date} a_from_date
     */
    this.fill_accommodation_weeks = function(a_from_date)
    {
        var weeksSet = [];
            
        // Limit the accommodation price list
        var accommodationPrices = find_period_with_date(a_from_date, this.accommodation_price_set);
        
        // Have we found a suitable course price?
        if (accommodationPrices)
        {
            // Now find which weeks are available
            var maxWeeks = this.get_max_weeks(accommodationPrices);
            
            // ensure max(accommodation_weeks) <= current_course_weeks
            if (maxWeeks > this.course_weeks)
            {
                maxWeeks = this.course_weeks;
            }
            
            // Check weeks from 1 to max_weeks
            for (var i = 1; i < maxWeeks+1; i++ ) {                    
                // Add if there is an entry for 'i' number of weeks
                if (accommodationPrices['week' + i]) {
                    var week_entry = {
                        value: i,
                        text: i + ' ' + (i === 1 ? gettext('Week') : gettext('Weeks'))
                    };
                    
                    weeksSet.push(week_entry);
                }
            }                              
        } 
        
        var firstOption = new Option( gettext('Accommodation - How many weeks?'), '');
        var selectIfSingleOption = true;
        this.fill_select_box( weeksSet,
                              this.accommodation_weeks_select_box,
                              'value', 
                              'text',
                              [ firstOption ],
                              selectIfSingleOption );
    };
         
    /*
        Function: fill_transfer
            Fills the transfer requested select box.
    */
    this.fill_transfer = function(transfer_set)
    {
        this.transfer_select_box.options.length = 0;
        
        // transfer is only bookable if an accommodation is booked too
        var accommodation_booked = (this.accommodation_form !== this.void_accommodation_form);
        
        if (accommodation_booked && this.school_transfer_set.length > 0)
        {
            var firstOption = new Option( gettext('Choose transfer:'), '');
            var options = [];
            
            for (var j=0; j<this.school_transfer_set.length; j++)
            {
                var an_option = { 
                    value: this.school_transfer_set[j].id, 
                    text: this.school_transfer_set[j].name 
                };
                options.push(an_option);
            }
            
            /*
                Fill the options:
            */                                
            this.fill_select_box(options,
                                 this.transfer_select_box,
                                 'value', 'text',
                                 [firstOption] );
        }
        else
        {                
            /*
                There are no options,
                just set corresponding property to false, 
                style it and enable children:
            */
            this.transfer_select_box.style.disabled = true;
            var notAvailableOption = new Option( gettext('Transfer not available'), '0' );
            
            add_option_to_select_box( this.transfer_select_box,
                                      notAvailableOption,
                                      notAvailableOption.value );
            /*
                Set it:
            */ 
            this.set_transfer( notAvailableOption.value );
        }    
    };
    
    /**
     * Fills the bookable course start dates and enables the calendar widget
     */
    this.fill_course_start = function()
    {
        var accommodationIsVoid = this.accommodation_form === this.void_accommodation_form;
		var widget= dijit.byId("id_price_calculator_select_course_start");
		if (widget) {
			widget.value = null;
			widget._populateGrid();
		}
        
        if (this.course_price_set &&
            this.school_inscription_fee_set &&
            xor(this.accommodation_price_set, accommodationIsVoid) )
        {
            // Intersect periods
            var intersectionSets = [];
            intersectionSets.push(this.course_price_set);
            intersectionSets.push(this.school_inscription_fee_set);
            
            // add transfer if available
            if (this.transfer > 0)
            {
                intersectionSets.push(this.school_transfer_price_set);
            }
            
            // Add accommodation price set if an accommodation form is selected
            if (this.accommodation_form !== this.void_accommodation_form)
            {
                intersectionSets.push(this.accommodation_price_set);
            }            
            
            /*
             * Check whether there exist different price periods for
             * course prices, inscription fees, transfer or accommodations
             */
            var ignorePast = true; /* ignore periods with to_date before today */
            this.price_periods = [];
            //this.price_periods = intersect_periods_of_owners(intersectionSets, ignorePast, this.today);
            /*
            // Get text representation of periods
            for (var i = 0; i < this.price_periods.length; i++ ) {
                var a_price_period = this.price_periods[i];
                var from_date_text = date_str_to_i18n_date(a_price_period.from_date, 'd.My');
                var to_date_text = date_str_to_i18n_date(a_price_period.to_date, 'd.My');
                a_price_period.interval_text= from_date_text + ' - ' + to_date_text;
            }
            
            var preselectSinglePeriod = true; // preselect entry, if num(entries) == 1
            this.fill_select_box(
                a_price_periods_set,
                this.course_season_select_box,
                'from_date', 
                'interval_text',
                [ new Option(gettext('Your course is in:'), '') ],
                preselectSinglePeriod);
            */
            
            // fill the new seasons into school demonstrator
            //this.school_demonstrator.fill_seasons(a_price_periods_set);
            
            // If only one interval available, preselect this one
            if (this.price_periods.length > 1)
            {
                this.course_season_tmp = null;
            }
            else if (this.price_periods.length === 1)
            {
                this.course_season_tmp = this.price_periods[0].from_date;
            }
            
            var request = {
                course_id: (+this.course.id),
                is_beginner: 'false'
            };
            this.send_request(request, this, 'save_course_dates', 'fetch_course_dates/');
        }
    };
    
    this.save_course_dates = function (a_package) {
        this.starting_dates = a_package['starting_dates'];
        var widget = dijit.byId(this.course_start_select_box.id);
        widget._populateGrid();
    };
    
    /**
     * Function: fill_seasons
     *     Fills the seasons select box
     *     
     *       Checks if course and accommodation price sets are available
     *       
     *       Updates the price periods according to all price sets we have at our disposal.            
     *       
     *       Enables child elements and updates the user interface
     *       
     *       If course prices and accommodation prices are not available, nothing will be done.
     */
    this.fill_seasons = function() 
    {
        if (this.course_price_set &&
            this.school_inscription_fee_set &&
            xor(this.accommodation_price_set, this.accommodation_form === this.void_accommodation_form) )
        {
            // Intersect periods
            var intersectionSets = [];
            intersectionSets.push(this.course_price_set);
            intersectionSets.push(this.school_inscription_fee_set);
            
            // add transfer if available
            if (this.transfer > 0)
            {
                intersectionSets.push(this.school_transfer_price_set);
            }
            
            // Add accommodation price set if an accommodation form is selected
            if (this.accommodation_form !== this.void_accommodation_form)
            {
                intersectionSets.push(this.accommodation_price_set);
            }            
            
            /*
             * Check whether there exist different price periods for
             * course prices, inscription fees, transfer or accommodations
             */
            var ignorePast = true; /* ignore periods with to_date before today */
            this.price_periods = intersect_periods_of_owners(intersectionSets, ignorePast, this.today);
            this.fill_price_periods(this.price_periods);
            
            // If only one interval available, preselect this one
            if (this.price_periods.length > 1)
            {
                this.course_season_tmp = null;
            }
            else if (this.price_periods.length === 1)
            {
                this.course_season_tmp = this.price_periods[0].from_date;
            }
        }
    };
       
    /*
        Function: fill_select_box
            his function fills the select box with set received from the server.                  
            
        Parameters:
            a_set - option set
            a_select_box - select box element
            value_attribute - specifies which attribute to use as value for an option.
            caption_attribute - specifies which attribute to use as caption.                       
            please_choose_options_array - All this options are inserted at the beginning. (optional), elements are directly inserted into the DOM, please supply copies if you insert the same elements in other locations
            set_if_only_one_option - If true and there is only one option, it will be automatically set. OnChange event will be called.
    */
    this.fill_select_box = function ( a_set, 
                                      a_select_box, 
                                      value_attribute, 
                                      caption_attribute,
                                      please_choose_options_array,
                                      set_if_only_one_option )
    {
        var i = 0;
        var j = 0;
        var option = null;
        
        // Disable object, enable it later, if needed
        //this.disable_obj ( a_select_box );                 
        
        if (a_set.length)
        {                   
            // Clear all existing options
            a_select_box.options.length = 0;
                                    
            if (please_choose_options_array)
            {
                // Insert 'please choose' options
                for (i = 0; i < please_choose_options_array.length; i++ )
                {
                    option = please_choose_options_array[i];
                    add_option_to_select_box(a_select_box, option);
                }
            }
            
            // Insert entries
            for (j = 0; j < a_set.length; j ++ )
            {
                create_and_add_option_to_select_box(
                    a_select_box,
                    a_set[j][caption_attribute],
                    a_set[j][value_attribute]);
            }                  
                                             
            /*
                Check if we have only one option 
                and if we would like to set it:
            */                        
            if (set_if_only_one_option && a_set.length === 1)
            {
                // The option with index 0 is a 'please choose'
                var an_index_to_set = 0;
                
                if (please_choose_options_array)
                {
                    an_index_to_set = please_choose_options_array .length;
                }
                
                a_select_box.selectedIndex = an_index_to_set;
                                               
                if (a_select_box.set_function)
                {
                    var base_object  = a_select_box.set_function.base_object;
                    var function_str = a_select_box.set_function.function_str;
                    base_object[function_str]( a_select_box.value );
                }
                
                this.set_inactive(a_select_box);
            }
            
            // Enable the select box
            this.enable_obj(a_select_box);
        }
    };
    
    /*
        Function: fill_cell
            enables the given cell
            and fills it with content given in 'a_text'
    */
    this.fill_cell = function ( a_cell, a_text )
    {
        a_cell.innerHTML = a_text;
        this.enable_obj(a_cell);
    };
    
    /*
        Enables a user interface element
            - If an_obj has a setDisabled function, it will be prefered.
            - If an_obj is a select box, it enables it.
            - If it is a span, it makes it visible.
            - If it is a table row, it makes it visible.
            - If it is a table cell, it makes it visible.
            - If it is a button, it enables it.
            - If it is a checkbox, it enables it.
    */
    this.enable_obj = function ( an_obj )
    {
        if ( an_obj.setDisabled )
        {            
            an_obj.setDisabled ( false );
        }
        else if (an_obj.isWidget) {
            var widget = dijit.byId(an_obj.id);
            if (widget) {
                widget.disabled = false; // newer style -- delays!
                //widget.setDisabled(false); // deprecated -- fast
            }
        }
        else if ( an_obj.nodeName === 'SELECT' )
        {
            an_obj.disabled = false;
        }
        else if ( an_obj.nodeName === 'SPAN' )
        {
            an_obj.style.display = '';
        }
        else if ( an_obj.nodeName === 'TR' )
        {
            an_obj.style.display = '';
        }  
        else if ( an_obj.nodeName === 'TD' )
        {
            an_obj.style.display = '';
        }     
        else if ( an_obj.nodeName === 'INPUT' )
        {
            an_obj.disabled = false;
        }
        if (an_obj.nodeName === 'A')
        {
            an_obj.style.display = '';
        }
    };
    
    /*
        Function: enable_only_direct_obj_children
            enables all select boxes specified 
            in children_select property of a_select_box.   
    */
    this.enable_only_direct_obj_children = function ( an_obj )
    {
        var i = 0;
        
        if ( an_obj.children_elements !== undefined )
        {
            for (i = 0; i < an_obj.children_elements.length; i ++ )
            {
                this.enable_obj ( an_obj.children_elements [ i ] );
            }
        }
    };

    /*
        Function: disable_obj.
            Disables an object
            - If it has a setDisabled function, it will be prefered.
            - If an_obj is a select box, it disables it.
            - If it is a span, it makes it invisible.
            - If it is a table row, it makes it invisible.
            - If it is a table cell, it makes it invisible.
            - If it is a button, it disables it.
            - If it is a checkbox, it disables it and sets it to off.
            - If exists, a corresponding property will be set to null.
    */
    this.disable_obj = function ( an_obj )
    {
		if (an_obj == null) {
			return; // don't do anything
		}
		
        if ( an_obj.setDisabled ) {
            an_obj.setDisabled(true);
        }
        else if (an_obj.isWidget) {
            var widget = dijit.byId(an_obj.id);
            if (widget) {
                widget.disabled = true; // newer style -- delays!
                //widget.setDisabled(true); // deprecated -- fast
                //widget.value = null;
                //if (widget.value) {
                //    widget.reset();
                //}
                widget._populateGrid();
            }
        }
        else if ( an_obj.nodeName === 'SELECT' )
        {
            //an_obj.innerHTML = '<option>N/A</option>';
            if (an_obj.options.length > 0) {
                an_obj.options[0].selected = true;
            }
            an_obj.disabled  = true;
            this.set_inactive(an_obj);
        }
        else if ( an_obj.nodeName === 'SPAN' )
        {
            an_obj.style.display = 'none';
        }      
        else if ( an_obj.nodeName === 'TR' )
        {
            an_obj.style.display = 'none';
        }  
        else if ( an_obj.nodeName === 'TD' )
        {
            an_obj.style.display = 'none';
        }             
        else if ( an_obj.nodeName === 'INPUT' )
        {
            if ( an_obj.type === 'button' )
            {
                an_obj.disabled = true;
            }
            else if ( an_obj.type === 'checkbox' )
            {
                an_obj.checked  = false;                    
                an_obj.disabled = true;
            }
        }
        else if (an_obj.nodeName === 'A')
        {
            an_obj.style.display = 'none';
        }
        
        /*
            Set all corresponding properties to null:
        */
        this.clear_properties ( an_obj );
    };    

    /*
        Function: disable_obj_children
            Traverses all children recursevly and disables them.            
            
            Omit_this_one instructs the function to omit the given select box.
    */
    this.disable_obj_children = function ( an_obj )
    {
		if (an_obj == null)
		{
			return; // don't do anything
		}
        if ( an_obj.children_elements !== undefined )
        {
            /*
                Proceed recursevly:                
            */
        	var i = 0;
            for (i = 0; i < an_obj.children_elements.length; i ++ )
            {                
                this.disable_obj(an_obj.children_elements[i]);
                this.disable_obj_children(an_obj.children_elements[i]);
            }
        }
    };
        
    /*
        Function: clear_properties
            Sets all corresponding properties to null.
    */
    this.clear_properties = function( an_obj )
    {
        var i = 0;
        var property = null;
        
        if (an_obj.properties)
        {
            for (i = 0; i < an_obj.properties.length; i ++ )
            {
                property = an_obj.properties[i];
                this[property] = null;
            }
        }    
    }; 
   
    /**
     * Searches the array for an object with object.id = an_id
     * @param {Object} an_id
     * @param {Object} an_array
     * @return Object with id == an_id, Null if no object is found
     */
    this.find_obj_with_id = function ( an_id, an_array )
    {
        var result = null;
        var i = 0;
        
        for (i = 0; i < an_array.length; i ++ )
        {
            if ( an_array[i].id == an_id )
            {
                result = an_array[i];
                break;
            }
        }
        
        return result;
    };
      
               
    /*
        ---------------------------
        Request /receive functions:
        ---------------------------
    */               
    
    /*
        POST Request templates:
            obj_id - Int, object identifier
            app_name - String, django application name
            model_name - String, django model name
            children_set_name - String, django relation name
            fetch_only_these_fields - JSON String, array of model fields
    */
    
    this.city_set_request = function( country_id )
    {
        var request = {
            obj_id:                  (+country_id),
            app_name:                'destinations', 
            model_name:              'Country',
            children_set_name:       'city_set', 
            fetch_only_these_fields: dojo.toJson(['id', 'name', 'city_id'])
        };
        return request;
    };
    
    this.school_set_request = function(city_id)
    {
        var request = {
            obj_id:                  (+city_id),
            app_name:                'destinations', 
            model_name:              'City',
            children_set_name:       'school_set', 
            fetch_only_these_fields: dojo.toJson(['id', 'name'])
        };
        return request;
    };
    
    this.school_content_request = function (school_id)
    {
        var request = {    
            obj_id:                  (+school_id),
            app_name:                'main_database_models', 
            model_name:              'School',
            children_set_name:       '',    /* we want the school, not a children set!*/
            fetch_only_these_fields: dojo.toJson(['id', 'name', 'currency', 'transfer_availability', 'calculation_mode'])
        };
        return request;
    };
    
    this.course_set_request = function (school_id)
    {
        var request = {
            obj_id:                  (+school_id),
            app_name:                'main_database_models', 
            model_name:              'School',
            children_set_name:       'course_set',  
            fetch_only_these_fields: dojo.toJson(['id', 'name', 'type', 'min_level', 'max_level', 'first_day', 'last_day'])
        };
        return request;
    };
    
    this.accommodation_set_request = function (school_id)
    {
        var request = {
            obj_id:                  (+school_id),
            app_name:                'main_database_models', 
            model_name:              'School',
            children_set_name:       'accommodation_form_set',
            fetch_only_these_fields: dojo.toJson(['id', 'name', 'type'])
        };
        return request;
    };
    
    this.inscription_fee_set_request = function (school_id)
    {
        var inscription_fee_set_request = {
            obj_id:                  (+school_id),
            app_name:                'main_database_models', 
            model_name:              'School',
            children_set_name:       'school_inscription_fee_set',  
            fetch_only_these_fields: dojo.toJson(['id', 'from_date', 'to_date', 'fee'])
        };
        return inscription_fee_set_request;
    };
    
    this.transfer_set_request = function (school_id)
    {
        var request = {
            obj_id:                  (+school_id),
            app_name:                'main_database_models', 
            model_name:              'School',
            children_set_name:       'school_transfer_set',  
            fetch_only_these_fields: dojo.toJson([ 'id', 'name', 'description' ])
        };
        return request;
    };
    
    this.transfer_price_request = function (transfer_id)
    {
        var request = {
            obj_id:                  (+transfer_id),
            app_name:                'main_database_models', 
            model_name:              'School_transfer',
            children_set_name:       'school_transfer_price_set',  
            fetch_only_these_fields: dojo.toJson([ 'id', 'from_date', 'to_date', 'cost'])
        };
        return request;
    };
    
    this.course_price_request = function (course_id)
    {
        var request = {
            obj_id:                  (+course_id),
            app_name:                'main_database_models', 
            model_name:              'Course',
            children_set_name:       'course_price_set',  
            fetch_only_these_fields: dojo.toJson([ ]) /* all fields are interesting!*/
        };
        return request;
    };
    
    this.accommodation_price_request = function (accommodation_form_id)
    {
        var request = {
            obj_id:                  (+accommodation_form_id),
            app_name:                'main_database_models', 
            model_name:              'Accommodation_form',
            children_set_name:       'accommodation_price_set',  
            fetch_only_these_fields: dojo.toJson([ ]) /* all fields are interesting!*/ 
        };
        return request;
    };
    
    this.material_fee_request = function (school_id)
    {
        var request = {
            obj_id:                  (+school_id),
            app_name:                'main_database_models', 
            model_name:              'School',
            children_set_name:       'school_material_fee_set',  
            fetch_only_these_fields: dojo.toJson([ ]) /* all fields are interesting!*/ 
        };
        return request;
    };
    
    /**
     * Function: send_school_request
     *       Sends a request for school information for the given school
     *       
     * @param {Int} a_school_id School identifier
     */
    this.send_school_request = function ( a_school_id )
    {
        /*
                Set values that we are 
                sending requests for to null
        */
        this.school                       = null;
        this.course_set                   = null;
        this.accommodation_form_set       = null;
        this.school_inscription_fee_set   = null;
        this.school_transfer_set    	  = null;
		
        /*
            Send request for the school:                
        */                                
        this.send_request( this.school_content_request(a_school_id), this, 'save_school_sets');
        
        /*
            Get courses and accommodation forms: 
        */                                            
        this.send_request( this.course_set_request(a_school_id), this, 'save_school_sets');            
        this.send_request( this.accommodation_set_request(a_school_id), this, 'save_school_sets');
        
        /*
          Get additional fees and costs
        */					
        this.send_request( this.inscription_fee_set_request(a_school_id), this, 'save_school_sets');
        this.send_request( this.transfer_set_request(a_school_id), this, 'save_school_sets');
        this.send_request( this.material_fee_request(a_school_id), this, 'save_school_sets');
    }; 
   
    /**
     * Function: send_request_for
     *       Sends a request for a given object.
     *       
     *       When response arrives, it will call a_func with ( type, data ) arguments.
     *       
     *       If package is already available, it will just forward it to this.receive_response. 
     *
     * @param {Object} request
     * @param {Object} a_self
     * @param {Object} save_func_str
     * @param {String} url_str - optional argument
     */
    this.send_request = function ( request, a_self, save_func_str, url_str )
    {
        // Let the user know we are doing something
        a_wait_cursor_manager.push_pending_job_signal();
        
        var url = url_str;
        if (url === undefined) {
            url = this.url_fetch_obj;
        }
        var request_content_str = dojo.toJson(request);

        // Do we have a cached response?
        if ( this.cached_responses[request_content_str] ) {
			var a_cached_response = this.cached_responses[request_content_str];
			var data = copy_an_object( a_cached_response );
			this.receive_response( data, a_self, save_func_str );
        }
        else {
            //console.warn("cache miss: " + request_content_str);
            //console.dir(this.cached_responses);
            // We have to request it from server
            // Make receive_response global,
            // because handle function refers to it
            var receive_response = this.receive_response;
            var cached_responses = this.cached_responses;
            var handle_errors = this.handle_transmit_errors;

            // Send a request
            dojo.xhrPost ( 
            {
                url: url,
                content: request,
                handle: function ( type, data, evt )
                        {                            
                                receive_response ( data, 
                                                   a_self, 
                                                   save_func_str,
                                                   cached_responses,
                                                   request_content_str );
                        },
                handleAs: 'text',
                error: handle_errors
            } );                	
        }        	
    };
    
    /**
     * Function: receive_response
     *       Callback function for ajax requests
     *       Decodes the JSON string and invokes the function passed in parameters
     *       Caches the response if no errors occurred
     *
     * @param {Object} data Received data
     * @param {Object} a_self Reference to current object
     * @param {String} a_func_str Function that should be called
     * @param {Hashtable} cached_responses Cache
     * @param {String} a_request_content_str Request
     */
    this.receive_response = function ( data, 
                                       a_self, 
                                       a_func_str,
                                       cached_responses,
                                       a_request_content_str )
    {
        // Let the user know we are finished
        a_wait_cursor_manager.pop_pending_job_signal();
        
        /*
            Unpack the package: 
            Just check if it's already an object
        */        
        var a_package = null;
        if (data.errors !== undefined) {
            a_package = data;
        }
        else {
            if (typeof(data) == "string")
            {
                a_package = dojo.fromJson(data);
            }
            else if (typeof(data) == "object" && data.xhr != undefined)
            {
                a_package = dojo.fromJson(data.xhr.responseText);
            }
            else if (typeof(data) == "object")
            {
                a_package = data;
            }
            else
            {
                console.error("price_calculator_class.receive_response(): data format not recognized" + data);
            }
        }
        
        /*
            Check for errors
        */                       
        if ( a_package.errors != undefined &&
             a_package.errors != null &&
             a_package.errors != '' )
        {
            show_errors(a_package.errors);
        }
        else
        {
            // Cache the response only if no error occurred
            if (a_request_content_str && cached_responses )
            {            
                cached_responses[a_request_content_str] = a_package;           
            }
        
            // Call the function
            if ( !a_self[a_func_str] )
            {
                show_errors( ['Function: ' + a_func_str + ' does not exist.' ] );
            }
            else
            {
                a_self[a_func_str](a_package);
            }                
        }                                
    };
    
    /**
     * Function: handle_transmit_errors
     *       Callback function for ajax request errors
     *
     * @param {Object} type
     * @param {Object} error
     */
    this.handle_transmit_errors = function ( type, error )
    {
        a_wait_cursor_manager.pop_pending_job_signal();
        //document.body.style.cursor = 'default';
        show_errors([error]);
    };
    
    /**
     * Saves a json object as string into cache
     * @param {Object} request Request Content
     * @param {Object} data Reply package
     */
    this.save_to_cache = function(request, data)
    {
        var request_str = dojo.toJson(request);
        this.cached_responses[request_str] = data;
    };
    
    
    /**
     * Function: init
     *       Initializes class variables
     */
    this.init = function ()
    {
        //this.city_set = prefetched_packages.city_set;
        //this.country_set = prefetched_packages.country_set;
        
        this.save_package(prefetched_packages.city_set);
        this.save_package(prefetched_packages.country_set);
    
        this.fill_countries(prefetched_packages.country_set);
        
        // global: city_id
        if (city_id) {
            this.save_to_cache(this.school_set_request(city_id), prefetched_packages.school_set);
        }
        
        this.save_to_cache(this.school_content_request(school_id), prefetched_packages.school);
        this.save_to_cache(this.material_fee_request(school_id), prefetched_packages.school_material_fee_set);
        this.save_to_cache(this.inscription_fee_set_request(school_id), prefetched_packages.school_inscription_fee_set);
        this.save_to_cache(this.transfer_set_request(school_id), prefetched_packages.school_transfer_set);
        this.save_to_cache(this.course_set_request(school_id), prefetched_packages.course_set);
        this.save_to_cache(this.accommodation_set_request(school_id), prefetched_packages.accommodation_form_set);
        
        // subscribe for currency change events    
        a_money_exchange.sign_up_object_for_update(this, this.calculate_prices);
        
        // initialize the first step
        this.update_active_step(1);
        
        if (country_id)
        {
            // select predefined country
            if (this.country_set)
            {
                this.set_country(country_id);
            }
        }
		if (city_id && this.city_set) {
			this.set_city(city_id);
		}
		if (school_id) {
			this.set_school(school_id);
		}
    };
    
    this.init();
}
