Timista

// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults

var IDEAL_NUMBER_OF_TIMELINE_DIVS = 5

var sequence_details_were_revealed = {}

var ACTIVITIES_SIZE_TEMP = null

var SUBMIT_TOOLTIP_HAS_BEEN_SHOWN = false

///// IE specific stuff
//http://stackoverflow.com/questions/3629183/why-doesnt-indexof-work-on-an-array-ie8
if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function(elt /*, from*/ ) {
    var len = this.length >>> 0;

    var from = Number(arguments[1]) || 0;
    from = (from < 0) ? Math.ceil(from) : Math.floor(from);
    if (from < 0) from += len;

    for (; from < len; from++) {
      if (from in this && this[from] === elt) return from;
    }
    return -1;
  };
}
//////////////////////////


function remove_fields(link, association, calling_from_select) {
  $(link).prev("input[type=hidden]").val("1");
  $(link).parents(".fields").first().hide();

  //if ((calling_from_select === undefined) || (calling_from_select == false)) {
  update_select_tag(link, association, 'remove', calling_from_select)
  //}

}

function add_fields(link, association, content, offset, calling_from_select) {
  if (offset === undefined) {
    offset = 0
  }
  var new_id = new Date().getTime() + offset;
  var regexp = new RegExp("new_" + association, "g")
  //$(link).parent().insert({
  //  before: content.replace(regexp, new_id)
  //});

  ////$(link).parent().prepend(content.replace(regexp, new_id))
  $(content.replace(regexp, new_id)).insertBefore($(link).parent())


  //if ((calling_from_select === undefined) || (calling_from_select == false)) {
  update_select_tag(link, association, 'add', calling_from_select)
  //}

  do_custom_updates(link, association)

  create_selectBoxes($(link).closest("." + association))

  refresh_jquerymobile_widgets($(link).closest("." + association))

}

function update_select_tag(link, association, nature, calling_from_select) {

  if (!((calling_from_select === undefined) || (calling_from_select == false))) {
    return
  }


  select_link = null
  increment = 0
  // We have to split the cases (add and remove) because the add and remove links are not at the same level
  // in the DOM hierarchy. Indeed, remove is at the model level (eg: remove of a user is in the user section itself),
  // whereare add is at the parent level (eg add user is above user level itself).
  try {
    if (nature == 'remove') {
      //PROTOTYPE select_link = link.up().up(".fields").down('.select_to_add_fields_for_'+association)
      select_link = $(link).parent().parents(".fields").first().find('.select_to_add_fields_for_' + association).first()
      increment = -1
    } else if (nature == 'add') {
      select_link = $(link).parents(".fields").first().find('.select_to_add_fields_for_' + association).first()
      increment = +1
    }
  } catch (e) {
    // In case no select tag exist at this level, we do nothing
  }

  if (!((select_link === undefined) || (select_link == null))) {
    select_link_new_value = parseInt(select_link.val()) + increment
    select_link.val(select_link_new_value)

    // Just to signal the change on elements that depend on the "onChange" event
    // of this select, e.g. selectBox.
    //select_link.change()
    select_link.trigger('update_dependents')
  }

}


do_custom_updates = function(link, association) {
  switch (association) {
  case "sequences":
    // What we do here:
    // - user click on "add a sequence"
    // - we want this sequence to be a sequence_with_dummy, so we pass the with_summy_user parameter //TODO!!
    // - we set the class of the sequence and first event accordingly, i.e. they mention "dummy" explicitly. 
    ldiv = $(link).parents('.' + association).first()
    lsequences = ldiv.find('.sequence');
    if (lsequences.length > 0) {
      lsequence = lsequences[lsequences.length - 1]
      //lsequence.writeAttribute('id', 'sequence_with_dummy')
      $(lsequence).addClass('sequence_with_dummy')
      levents = lsequence.find('.event')
      //levents.each(function(idx, levent) {
      //	$(levent).addClass('real_event');
      //});
      levents.addClass('real_event');

      levents.first().removeClass('real_event').addClass('dummy_event')
      levents.first().find("div[class='event_type']").first().find('input').first().val(100000)
      levents.first().find("div[class='event_chronology']").first().find('input').first().val(100000)
      //if (levents.length > 0) {
      //	levent = levents[0]
      //	levent.removeClass('real_event')
      //	levent.addClass('dummy_event')
      //	//levent.writeAttribute('id', 'dummy_event')
      //	//alert(levent.id)
      //	levent.find("div[class='event_type']")[0].find('input').first().val(100000)
      //	levent.find("div[class='event_chronology']")[0].find('input').first().val(100000)
      //	//alert(levent.select("div[class='event_type]")[0].down('input').value)
      //}

    }

    break;
  case "events":
    ldiv = $(link).parents('.' + association).first()
    levents = ldiv.find('.event')
    i = 0

    // Set the chronologies
    levents.each(function(idx, e) {
      //alert(e.select("div[class='event_chronology']")[0].down('input').value)
      temp = $(e).find("div[class='event_chronology']").first().find('input').first()
      if (temp.val() != 100000) { //TODO: don't hard code
        $(e).find("div[class='event_chronology']").first().find('input').first().val(i)
        i++
      }
      //alert(e.select("div[class='event_chronology']")[0].down('input').value)

    });

    $(link).parents('.sequence').first().find('.real_event').removeClass('hidden')
    //for (i = 0; i < events.length; i++) {		
    //		events[i].removeClass('hidden')
    //}
    break;
  }
}

//select_to_add_fields(this, association, content, select_value)
select_to_add_fields = function(el, association, content, parent, child) {
  lparent = $(el).parents('.' + parent).first();
  lchilds = lparent.find('.' + child);
  //lchilds = lparent.select("div[class='"+child+"']");//"[display='']");
  active_childs = []
  for (i = 0; i < lchilds.length; i++) {
    remove_link = $(lchilds[i]).find('.remove_fields_link_for_' + child).first()
    if (remove_link.prev("input[type=hidden]").val() != "1") {
      active_childs.push(lchilds[i])
    }
  }
  current_nb_childs = active_childs.length
  //alert(current_nb_childs)
  new_select_value = $(el).val()
  if (new_select_value > current_nb_childs) {
    lclass = 'add_fields_link_for_' + child
    add_user_link = lparent.find("a[class~='" + lclass + "']").first();
    //add_user_link = lparent.down("a[class='add_fields_link']");
    loffset = 0
    while (new_select_value > current_nb_childs) {
      // Here we need to simulate a click on the link
      add_fields(add_user_link, association, content, loffset, true)
      //myrules['.add_nested_item_lvl3'](add_user_link, loffset) // need a delta here! Otherwise two records might have the same id!!

      new_select_value -= 1
      loffset += 1
    }
  } else {
    while (new_select_value < current_nb_childs) {

      remove_fields($(active_childs[current_nb_childs - 1]).find('.remove_fields_link_for_' + child).first(), association, true)

      //active_childs[current_nb_childs-1].remove()
      current_nb_childs -= 1
    }

  }

}

display_events_for_sequence = function(el, state) {
  //if (state) {
  //	lvalue = ''
  //}
  //else {
  //	lvalue = 'none'
  //}

  //if (IS_MOBILE_DEVICE){ // On mobile, we never toggle, they are always visible.
  //	state = true
  //}




  el.parents('.sequence').first().find('.real_event').each(function(idx, ev) {
    if (state) {
      $(ev).removeClass('hidden')
    } else {
      $(ev).addClass('hidden')
    }
  })


  //for (i = 0; i < events.length; i++) {		
  //	if (state) {
  //		events[i].removeClass('hidden')
  //	}
  //	else {
  //		events[i].addClass('hidden')
  //	}

  //}

  el.parents('.sequence').first().find('.event_add_link').first().each(function(idx, lnk) {
    if (state) {
      $(lnk).removeClass('hidden')
    } else {
      $(lnk).addClass('hidden')
    }
  })
  //if (event_add_link) {
  //	if (state) {
  //		event_add_link.removeClass('hidden')
  //	}
  //	else {
  //		event_add_link.addClass('hidden')
  //	}
  //}

}



debug_display_hash = function(h) {
  //return(h.get('event_id')+' '+h.get('provider_id')+' '+h.get('time_slot_id'))
  return (h['event_id'] + ' ' + h['provider_id'] + ' ' + h['time_slot_id'])
}

debug_display_hash_old_way = function(h) {
  return (h['event_id'] + ' ' + h['provider_id'] + ' ' + h['time_slot_id'])
}

debug_display_arrangements_array = function(parray) {
  //TODO remove DEBUG
  dbg = ''
  for (i = 0; i < parray.length; i++) {
    for (j = 0; j < parray[i].length; j++) {
      dbg = dbg + '{' + debug_display_hash_old_way(parray[i][j]) + '}'
    }
  }
  //alert(dbg)
  //////////
}


// The user is asking for details regarding timing of proposals.
display_time_slots_possibilities = function(el, bool) {
  time_slots_possibilities = el.parents('.provider').first().find('.time_slots.possibility').first()
  if (bool) {
    time_slots_possibilities.removeClass('hide')
    //time_slots_possibilities.addClassName('.show')
  } else {
    //time_slots_possibilities.removeClassName('.show')
    time_slots_possibilities.addClass('hide')
  }
}





// This function displays sequence_individual_proposals.
// Principle: all the providers possible have already been created on the page (cf event_proposals). So here we
// - parse the nuplets returned by the algo
// - find in the page the corresponding nodes: providers/time_slots/AIs
// - copy each node
// - clean the node (by keeping only the targeted time_slots as defined in the nuplet - not all the TS possible)
// - insert it in a newly created sequence_individual_proposal
// NOTE: the sequence_individual_proposal might have already been created and displayed. If it is the case, we do
// not re-render it. We only change its ranking if need be.

// Phase 2: we show alternative timing arrangements below the best one.
// Nomencalture:
// - master_arrangement: the best one (in time), for a given set of providers.
// - sub_arrangement: a worse one for the same set or providers.
//var DEFAULT_NUMBER_OF_PROPOSALS_SHOWN = 4 // Number of
//if (IS_MOBILE_DEVICE){
//	DEFAULT_NUMBER_OF_PROPOSALS_SHOWN = 3
//}

var NUMBER_OF_PROPOSALS_SHOWN = typeof(DEFAULT_NUMBER_OF_PROPOSALS_SHOWN) == "undefined" ? 3 : DEFAULT_NUMBER_OF_PROPOSALS_SHOWN
var shown_proposals_infos = {} // In this hash we are storing information per individual proposal displayed.
// Attention: this can't be reset between calls to render_sequence_proposals.
// Indeed: if a sequence_proposal is not re-rendered (i.e. it was already on the page),
// the respective info would be lost.
render_sequence_proposals = function(psequence_id) {



    if (proposed_arrangements == null) {
      return false
    }

    jq_sequence_main_container = $('#sequence__' + psequence_id)

    // We create the timegrid here
    min_time = 10000000000000000
    max_time = 0
    $.each(time_slots_internals, function(idx, ts_infos) {
      min_time = min_time > ts_infos[0] ? ts_infos[0] : min_time
      max_time = max_time < ts_infos[1] ? ts_infos[1] : max_time
    })

    jq_proposals_area = jq_sequence_main_container.find('.proposals_area')
    jq_proposals_area.find('.timeline_container').remove();
    jq_proposals_area.prepend(generate_timeline(min_time, max_time));


    // The parray contains the nuplets from the algo (modified with View info) that we want to feature.
    // parray = [ [{ev_id, prov_id, ts_id, ai_id, bl}, {}...], ... ]
    // The user may have already liked/dislike some providers. We need to adapt the possible
    // arrangements to be displayed based on this pre-selection.
    parray = adapt_available_arrangements_based_on_preselections(jq_sequence_main_container)

    // There are no valid arrangements left
    if (parray.length == 0) {
      $('#no_arrangements_found_modal').dialog({
        modal: true,
        resizable: false,
        draggable: false,
        'buttons': [{
          text: "Undo",
          click: function() {
            $(this).dialog("close");
            resize_timing_bars(jq_sequence_main_container);
            roll_back_select_change(jq_sequence_main_container)
          }
        }, {
          text: "Search again",
          click: function() {
            $('form').first().submit();
          }
        }],
        dialogClass: 'message_dialog'
      })

      return false
    }

    mark_non_available_elements(jq_sequence_main_container, parray)

    //TODO remove DEBUG
    debug_display_arrangements_array(parray)

    parray_for_view = clean_duplicates(parray) // Parray contains nuplets that differ only by the AIs.
    // This is a "problem" for the view. Indeed, what we display on each line is not really a nuplet as per matlab,
    // but a "pseudo" one on the Provider/TS level. I.e. on each line, we will display Prov/Ts/ALL AI possible.
    // So we need to "remove" nuplets that are duplicates at the Prov/TS level.
    debug_display_arrangements_array(parray_for_view)

    sort_option = 'score' // Default, in case.
    jq_sequence_main_container.find('.sorting_selector').find('option').each(function(idx, opt) {
      if (opt.selected == true) {
        sort_option = opt.value
      }
    });


    switch (sort_option) {
    case 'score':
      // Here we prepare the array of arrangements so that we can render it in order.
      parray_for_view = parray_for_view.sort(sort_by_score) // Here we sort the possible arrangements based on
      // their respective score.

      parray_for_view = sorter(parray_for_view) // Grouping: now the array of arrangement looks like:
      // - master_arrangement XX%
      //    - sub_arrangement aa% (aa < XX)
      //    - sub_arrangement bb% (bb < XX)
      //    - ...
      // - master_arrangement YY%
      //    - sub_arrangement cc% (cc < YY)
      break;
    case 'starting_time':
      //parray_for_view = parray_for_view.sort(sort_by_starting_time)
      sort_it(parray_for_view, 1, 'begin_time', -1, 'score')
      break;
    case 'price':
      //parray_for_view = parray_for_view.sort(sort_by_price)
      sort_it(parray_for_view, 1, 'price', -1, 'score')
      parray_for_view = sorter(parray_for_view)
      break;
    default:
      //parray_for_view = parray_for_view.sort(sort_by_score)
      parray_for_view = sorter(parray_for_view)
    }


    // DEBUG
    //dstring = ""
    //$(parray_for_view).each( function (idx, el){
    //	dstring += el['arrangement'][0]['provider_id'] + " / " + el['arrangement'][1]['provider_id'] + " / " + el['score'] + "\n"
    //})
    //alert(dstring);

    //parray_for_view = check_diversity(parray_for_view, 5)
    parray_for_view = remove_adjacent(parray_for_view)
    parray_for_view = remove_adjacent(parray_for_view)

    // We don't want to re-render what is already on the page. We check here what has already been rendered.
    div_individual_proposals = jq_sequence_main_container.find('.sequence_individual_proposal')
    previous_ids = [] // previous_ids will contain the IDs of the sequence_individual_proposal that were already
    // displayed before this call.
    div_individual_proposals.each(function(idx, d) {
      previous_ids.push(d.id);
    });




    new_ids = []
    previous_id = null

    current_master_arrangement = null
    master_count = 0
    sub_count = 0
    // We iterate on each arrangement
    while (sub_count < parray_for_view.length) {
      // We have found a new master_arrangement.
      if ((current_master_arrangement == null) || !(is_similar(current_master_arrangement, parray_for_view[sub_count]['arrangement']))) {
        if (master_count >= NUMBER_OF_PROPOSALS_SHOWN) {
          break;
        } else {
          master_count += 1
          current_master_arrangement = parray_for_view[sub_count]['arrangement']
          additional_class = 'master_arrangement'
        }
      } else {
        additional_class = 'sub_arrangement'
      }

      //for (i = 0; i < Math.min(parray_for_view.length, NUMBER_OF_PROPOSALS_SHOWN); i++) { //TODO: don't hard code the number of proposals

      i = sub_count

      jq_proposal_element = null

      larrangement = parray_for_view[i]['arrangement']

      // We construct the ID for the arrangement (it contains the sequence_id and provider_id/ts_id for each event).
      id_buffer = ''
      for (j = 0; j < larrangement.length; j++) {
        id_buffer = id_buffer + '__' + 'sequence__' + psequence_id + '__event__' + larrangement[j]['event_id'] + '__provider__' + larrangement[j]['provider_id'] + '__time_slot__' + larrangement[j]['time_slot_id']
      }

      // We check whether this arrangement has already been rendered.
      if (previous_ids.indexOf(id_buffer) >= 0) {

        // If it was already on the page, we may have to update its ranking. We do it here.
        temp_selector = '#' + id_buffer
        jq_proposal_element = $(temp_selector)

        // Cleaning
        jq_proposal_element.removeClass('sub_arrangement')
        jq_proposal_element.removeClass('master_arrangement')

        html_classes = (jq_proposal_element.first().attr('class')).split(" ")
        temp = jQuery.grep(html_classes, function(el, idx) {
          return (el.search(/rank__/) >= 0)
        })
        if (temp.length > 0) {
          previous_rank = parseInt(temp[0].replace(/rank__/, ''))
          // The rank has changed. We treat this proposal as a new one (i.e. we remove it from the view
          // and we will re-add it with the new rank).
          if (previous_rank != master_count) {
            // In case this proposal had a "show sub arrangements button" we remove it here (we don't know)
            // at this stage whether it will still need one - will be re-added later if need be.
            jq_proposal_element.find('.sub_arrangement_toggle_button').first().remove();
            jq_proposal_element.removeClass(temp[0])
            /////$(proposal_element).find('proposal').removeClass(temp[0])
            //$(proposal_element).next('.sequence_proposals_spacer').remove()
            jq_proposal_element.detach(); // Attention : use detach here and not remove! We need to keep the binding in place
            // since we will re-add this element later.
            previous_ids.splice(previous_ids.indexOf(id_buffer), 1)
          } else {
            ////container.removeClass(temp[0])
            ////container.addClass('rank__' + i)
            jq_proposal_element.addClass(additional_class)

            // Remove or add "show sub arrangements button"
            if ((additional_class == 'sub_arrangement') || (i == parray_for_view.length - 1) || //Master but Last element
            !(is_similar(larrangement, parray_for_view[i + 1]['arrangement']))) { // Master and Next one is different
              jq_proposal_element.find('.sub_arrangement_toggle_button').first().remove();
            } else if ((additional_class == 'master_arrangement') && (i != parray_for_view.length - 1) && (is_similar(larrangement, parray_for_view[i + 1]['arrangement']))) {
              if (jq_proposal_element.find('.sub_arrangement_toggle_button').length == 0) {
                // Add "show sub arrangements button"
                sub_arrangement_toggle_button = $("<a/>", {
                  'class': 'sub_arrangement_toggle_button',
                  'href': '#'
                }).html("Alternate times")
                sub_arrangement_toggle_button.click(function() {
                  $(this).parent().nextUntil('.master_arrangement').each(function(idx, el) {
                    //$(this).parent().next('.sequence_proposals_spacer').nextUntil('.master_arrangement').each(function(idx,el){
                    if ($(el).hasClass('sub_arrangement')) {
                      $(el).toggle();
                      //$(el).next().toggle();//spacer
                    }
                  })

                  resize_timing_bars($(this).parents('.proposals_area').first())
                  return false;

                })
                jq_proposal_element.append(sub_arrangement_toggle_button)
              }
            }

            previous_id = id_buffer;
            new_ids.push(id_buffer);

            //$(proposal_element).find('proposal').each(function(idx, element){
            //	element.className.replace(/rank__[0-9]*/,'rank__' + i)
            //})
            sub_count += 1
            continue; // We are done with this arrangement, we skip to the next one.
          }
        }

      }




      // For each nuplet, we create a sequence_individual_proposal container
      jq_proposal_element = ((jq_proposal_element != null) && (jq_proposal_element.length > 0)) ? jq_proposal_element : generate_proposal(jq_sequence_main_container, i, parray_for_view[i]['arrangement'], parray_for_view[i]['score'], parray_for_view[i]['price']['value'], parray_for_view[i]['price']['currency'], shown_proposals_infos)
      // Add a "show sub arrangements button" if necessary.
      if ((additional_class != 'sub_arrangement') && (i != parray_for_view.length - 1) && (is_similar(larrangement, parray_for_view[i + 1]['arrangement']))) { // It is a meta_arrangement
        sub_arrangement_toggle_button = $("<a/>", {
          'class': 'sub_arrangement_toggle_button',
          'href': '#'
        }).html("Alternate times")
        sub_arrangement_toggle_button.click(function() {
          $(this).parent().nextUntil('.master_arrangement').each(function(idx, el) {
            //$(this).parent().next('.sequence_proposals_spacer').nextUntil('.master_arrangement').each(function(idx,el){
            if ($(el).hasClass('sub_arrangement')) {
              $(el).toggle();
              //$(el).next().toggle();//spacer
            }
          })
          resize_timing_bars($(this).parents('.proposals_area').first())
          return false;

        })
        jq_proposal_element.append(sub_arrangement_toggle_button)
      }




      // We have created the html element for the arrangement. We can't just append
      // to the main container: it might contain some lower ranked arrangements that won't
      // be removed. So we need to fund where to insert the newly created arrangement.
      if ((jq_proposal_element != null) && (jq_proposal_element.length > 0)) {
        new_ids.push(jq_proposal_element.attr('id'))
        jq_proposal_element.addClass('rank__' + master_count)
        jq_proposal_element.addClass(additional_class)
        //$(proposal_element).find('.proposal').addClass('rank__' + i)
        if ((jq_sequence_main_container != null) && (jq_sequence_main_container.length > 0)) {
          jq_sequence_proposals = jq_sequence_main_container.find('.sequence_proposals').first()
          if (previous_id != null) {
            jq_previous_proposal = jq_sequence_proposals.find("#" + previous_id).first()
            ////previous_proposal.after(proposal_element)
            if (!(jq_proposal_element.hasClass('sub_arrangement'))) {
              if (!IS_MOBILE_DEVICE) {
                jq_proposal_element.hide().insertAfter(jq_previous_proposal).fadeIn('slow');
              } else {
                jq_proposal_element.insertAfter(jq_previous_proposal); // Faster
              }
            } else {
              jq_previous_proposal.after(jq_proposal_element)
            }

            // After each arrangement, we add a spacer.
            //previous_proposal.after($("<div/>", { 'class': 'sequence_proposals_spacer' }))

          } else {
            ////sequence_proposals.prepend(proposal_element)
            if (!(jq_proposal_element.hasClass('sub_arrangement'))) {
              if (!IS_MOBILE_DEVICE) {
                jq_proposal_element.hide().prependTo(jq_sequence_proposals).fadeIn('slow')
              } else {
                jq_proposal_element.prependTo(jq_sequence_proposals) // Faster
              }
            } else {
              jq_sequence_proposals.prepend(jq_proposal_element)
            }


            // After each arrangement, we add a spacer.
            //new_proposal_container.after($("<div/>", { 'class': 'sequence_proposals_spacer' }))

          }

        }
        previous_id = jq_proposal_element.attr('id') //full_id
      }

      sub_count += 1

    }



    elements_to_be_removed = $()
    $(previous_ids).each(function(idx, e) {
      if (new_ids.indexOf(e) < 0) {
        temp_selector = '#' + e
        //temp = $(temp_selector).next('.sequence_proposals_spacer')
        //if (temp.length > 0) {
        //	temp.remove();
        //}

        elements_to_be_removed = elements_to_be_removed.add($(temp_selector))

        // Old method
        ////$(temp_selector).remove_safe();

        // We also need to remove from the global shown_proposals_infos hash
        delete shown_proposals_infos[e];
      }
    });



    elements_to_be_removed.remove_safe()


    ////mark_non_available_elements(psequence_id, parray)


    // In each proposal, the providers have not yet been "positioned" to match the timegrid. We do it now.
    position_possibilities(jq_sequence_main_container, shown_proposals_infos, min_time, max_time)

    //sequence_main_container.find('.vertical_line').css('height', sequence_main_container.find('.sequence_proposals').first().height() + 10)
    resize_timing_bars(jq_sequence_main_container)


    // Setup the href for the "show less proposals link", so that the automatic repositioning in the page makes sense.
    jq_visible_proposals = jq_sequence_main_container.find(".sequence_individual_proposal").filter(":visible")
    if (jq_visible_proposals.length > 2) {
      jq_sequence_main_container.find(".show_less_proposals_button").attr('href', "#" + jq_visible_proposals[jq_visible_proposals.length - 2].id)
    }


    // Mobile specific
    if (IS_MOBILE_DEVICE) {
      jq_ltimeline = jq_sequence_main_container.find('.proposals_area').find('.timeline_container').detach();
      jq_sequence_main_container.find('.proposals_area').find('.sequence_individual_proposal').find('.activities').before(jq_ltimeline)
    }


    do_updates_after_content_change();

    return true

  }

  // TODO: pass jq object as parameter. Where is this used?
  do_manual_updates = function(proposal_element, parray_for_view_element) {
    jq_proposal_element = $(proposal_element)
    jq_proposal_element.find('.sequence_individual_proposal_information_section_after').first().find('.pricing_info').first().html(format_price(parray_for_view_element['price']['value'], parray_for_view_element['price']['currency']))
  }


  // Input: an ordered by score array of arrangement
  // Output:
  // - master_arrangement XX%
  //    - sub_arrangement aa% (aa < XX)
  //    - sub_arrangement bb% (bb < XX)
  //    - ...
  // - master_arrangement YY%
  //    - sub_arrangement cc% (cc < YY)
  sorter = function(parray) {

    new_array = []
    current_master = null
    while (parray.length > 0) {
      if (current_master == null) {
        current_master = (parray.splice(0, 1))[0]
        new_array.push(current_master)
      }
      j = parray.length
      for (i = 0; i < j; i++) {
        if (is_similar(parray[i]['arrangement'], current_master['arrangement'])) {
          new_array.push((parray.splice(i, 1))[0])
          j = j - 1;
          i = i - 1;
        }
      }

      current_master = null

    }

    return new_array

  }

  // Returns true if both arrangements feature the same providers in the same order.
  is_similar = function(arr1, arr2) {
    temp = true
    for (ii = 0; ii < arr1.length; ii++) {
      if (arr1[ii]['provider_id'] != arr2[ii]['provider_id']) {
        temp = false
        break;
      }
    }

    //if (temp)

    return temp
  }

resize_timing_bars = function(jq_sequence_main_container) {
  if (!(IS_MOBILE_DEVICE)) {
    jq_sequence_main_container.find('.vertical_line').css('height', jq_sequence_main_container.find('.sequence_proposals').first().height() + 10)
  }
}

// Returns a JQuery object!
generate_proposal = function(jq_sequence_main_container, rank, larrangement, score, price, currency, ts_infos) {

  psequence_id = jq_sequence_main_container.attr('id').replace(/sequence__/, '')

  new_proposal_container = $("<div/>", {
    'class': 'sequence_individual_proposal'
  })
  new_proposal_container.append($("<div/>", {
    'class': 'activities'
  }))

  full_id = ''
  temp_info = {} // Will be used to store the time_slots info
  // We iterate on each element of the nuplet
  for (j = 0; j < larrangement.length; j++) {
    temp = 'sequence__' + psequence_id + '__event__' + larrangement[j]['event_id'] + '__provider__' + larrangement[j]['provider_id']
    temp_selector = '#' + temp

    ldiv = $(temp_selector) // Here we locate the already created node.
    if (ldiv == null) {
      alert('Oups. Something went wrong. E735')
      //everything_fine = false
      return null;
    } else {

      cloned_div = ldiv.clone(); // We copy this node

      // In the proposal view, we only keep the right time_slot
      temp2 = temp + '__time_slot__' + larrangement[j]['time_slot_id']
      cloned_div.find('.time_slot.possibility').not('#' + temp2).remove();


      cloned_div.attr('id', cloned_div.attr('id') + "__duplicate") // In the proposal section,
      // the providers id end with __duplicate.
      // These IDs are used by AJAX - so we need to mark them as duplicates.
      cloned_div.find('.time_slots').attr('id', function() {
        return this.id + "__duplicate"
      });


      // Here we prepare the info linked to this time_slot
      temp_info[temp2] = {
        'start_date': time_slots_internals[larrangement[j]['time_slot_id']][0],
        'end_date': time_slots_internals[larrangement[j]['time_slot_id']][1]
      }


      cloned_div.addClass('proposal')
      cloned_div.find('.possibility').addClass('proposal')


      full_id = full_id + '__' + temp2
      //new_proposal_container.find('.activities').append(cloned_div.css('display','none').fadeIn())
      new_proposal_container.find('.activities').append(cloned_div)

    }
  }
  new_proposal_container.attr('id', full_id)
  ////new_ids.push(full_id)
  ts_infos[full_id] = temp_info


  // Score container
  if (IS_MOBILE_DEVICE) { // No tooltip
    proposal_container_info_section_before = $("<div/>", {
      'class': 'sequence_individual_proposal_information_section_before',
      'title': 'Score is a combination of distance, time, reviews and more.'
    }).html("<small>Score:</small>" + "<strong>" + Math.round(score * 100) + "%</strong>")
  } else {
    proposal_container_info_section_before = $("<div/>", {
      'class': 'sequence_individual_proposal_information_section_before',
      'title': 'Score is a combination of distance, time, reviews and more.'
    }).html("<small>Score:</small>" + "<strong>" + Math.round(score * 100) + "%</strong>").tooltip({
      'tipClass': 'score_tooltip',
      'position': 'bottom right',
      'offset': [-20, -20]
    }).addClass('tooltip_trigger')
  }

  // Additional info container
  proposal_container_info_section_after = $("<div/>", {
    'class': 'sequence_individual_proposal_information_section_after'
  })
  // Display Min Price for arrangement:
  pricing_div = $("<div/>", {
    'class': 'pricing_info'
  }).html(format_price(price, currency))
  proposal_container_info_section_after.append(pricing_div) //("Starting at: " + format_price(price, currency))//get_proposal_minimum_price(new_proposal_container))
  // Select Button
  if (IS_MOBILE_DEVICE) {
    additional_info_button = $("<a/>", {
      'href': '#',
      'class': 'additional_info_test',
      'data-role': 'button',
      'data-icon': 'arrow-r',
      'data-iconpos': 'notext'
    }).html("More info")
    proposal_container_info_section_after.prepend(additional_info_button) //.trigger( "create" ); // Trigger event is used to enhance the control once added dynamically.

    //additional_info_button.button()

    //additional_info_button = $("<a/>", {'class': 'additional_info_test'}).html("<button class='desktop_navigation' type='button'>Details & Booking</button><br><img class='mobile_navigation' src='/images/mobile_next_page.png'/>")
    //additional_info_button = $("<a/>", {'data-role': 'button', 'data-icon': 'geo_location_icon', 'data-iconpos': 'notext'})
  } else {
    additional_info_button = $("<a/>", {
      'class': 'additional_info_test'
    }).html("<button class='desktop_navigation' type='button'>Details & Booking</button>")
    proposal_container_info_section_after.prepend(additional_info_button);
  }

  //proposal_container_info_section_after.prepend(additional_info_button).trigger( "create" );
  //additional_info_button = $("<a/>", {'class': 'additional_info_test'}).html("Map")
  //proposal_container_info_section_after.append(additional_info_button)
  additional_info_button.click(function(event) {

    var lcontainer = $(this).parents('.sequence_individual_proposal').first()
    if (IS_MOBILE_DEVICE) {
      if (USING_LOADING_MESSAGE) {
        $.mobile.showPageLoadingMsg();
      }
      var lmap_container = $("#booking_options_page #map_canvas_container" + lcontainer.attr('id')) //.find('#map_canvas_container' + lcontainer.attr('id'))
    } else {
      var lmap_container = lcontainer.find('.map_canvas_container')
    }


    // If user clicks (first time), we create the div.
    if (lmap_container.length == 0) {

      lmap_container = $('.map_canvas_container').first().clone_safe();
      lcontainer.append(lmap_container)
      update_map(lcontainer, lmap_container)

      lmap_container.attr('id', 'map_canvas_container' + lcontainer.attr('id'))
      lmap_container.addClass('hide'); // Just so that thet first toggle (below) works.

    }


    if (IS_MOBILE_DEVICE) {

      // Hide everything
      $('#booking_options_page .map_canvas_container').addClass('hide')

      // Update booking options with the latest
      lmap_container.find('.booking_options').remove()
      jq_booking_options = render_booking_options(lcontainer)
      ////jq_booking_options.trigger( "create" )
      lmap_container.prepend(jq_booking_options)

      $('#booking_options_page .booking_options_content').append(lmap_container.removeClass('hide'))

      jq_booking_options.trigger("create")

      $.mobile.changePage($('#booking_options_page'))
    } else { // Desktop	

      lcontainer.find('.booking_options').remove()

      // TODO: can't we just toggle?
      if (lmap_container.hasClass('hide')) {

        lmap_container.removeClass('hide');
        jq_booking_options = render_booking_options(lcontainer)
        lmap_container.prepend(jq_booking_options)

        lheight = lcontainer.css('height')
        jq_booking_options.css('float', 'left')
        lcontainer.css('height', lheight)

      } else {

        lmap_container.addClass('hide');
        //lcontainer.find('.booking_options').remove()
        lcontainer.css('height', 'auto')
      }
    }
    resize_timing_bars($(this).parents('.proposals_area').first());

    //refresh_scroller();


  });


  new_proposal_container.prepend(proposal_container_info_section_before)
  new_proposal_container.append(proposal_container_info_section_after)
  new_proposal_container.append($("<div/>", {
    'class': 'clear_float'
  }))


  // In the mobile version, we actually show the activities twice:
  // - once in the timeline with limited info
  // - once after, with the details.
  if (IS_MOBILE_DEVICE) {
    activities = new_proposal_container.find('.activities')
    activities2 = activities.clone_safe()
    activities2.removeClass('activities').addClass('activities_repeat')

    activities.find('.provider_type_icon').removeClass('black')

    if (false) {
      activities2.find('.provider').each(function() {
        //show_all_possibilities_link = $("<a/>", {'id': $(this).attr('id') + "_show_possibilities", 'href': '#', 'class': 'show_all_possibilities_link', 'data-role': 'button', 'data-icon': 'plus', 'data-iconpos': 'notext'}).html("Others")
        show_all_possibilities_link = $("<div/>", {
          'id': $(this).attr('id') + "_show_possibilities",
          'class': 'show_all_possibilities_link'
        }).html("...")


        show_all_possibilities_link.click(function(event) {
          target_id = $(this).attr('id').match(/sequence__[0-9]*__event__[0-9]*/) + '__proposals'
          $('#' + target_id).dialog({
            modal: true,
            draggable: false,
            resizable: false,
            dialogClass: 'all_possibilities_dialog'
          }).bind('close_all_possibilities_modal', function() {
            $(this).dialog('close')
          });
          return false;
        })
        //show_all_possibilities_link.trigger( "create" )
        $(this).after(show_all_possibilities_link)
      })
    }

    ////activities2.trigger( "create" )  // Nothing needs to be enhanced in our current design.

    activities.after(activities2)

    section_after = new_proposal_container.find('.sequence_individual_proposal_information_section_after').detach()
    new_proposal_container.find('.sequence_individual_proposal_information_section_before').after(section_after)

  }

  return new_proposal_container // We return the jq element

}


generate_timeline = function(min_time, max_time) {

  timeline_container = $("<div/>", {
    'class': 'timeline_container'
  })
  timeline_container.append($("<div/>", {
    'class': 'sequence_individual_proposal_information_section_before'
  }))

  timeline0 = $("<div/>", {
    'class': 'timeline'
  })
  timeline = $("<div/>", {
    'class': 'timeline_positioner'
  })
  timeline.css('position', 'relative')
  timeline.css('height', '30px')
  vertical_lines = $("<div/>", {
    'class': 'vertical_lines'
  })
  vertical_lines.css('position', 'relative')
  vertical_lines.css('height', '10px')

  timeline0.append(timeline)
  timeline0.append(vertical_lines)

  number_of_hours = parseInt((max_time - min_time) / 3600000)

  //number_of_divisions = Math.min(number_of_hours, 5)
  //division_duration = parseInt(number_of_hours / number_of_divisions)


  division_duration = parseInt(number_of_hours / IDEAL_NUMBER_OF_TIMELINE_DIVS)
  if (division_duration == 0) {
    division_duration = 1
  }
  number_of_divisions = parseInt(number_of_hours / division_duration)

  start_date = new Date(min_time)
  first_time_shown = start_date
  if (first_time_shown.getMinutes() != 0) {
    first_time_shown.setHours(start_date.getHours() + 1)
    first_time_shown.setMinutes(0)
  } else {
    number_of_divisions += 1
  }



  for (i = 0; i < number_of_divisions; i++) {
    marker_time_ms = first_time_shown.getTime() + i * division_duration * 3600000 // in milliseconds
    marker_display_value = ((new Date(marker_time_ms)).getUTCHours()) % 24 + ':00'
    marker = $("<div/>", {
      'class': 'time_marker'
    }).html(marker_display_value);
    marker.css('left', ((marker_time_ms - min_time) / (max_time - min_time) * 100.0 - 2) + '%')
    timeline.append(marker)

    line = $("<div/>", {
      'class': 'vertical_line'
    });
    line.css('left', ((marker_time_ms - min_time) / (max_time - min_time) * 100.0) + '%')
    vertical_lines.append(line)

  }

  timeline_container.append(timeline0)
  timeline_container.append($("<div/>", {
    'class': 'sequence_individual_proposal_information_section_after'
  }))
  timeline_container.append($("<div/>", {
    'class': 'clear_float'
  }))

  return timeline_container[0]
}


position_possibilities = function(jq_sequence_main_container, shown_proposals_infos, min_time, max_time) {

  activities_size = $('.master_arrangement').first().find('.activities').first().innerWidth();

  // TODO: There is a "bug" in JQueryMobile: when the page is not displayed, its elements have a "random" size. The
  // is only right when the objects are actually shown on the page.
  // So we are just storing the real size here. Awful solution...
  if (IS_MOBILE_DEVICE) {
    if ((ACTIVITIES_SIZE_TEMP == null) || (ACTIVITIES_SIZE_TEMP < 150)) {
      ACTIVITIES_SIZE_TEMP = activities_size
    }
    if (activities_size < 100) {
      activities_size = ACTIVITIES_SIZE_TEMP
    }
  }



  $.each(shown_proposals_infos, function(key, sequence) {
    container = jq_sequence_main_container.find('#' + key)
    //accu = 0
    previous_end = min_time
    $.each(sequence, function(key2, infos) {
      //possibility = container.find('.activities').find('#' + key2.replace(/__time_slot__[0-9]*/,'__duplicate'))
      possibility = container.find('.activities ' + '#' + key2.replace(/__time_slot__[0-9]*/, '__duplicate'))
      lm = (infos['start_date'] - previous_end) / (max_time - min_time)
      //possibility.css('margin-left', lm + '%')
      possibility.css('margin-left', parseInt(lm * activities_size)) //TODO: this is slow!
      width = (infos['end_date'] - infos['start_date']) / (max_time - min_time)
      //possibility.css('width', width + '%')
      //possibility.css('width', parseInt(width/100 * activities_size))
      possibility.outerWidth(parseInt(width * activities_size))

      previous_end = infos['end_date']
      //accu = lm + width
    })
  })

}


// Initialization stuff.
$(document).ready(function() {

  // Initialize selectBoxes
  create_selectBoxes();

  // Pre-set the correct dates for the dates choosers (the select has been rendered in JS,
  // we need to replicate in the input fields)
  $('select[name=when]').change()


  //initiate_geolocation()

  // We need to store the current value of type/sub_type for each event
  // to be able to roll back. Cf activity_type_change.
  $('.events').find('.event_type select').each(function() {
    $(this).data('current', $(this).val())
    $(this).data('current_sub_type', $(this).parents('.event_data').find('.event_sub_type select').val())
  })

  $(document).trigger('updated_fields')


  // Display error messages
  if (!(typeof error_message === 'undefined') && (error_message.length > 0)) {
    $('#main_error').empty();
    $('#main_error').html(error_message).dialog({
      modal: true,
      resizable: false,
      draggable: false,
      buttons: {
        "OK": function() {
          $(this).dialog("close");
        }
      },
      dialogClass: 'message_dialog'
    })
  }


  //Placeholder script
  $('input[placeholder], textarea[placeholder]').placeholder();


});

$(document).bind("pagecreate", function() { //After the initialization
  if (!(typeof in_results_view === 'undefined') && in_results_view) {
    document.location.href = "#results_page";
    //$('#results_page').append($('#waiting_screen'))
  }

  // Mobile specifics
  do_mobile_specific_updates()

});

$(document).bind('updated_fields', function() {
  if (!(IS_MOBILE_DEVICE)) {
    $('.sequence').each(function() {
      if ($(this).find('.event:visible').length <= 2) {
        $(this).find('.event_add_link').css('margin-top', 50)
      } else {
        $(this).find('.event_add_link').css('margin-top', 0)
      }

    })
  }
});


$(document).bind('parameters_got_updated', function() {

  if (IS_MOBILE_DEVICE) {

    if (SUBMIT_TOOLTIP_HAS_BEEN_SHOWN || (typeof in_results_view === 'undefined') || in_results_view == false) {
      return
    }

    if (IS_MOBILE_DEVICE) {
      submit_button = $('#results_page .go_button')
      submit_button.show()
      lmy = 'right top'
      lat = 'right bottom'
    } else {
      submit_button = $('#solution_submit')
      lmy = 'center top'
      lat = 'center bottom'
    }

    go_position = submit_button.position()
    //$('#submit_tooltip').dialog({ position: [go_position.left + 70, go_position.top + 100], draggable: false, resizable: false, dialogClass: "submit_button_dialog"});
    $('#submit_tooltip').dialog({
      draggable: false,
      resizable: false,
      width: 50,
      height: 90,
      dialogClass: "submit_button_dialog"
    }).dialog('widget').position({
      my: lmy,
      at: lat,
      of: submit_button
    });

    if (IS_ANDROID_DEVICE) {
      $('#submit_tooltip').dialog().click(function() {
        $(this).close()
      })
    }


    SUBMIT_TOOLTIP_HAS_BEEN_SHOWN = true
    submit_button = null

  }

});


show_inline_tips = function() {

  if ($('.post_it').length > 0) {
    return;
  }

  var lmy = 'center top'
  var lat = 'center bottom'
  var target = $('.show_all_possibilities_link').first()
  $('.inline_tips .show_all_possibilities_tip').dialog({
    draggable: false,
    resizable: false,
    dialogClass: "post_it rotate_right",
    height: 'auto',
    width: 200
  }).dialog('widget').position({
    my: lmy,
    at: lat,
    of: target,
    offset: "0 0"
  });

  lmy = 'left top'
  lat = 'right bottom'
  target = $('.sequence_proposals .pre_select_button').first()
  $('.inline_tips .padlock_tip').dialog({
    draggable: false,
    resizable: false,
    dialogClass: "post_it rotate_right",
    height: 'auto',
    width: 200
  }).dialog('widget').position({
    my: lmy,
    at: lat,
    of: target,
    offset: "0 0"
  });

  //lmy = 'right center'
  //lat = 'left top'
  //target = $('.event').first()
  //$('.inline_tips .activities_type_tip').dialog({ draggable: false, resizable: false, dialogClass: "post_it", height: 'auto', width: 200}).dialog('widget').position({ my: lmy, at: lat, of: target, offset:"40 60" });

  //lmy = 'center top'
  //lat = 'center bottom'
  //target = $('.event_add_link').first()
  //$('.inline_tips .activities_tip').dialog({ draggable: false, resizable: false, dialogClass: "post_it rotate_left", height: 'auto', width: 100}).dialog('widget').position({ my: lmy, at: lat, of: target, offset:"0 40" });

  lmy = 'center top'
  lat = 'left bottom'
  target = $('.form_submit_button input').first()
  $('.inline_tips .submit_tip').dialog({
    draggable: false,
    resizable: false,
    dialogClass: "post_it rotate_left",
    height: 'auto',
    width: 230
  }).dialog('widget').position({
    my: lmy,
    at: lat,
    of: target,
    offset: "0 0"
  });


}

do_mobile_specific_updates = function() {
  if (IS_MOBILE_DEVICE) {
    //$('.input_for_datepicker').attr('readonly', 'readonly')

/*
		setTimeout(function(){
		    // Hide the address bar!
		    window.scrollTo(0, 1);
		  }, 50);
		*/
  }

  IDEAL_NUMBER_OF_TIMELINE_DIVS = 3

}

create_selectBoxes = function(el) {

  //return false

  if (USING_SELECTBOX) {
    if (el === undefined) {
      lselects = $("select")

      //lselects = lselects.not('#directions_travel_mode')
    } else {
      lselects = $(el).find("select")
    }
    lselects.each(function(idx, lselect) {
      $(lselect).selectBox() // Create a select box for each select

      //$(lselect).selectBox('control').attr('name', $(lselect).attr('name')) // Matching the names

      //$(lselect).selectBox('control').addClass($(lselect).attr('class')) // Gives to the select box 
      // (an anchor tag in fact) the same class as for the original select.

      // The role of the below is to ensure that when the real select is
      // changed (say via javascript), this is repercuted on the selectBox itself.
      //$(lselect).change(function(event){
      $(lselect).bind('update_dependents', function(event) {
        try {
          $(lselect).selectBox('value', $(this).val());
        } // For SelectBox
        catch (err) {}
      })
    })
  } else {
    if (el === undefined) {
      lselects = $("select")

      //lselects = lselects.not('#directions_travel_mode')
    } else {
      lselects = $(el).find("select")
    }
    lselects.each(function(idx, lselect) {
      $(lselect).bind('update_dependents', function(event) {
        if (!($(lselect).is(':data(selectmenu)'))) {
          $(lselect).selectmenu();
        }
        $(lselect).selectmenu('refresh'); // For JqueryMobile
      })
    })
  }
}

update_select_box = function(jq_element) {
  //return false

  if (USING_SELECTBOX) {
    try {
      jq_element.selectBox('destroy');
    } catch (err) {}
    jq_element.selectBox();
    //jq_element.selectBox('control').attr('name', jq_element.attr('name')) // Matching the names
  }
}


////////////


mark_non_available_elements = function(jq_sequence_main_container, pvalid_arrangements_array) {

  psequence_id = jq_sequence_main_container.attr('id').replace(/sequence__/, '')

  //sequence = $('#sequence__'+psequence_id)
  // We can't do sequence.find to find providers: when they are shown in a dialog, they are taken out of the normal DOM.
  providers = $('.provider[id*="sequence__' + psequence_id + '"]')
  providers.addClass('invalid').removeClass('valid')

  already_done = []
  $(pvalid_arrangements_array).each(function(idx, lhash) { //TODO: instead of going through all arrangements, we should first
    // return only the different ones in the hash.
    $(lhash['arrangement']).each(function(idx, a) {
      if (already_done.indexOf(a['event_id'] + '__' + a['provider_id']) < 0) {
        //$('.provider[id*="sequence__' + psequence_id + '__event__' + a['event_id'] + '__provider__' + a['provider_id'] + '"]').addClass('valid').removeClass('invalid')

        //providers.filter(function(){ return (this.id.search('sequence__' + psequence_id + '__event__' + a['event_id'] + '__provider__' + a['provider_id']) >= 0) }).addClass('valid').removeClass('invalid')
        providers.filter('[id*="sequence__' + psequence_id + '__event__' + a['event_id'] + '__provider__' + a['provider_id'] + '"]').addClass('valid').removeClass('invalid')

        already_done.push(a['event_id'] + '__' + a['provider_id'])
      }
    })

  })

}


update_map = function(pproposal_container, pmap_container) {
  //////////////////////////////////////////////////////////////////////////////
  // Maps representation
  vproviders_list = pproposal_container.find('.activities').first().find('.provider')
  maps_points = []
  vproviders_list.each(function(idx, e) {
    llatitude = $(e).find('.latitude').first().html()
    llongitude = $(e).find('.longitude').first().html()
    lhtml_data = $(e).find('.provider_name').first().clone().html() //e.down('.provider_data')//e.down('.address').innerHTML
    //alert(laddress);
    maps_points.push({
      'latitude': llatitude,
      'longitude': llongitude,
      'html_data': lhtml_data
    })
  });
  //prepare_map(maps_points, pmap_container)

  if (maps_points.length <= 1) {
    pmap_container.find('.travel_mode').first().hide()
    pmap_container.find('.directionsPanel').first().hide()
  }

  //render_map(maps_points, pmap_container.find('#map_canvas')[0], pmap_container.find('#directions_travel_mode').first().val())
  render_map_generic(pmap_container.find('.map_canvas')[0], null, maps_points, true, pmap_container.find('.directionsPanel')[0], pmap_container.find('.directions_travel_mode').first().val())
}

display_map_with_marker = function(el, pmap_container) {
  //alert(el)

  vprovider = el //.parents('.provider').first()

  //alert(vprovider)

  llatitude = vprovider.find('.latitude').first().html()
  llongitude = vprovider.find('.longitude').first().html()
  lhtml_data = vprovider.clone().html()

  larray = [];
  larray.push({
    'latitude': llatitude,
    'longitude': llongitude,
    'html_data': lhtml_data
  })

  ////display_markers(larray, pmap_container[0]);
  render_map_generic(pmap_container[0], null, larray, false)
}

//attach_tip = function(el,target_content) {
//	ltip = new Tip(el, $(target_content), {
//					//title: "Click",
//					closeButton: true,
//					showOn: 'click',
//					hideOn: { element: 'closeButton', event: 'click'},
//					//stem: 'bottomMiddle',
//					//hook: { target: 'topMiddle', tip: 'bottomMiddle' },
//					viewport: true,
//					offset: { x: 0, y: -2 },
//					width: 'auto'
//				});
//	el.prototip.show();
//}


// This function takes our available_arrangements hash and removes duplicates - duplicates being two proposed arrangements
// with the same Prov/TS. Indeed, they might only differ by their AIs, but for the view we sometimes need to remove 
// these duplicates at Prov/TS level.
// Example: parray = [
// [{"time_slot_id":269983,"provider_id":51636,"event_id":3257,"availability_information_id":684057},{"time_slot_id":510786,"provider_id":52862,"event_id":3258,"availability_information_id":659899}],
// [{"time_slot_id":269986,"provider_id":51636,"event_id":3257,"availability_information_id":684057},{"time_slot_id":510786,"provider_id":52862,"event_id":3258,"availability_information_id":659899}]
// ];
clean_duplicates = function(parray) {

  for (i = 0; i < parray.length; i++) {
    for (j = i + 1; j < parray.length; j++) {
      is_equal = true
      array_i = parray[i]['arrangement']
      array_j = parray[j]['arrangement']
      for (k = 0; k < array_i.length; k++) {
        if (array_j[k]['time_slot_id'] != array_i[k]['time_slot_id']) {
          is_equal = false
        }
      }
      if (is_equal) {

        // We assign the minimum price to the arrangement we keep
        if (parray[i]['price']['value'] > parray[j]['price']['value']) {
          parray[i]['price']['value'] = parray[j]['price']['value']
        }


        parray.splice(j, 1)
        j--
      }
    }
  }
  return parray

}


pre_selection_button_touched_tentative = function(el, is_initiator, update_pre_selection) {
  if (IS_MOBILE_DEVICE) {
    $.mobile.showPageLoadingMsg();
    pre_selection_button_touched_s(el, is_initiator, update_pre_selection)
    $.mobile.hidePageLoadingMsg();
  } else {
    pre_selection_button_touched_s(el, is_initiator, update_pre_selection)
  }
}

pre_selection_button_touched = function(el, is_initiator, update_pre_selection) {
  if (is_initiator === undefined) {
    is_initiator = false
  } else {

  }

  if (update_pre_selection === undefined) {
    update_pre_selection = true
  }
  if (el[0].tagName.toLowerCase() == 'input') {
    if (!is_initiator) {
      el[0].checked = !(el[0].checked)
    }
    //temp = el.next()
    temp = el.parents('.pre_select_button')
    if (temp.attr('class').match('padlock_open') != null) {
      temp.removeClass('padlock_open').addClass('padlock_close')
    } else {
      temp.removeClass('padlock_close').addClass('padlock_open')
    }

    if (update_pre_selection) {
      pre_selection_change(el);
    }
  }
  if ((el.attr('class') != undefined) && (el.attr('class').match('pre_select_button') != null)) {
    //pre_selection_button_touched(el.prev())
    pre_selection_button_touched(el.find('input'))
  }


  //if (IS_MOBILE_DEVICE){
  //	$.mobile.changePage( "#results_page");
  //}
  if (is_initiator == true) {
    $(document).trigger('parameters_got_updated')
    if (IS_MOBILE_DEVICE) {
      mobile_scroll_to_top($('#results_page_content'))
    }
  }

}

mobile_scroll_to_top = function(jqel) {
  if ($.support.touchOverflow) {
    jqel.animate({
      scrollTop: 40
    }, 1000)
  } else {
    $.mobile.silentScroll(40)
  }
}

pre_selection_change = function(el, called_recursively) {

  //////////////////////////////////////////////////////////////////////////////
  // In this first part, we simply make sure that a user can't select
  // several concurrent proposals (e.g. 2 different providers for a same event)
  elements = el[0].name.match(/(\w+)/g)
  caller_type = elements[0]


  // TODO: Not used for now but KEEP. Needs optimisation, cf case provider just below
/*
	if (caller_type == "time_slot") {
		caller = el.parents('.time_slot.possibility').first()
		vtime_slots = []
		
		vtime_slots = el.parents('.provider.possibility').first().find('.time_slot.possibility')
		//alert(vtime_slots.length)
		for(i=0; i < vtime_slots.length; i++) {
			//alert(4)
			if ($(vtime_slots[i]) != caller) {
				//alert(5)
				pre_select_buttons = []
				pre_select_buttons = $(vtime_slots[i]).find('.pre_select_button')
				for (j=0; j < pre_select_buttons.length; j++) { //TODO: there should be only one. No need for loop.
				//	alert(6)
				//	alert(pre_select_buttons[j])
				//	alert(pre_select_buttons[j].id)
					$(pre_select_buttons[j]).find('input').first().attr('checked', false)
					// If the checkbox is followed by an image lock.
					img = $(pre_select_buttons[j]).find('input').first().next()
					if ((img != null) && (img[0].tagName.toLowerCase() == 'img')) {
						img[0].src = '/images/padlock_open.png'
					}
					
				}
			}
		}
		caller_type = "provider"
	}
	*/

  caller_id = el.parents('.provider.possibility').first().attr('id')
  if (caller_type == "provider") {
    //caller_id = el.parents('.provider.possibility').first().attr('id')
    event_proposals_container = el.parents('.event_proposals').first()
    event_proposals_container.find('.provider.possibility:not(#' + caller_id + ') .pre_select_button input').attr('checked', false)
    event_proposals_container.find('.provider.possibility:not(#' + caller_id + ') .pre_select_button').removeClass('padlock_close').addClass('padlock_open')
  }
  // Old method
/*
		if (caller_type == "provider") {
			//alert(1)
			caller = el.parents('.provider.possibility').first()
			vproviders = []
			event_proposals_container = el.parents('.event_proposals').first()
			if (event_proposals_container.length > 0) { // If the pre-select happened in a sequence_individual_proposal, there is no
				// need to "un-check" other providers - there is only one per proposal and event.
				vproviders = event_proposals_container.find('.provider.possibility')// CHANGED FOR AJAX vproviders = el.up('.event_proposals').select('.provider.possibility')
			}
			for (i=0; i < vproviders.length; i++) {
				//alert(2)
				if (vproviders[i] != caller[0]) {
					//alert(3)
					pre_select_buttons = []
					pre_select_buttons = $(vproviders[i]).find('.pre_select_button')
					for (j=0; j < pre_select_buttons.length; j++) {
						$(pre_select_buttons[j]).find('input').first().attr('checked', false)
						// If the checkbox is followed by an image lock.
						//img = $(pre_select_buttons[j]).find('input').first().next()
						img = $(pre_select_buttons[j]).find('input').first().parents('.pre_select_button').find('img')
						if ((img[0] != null) && (img[0].tagName.toLowerCase() == 'img')) {
							img[0].src = '/images/padlock_open.png'
						}
					}
				}
			}
		}
		*/



  // In this second part, we deal with the following problem:
  // a vprovider can be in two different places in the page: in event_proposals and in
  // sequence_proposals.
  // So if the user pre-select a provider or ts in one of the sections, we need to replicate
  // in the other section.
  //need_visual_and_model_update = false
  if (called_recursively === undefined) { // There are two sections. So we set this to true when
    // calling the method after having set the elements of one of the two sections.
    called_recursively = false
    //need_visual_and_model_update = true
  }

  if (!called_recursively) {
    //caller_id = el.parents('.possibility').first().attr('id')

    // In the proposals section, the provider possibility IDs have been appended with __duplicate.
    caller_id = caller_id.replace(/__duplicate/, '')


    switch (elements[0]) {
    case "time_slot":
      comparables = $(".time_slot[id^='" + caller_id + "']")
      break;
    case "provider":
      comparables = $(".provider[id^='" + caller_id + "']")
      comparables.each(function(idx, e) {
        temp = $(e).find('input').first()
        if ((temp.length > 0) && (temp[0] != el[0])) {
          temp.attr('checked', !(temp.attr('checked')))
          img = temp.parents('.pre_select_button')
          if (img.attr('class').match('padlock_open') != null) {
            img.removeClass('padlock_open').addClass('padlock_close')
          } else {
            img.removeClass('padlock_close').addClass('padlock_open')
          }
          pre_selection_change(temp, true)
        }
      })
      break;
    }

    // Old method
/*
		if (elements[0] == "time_slot") {
			comparables = $('.time_slot') // We can't search by ID since several elements can
			// have the same id. It seems to break the $$ function.
			// (TODO: several divs with the same ID is probably bad...)
		}
		else {
			comparables = $('.provider')
		}
		
		comparables.each(function(idx, e) {
			temp = $(e).find('input').first()
			if ( (temp.length != 0) && (e.id.replace(/__duplicate/,'') == caller_id) && (temp[0] != el[0]) ) {
				if ((temp.attr('checked') == true) || (temp.attr('checked') == 'checked')) {
					temp.attr('checked', false)
					// If the checkbox is followed by an image lock.
					//if (((temp.next()).length > 0) && ((temp.next())[0].tagName.toLowerCase() == 'img')) {
					//	(temp.next())[0].src = '/images/padlock_open.png'
					//}
					temp.parents('.pre_select_button').find('img').attr('src', '/images/padlock_open.png')
					
				}
				else {
					temp.attr('checked', true)
					// If the checkbox is followed by an image lock.
					//if (((temp.next()).length > 0 ) && ((temp.next())[0].tagName.toLowerCase() == 'img')) {
					//	(temp.next())[0].src = '/images/padlock_close.png'
					//}
					temp.parents('.pre_select_button').find('img').attr('src', '/images/padlock_close.png')
				}
				pre_selection_change($(e).find('input').first(), true)
			}
		});
		*/

    //////////////////////////////////////////////////////////////////////////////
    // In this part, we update the corresponding real models accordingly.
    update_corresponding_model(el)
    ////visual_update(el)

    // Find sequence_id
    sequence_id = caller_id.match(/sequence__[0-9]*/g)[0].replace('sequence__', '')
    // Old method
/*
			full_id = el.parents('.possibility').first().attr('id')
			id_infos = full_id.split(/__/g) // [sequence,1545,event,3461,provider,51636]
			sequence_id = null
			for (i=0; i < id_infos.length; i++) {
				if (id_infos[i] == "sequence") {
					sequence_id = id_infos[i+1]
					break;
				}
			}
			*/

    parray = proposed_arrangements[sequence_id]

    render_sequence_proposals(sequence_id, parray)

  }


}

// This is called from pre_selection_change: the user has pre-selected a VProvider or VTime_Slot.
// We need to make sure it is reflected in the real_event of the solution.
// For example: if a VProvider is pre-selected, we need to ensure that real_event.provider_id is set
// to the VProvider id.
update_corresponding_model = function(el) {

  //alert(el.name + ' '+ el.id)

  // Ex: sequence__1545__event__3461__provider__51636__time_slot__269986
  // or: sequence__1545__event__3461__provider__51636
  full_id = el.parents('.possibility').first().attr('id')
  id_infos = full_id.split(/__/g) // [sequence,1545,event,3461,provider,51636]
  sequence_id = null
  event_id = null
  for (i = 0; i < id_infos.length; i++) {
    if (id_infos[i] == "sequence") {
      sequence_id = id_infos[i + 1]
    }
    if (id_infos[i] == "event") {
      event_id = id_infos[i + 1]
      break;
    }
  }

  //temp = "sequence__"+sequence_id
  //temp3 = $(temp)
  real_event = $("#sequence__" + sequence_id + " .events #sequence__" + sequence_id + "__event__" + event_id).first()
  // Old method
  //real_event = $("#sequence__"+sequence_id).first().find('.events').first().find("#sequence__"+sequence_id+"__event__"+event_id).first()

  // First we "clean" the real event
  real_event.find('.event_provider_id input').first().val('')
  real_event.find('.event_start_date input').first().val('')
  real_event.find('.event_end_date input').first().val('')
  // Old method
  //real_event.find('.event_provider_id').first().find('input').first().val('')
  //real_event.find('.event_start_date').first().find('input').first().val('')
  //real_event.find('.event_end_date').first().find('input').first().val('')



  // The "caller" el might be in any of the two sections. But we will always use its occurence
  // in the event_proposals section. Indeed, we know it will always be present at least in this section. On the other side
  //  we can't guarantee that it will be present in the sequence_individual_proposals section.
  selected_vprovider = $(".event_proposals" + "#sequence__" + sequence_id + "__event__" + event_id + "__proposals " + ".provider.possibility .from_provider.pre_select_button input:checked").parents('.provider.possibility')
  if (selected_vprovider.length > 0) {
    real_event.find('.event_provider_id input').first().val(selected_vprovider.find('.provider_id').first().html())
  }

  selected_vtime_slot = $(".event_proposals" + "#sequence__" + sequence_id + "__event__" + event_id + "__proposals " + ".time_slot.possibility .from_time_slot.pre_select_button input:checked").parents('.time_slot.possibility')
  if (selected_vtime_slot.length > 0) {
    real_event.find('.event_start_date input').first().attr('value', selected_vtime_slot.find('.start_date').first().html())
    real_event.find('.event_end_date input').first().attr('value', selected_vtime_slot.find('.end_date').first().html())
  }

  // Old method
/*
		event_proposals_containers = $('.event_proposals')
		event_proposals_containers.each(function(idx, e) {
			if (e.id == ("sequence__"+sequence_id+"__event__"+event_id+"__proposals") ) {
				event_proposals_container = e
				return false // break
			}
		}); 
	
		// TODO handle case event_proposals_container is null
		if (event_proposals_container == null) {
			return
		}
	
		vproviders = null
		vproviders = $(event_proposals_container).find('.provider.possibility')
		selected_vprovider = null
		vproviders.each(function(idx, e) {
			if (($(e).find('.from_provider.pre_select_button').first().find('input').first().attr('checked') == true) || ($(e).find('.from_provider.pre_select_button').first().find('input').first().attr('checked') == 'checked')) {
				selected_vprovider = e
				return false // break
			}
		});
		if (selected_vprovider != null) {
			real_event.find('.event_provider_id').first().find('input').first().val($(selected_vprovider).find('.provider_id').first().html())
		}
	
		vtime_slots = null
		vtime_slots = $(event_proposals_container).find('.time_slot.possibility')
		selected_vtime_slot = null
		vtime_slots.each(function(idx, e) {
			if (($(e).find('.from_time_slot.pre_select_button').first().find('input').first().attr('checked') == true) || ($(e).find('.from_time_slot.pre_select_button').first().find('input').first().attr('checked') == 'checked')) {
				selected_vtime_slot = e
				return false
			}
		});
		if (selected_vtime_slot != null) {
			real_event.find('.event_start_date').first().find('input').first().attr('value', $(selected_vtime_slot).find('.start_date').first().html())
			real_event.find('.event_end_date').first().down('input').first().attr('value', $(selected_vtime_slot).find('.end_date').first().html())
		}
		*/

}


adapt_available_arrangements_based_on_preselections = function(sequence) {

  // NOTE: sequence is a JQ object (the jq_main_sequence_container)
  sequence_id = sequence.attr('id').replace(/sequence__/, '')

  // Here, we scan the page to find all the event_proposals containers corresponding to the sequence 
  // being dealt with.
  events_proposals = []
  temp = $('.event_proposals')
  i = 0
  temp.each(function(idx, e) { // We can't just do events = sequence.select('.event_proposals') since the overlay
    // takes the event_proposals div outside of the normal DOM
    if ((e.id.match(/sequence__[0-9]*/g) != null) && (e.id.match(/sequence__[0-9]*/g)[0].replace(/sequence__/, '') == sequence_id)) {
      events_proposals.push(e)
    }
  });

  currently_pre_selected = []
  currently_disliked_providers = []
  for (i = 0; i < events_proposals.length; i++) {

    event_id = null
    event_type = null
    event_sub_type = null // not used yet
    event_sub_sub_type = null // not used yet
    provider_id = null
    time_slot_id = null
    new_hash = {};

    levent = $(events_proposals[i])

    event_id = levent.attr('id').replace(/.*event__/, '').replace(/__proposals/, '')

    // The event_proposals container doesn't have the event_type, sub_type etc information.
    // This info is only with the "real event". Therefore here we look in the page for the
    // real event that corresponds to the event_proposals curently worked on.
    real_event = sequence.find('.events').first().find("#sequence__" + sequence_id + "__event__" + event_id).first()
    real_event_type = real_event.find('.event_type').first().find('select').first().val()
    if ((real_event_type == (100000 + '')) || (real_event_type == (-10 + ''))) { // 100000 is "Any", -10 is any "live show"
      event_type = null
    } else {
      event_type = real_event_type
    }
    // TODO: sub_sub_type - but it is not yet implemented
    real_event_sub_type = real_event.find('.event_sub_type').first().find('select').first().val()
    if ((real_event_sub_type == 'Any') || (real_event_sub_type == (-10 + '')) || (real_event_sub_type == '') || (real_event_sub_type == '-')) { // 100000 is "Any"
      event_sub_type = null
    } else {
      event_sub_type = real_event_sub_type
    }


    lpre_selected_providers = levent.find('.provider.possibility .pre_select_button.from_provider input:checked')
    if (lpre_selected_providers.length > 0) { // There should be only one. TODO: handle case several - i.e. error!
      provider_id = lpre_selected_providers.first().parents('.provider.possibility').first().attr('id').replace(/.*provider__/, '')
    }

    new_hash['event_id'] = event_id;
    new_hash['event_type'] = event_type;
    new_hash['event_sub_type'] = event_sub_type;
    new_hash['event_sub_sub_type'] = event_sub_sub_type;
    new_hash['provider_id'] = provider_id;
    new_hash['time_slot_id'] = time_slot_id;

    currently_pre_selected.push(new_hash);

  }


  //TODO remove DEBUG
  //TODO remove DEBUG
  if (false) {
    disp = ''
    $(currently_pre_selected).each(function(idx, e) {
      disp += '['
      //e.each(function(e2) {
      disp += '{ ' + debug_display_hash(e) + ' }'
      //});
      disp += ']'
    });
    disp2 = '[ '
    $(currently_disliked_providers).each(function(idx, e) {
      disp2 += e
      disp2 += ' '
    });
    disp2 += ']'
    //alert(disp + disp2)
  }
  /////////////////

  //for (i=0; i < currently_pre_selected.length; i++) {
  //	alert(debug_display_hash(currently_pre_selected[i]))
  //}

  //////////////////////////////////////////////////////////////////////////////
  // In this 3rd part, we compare the current pre-selection to the recommended assortments
  // proposed_arrangements = {'seq_id': [ [{ev_id, prov_id, ts_id, ai_id, bl}, {}...], [], ... ], 'seq_id' : ...}
  keep_available = []
  l_proposed_arrangements = proposed_arrangements[sequence_id]

  // Debug
  if (false) {
    dbg_string = ""
    $(l_proposed_arrangements).each(function(idx, elem) {
      dbg_string += ("//" + elem['arrangement'][0]['provider_id'] + "--" + elem['arrangement'][1]['provider_id'])
    });
    alert(dbg_string);
  }


  $(l_proposed_arrangements).each(function(idx, elem) {
    //alert(elem)
    if (is_included(elem['arrangement'], currently_pre_selected) && does_not_contain_a_disliked_provider(elem['arrangement'], currently_disliked_providers)) {
      keep_available.push(elem)
    }
  });

  //alert('1081')

  global_pre_selected_elements[sequence_id] = currently_pre_selected

  //alert('1085')

  return keep_available
}



// Internal method
is_included = function(elem, target) { // Returns true if elem 'is included in' target.
  // By 'is included' we mean: {1, 2, 3} is included in {1, null, null} but it is not included in {2, 2, null}
  // Note that in fact, elem and target are arrays of hashes.
  // NOTE/TODO: elem and target are both supposed to be hashes. For some reasons, elem is not a 'real' prototype hash, i.e.
  // it doesn't respond to get and set. I guess it comes from the fact elems are in fact trasnfered from the server, using to_json.
  // In the process it becomes an array [ 'bla': 'tata'] instead of a real hash.

  if (target.length == 0) {
    return true
  }

  to_return = false

  // We don't use iterators here because they are difficult to "break" properly.
  for (i = 0; i < elem.length; i++) {
    elem_h = elem[i]

    //	internals = $$('.provider__' + elem_h['provider_id'] + '__internals').first()
    //	elem_h['event_type'] = internals.getAttribute('name').match(/provider_type__[0-9]*/g)[0].gsub(/provider_type__/,'')
    //	elem_h['event_sub_type'] = internals.getAttribute('name').match(/provider_sub_type__[0-9]*/g)[0].gsub(/provider_sub_type__/,'')
    //	elem_h['event_sub_sub_type'] = internals.getAttribute('name').match(/provider_sub_sub_type__[0-9]*/g)[0].gsub(/provider_sub_sub_type__/,'')

    elem_h['event_type'] = providers_internals[elem_h['provider_id'] + ''][0]
    elem_h['event_sub_type'] = providers_internals[elem_h['provider_id'] + ''][1]


    for (j = 0; j < target.length; j++) {
      target_h = target[j]
      if (elem_h['event_id'] == target_h['event_id']) {
        if ((target_h['event_type'] == null || target_h['event_type'] == elem_h['event_type']) && (target_h['event_sub_type'] == null || target_h['event_sub_type'] == elem_h['event_sub_type']) && (target_h['event_sub_sub_type'] == null || target_h.get('event_sub_sub_type') == elem_h['event_sub_sub_type']) && (target_h['time_slot_id'] == null || target_h['time_slot_id'] == elem_h['time_slot_id']) && (target_h['provider_id'] == null || target_h['provider_id'] == elem_h['provider_id'])) {
          to_return = true
          break;
        } else {
          return false
        }
      }
    }

  }

  return to_return

}

// Internal method
does_not_contain_a_disliked_provider = function(elem, currently_disliked_providers) {
  for (i = 0; i < elem.length; i++) {
    elem_h = elem[i]
    if (currently_disliked_providers.indexOf(elem_h['provider_id'] + "") >= 0) {
      return false; // This elem contains a provider that is disliked
    }
  }
  return true;
}

dislike_status_change = function(el) {

  full_id = el.parents('.possibility').first().id
  id_infos = full_id.split(/__/g) // [sequence,1545,event,3461,provider,51636]
  sequence_id = null
  event_id = null
  for (i = 0; i < id_infos.length; i++) {
    if (id_infos[i] == "sequence") {
      sequence_id = id_infos[i + 1]
    }
    if (id_infos[i] == "event") {
      event_id = id_infos[i + 1]
      break;
    }
    //if (id_infos[i] == "provider") {
    //	provider_id = id_infos[i+1]
    //	break;
    //}
  }

  sequence = $('#sequence__' + sequence_id) // We need to scan the entire document because anything that is
  // in an overlay is taken out of the standard DOM structure (i.e. up, down do not work).

  events = []
  temp = $('.event_proposals')
  temp.each(function(idx, e) { // We can't just do events = sequence.select('.event_proposals') since the overlay
    // takes the event_proposals div outside of the normal DOM
    if (e.id.match(/sequence__[0-9]*/g)[0].replace(/sequence__/, '') == sequence_id) {
      events.push(e)
    }
  });
  //alert('events '+ events.length)

  provider = el.parents('.provider.possibility').first()
  dislike_button = provider.find('.dislike_button.from_provider').first().find('input').first()
  if (dislike_button == true) {
    provider.removeClass('valid')
    provider.addClass('invalid')
  } else {
    provider.removeClass('invalid')
    provider.addClass('valid')
  }



  provider_id = provider.id.replace(/__duplicate/, '') // We can keep the full DOM id here
  ////display_containers = (sequence.select('.sequence_individual_proposal')).add(events)
  //alert(display_containers.length)
  ip = sequence.find('.sequence_individual_proposal').first()
  for (i = 0; i < events.length; i++) {
    ip.add(events[i]);
  }


  display_containers.each(function(idx, e) {
    comparable_provider = e.find("#" + provider_id)[0] || e.find("#" + provider_id + "__duplicate")[0]
    if ((comparable_provider != null) && (comparable_provider != provider)) {
      dislike_button = comparable_provider.find('.dislike_button.from_provider').first().find('input').first()
      if (dislike_button.checked == true) {
        dislike_button.checked = false
        comparable_provider.removeClass('invalid')
        comparable_provider.addClass('valid')

      } else {
        dislike_button.checked = true
        comparable_provider.addClass('invalid')
        comparable_provider.removeClass('valid')
      }

    }
  });

  //visual_update(el)
  parray = proposed_arrangements.get(sequence_id)
  render_sequence_proposals(sequence_id, parray)

}

generate_options_for_type_select = function(el, default_value) {
  //alert('coucou')
  //options = el.childElements();
  //options.each(function(loption) {
  //	loption.remove();
  //});

  //el.children().each( function(idx,loption){
  //	loption.remove();
  //});
  el.children().remove()

  $(available_provider_types).each(function(idx, ltype) {

    el.append($('<option/>', {
      'value': ltype[1]
    }).html(ltype[0]));
  });


}

generate_options_for_sub_type_select = function(el, parent_type) {
  //options = el.childElements();
  //options.each(function(loption) {
  //	loption.remove();
  //});
  el.children().remove()

  corresponding_sub_types = available_provider_sub_types[parent_type]
  $(corresponding_sub_types).each(function(idx, ltype) {
    temp = $('<option/>', {
      'value': ltype[1]
    }).html(ltype[0])
    //alert(temp.value)
    if (temp.html() == 'Any') {
      temp.attr('selected', true); // We pre-select "Any".
    }
    el.append(temp);

  });

  //el.selectBox('destroy');
  //el.selectBox();
  update_select_box(el);
  el.trigger('update_dependents') //ADDED with jquerymobile


  if (USING_SELECTBOX) {
    el.data('current', el.selectBox('value'));
  } else {
    el.data('current', el.val());
  }

}

sort_by_score = function(a, b) {
  //return ((b['score'] + 0.0) - (a['score'] + 0.0))
  return b['score'] - a['score']
}

sort_by_starting_time = function(a, b) {
  //return ((a['begin_time'] + 0.0) - (b['begin_time'] + 0.0))
  return a['begin_time'] - b['begin_time']
}

sort_by_price = function(a, b) {
  //return ((a['price']['value'] + 0.0) - (b['price']['value'] + 0.0))
  return a['price']['value'] - b['price']['value']
}

date_range_preselected = function(el) {

  today = new Date()
  switch (el.value) {
  case 'tonight':
    start = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 18, 0, 0, 0)
    end = new Date()
    end.setHours(start.getHours() + 14)
    end.setMinutes(0)
    end.setSeconds(0)
    end.setMilliseconds(0)
    break;
  case 'friday_evening':
    start = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 18, 0, 0, 0)
    start.setDate(start.getDate() + (7 + 5 - start.getDay()) % 7) // 5 for Friday
    end = new Date(start)
    end.setHours(start.getHours() + 10)
    break;
  case 'saturday':
    start = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 10, 0, 0, 0)
    start.setDate(start.getDate() + (7 + 6 - start.getDay()) % 7)
    end = new Date(start)
    end.setHours(start.getHours() + 16)
    break;
  case 'sunday':
    start = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 10, 0, 0, 0)
    start.setDate(start.getDate() + (7 + 0 - start.getDay()) % 7)
    end = new Date(start)
    end.setHours(start.getHours() + 16)
    break;
  default:
    //TODO error. Default to friday evening
    start = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 18, 0, 0, 0)
    start.setDate(start.getDate() + (7 + 5 - start.getDay()) % 7) // 5 for Friday
    end = new Date(start)
    end.setHours(start.getHours() + 10)

  }

  hidden_dates_selector = el.parents('.sequence_parameters').first().find('[type="hidden"]')
  hidden_dates_selector.each(function(idx, input) {
    if (input.id.include('start_date')) {
      input.value = start.toISOString();
      return
    }
    if (input.id.include('end_date')) {
      input.value = end.toISOString();
      return
    }
  });

  date_picker_values = el.parents('.sequence_parameters').first().find('.manual_dates_selector').first().find('[type="text"]')
  date_picker_values.each(function(idx, input) {
    if (input.id.search(/start_date/) >= 0) {
      temp = start.toISOString().split("T")[0].split("-")
      //input.value = start.getDate() + "-" + (start.getMonth() + 1) + "-" + start.getFullYear()
      input.value = temp[2] + "-" + temp[1] + "-" + temp[0]
      return
    }
    if (input.id.search(/end_date/)) {
      temp = end.toISOString().split("T")[0].split("-")
      //input.value = start.getDate() + "-" + (start.getMonth() + 1) + "-" + start.getFullYear()
      input.value = temp[2] + "-" + temp[1] + "-" + temp[0]
      return
    }
  });

  time_picker_selects = el.parents('.sequence_parameters').first().find('.manual_dates_selector').first().find('select')
  time_picker_selects.each(function(idx, lselect) {
    if (lselect.id.search(/start_date/) >= 0) {
      options = lselect.find('option')
      options.each(function(idx2, loption) {
        ltime = start.toISOString().split("T")[1].replace(/[A-Za-z]/, "").substring(0, 5)
        if (loption.attr('value') == ltime) {
          loption.selected = true;
          return false;
        }
      });
      return
    }
    if (lselect.id.search(/end_date/) >= 0) {
      options = lselect.find('option')
      options.each(function(idx2, loption) {
        ltime = end.toISOString().split("T")[1].replace(/[A-Za-z]/, "").substring(0, 5)
        if ($(loption).attr('value') == ltime) {
          loption.selected = true;
          return false;
        }
      });
      return
    }


  });

}



generate_sorter_selector = function(sequence_id, pre_selected) {

}

generate_date_preselectors = function(sequence_id, pre_selected) {
  possibilities = ["now", "tonight", "tomorrow", "friday night", "saturday lunch", "saturday night", "sunday lunch"]
  selectors_and_time_values = {};
  now = new Date();
  startHourTonight = 0;
  endHourTonight = 0;
  selectors = []


  switch (now.getDay()) {
  case 1:
  case 2:
    selectors.push(possibilities[0], possibilities[1], possibilities[2], possibilities[3], possibilities[4], possibilities[5], possibilities[6]);
    startHourTonight = 18;
    endHourTonight = 2;
    startHourTomorrow = 18;
    endHourTomorrow = 2;
    break;
  case 3:
    selectors.push(possibilities[0], possibilities[1], possibilities[2], possibilities[3], possibilities[4], possibilities[5], possibilities[6]);
    startHourTonight = 18;
    endHourTonight = 2;
    startHourTomorrow = 18;
    endHourTomorrow = 5;
    break;
  case 4:
    // thursday
    selectors.push(possibilities[0], possibilities[1], possibilities[3], possibilities[4], possibilities[5], possibilities[6]);
    startHourTonight = 18;
    endHourTonight = 5;
    startHourTomorrow = 18;
    endHourTomorrow = 5;
    break;
  case 5:
    // friday
    selectors.push(possibilities[0], possibilities[1], possibilities[4], possibilities[5], possibilities[6]);
    startHourTonight = 18;
    endHourTonight = 5;
    startHourTomorrow = 18;
    endHourTomorrow = 5;
    break;
  case 6:
    // saturday
    selectors.push(possibilities[0], possibilities[1], possibilities[6], possibilities[3]);
    startHourTonight = 18;
    endHourTonight = 5;
    startHourTomorrow = 18;
    endHourTomorrow = 2;
    break;
  case 0:
    // sunday
    selectors.push(possibilities[0], possibilities[1], possibilities[2], possibilities[3], possibilities[4], possibilities[5]);
    startHourTonight = 18;
    endHourTonight = 2;
    startHourTomorrow = 18;
    endHourTomorrow = 2;
    break;

  }

  $(possibilities).each(function(idx, p) {
    switch (p) {
    case 'tonight':
      if (now.getHours() < 3) { // before 3am in the morning, we consider tonight as now
        start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), Math.ceil((now.getMinutes() + 15) / 30) * 30, 0, 0);
        end = new Date(start);
        end.setMinutes(start.getMinutes() + 180);
      } else {
        start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), startHourTonight, 0, 0, 0);
        thresholdStart = new Date(start.getTime() - 30 * 60000);
        if (now > thresholdStart) {
          start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), Math.ceil((now.getMinutes() + 15) / 30) * 30, 0, 0);
        } // we need 30 minutes before 1st event
        end = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, endHourTonight, 0, 0, 0);
        thresholdEnd = new Date(start.getTime() + 180 * 60000); // at least 3 hours
        if (thresholdEnd > end) {
          end = new Date(thresholdEnd);
        }
      }
      break;
    case 'tomorrow':
      start = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, startHourTomorrow, 0, 0, 0)
      end = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 2, endHourTomorrow, 0, 0, 0)
      break;
    case 'friday night':
      start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 18, 0, 0, 0)
      start.setDate(start.getDate() + (7 + 5 - start.getDay()) % 7) // 5 for Friday
      if (start.getDate() == now.getDate()) {
        start.setDate(start.getDate() + 7)
      }
      end = new Date(start)
      end.setHours(start.getHours() + 11) // 6pm to 5am
      break;
    case 'saturday lunch':
      start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 11, 0, 0, 0)
      start.setDate(start.getDate() + (7 + 6 - start.getDay()) % 7) // 6 for Saturday
      if (start.getDate() == now.getDate()) {
        start.setDate(start.getDate() + 7)
      }
      end = new Date(start)
      end.setHours(start.getHours() + 4) // 11am to 3pm
      break;
    case 'saturday night':
      start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 18, 0, 0, 0)
      start.setDate(start.getDate() + (7 + 6 - start.getDay()) % 7) // 6 for Saturday
      if (start.getDate() == now.getDate()) {
        start.setDate(start.getDate() + 7)
      }

      end = new Date(start)
      end.setHours(start.getHours() + 11) // 6pm to 5am
      break;
    case 'sunday lunch':
      start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 11, 0, 0, 0)
      start.setDate(start.getDate() + (7 + 0 - start.getDay()) % 7) // 0 for Sunday
      if (start.getDate() == now.getDate()) {
        start.setDate(start.getDate() + 7)
      }

      end = new Date(start)
      end.setHours(start.getHours() + 4) // 11am to 3pm
      break;
    case 'now':
      start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), Math.ceil((now.getMinutes() + 15) / 30) * 30, 0, 0);
      end = new Date(start)
      end.setMinutes(start.getMinutes() + 420)
      break;
    default:
      start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 18, 0, 0, 0)
      start.setDate(start.getDate() + (7 + 5 - start.getDay()) % 7) // 5 for Friday
      end = new Date(start)
      end.setHours(start.getHours() + 11) // 6pm to 5am


    }


    selectors_and_time_values[p] = [start, end]


  })






  sequence = $('#' + sequence_id)
  container = sequence.find('.time_frame').first()
  if (false) { // case individual buttons
    $(selectors).each(function(idx, sel) {
      element = $('<span/>').html(sel + ": ")
      //button = $('<input/>', { 'type': 'radio', 'name': 'when', 'value': sel, 'onclick' : "javascript:update_time_date_picker($(this), '"+ selectors_and_time_values[sel][0].toISOString()+"', '" + selectors_and_time_values[sel][1].toISOString()+ "')" })
      button = $('<input/>', {
        'type': 'radio',
        'name': 'when',
        'value': sel,
        'click': function() {
          update_time_date_picker($(this), selectors_and_time_values[sel][0], selectors_and_time_values[sel][1])
        }
      })
      //button = $('<input/>', { 'type': 'radio', 'name': 'when', 'value': sel, 'click': function(){blabla($(this).attr("value"))} })


      if ((pre_selected != null) && (pre_selected == sel)) {
        button.attr('checked', true);
      }


      //button.onclick = update_time_date_picker(element, selectors_and_time_values.get(sel)[0], selectors_and_time_values.get(sel)[1])
      element.append(button) //.observe('onClick', update_time_date_picker(element, selectors_and_time_values.get(sel)[0], selectors_and_time_values.get(sel)[1])))


      element.append($('<span>&nbsp;&nbsp;</span>'))
      //element.observe('onClick', update_time_date_picker(element, selectors_and_time_values.get(sel)[0], selectors_and_time_values.get(sel)[1]))
      container.append(element);
      //button.onclick = update_time_date_picker(element, selectors_and_time_values.get(sel)[0], selectors_and_time_values.get(sel)[1])

    })
  } else { // case select
    select_element = $('<select/>', {
      'name': 'when'
    })


    temp1 = {} // Careful here: if you call this variable temp, it seems it is being overwritten by another one
    // (cf the fact we use this temp variable in the .change below.). Not sure how to avoid this in JS.
    $(selectors).each(function(idx, sel) {
      //loption = $('<option/>',{ 'value': sel, 'click': function(){update_time_date_picker($(this), selectors_and_time_values[sel][0], selectors_and_time_values[sel][1])} }).html(sel)

      loption = $('<option/>', {
        'value': sel
      }).html(sel)
      temp1[sel] = [selectors_and_time_values[sel][0], selectors_and_time_values[sel][1]]

      if ((pre_selected != null) && (pre_selected == sel)) {
        loption.attr('selected', true);
      }
      select_element.append(loption)
    })

    if (pre_selected == "manual") {
      $('<option/>', {
        'value': pre_selected
      }).html(pre_selected).appendTo(select_element).attr('selected', true)
      select_element.addClass('greyed_out')
    }

    select_element.change(function(event) {
      lval = $(this).children(":selected").attr('value')
      if (lval != "manual") {
        update_time_date_picker($(this), temp1[lval][0], temp1[lval][1])
        $(this).removeClass('greyed_out')
        $(this).find("option[value=manual]").remove()
      }

      update_select_box($(this))
      $(this).trigger('update_dependents') //ADDED with jquerymobile

      render_default_sequence(sequence)

    })

    select_element.bind('deactivateWhenSelect', function(event) {
      if ($(this).find("option[value=manual]").length == 0) {
        $("<option value='manual'>manual</option>").appendTo($(this))
      }
      $(this).addClass('greyed_out').find("option[value=manual]").first().attr('selected', true)

      ////update_select_box(when_select)
      update_select_box($(this))
      $(this).trigger('update_dependents') //ADDED with jquerymobile

      render_default_sequence(sequence)
    })


    container.append(select_element)

  }


}

generate_date_preselectorsORIGINAL = function(sequence_id, pre_selected) {
  possibilities = ["tonight", "tomorrow night", "friday night", "saturday", "sunday", "tomorrow", "now", "next friday", "next saturday", "next sunday"]
  selectors_and_time_values = {};
  now = new Date();
  $(possibilities).each(function(idx, p) {
    switch (p) {
    case 'tonight':
      start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 18, 0, 0, 0)
      if ((now > start) || (now.getHours() < 3)) { // between 0 and 3h in the morning, we consider that
        // tonight is the same night.
        start = new Date(); // now
        if (start.getMinutes() <= 30) {
          start.setMinutes(30)
        } else {
          start.setMinutes(0)
          start.setHours(start.getHours() + 1)
        }
        start.setSeconds(0)
        start.setMilliseconds(0)
      }
      end = new Date()
      if (start.getHours() > 3) { // between 0 and 3h in the morning, we consider that
        // tonight is the same night.
        end.setDate(start.getDate() + 1)
      } else if ((now.getHours() == 23) && (now.getMinutes() > 30)) { // This second case applies if now is between 23.30 and midnight. If it is the case
        // it means "tonight" will start at midnight the "day after".
        end.setDate(start.getDate())
      }
      end.setHours(8)
      end.setMinutes(0)
      end.setSeconds(0)
      end.setMilliseconds(0)
      break;
    case 'tomorrow night':
      start = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 18, 0, 0, 0)
      end = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 2, 8, 0, 0, 0)
      break;
    case 'friday night':
    case 'next friday':
      start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 18, 0, 0, 0)
      start.setDate(start.getDate() + (7 + 5 - start.getDay()) % 7) // 5 for Friday
      if (start.getDate() == now.getDate()) {
        start.setDate(start.getDate() + 7)
      }
      end = new Date(start)
      end.setHours(start.getHours() + 10)
      break;
    case 'saturday':
    case 'next saturday':
      start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 13, 0, 0, 0)
      start.setDate(start.getDate() + (7 + 6 - start.getDay()) % 7)
      if (start.getDate() == now.getDate()) {
        start.setDate(start.getDate() + 7)
      }
      end = new Date(start)
      end.setHours(start.getHours() + 16)
      break;
    case 'sunday':
    case 'next sunday':
      start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 13, 0, 0, 0)
      start.setDate(start.getDate() + (7 + 0 - start.getDay()) % 7)
      if (start.getDate() == now.getDate()) {
        start.setDate(start.getDate() + 7)
      }
      end = new Date(start)
      end.setHours(start.getHours() + 16)
      break;
    case 'tomorrow':
      start = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 10, 0, 0, 0)
      end = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 2, 8, 0, 0, 0)
      break;
    case 'now':
      start = new Date()
      if (start.getMinutes() <= 20) {
        start.setMinutes(30)
      } else {
        start.setMinutes(0)
        start.setHours(start.getHours() + 1)
      }
      start.setSeconds(0)
      start.setMilliseconds(0)

      end = new Date()
      if (end.getHours() > 2) {
        end.setDate(end.getDate() + 1)
      }
      end.setHours(8)
      end.setMinutes(0)
      end.setSeconds(0)
      end.setMilliseconds(0)
      break;
    default:
      //TODO error. Default to friday evening
      start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 18, 0, 0, 0)
      start.setDate(start.getDate() + (7 + 5 - start.getDay()) % 7) // 5 for Friday
      end = new Date(start)
      end.setHours(start.getHours() + 10)

    }

    selectors_and_time_values[p] = [start, end]

  })



  //now = new Date();
  selectors = []
  switch (now.getDay()) {
  case 1:
  case 2:
  case 3:
  case 4:
    selectors.push(possibilities[6], possibilities[0], possibilities[2], possibilities[3], possibilities[4]);
    break;
  case 5:
    // friday
    selectors.push(possibilities[0], possibilities[5], possibilities[4]);
    break;
  case 6:
    // saturday
    selectors.push(possibilities[6], possibilities[0], possibilities[5]);
    break;
  case 0:
    // sunday
    selectors.push(possibilities[6], possibilities[0], possibilities[7], possibilities[8], possibilities[9]);
    break;

  }

  sequence = $('#' + sequence_id)
  container = sequence.find('.time_frame').first()
  if (false) { // case individual buttons
    $(selectors).each(function(idx, sel) {
      element = $('<span/>').html(sel + ": ")
      //button = $('<input/>', { 'type': 'radio', 'name': 'when', 'value': sel, 'onclick' : "javascript:update_time_date_picker($(this), '"+ selectors_and_time_values[sel][0].toISOString()+"', '" + selectors_and_time_values[sel][1].toISOString()+ "')" })
      button = $('<input/>', {
        'type': 'radio',
        'name': 'when',
        'value': sel,
        'click': function() {
          update_time_date_picker($(this), selectors_and_time_values[sel][0], selectors_and_time_values[sel][1])
        }
      })
      //button = $('<input/>', { 'type': 'radio', 'name': 'when', 'value': sel, 'click': function(){blabla($(this).attr("value"))} })

      if ((pre_selected != null) && (pre_selected == sel)) {
        button.attr('checked', true);
      }

      //button.onclick = update_time_date_picker(element, selectors_and_time_values.get(sel)[0], selectors_and_time_values.get(sel)[1])
      element.append(button) //.observe('onClick', update_time_date_picker(element, selectors_and_time_values.get(sel)[0], selectors_and_time_values.get(sel)[1])))

      element.append($('<span>&nbsp;&nbsp;</span>'))
      //element.observe('onClick', update_time_date_picker(element, selectors_and_time_values.get(sel)[0], selectors_and_time_values.get(sel)[1]))
      container.append(element);
      //button.onclick = update_time_date_picker(element, selectors_and_time_values.get(sel)[0], selectors_and_time_values.get(sel)[1])

    })
  } else { // case select
    select_element = $('<select/>', {
      'name': 'when'
    })


    temp1 = {} // Careful here: if you call this variable temp, it seems it is being overwritten by another one
    // (cf the fact we use this temp variable in the .change below.). Not sure how to avoid this in JS.
    $(selectors).each(function(idx, sel) {
      //loption = $('<option/>',{ 'value': sel, 'click': function(){update_time_date_picker($(this), selectors_and_time_values[sel][0], selectors_and_time_values[sel][1])} }).html(sel)

      loption = $('<option/>', {
        'value': sel
      }).html(sel)
      temp1[sel] = [selectors_and_time_values[sel][0], selectors_and_time_values[sel][1]]

      if ((pre_selected != null) && (pre_selected == sel)) {
        loption.attr('selected', true);
      }
      select_element.append(loption)
    })

    if (pre_selected == "manual") {
      $('<option/>', {
        'value': pre_selected
      }).html(pre_selected).appendTo(select_element).attr('selected', true)
      select_element.addClass('greyed_out')
    }

    select_element.change(function(event) {
      lval = $(this).children(":selected").attr('value')
      if (lval != "manual") {
        update_time_date_picker($(this), temp1[lval][0], temp1[lval][1])
        $(this).removeClass('greyed_out')
        $(this).find("option[value=manual]").remove()
      }

      update_select_box($(this))
      $(this).trigger('update_dependents') //ADDED with jquerymobile

      render_default_sequence(sequence)

    })

    select_element.bind('deactivateWhenSelect', function(event) {
      if ($(this).find("option[value=manual]").length == 0) {
        $("<option value='manual'>manual</option>").appendTo($(this))
      }
      $(this).addClass('greyed_out').find("option[value=manual]").first().attr('selected', true)
      ////update_select_box(when_select)
      update_select_box($(this))
      $(this).trigger('update_dependents') //ADDED with jquerymobile

      render_default_sequence(sequence)
    })


    container.append(select_element)

  }


}

// This function take a JS Date and make it a Rails comptible date without the time zone.
date_formatter = function(datetime) {
  day = datetime.getDate()
  if (day < 10) {
    day = "0" + day
  }
  month = (datetime.getMonth() + 1)
  if (month < 10) {
    month = "0" + month
  }
  hour = datetime.getHours()
  if (hour < 10) {
    hour = "0" + hour
  }
  minutes = datetime.getMinutes()
  if (minutes < 10) {
    minutes = "0" + minutes
  }

  return day + "-" + month + "-" + datetime.getFullYear() + " " + hour + ":" + minutes
}

update_time_date_picker = function(el, start, end) {
  //return function(){

  start = date_formatter(start)
  end = date_formatter(end)

  hidden_dates_selector = el.parents('.sequence_parameters').first().find('[type="hidden"]')
  hidden_dates_selector.each(function(idx, input) {
    if (input.id.search(/start_date/) >= 0) {
      input.value = start;
      return
    }
    if (input.id.search(/end_date/) >= 0) {
      input.value = end;
      return
    }
  });

  date_picker_values = el.parents('.sequence_parameters').first().find('.manual_dates_selector').first().find('[type="text"]')
  date_picker_values.each(function(idx, input) {
    if (input.id.search(/start_date/) >= 0) {
      //temp = start.split("T")[0].split("-")
      //input.value = temp[2] + "-" + temp[1] + "-" + temp[0]
      input.value = start.split(" ")[0]
      return
    }
    if (input.id.search(/end_date/) >= 0) {
      //temp = end.split("T")[0].split("-")
      //input.value = temp[2] + "-" + temp[1] + "-" + temp[0]
      input.value = end.split(" ")[0]
      return
    }
  });

  time_picker_selects = el.parents('.sequence_parameters').first().find('.manual_dates_selector').first().find('select')
  time_picker_selects.each(function(idx, lselect) {
    if (lselect.id.search(/start_date/) >= 0) {
      options = $(lselect).find('option')
      options.each(function(idx2, loption) {
        //ltime = start.split("T")[1].replace(/[A-Za-z]/,"").substring(0, 5)
        ltime = start.split(" ")[1]
        if ($(loption).attr('value') == ltime) {
          loption.selected = true;
          return false;
        }
      });

      // Just to signal the change on elements that depend on the "onChange" event
      // of this select, e.g. selectBox.
      //$(lselect).change();
      $(lselect).trigger('update_dependents')

      return
    }
    if (lselect.id.search(/end_date/) >= 0) {
      options = $(lselect).find('option')
      options.each(function(idx2, loption) {
        //ltime = end.split("T")[1].replace(/[A-Za-z]/,"").substring(0, 5)
        ltime = end.split(" ")[1]
        if ($(loption).attr('value') == ltime) {
          loption.selected = true;
          return false;
        }
      });

      // Just to signal the change on elements that depend on the "onChange" event
      // of this select, e.g. selectBox.
      //$(lselect).change();
      $(lselect).trigger('update_dependents')

      return
    }


  });


  //}
}

deactivate_preselectors = function(el) {
  //el.parents('.sequence_parameters').first().find("input[name='when']").each(function(idx, e){
  //e.checked = false
  //})

  when_select = el.parents('.sequence_parameters').first().find("select[name='when']").first()
  when_select.trigger('deactivateWhenSelect')

}


get_proposal_minimum_price = function(proposal) {
  providers = proposal.find('.provider')
  prices = [];
  providers.each(function(idx, provider) {
    ais = $(provider).find('.availability_information')
    min_price = null
    currency = null
    ais.each(function(idx2, ai) {
      if (currency == null) {
        currency = $(ai).find('.availability_information_currency').first().html()
        // Note we assume that for a same provider, all the ais use the same currency.
      }
      ai_price = $(ai).find('.availability_information_price').first().html()
      if (min_price == null || ai_price < min_price) {
        min_price = ai_price
      }

    })

    temp = {};
    temp['currency'] = currency
    temp['price'] = min_price
    prices.push(temp)

  })


  concat = {}
  $(prices).each(function(idx, p) {
    currency = p['currency']
    if (concat[currency]) {
      concat[currency] = parseFloat(concat[currency]) + parseFloat(p['price'])
    } else {
      concat[currency] = parseFloat(p['price'])
    }
  })

  to_display = ""
  number_of_elements = 1
  $(concat).each(function(idx, e) {
    if (number_of_elements > 1) {
      to_display += " and "
    }
    for (var i in e) {
      to_display += i + e[i]
    }
    number_of_elements += 1
  })

  return to_display
}


format_price = function(price, currency) {
  return "<p>Starting at:</p>" + "<span>" + "£" + price + "</span>"
}

prepare_global_pre_selected_elements = function() {
  temp = $('#global_pre_selected_elements')
  if (temp[0] != null) {
    temp.val(JSON.stringify(global_pre_selected_elements));
  }
}

restore_preselections = function() {
  $.each(global_pre_selected_elements, function(idx, sequ_lvl) {
    $.each(sequ_lvl, function(idx2, h) {
      //if (!(Object.isHash(h))){
      //	h = new Hash(h);
      //}
      if (h == null) { //TODO ???
        h = {}
      }
      if (h['provider_id'] != null) {
        provider = $('#sequence__' + idx + '__event__' + h['event_id'] + '__provider__' + h['provider_id'])
        if (provider != null) {
          el = provider.find('.pre_select_button input').first();
          el.attr('checked', true);
          provider.find('.pre_select_button').removeClass('padlock_open').addClass('padlock_close')
          //pre_selection_button_touched(link,false,false);
        }
      }
    })
  })
}


string_to_date_conversion = function(pstring) {
  temp = pstring.match(/[0-9]+/g)
  if (pstring.search(/ /) >= 0) { // Case: 01-12-2010 00:30
    return new Date(temp[2], temp[1] - 1, temp[0], temp[3], temp[4])
  } else { // Case 2010-12-04T10:00:00.000Z
    return new Date(temp[0], temp[1] - 1, temp[2], temp[3], temp[4], temp[5])
  }
}


// FORM VALIDATION
// This is a bit complex: 
// - we need Google to geocode the address, and this feature is asynchronous.
// - we may have several sequences, i.e. several successive async calls to geocoding.
// So we:
// - call the geocoder with a callback function, "targeting" one sequence
// - the callback function actually validates the form for the seqeunce
// - if there are more sequence, this callback actually call the geocoder as well, passing "itself" (i.e. it is
//	kind of recursive).
validate_main_form = function() {
  //return true;

  $('.form_error').removeClass('form_error')

  remaining_sequences = []
  $('.sequence').each(function(idx, s) {
    remaining_sequences.push(s)
  })
  sequ = remaining_sequences.shift();
  destination_container = $(sequ).find('.destination').first()
  destination_container.find('input').first().each(function(idx, input) {
    // Careful of the order of the ifs here!
    if (input.id.search(/destination_longitude/) >= 0) {
      //destination_longitude = input
      return;
    }
    if (input.id.search(/destination_latitude/) >= 0) {
      //destination_latitude = input
      return;
    }
    if (input.id.search(/destination/) >= 0) {
      destination = input
      return;
    }
  })
  message = ""

  geocoder.geocode({
    'address': destination.value
  }, recursive_form_validation(sequ, remaining_sequences, message));

}

// The geocoder call back, which is going to call itself if there are several sequences.
recursive_form_validation = function(sequ, remaining_sequences, message) {
  return function(results, status) {

    // Check Time Frame
    $(sequ).find('.manual_dates_selector').first().find("input[type=hidden]").each(function(idx, linput) {
      if (linput.id.search(/start_date/) >= 0) {
        start_date = string_to_date_conversion(linput.value)
      } else if (linput.id.search(/end_date/) > 0) {
        end_date = string_to_date_conversion(linput.value)
      }
    })
    now = new Date();
    if (start_date < now.setMinutes(now.getMinutes() - 30)) {
      message += "It seems that the start date has not been setup correctly.<br>"
      $(sequ).find('.time_frame').addClass('form_error')
    }
    if (end_date - start_date <= 0) {
      message += "It seems you haven't specified a correct time frame.<br>"
      $(sequ).find('.time_frame').addClass('form_error')
    }

    if (end_date - start_date > 25 * 60 * 60 * 1000) { // More than 25 hours time frame
      message += "At this stage, we can't search over long periods. Sorry...<br>"
      $(sequ).find('.time_frame').addClass('form_error')
    }
    if (start_date >= now.setDate(now.getDate() + 30)) { // We can search over one month only
      message += "At this stage, we can only search for the coming month. Sorry...<br>"
      $(sequ).find('.time_frame').addClass('form_error')
    }
    // End Check Time Frame

    // Check number of people
    number_of_invitees = 0
    number_of_invitees_selector = $(sequ).find('#number_of_invitees2').first() // In the edit view, the selector is a mirror,
    // and its id is number_of_invitees2.
    if (number_of_invitees_selector.length == 0) {
      number_of_invitees_selector = $(sequ).find('#number_of_invitees').first()
    }
    number_of_invitees_selector.find('option').each(function(idx, opt) {
      if (opt.selected == true) {
        number_of_invitees = parseInt(opt.value)
        return false;
      }
    })
    if ((number_of_invitees <= 0) || (number_of_invitees > 8)) {
      message += "It seems the number of people coming is wrong?<br>"
      ////number_of_invitees_selector.addClass('form_error')
      $(sequ).find('.number_of_users_select').addClass('form_error')
    }
    // End Check number of people

    // Check number of real events
    number_of_events = $(sequ).find('.event.fields.real_event')
    number_of_deleted_events = 0
    $(sequ).find('.event_remove_link.real_event').each(function(idx, el) {
      hidden_field = $(el).find("input[type=hidden]")[0]
      if ((hidden_field != undefined) && (hidden_field.value != "")) {
        number_of_deleted_events += parseInt(hidden_field.value)
      }
    })

    if (number_of_events.length == number_of_deleted_events) {
      message += "It seems you haven't selected any activities.<br>"
      $(sequ).find('.add_fields_link_for_event').first().addClass('form_error')
    }
    // End Check number of real events

    // Check timeframe vs number of events
    // Min 2x hour per event
    number_of_active_events = number_of_events.length - number_of_deleted_events

    if (end_date - start_date < number_of_active_events * 2 * 60 * 60 * 1000) {
      message += "It seems you only have roughly " + (end_date - start_date) / (60 * 60 * 1000) + " hours for " + number_of_active_events + " activities. It probably won't be enough.<br>"
      $(sequ).find('.time_frame').addClass('form_error')
    }

    if (end_date - start_date > 25 * 60 * 60 * 1000) { // More than 25 hours time frame
      message += "At this stage, we can't search over long periods. Sorry...<br>"
      $(sequ).find('.time_frame').addClass('form_error')
    }
    // End check timeframe vs number of events	


    // Check location
    destination_container = $(sequ).find('.destination').first()

    destination_container.find('input').each(function(idx, input) {
      // Careful of the order of the ifs here!
      if (input.id.search(/destination_longitude/) >= 0) {
        destination_longitude = input
        return;
      }
      if (input.id.search(/destination_latitude/) >= 0) {
        destination_latitude = input
        return;
      }
      if (input.id.search(/destination/) >= 0) {
        destination = input
        return;
      }
    })
    // Case Anywhere
    if (destination.value.search(/Anywhere/) >= 0) { //TODO don't hard code!
      destination_longitude.value = -1000
      destination_latitude.value = -1000
    } else if (status == google.maps.GeocoderStatus.OK) {
      //destination_container.select('input').each(function(input){
      // Careful of the order of the ifs here!
      //if (input.id.include('destination_longitude')){
      //	destination_longitude = input
      //	return;
      //}
      //if (input.id.include('destination_latitude')){
      //	destination_latitude = input
      //	return;
      //}
      //if (input.id.include('destination')){
      //	destination = input
      //	return;
      //}
      //})

      if (!is_in_area("London", results[0].geometry.location.lat(), results[0].geometry.location.lng())) {
        message += "When we locate this destination, it seems outside of London."
        //destination_container.addClass('form_error')
        $(sequ).find('.destination').addClass('form_error')
      } else {
        destination_longitude.value = results[0].geometry.location.lng()
        destination_latitude.value = results[0].geometry.location.lat()
      }
    } else {

      // TODO: OVER_QUERY_LIMIT problem!!! 3 lines below should not be commented.
      message += "We could not locate your destination.<br>"
      //message += status
      //destination_container.addClass('form_error')
      $(sequ).find('.destination').addClass('form_error')



    }
    // End check location

    if (remaining_sequences.length > 0) {
      next_sequ = remaining_sequences.shift();
      destination_container = $(next_sequ).find('.destination').first()
      destination_container.find('input').each(function(idx, input) {
        // Careful of the order of the ifs here!
        if (input.id.search(/destination_longitude/) >= 0) {
          //destination_longitude = input
          return;
        }
        if (input.id.search(/destination_latitude/) >= 0) {
          //destination_latitude = input
          return;
        }
        if (input.id.search(/destination/) >= 0) {
          destination = input
          return;
        }
      })


      geocoder.geocode({
        'address': destination.value
      }, recursive_form_validation(next_sequ, remaining_sequences, message));

    } else {
      if (message != "") {
        $('#main_form_error').find('.error_message').first().html(message)
        ////Lightview.show('#main_form_error')
        //(new Control.Modal($('main_form_error_link'),{  
        //          className: 'modal',  
        //          closeOnClick: true, 
        //	        overlayOpacity: 0.75 
        //          })).open();

        //open_livepipe_window('modal', $('main_form_error'),{  
        //		          className: 'modal',  
        //		          closeOnClick: true, 
        //			       overlayOpacity: 0.75 
        //		          })


        if (USING_LOADING_MESSAGE) {
          $.mobile.hidePageLoadingMsg();
        }

        $('#main_form_error').dialog({
          resizable: false,
          draggable: false,
          buttons: {
            "OK": function() {
              $(this).dialog("close");
            }
          },
          dialogClass: 'message_dialog'
        })

        return false
      }

      // We are done, everything is fine, we can submit the form.
      form_is_validated = true
      $('form').first().submit();
      //return true;

    }


  }
}


is_in_area = function(area_as_string, lat, lon) {

  if ((area_as_string == null) || (area_as_string == "")) {
    return false
  }

  switch (area_as_string) {
  case "London":
    lat_max = 51.643590
    lat_min = 51.360634
    lon_max = 0.137329
    lon_min = -0.379028
    if ((lat <= lat_max) && (lat >= lat_min) && (lon <= lon_max) && (lon >= lon_min)) {
      return true;
    }
    break;
  default:
    return false;
  }
  return false;

}

display_debug_string = function(debug_string) {
  $('#debug_area').text(debug_string)
}

// Below is some LivePipe setup stuff:

// Wrapper from PJ: there is a bug with the library. When you
// click a link, it opens the window but also sends it a click notification. When
// closeOnClick is set to true, it then closes the window instantly (i.e. you don't see the
// window at all).
open_livepipe_window = function(type, target_el, options) {
  return true;
  switch (type) {
  case 'modal':
    setTimeout(function() {
      Control.Modal.open(target_el, options);
    }, 0);
  case 'window':
    setTimeout(function() {
      Control.Window.open(target_el, options);
    }, 0);
    break;
  }
}

//styled examples use the window factory for a shared set of behavior  
window_factory = function(container, target_link, options) {
  return true
  var window_header = new Element('div', {
    className: 'window_header'
  });
  var window_title = new Element('div', {
    className: 'window_title'
  });
  var window_close = new Element('div', {
    className: 'window_close'
  });
  var window_contents = new Element('div', {
    className: 'window_contents'
  });
  var w = new Control.Window(container, Object.extend({
    className: 'window',
    closeOnClick: window_close,
    draggable: window_header,
    //position: 'mouse',
    indicator: $('spinner'),
    insertRemoteContentAt: window_contents //,  
    //afterOpen: function(){  
    //    window_title.update(container.readAttribute('title'))  
    //}  
  }, options || {}));
  w.container.insert(window_header);
  window_header.insert(window_title);
  window_header.insert(window_close);
  w.container.insert(window_contents);
  return w;
};


sort_it = function(TheArr, us, u, vs, v, ws, w, xs, x, ys, y, zs, z) {
  // From: http://javascript.keithjoubert.com/sorting.htm    
  // us-zs: 1=asc, -1=desc.  u-z: column-numbers.  See example
  if (u == undefined) {
    TheArr.sort(Sortsingle);
  }
  // if this is a simple array, not multi-dimensional, ie, SortIt(TheArr,1): ascending.
  else {
    TheArr.sort(Sortmulti);
  }

  function Sortsingle(a, b) {
    var swap = 0;
    if (isNaN(a - b)) {
      if ((isNaN(a)) && (isNaN(b))) {
        swap = (b < a) - (a < b);
      } else {
        swap = (isNaN(a) ? 1 : -1);
      }
    } else {
      swap = (a - b);
    }
    return swap * us;
  }

  function Sortmulti(a, b) {
    var swap = 0;

    //TODO: this is dirty. I am just taking into account
    // that price is a two-level hash: a[price][value].
    // We should find a better way to do this. This function is
    // not generic anymore...
    if (u == 'price') {
      a_u = a[u]['value']
      b_u = b[u]['value']
    } else {
      a_u = a[u]
      b_u = b[u]
    }


    if (isNaN(a_u - b_u)) {
      if ((isNaN(a_u)) && (isNaN(b_u))) {
        swap = (b_u < a_u) - (a_u < b_u);
      } else {
        swap = (isNaN(a_u) ? 1 : -1);
      }
    } else {
      swap = (a_u - b_u);
    }
    if ((v == undefined) || (swap != 0)) {
      return swap * us;
    } else {
      if (isNaN(a[v] - b[v])) {
        if ((isNaN(a[v])) && (isNaN(b[v]))) {
          swap = (b[v] < a[v]) - (a[v] < b[v]);
        } else {
          swap = (isNaN(a[v]) ? 1 : -1);
        }
      } else {
        swap = (a[v] - b[v]);
      }
      if ((w == undefined) || (swap != 0)) {
        return swap * vs;
      } else {
        if (isNaN(a[w] - b[w])) {
          if ((isNaN(a[w])) && (isNaN(b[w]))) {
            swap = (b[w] < a[w]) - (a[w] < b[w]);
          } else {
            swap = (isNaN(a[w]) ? 1 : -1);
          }
        } else {
          swap = (a[w] - b[w]);
        }
        if ((x == undefined) || (swap != 0)) {
          return swap * ws;
        } else {
          if (isNaN(a[x] - b[x])) {
            if ((isNaN(a[x])) && (isNaN(b[x]))) {
              swap = (b[x] < a[x]) - (a[x] < b[x]);
            } else {
              swap = (isNaN(a[x]) ? 1 : -1);
            }
          } else {
            swap = (a[x] - b[x]);
          }
          if ((y == undefined) || (swap != 0)) {
            return swap * xs;
          } else {
            if (isNaN(a[y] - b[y])) {
              if ((isNaN(a[y])) && (isNaN(b[y]))) {
                swap = (b[y] < a[y]) - (a[y] < b[y]);
              } else {
                swap = (isNaN(a[y]) ? 1 : -1);
              }
            } else {
              swap = (a[y] - b[y]);
            }
            if ((z = undefined) || (swap != 0)) {
              return swap * ys;
            } else {
              if (isNaN(a[z] - b[z])) {
                if ((isNaN(a[z])) && (isNaN(b[z]))) {
                  swap = (b[z] < a[z]) - (a[z] < b[z]);
                } else {
                  swap = (isNaN(a[z]) ? 1 : -1);
                }
              } else {
                swap = (a[z] - b[z]);
              }
              return swap * zs;
            }
          }
        }
      }
    }
  }
}


activate_waiting_splash_screen = function(paction) {

  if (IS_MOBILE_DEVICE) {
    if (paction) {
      $('#waiting_screen').dialog({
        modal: true,
        resizable: false,
        dialogClass: 'waiting_screen_dialog'
      })
      //$('#footer_bar').hide()
    } else {
      $('#waiting_screen').dialog('close')
      //$('#footer_bar').show()


    }
    return;
  }

  if (paction) {
    //$('.sequence').children().not('.sequence_parameters').hide()
    $('.sequence').children().not('.sequence_parameters_no_edit').hide()
    $('.form_submit_button').hide()
    $('#waiting_screen').show()
  } else {
    //$('.sequence').children().not('.sequence_parameters_dialog').show()

    ////$('.sequence').children().not('.sequence_parameters').not('.sequence_parameters_dialog').show()
    ////$('.form_submit_button').show()
    $('.sequence').children().not('.sequence_parameters').not('.sequence_parameters_dialog').not('.form_submit_button').show()

    $('#waiting_screen').hide()
    $(document).trigger('updated_fields')
  }

}

/**** Geo-location ***/

initiate_geolocation = function() {
  navigator.geolocation.getCurrentPosition(handle_geolocation_query, handle_geolocation_errors, {
    enableHighAccuracy: false,
    timeout: 3000
  });
}


handle_geolocation_errors = function(error) {
  alert("Sorry, we could not locate you. Please enter an address manually or use the map.");

  //switch(error.code)  
  //{  
  //    case error.PERMISSION_DENIED: alert("user did not share geolocation data");  
  //    break;  

  //    case error.POSITION_UNAVAILABLE: alert("could not detect current position");  
  //    break;  

  //    case error.TIMEOUT: alert("retrieving position timed out");  
  //    break;  

  //    default: alert("unknown error");  
  //    break;  
  //}

  $('.target_for_geolocation').removeClass('target_for_geolocation')
}

function handle_geolocation_query(position) {
  //alert('Lat: ' + position.coords.latitude +  
  //     ' Lon: ' + position.coords.longitude);

  var latlng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
  geocoder.geocode({
    'latLng': latlng
  }, function(results, status) {
    if (status == google.maps.GeocoderStatus.OK) {
      i = 0
      while ((results[i] == null) && (i < 7)) {
        i++;
      }
      if (results[i]) {
        //alert(results[i].formatted_address)
        $('.target_for_geolocation').first().val(results[i].formatted_address).removeClass('target_for_geolocation')
      } else {
        alert("Sorry, we could not locate you. Please enter an address manually or use the map.");
      }
    } else {
      alert("Sorry, we could not locate you. Please enter an address manually or use the map.");
    }
  });

  //$('.target_for_geolocation').removeClass('target_for_geolocation')
}

create_auto_locator_button = function(psequence_id) {
  if (IS_MOBILE_DEVICE) {
    destination_div = $("#" + psequence_id).find(".destination").first()
    target_icon = $("<a/>", {
      'class': 'geo-location',
      'data-role': 'button',
      'data-icon': 'geo_location_icon',
      'data-iconpos': 'notext'
    })
    //target_icon = $("<a/>", {'class': 'geo-location'}).html("<img src='/images/geolocation_icon.png' />")

    target_icon.appendTo(destination_div).click(function(event) {
      destination_div.find("input[type=text]").addClass('target_for_geolocation')
      initiate_geolocation();
    })

    $("<div/>", {
      'class': 'clear_float'
    }).appendTo(destination_div)

  }
}


// The standard clone() seems to break the selectBoxes. 
// safe_clone first remove the selectBoxes, then clone and add them
// back on both the original and the copy. So it is transparent.
$.fn.clone_safe = function() {
  try {
    $(this).find('select').selectBox('destroy')
  } catch (err) {

  }
  cloned_container = $(this).clone()
  create_selectBoxes($(this))
  create_selectBoxes(cloned_container)
  return cloned_container
}

// The standard remove() combined with clone() seems to break the selectBoxes. 
// remove_safe first remove the selectBoxes, then remove the element.
// It is transparent.
$.fn.remove_safe = function() {
  if (($(this).length > 0) && (!IS_MOBILE_DEVICE)) {
    $(this).find('select').selectBox('destroy')
    //$('.score_tooltip').mouseout()
    $(this).find('.tooltip_trigger').data('tooltip').hide()
  }

  return $(this).remove()
}

// Check for value of fields marked with class "check_present" in form.
check_presence = function(jq_form, jq_error_container) {

  form_is_valid = true

  jq_form.find('.check_present').each(function(idx) {
    $(this).removeClass('form_error') // Cleaning
    if (($(this).val() == null) || ($(this).val() == "")) {
      form_is_valid = false
      $(this).addClass('form_error')
    }
  })

  if (!form_is_valid) {
    jq_error_container.html("Some information is missing. Please check the form.")
  } else {
    jq_error_container.html("")
  }

  return form_is_valid

}


rearrange_availability_informations = function(jq_provider) {
  ais = jq_provider.find('.availability_information')
  ais_hash = {}
  ais.each(function(idx) {
    source_name = get_availability_information_source_name($(this))
    // First we create a hash of ais infos
    if (ais_hash[source_name] == null) {
      ais_hash[source_name] = {}
      ais_hash[source_name]['booking_link'] = get_availability_information_booking_link($(this))
      ais_hash[source_name]['availability_informations'] = []
      ais_hash[source_name]['availability_informations'].push($(this))
      return true;
    }
    for (i = 0; i < ais_hash[source_name]['availability_informations'].length; i++) { // Order by price
      if (get_availability_information_price(ais_hash[source_name]['availability_informations'][i]) > get_availability_information_price($(this))) {
        ais_hash[source_name]['availability_informations'].splice(i, 0, $(this))
        return true;
      }
    }
    ais_hash[source_name]['availability_informations'].push($(this))
  })

  return ais_hash

}

get_availability_information_price = function(jq_ai) {
  return parseFloat(jq_ai.find('.availability_information_price').html())
}
get_availability_information_source_name = function(jq_ai) {
  return jq_ai.find('.source_name').first().html()
}
get_availability_information_booking_link = function(jq_ai) {
  return jq_ai.find('.booking_link').first().find('a')
}


render_booking_options = function(jq_sequence_individual_proposal) {
  booking_options_div = $("<div/>", {
    'class': 'booking_options'
  }).html("<h3>Booking options:</h3>")
  jq_sequence_individual_proposal.find('.activities .provider').each(function(idx) {
    provider = $(this).clone()
    provider.css('margin-left', 'auto')
    provider.css('width', 'auto')

    provider.find('.pre_select_button').remove()
    provider.find('.time_slots').removeClass('hide')
    provider.find('.time_slots .availability_informations').addClass('hide')

    ais_hash = rearrange_availability_informations(provider)
    for (var key in ais_hash) {
      source_name = $("<div/>", {
        'class': 'source_name'
      }).html(key)
      provider.append(source_name)
      ais_hash[key]['booking_link'].html("<button type='button' class='booking_link'>Book!</button>") //.click(function(event){event.stopPropagation();})

      if (!IS_MOBILE_DEVICE) {
        ais_hash[key]['booking_link'].click(function(event) {
          event.stopPropagation();
        })
      }


      provider.append(ais_hash[key]['booking_link'])
      $(ais_hash[key]['availability_informations']).each(function(idx2) {
        ai = $(this).clone()
        get_availability_information_booking_link(ai).remove()
        provider.append(ai)
      })
    }

    if (IS_MOBILE_DEVICE) {
      provider.attr('onclick', '').unbind('click');
    }


    booking_options_div.append(provider)
  })

  return booking_options_div
  //jq_sequence_individual_proposal.find('.map_canvas_container').prepend(booking_options_div)
}

display_booking_optionsOLD = function(jq_sequence_individual_proposal) {
  booking_options_div = $("<div/>", {
    'class': 'booking_options'
  }).html("<h3>Booking options:</h3>")
  jq_sequence_individual_proposal.find('.provider').each(function(idx) {
    provider = $(this).clone()
    provider.css('margin-left', 'auto')
    provider.css('width', 'auto')

    provider.find('.pre_select_button').remove()
    provider.find('.time_slots').removeClass('hide')
    provider.find('.time_slots').find('.availability_informations').addClass('hide')
    //provider.find('.time_slot_data').show()
    ais_hash = rearrange_availability_informations(provider)
    for (var key in ais_hash) {
      source_name = $("<div/>", {
        'class': 'source_name'
      }).html(key)
      provider.append(source_name)
      ais_hash[key]['booking_link'].html("<button type='button' class='booking_link'>Book!</button>")
      provider.append(ais_hash[key]['booking_link'])
      $(ais_hash[key]['availability_informations']).each(function(idx2) {
        ai = $(this).clone()
        get_availability_information_booking_link(ai).remove()
        provider.append(ai)
      })
    }
    booking_options_div.append(provider)
  })

  //booking_options_div.dialog({ width: 300 })
  jq_sequence_individual_proposal.find('.map_canvas_container').prepend(booking_options_div)
}


render_default_sequence = function(jq_sequence) {
  if (sequence_details_were_revealed[jq_sequence.attr('id')] || in_results_view) {
    return; // The user saw the default sequence or is in the results view - so we should not 
    // modify it programatically now.
  } else {

    $(jq_sequence).find('.manual_dates_selector').first().find("input[type=hidden]").each(function(idx, linput) {
      if (linput.id.search(/start_date/) >= 0) {
        pstart = string_to_date_conversion(linput.value)
      } else if (linput.id.search(/end_date/) > 0) {
        pend = string_to_date_conversion(linput.value)
      }
    })

    default_sequence = fetch_default_sequence(pstart, pend)
    preset_activities(jq_sequence, default_sequence)

    if (IS_MOBILE_DEVICE) {
      $('.events').trigger("create")
    }

  }
}


fetch_default_sequence = function(pstart, pend) {
  start_hour = pstart.getHours()
  duration = (pend - pstart) / (1000 * 3600) // In hours

  // Late night
  if (start_hour <= 5) {
    if (duration <= 5) { // Really a late night thing
      return [6] // [Resto]
    } else { // Could be an ealy morning thing?
      return [6] //return [5, -10]
    }
  }
  // Morning
  if (start_hour < 11) {
    if (duration < 5) { // Very short
      return [-10] // [Any event]
    } else {
      return [-10, 5]
    }
  }
  // Lunch time
  if (start_hour < 14) {
    if (duration < 5) { // Short
      return [5] // [Resto]
    } else {
      return [5, -10]
    }
  }
  // Afternoon / Evening
  if (start_hour <= 20) {
    if (duration < 5) { // Very short
      return [-10] // [Any event]
    }
    //else if (duration <= 4){ // Short
    //	return [-10, 5]
    //}
    else {
      return [-10, 5] //TEMPORARY return [-10, 5, 6]
    }
  }
  if (start_hour <= 21) {
    if (duration < 5) { // Very short
      return [-10]
    } else {
      return [-10, 6]
    }
  } else {
    return [6]
  }


}

preset_activities = function(jq_sequence, default_sequence) {
  if (jq_sequence == null) {
    return
  }

  jq_sequence.find('.real_event').find('.remove_fields_link_for_event').click()

  add_activity_link = jq_sequence.find('.add_fields_link_for_event').first()
  $(default_sequence).each(function(idx) {
    add_activity_link.click()
    levent = jq_sequence.find('.event').addClass('hidden').last()
    //levent.addClass('hidden')
    lselect = levent.find('select').first()
    lselect.val(default_sequence[idx] + "")
    lselect.change()
    lselect.trigger('update_dependents')
  })

}

set_advanced_search_link_flag = function(jq_advanced_search_link) {
  sequence_id = jq_advanced_search_link.parents('.sequence').first().attr('id')
  sequence_details_were_revealed[sequence_id] = true
}

$.fn.modal_datepicker = function(options) {

  ////modal_dialog_div = $("<div />", {'class': 'modal_datepicker_dialog'})
  modal_dialog_div = $("<div />")
  modal_datepicker_div = $("<div />", {
    'class': 'modal_datepicker_datepicker'
  })
  modal_dialog_div.append(modal_datepicker_div);




  jqready_target_id = "#" + $(this).attr('id')
  all_options = {
    altField: jqready_target_id,
    onSelect: function(dateText, inst) {
      $(jqready_target_id).change();
      modal_dialog_div.dialog('destroy');
      modal_dialog_div.remove();
      opening_modal_window(false);
    },
    defaultDate: $(this).val()
  }



  for (attr in options) {
    all_options[attr] = options[attr];
  }

  modal_datepicker_div.datepicker(all_options)



  //modal_dialog_div.dialog({modal: true, resizable: false, draggable: false, position: 'center', dialogClass: 'modal_datepicker_dialog'})
  modal_dialog_div.dialog({
    modal: true,
    resizable: false,
    draggable: false,
    position: 'center',
    dialogClass: 'modal_datepicker_dialog',
    open: opening_modal_window(true)
  })


}

// Android bug: when taping in a modal window, the event is passed to selects "behind"
// and it does not seem possible to prevent propagation. In the workaround below we
// deactivate the selects entirely.
opening_modal_window = function(bool) {
  if (!IS_ANDROID_DEVICE) {
    return
  }

  if (bool) {
    $('select').selectmenu('disable');
  } else {
    $('select').selectmenu('enable');
  }
}

remap_image = function(image_container, original_width) {

  return;

  img = image_container.find('img').first()
  map = image_container.find('map').first()

  new_width = img.width()

  if (new_width != original_width) {
    ratio = (new_width + 0.0) / original_width

    map.find('area').each(function() {
      var coord_vals = $(this).attr('coords').split(',');
      var new_vals = [];
      for (var i = 0; i < coord_vals.length; i++) {
        new_vals[i] = Math.round(coord_vals[i] * ratio);
      }
      new_vals = new_vals.join(",");
      $(this).attr('coords', new_vals);
    });

  }


}

/*refresh_scroller = function(){
	if (typeof scrollers === 'undefined') { return; } 
	
	setTimeout(function () {
		$(scrollers).each(function(idx){
			this.refresh();
			this.refresh();
		})
		
	}, 0);
}*/

refresh_jquerymobile_widgets = function(jq_element) {

  if ((IS_MOBILE_DEVICE) || (IS_TABLET_DEVICE)) {
    jq_element.find('select').selectmenu();
  }

}


jquery_mobile_custom_init = function() {
  $(document).bind('mobileinit', function() {

    $.mobile.defaultPageTransition = 'none'; // When transitions are "on", pages keep scrolling
    // up before transitioning which is very ennoying. Should get fixed with iOS 5.
    ////$.mobile.fixedToolbars.setTouchToggleEnabled(false); USED Pre 1.1

    //$.mobile.selectmenu.prototype.options.nativeMenu = false;

    //$.mobile.nativeSelectMenu = true;
    ////$.mobile.touchOverflowEnabled = true; USED Pre 1.1


    if (USING_LOADING_MESSAGE) {
      //$("div[data-role='navbar'] a").live('click', function(){$.mobile.showPageLoadingMsg();})
      //$('#sequence_parameters_page').live('pageshow',function(event, ui){$.mobile.hidePageLoadingMsg();});
      //$('#activities_page').live('pageshow',function(event, ui){$.mobile.hidePageLoadingMsg();});
      //$('#results_page').live('pageshow',function(event, ui){$.mobile.hidePageLoadingMsg();});

      $('#booking_options_page').live('pageshow', function(event, ui) {
        $.mobile.hidePageLoadingMsg();
      });

      $('#results_pageBK').live('pageshow', function(event, ui) {
        $('.sequence').each(function() {
          $(this).find('.events').hide().insertAfter($(this).find('.show_all_possibilities_link').last()).before(
          $("<div/>", {
            'class': 'activities_toggle_link'
          }).html("Activities").click(function() {
            exclusive_toggle($(this).parents('.sequence').find('.events').addClass('exclusive_toggle'), $(this).parents('.individual_proposals'));
          }));
        })
      });

      $('#results_page').live('pageshow', function(event, ui) {

        if ((ui.prevPage == null) || (ui.prevPage.attr('id') == 'sequence_parameters_page')) {

          $('.sequence').each(function() {
            levents = $(this).find('.events')
            levents.hide()
            levents.appendTo($(this).find('.individual_proposals'))
            $(this).find('.show_all_possibilities_link').last().after(
            $("<div/>", {
              'class': 'activities_toggle_link'
            }).html("Activities").click(function() {
              exclusive_toggle($(this).parents('.sequence').find('.events').addClass('exclusive_toggle'), $(this).parents('.individual_proposals'));
            }))
          })
        }
      })

      $('#sequence_parameters_page').live('pageshow', function(event, ui) {
        $('.sequence').each(function() {
          $(this).find('.activities_toggle_link').remove()
          $(this).find('.events').removeClass('exclusive_toggle').insertBefore($(this).find('#sequence_parameters_page .skyline_mobile').first()).show();
        })
      });
    }

  });

}

jquery_mobile_custom_init_tablet = function() {
  $(document).bind('mobileinit', function() {
    //$.mobile.ns = "tablet-";
    $.mobile.ajaxEnabled = false;
    $.mobile.linkBindingEnabled = false;
    $.mobile.hashListeningEnabled = false;
    $.mobile.pushStateEnabled = false;
    $.mobile.defaultPageTransition = "none";
    $.mobile.touchOverflowEnabled = false;
    //$.mobile.nativeSelectMenu = true;
    //$.mobile.page.prototype.options.keepNative = "input";
  });

}

do_updates_after_content_change = function() {
  if (IS_MOBILE_DEVICE) {
    $('.provider.possibility').bind('vmousedown', function() {
      $(this).addClass('pressed')
    }).bind('vmouseup', function() {
      $(this).removeClass('pressed')
    }).find('.pre_select_button').bind('vmousedown', function(e) {
      $(this).addClass('pressed');
      e.stopPropagation();
    }).bind('vmouseup', function() {
      $(this).removeClass('pressed')
    });

    $('.sequence_individual_proposal_information_section_after').trigger("create")
  }

}

generate_provider_additional_info_dialog = function(jq_caller) {

  additional_info_container = $('#individual_map_container')

  additional_info_container.dialog({
    dialogClass: 'provider_additional_info_dialog',
    resizable: false,
    title: jq_caller.find('.provider_name').html()
  });
  if (IS_MOBILE_DEVICE) {
    additional_info_container.dialog("option", {
      modal: true,
      draggable: false
    });
  }

  display_map_with_marker(jq_caller, $('#individual_map_canvas'));
  additional_info_container.find('.provider_description').first().html(jq_caller.find('.description').first().clone(true).removeClass('hide'));
  //additional_info_container.find('.provider_name').first().html(jq_caller.find('.provider_name').first().clone(true));
  $('.spinner2_reviews').show();


}

activity_type_changing = function(el, pcontext) {

  type_input = el.parents('.event_type').first().find('input').first()
  sub_type_input = el.parents('.event_data').first().find('.event_sub_type').first().find('input').first()

  previous_type = type_input.val()
  previous_sub_type = sub_type_input.val()

  select = el.parents('.event_type').find('select')

  type_input.val(el.val());
  sub_type_input.val(-10);

  el.parents('.event_type').first().find('.event_type_icon').first().removeClass(function(idx, klass) {
    return klass.match(/type_[-]*[0-9]+/g)[0];
  }).addClass('type_' + el.val())

  //alert(el.parents('.event_type').first().find('.event_type_icon').first().attr('class'))

  generate_options_for_sub_type_select(el.parents('.event_data').first().find('#event_sub_type2').first(), el.val());
  //sub_type_select = el.parents('.event_data').first().find('.event_sub_type').find('select')

  if (pcontext) {
    if (!render_sequence_proposals(el.parents('.sequence').first().attr('id').match(/sequence__[0-9]*/g)[0].replace(/sequence__/, ''))) {
      // Something wrong happened, we roll back.
      //type_input.val(previous_type)
      //sub_type_input.val(previous_sub_type)
      //if (USING_SELECTBOX){
      //	select.selectBox('value', select.data('current'));
      //}
      //else {
      //	select.val(select.data('current'));
      //}
      return
    }
  }

  // Everything OK, we update the "current" values
  if (USING_SELECTBOX) {
    select.data('current', select.selectBox('value'));
    //sub_type_select.data('current', sub_type_select.selectBox('value'))
  } else {
    select.data('current', select.val());
    //sub_type_select.data('current', sub_type_select.val())
  }

  $(document).trigger('parameters_got_updated')
  if (IS_MOBILE_DEVICE) {
    mobile_scroll_to_top($('#results_page_content'))
  }

}


activity_sub_type_changing = function(el, pcontext) {

  sub_type_input = el.parents('.event_sub_type').first().find('input').first()
  previous_sub_type = sub_type_input.val()

  select = el.parents('.event_sub_type').find('select')

  sub_type_input.val(el.val());
  if (pcontext) {
    if (!render_sequence_proposals(el.parents('.sequence').first().attr('id').match(/sequence__[0-9]*/g)[0].replace(/sequence__/, ''))) {
      //sub_type_input.val(previous_sub_type)
      //if (USING_SELECTBOX){
      //	select.selectBox('value', select.data('current'));
      //}
      //else {
      //	select.val(select.data('current'));
      //}
      return
    }
  }

  type_select = el.parents('.event_data').find('.event_type select').first()
  if (USING_SELECTBOX) {
    type_select.data('current_sub_type', select.selectBox('value'));
  } else {
    type_select.data('current_sub_type', select.val());
  }

  $(document).trigger('parameters_got_updated')
  if (IS_MOBILE_DEVICE) {
    mobile_scroll_to_top($('#results_page_content'))
  }

}

roll_back_select_change = function(jq_sequence_main_container) {

  jq_sequence_main_container.find('.events .event_type select').each(function() {

    // First we set all type selects to their previous value
    if (USING_SELECTBOX) {
      $(this).selectBox('value', $(this).data('current'));
    } else {
      $(this).val($(this).data('current'));
    }
    if (IS_MOBILE_DEVICE) {
      $(this).selectmenu("refresh"); // Needed for the MUI to update
    }

    // Then we store the sub_type previous value (they will be overriden when we call activity_type_changing)
    sub_type_select = $(this).parents('.event_data').find('.event_sub_type select').first()
    previous_sub_type_value = $(this).data('current_sub_type')

    // Then we trigger the updates required by the type change
    activity_type_changing($(this), false)


    // We manually set the sub_type selects to their previous value
    if (USING_SELECTBOX) {
      sub_type_select.selectBox('value', previous_sub_type_value);
    } else {
      sub_type_select.val(previous_sub_type_value);
    }
    if (IS_MOBILE_DEVICE) {
      sub_type_select.selectmenu("refresh"); // Needed for the MUI to update
    }
    // We trigger the dependent updates
    activity_sub_type_changing(sub_type_select, false)

  })


  $('.submit_button_dialog').hide()

}


remove_adjacent = function(parray) {
  if (parray.length > 1) {
    nEvents = parray[0]['arrangement'].length

    // 1st part: remove adjacent duplicate
    for (i = 0; i < nEvents; i++) {
      // check if the event has been locked
      currentProvider = parray[0]['arrangement'][i]['provider_id']
      last_j = 0
      j = 1
      while (j < parray.length) {

        while (j < parray.length && currentProvider == parray[j]['arrangement'][i]['provider_id']) {
          j++
        }

        if (j < parray.length) {
          parray.splice(last_j + 1, j - last_j - 1)
          last_j++j = last_j + 1
          currentProvider = parray[last_j]['arrangement'][i]['provider_id']
        }
      }
    }
  }
  return parray
}


check_diversity = function(parray, n) {
  if (parray.length > 1) {
    nEvents = parray[0]['arrangement'].length

    // 1st part: remove adjacent duplicate
    for (i = 0; i < nEvents; i++) {
      // check if the event has been locked
      currentProvider = parray[0]['arrangement'][i]['provider_id']
      last_j = 0
      j = 1
      while (j < parray.length) {

        while (j < parray.length && currentProvider == parray[j]['arrangement'][i]['provider_id']) {
          j++
        }

        if (j < parray.length) {
          parray.splice(last_j + 1, j - last_j - 1)
          last_j++j = last_j + 1
          currentProvider = parray[last_j]['arrangement'][i]['provider_id']
        }
      }
    }

    for (i = 0; i < nEvents; i++) {
      currentProvider = parray[0]['arrangement'][i]['provider_id']
      last_j = 0
      j = 2
      while (j < parray.length) {
        if (parray[last_j + 1]['arrangement'][i]['provider_id'] != currentProvider) {
          while (j < last_j + n && parray.length) {
            if (parray[j]['arrangement'][i]['provider_id'] == currentProvider) {
              parray.splice(j, 1)
            } else {
              j++
            }
          }
          last_j++j = last_j + 2;
        } else {
          lastj++j = last_j + 2
        }
      }

    }

  }
  return parray
}


// With this method, we make sure only one element in the container can be visible among the exclusive one.
exclusive_toggle = function(jq_visible_el, jq_container) {
  if (jq_visible_el.hasClass('visible')) {
    jq_visible_el.removeClass('visible')
    return
  }

  jq_container.find('.exclusive_toggle').removeClass('visible')
  jq_visible_el.addClass('visible')
}

var providers_positioning_in_view = {}
position_html = function(html_object, container_id, object_parameters_as_hash) {
  container_references = providers_positioning_in_view[container_id]
  if (container_references === undefined) {
    container_references = []
    providers_positioning_in_view[container_id] = container_references
  }
  container_references.push(object_parameters_as_hash)
  //	if (object_parameters_as_hash['provider_type'] == 5) { // Restaurant
  //		sort_it(container_references,1,'provider_type',-1,'rating')//, 1, 'name')
  //	}
  //	else {
  //		//sort_it(container_references,1,'provider_type',1,'provider_sub_type',-1,'rating')
  //		sort_it(container_references,1,'provider_type',-1,'rating')//, 1, 'additional_name')
  //	}
  sort_it(container_references, -1, 'rating')

  el_index = $.inArray(object_parameters_as_hash, container_references)

  if ((el_index == -1) || (el_index == 0)) {
    $('#' + container_id).prepend(html_object) //That's the standard behavior, no ranking.
  } else {
    $('#' + container_references[el_index - 1]['html_id']).after(html_object)
  }

}
// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults

var IDEAL_NUMBER_OF_TIMELINE_DIVS = 5

var sequence_details_were_revealed = {}

var ACTIVITIES_SIZE_TEMP = null

var SUBMIT_TOOLTIP_HAS_BEEN_SHOWN = false

///// IE specific stuff
//http://stackoverflow.com/questions/3629183/why-doesnt-indexof-work-on-an-array-ie8
if (!Array.prototype.indexOf)
{
  Array.prototype.indexOf = function(elt /*, from*/)
  {
    var len = this.length >>> 0;

    var from = Number(arguments[1]) || 0;
    from = (from < 0)
         ? Math.ceil(from)
         : Math.floor(from);
    if (from < 0)
      from += len;

    for (; from < len; from++)
    {
      if (from in this &&
          this[from] === elt)
        return from;
    }
    return -1;
  };
}
//////////////////////////


function remove_fields(link, association, calling_from_select) {
  $(link).prev("input[type=hidden]").val("1");
  $(link).parents(".fields").first().hide();

  //if ((calling_from_select === undefined) || (calling_from_select == false)) {
  	update_select_tag(link, association, 'remove', calling_from_select)
  //}

}

function add_fields(link, association, content, offset, calling_from_select) {
  if (offset === undefined) {
		offset = 0
  }
  var new_id = new Date().getTime() + offset;
  var regexp = new RegExp("new_" + association, "g")
  //$(link).parent().insert({
  //  before: content.replace(regexp, new_id)
  //});

  ////$(link).parent().prepend(content.replace(regexp, new_id))
  $(content.replace(regexp, new_id)).insertBefore($(link).parent())


  //if ((calling_from_select === undefined) || (calling_from_select == false)) {
  	update_select_tag(link, association, 'add', calling_from_select)
  //}

	do_custom_updates(link, association)
	
	create_selectBoxes($(link).closest("." + association))
	
	refresh_jquerymobile_widgets($(link).closest("." + association))

}

function update_select_tag(link, association, nature, calling_from_select) {
	
	if (!((calling_from_select === undefined) || (calling_from_select == false))) {
		return
	}
	
	
	select_link = null
	increment = 0
	// We have to split the cases (add and remove) because the add and remove links are not at the same level
	// in the DOM hierarchy. Indeed, remove is at the model level (eg: remove of a user is in the user section itself),
	// whereare add is at the parent level (eg add user is above user level itself).
	try {
		if (nature == 'remove') {
			//PROTOTYPE select_link = link.up().up(".fields").down('.select_to_add_fields_for_'+association)
			select_link = $(link).parent().parents(".fields").first().find('.select_to_add_fields_for_'+association).first()
			increment = -1
		}
		else if (nature == 'add') {
			select_link = $(link).parents(".fields").first().find('.select_to_add_fields_for_'+association).first()
			increment = +1
		}
	}
	catch (e) {
		// In case no select tag exist at this level, we do nothing
	}
	
	if (!((select_link === undefined) || (select_link == null))) {
		select_link_new_value = parseInt(select_link.val()) + increment
		select_link.val(select_link_new_value)
		
		// Just to signal the change on elements that depend on the "onChange" event
		// of this select, e.g. selectBox.
		//select_link.change()
		select_link.trigger('update_dependents')
	}	
	
}


do_custom_updates = function(link, association) {
	switch(association) {
		case "sequences":
			// What we do here:
			// - user click on "add a sequence"
			// - we want this sequence to be a sequence_with_dummy, so we pass the with_summy_user parameter //TODO!!
			// - we set the class of the sequence and first event accordingly, i.e. they mention "dummy" explicitly. 
			ldiv = $(link).parents('.'+association).first()		
			lsequences = ldiv.find('.sequence');
			if (lsequences.length > 0) {
				lsequence = lsequences[lsequences.length-1]
				//lsequence.writeAttribute('id', 'sequence_with_dummy')
				$(lsequence).addClass('sequence_with_dummy')
				levents = lsequence.find('.event')
				//levents.each(function(idx, levent) {
				//	$(levent).addClass('real_event');
				//});
				levents.addClass('real_event');
				
				levents.first().removeClass('real_event').addClass('dummy_event')
				levents.first().find("div[class='event_type']").first().find('input').first().val(100000)
				levents.first().find("div[class='event_chronology']").first().find('input').first().val(100000)
				//if (levents.length > 0) {
				//	levent = levents[0]
				//	levent.removeClass('real_event')
				//	levent.addClass('dummy_event')
				//	//levent.writeAttribute('id', 'dummy_event')
				//	//alert(levent.id)
				//	levent.find("div[class='event_type']")[0].find('input').first().val(100000)
				//	levent.find("div[class='event_chronology']")[0].find('input').first().val(100000)
				//	//alert(levent.select("div[class='event_type]")[0].down('input').value)
				//}

			}
			
		break;
		case "events":	
			ldiv = $(link).parents('.'+association).first()
			levents = ldiv.find('.event')
			i = 0
		
			// Set the chronologies
			levents.each(function(idx, e) {
				//alert(e.select("div[class='event_chronology']")[0].down('input').value)
				temp = $(e).find("div[class='event_chronology']").first().find('input').first()
				if (temp.val() != 100000) { //TODO: don't hard code
					$(e).find("div[class='event_chronology']").first().find('input').first().val(i)
					i++
				}
				//alert(e.select("div[class='event_chronology']")[0].down('input').value)
						
			});
		
			$(link).parents('.sequence').first().find('.real_event').removeClass('hidden')
			//for (i = 0; i < events.length; i++) {		
			//		events[i].removeClass('hidden')
			//}
			break;
	}
}

//select_to_add_fields(this, association, content, select_value)
select_to_add_fields = function(el, association, content, parent, child) {
	lparent = $(el).parents('.'+parent).first();
	lchilds = lparent.find('.'+child);
	//lchilds = lparent.select("div[class='"+child+"']");//"[display='']");
	active_childs = []
	for (i = 0; i < lchilds.length; i++) {
		remove_link = $(lchilds[i]).find('.remove_fields_link_for_'+child).first()
		if (remove_link.prev("input[type=hidden]").val() != "1") {
			active_childs.push(lchilds[i])
		}
	}	
	current_nb_childs = active_childs.length
	//alert(current_nb_childs)
	new_select_value = $(el).val()
	if (new_select_value > current_nb_childs) {
		lclass = 'add_fields_link_for_'+child
		add_user_link = lparent.find("a[class~='"+lclass+"']").first();
		//add_user_link = lparent.down("a[class='add_fields_link']");
		loffset = 0
		while (new_select_value > current_nb_childs) {
			// Here we need to simulate a click on the link
			add_fields(add_user_link, association, content, loffset, true)
			//myrules['.add_nested_item_lvl3'](add_user_link, loffset) // need a delta here! Otherwise two records might have the same id!!
			
			new_select_value -= 1
			loffset += 1
		}
	}
	else {
		while (new_select_value < current_nb_childs) {
			
			remove_fields($(active_childs[current_nb_childs-1]).find('.remove_fields_link_for_'+child).first(), association, true)
			
			//active_childs[current_nb_childs-1].remove()
			current_nb_childs -= 1
		}
		
	}
	
}

display_events_for_sequence = function(el, state) {
	//if (state) {
	//	lvalue = ''
	//}
	//else {
	//	lvalue = 'none'
	//}
	
	//if (IS_MOBILE_DEVICE){ // On mobile, we never toggle, they are always visible.
	//	state = true
	//}
	
	
	
		
	el.parents('.sequence').first().find('.real_event').each(function(idx,ev){
		if (state){
			$(ev).removeClass('hidden')
		}
		else {
			$(ev).addClass('hidden')
		}
	})
	
	
	//for (i = 0; i < events.length; i++) {		
	//	if (state) {
	//		events[i].removeClass('hidden')
	//	}
	//	else {
	//		events[i].addClass('hidden')
	//	}
	
	//}
	
	el.parents('.sequence').first().find('.event_add_link').first().each(function(idx,lnk){
		if (state){
			$(lnk).removeClass('hidden')
		}
		else {
			$(lnk).addClass('hidden')
		}
	})
	//if (event_add_link) {
	//	if (state) {
	//		event_add_link.removeClass('hidden')
	//	}
	//	else {
	//		event_add_link.addClass('hidden')
	//	}
	//}
	
}



debug_display_hash = function(h) {
	//return(h.get('event_id')+' '+h.get('provider_id')+' '+h.get('time_slot_id'))
	return(h['event_id']+' '+h['provider_id']+' '+h['time_slot_id'])
}

debug_display_hash_old_way = function(h) {
	return(h['event_id']+' '+h['provider_id']+' '+h['time_slot_id'])
}

debug_display_arrangements_array = function(parray) {
	//TODO remove DEBUG
	dbg = ''
	for (i=0; i < parray.length; i++) {
		for (j=0; j < parray[i].length; j++) {
			dbg = dbg + '{'+debug_display_hash_old_way(parray[i][j])+'}'
		}
	}
	//alert(dbg)
	//////////
}


// The user is asking for details regarding timing of proposals.
display_time_slots_possibilities = function(el,bool) {
	time_slots_possibilities = el.parents('.provider').first().find('.time_slots.possibility').first()
	if (bool) {
		time_slots_possibilities.removeClass('hide')
		//time_slots_possibilities.addClassName('.show')
	}
	else {
		//time_slots_possibilities.removeClassName('.show')
		time_slots_possibilities.addClass('hide')
	}
}





// This function displays sequence_individual_proposals.
// Principle: all the providers possible have already been created on the page (cf event_proposals). So here we
// - parse the nuplets returned by the algo
// - find in the page the corresponding nodes: providers/time_slots/AIs
// - copy each node
// - clean the node (by keeping only the targeted time_slots as defined in the nuplet - not all the TS possible)
// - insert it in a newly created sequence_individual_proposal
// NOTE: the sequence_individual_proposal might have already been created and displayed. If it is the case, we do
// not re-render it. We only change its ranking if need be.

// Phase 2: we show alternative timing arrangements below the best one.
// Nomencalture:
// - master_arrangement: the best one (in time), for a given set of providers.
// - sub_arrangement: a worse one for the same set or providers.
//var DEFAULT_NUMBER_OF_PROPOSALS_SHOWN = 4 // Number of
//if (IS_MOBILE_DEVICE){
//	DEFAULT_NUMBER_OF_PROPOSALS_SHOWN = 3
//}

var NUMBER_OF_PROPOSALS_SHOWN = typeof(DEFAULT_NUMBER_OF_PROPOSALS_SHOWN) == "undefined" ? 3 : DEFAULT_NUMBER_OF_PROPOSALS_SHOWN
var shown_proposals_infos = {} // In this hash we are storing information per individual proposal displayed.
							   // Attention: this can't be reset between calls to render_sequence_proposals.
							   // Indeed: if a sequence_proposal is not re-rendered (i.e. it was already on the page),
							   // the respective info would be lost.
render_sequence_proposals = function(psequence_id) {
	
	
	
	if (proposed_arrangements == null){
		return false
	}

	jq_sequence_main_container = $('#sequence__'+psequence_id)	
	
	// We create the timegrid here
	min_time = 10000000000000000
	max_time = 0
	$.each(time_slots_internals, function(idx, ts_infos){
		min_time = min_time > ts_infos[0] ? ts_infos[0] : min_time
		max_time = max_time < ts_infos[1] ? ts_infos[1] : max_time
	})	
	
	jq_proposals_area = jq_sequence_main_container.find('.proposals_area')
	jq_proposals_area.find('.timeline_container').remove();
	jq_proposals_area.prepend(generate_timeline(min_time, max_time));
	
	
	// The parray contains the nuplets from the algo (modified with View info) that we want to feature.
	// parray = [ [{ev_id, prov_id, ts_id, ai_id, bl}, {}...], ... ]
	// The user may have already liked/dislike some providers. We need to adapt the possible
	// arrangements to be displayed based on this pre-selection.
	parray = adapt_available_arrangements_based_on_preselections(jq_sequence_main_container)	
		
	// There are no valid arrangements left
	if (parray.length == 0) {
		$('#no_arrangements_found_modal').dialog({ 
			modal: true, 
			resizable: false,
			draggable: false, 
			'buttons': [
				{ text:"Undo", 
				  click: function() { $(this).dialog("close"); resize_timing_bars(jq_sequence_main_container); roll_back_select_change(jq_sequence_main_container)}},
				{ text:"Search again",
				  click: function() {$('form').first().submit();}}
				],
			 dialogClass: 'message_dialog'  
		})
		
		return false
	}
	
	mark_non_available_elements(jq_sequence_main_container, parray)

	//TODO remove DEBUG
	debug_display_arrangements_array(parray)
		
	parray_for_view = clean_duplicates(parray) // Parray contains nuplets that differ only by the AIs.
	// This is a "problem" for the view. Indeed, what we display on each line is not really a nuplet as per matlab,
	// but a "pseudo" one on the Provider/TS level. I.e. on each line, we will display Prov/Ts/ALL AI possible.
	// So we need to "remove" nuplets that are duplicates at the Prov/TS level.
	debug_display_arrangements_array(parray_for_view)
	
	sort_option = 'score' // Default, in case.
	jq_sequence_main_container.find('.sorting_selector').find('option').each(function(idx, opt){
		if (opt.selected == true){ sort_option = opt.value }
	});
	

	switch(sort_option){
		case 'score':
			// Here we prepare the array of arrangements so that we can render it in order.
			parray_for_view = parray_for_view.sort(sort_by_score) // Here we sort the possible arrangements based on
																  // their respective score.
			
			parray_for_view = sorter(parray_for_view)// Grouping: now the array of arrangement looks like:
			// - master_arrangement XX%
			//    - sub_arrangement aa% (aa < XX)
			//    - sub_arrangement bb% (bb < XX)
			//    - ...
			// - master_arrangement YY%
			//    - sub_arrangement cc% (cc < YY)
			break;
		case 'starting_time':
			//parray_for_view = parray_for_view.sort(sort_by_starting_time)
			sort_it(parray_for_view,1,'begin_time',-1,'score')
			break;
		case 'price':
			//parray_for_view = parray_for_view.sort(sort_by_price)
			sort_it(parray_for_view,1,'price',-1,'score')
			parray_for_view = sorter(parray_for_view)
			break;
		default:
			//parray_for_view = parray_for_view.sort(sort_by_score)
			parray_for_view = sorter(parray_for_view)
	}
	
	
	// DEBUG
	//dstring = ""
	//$(parray_for_view).each( function (idx, el){
	//	dstring += el['arrangement'][0]['provider_id'] + " / " + el['arrangement'][1]['provider_id'] + " / " + el['score'] + "\n"
	//})
	//alert(dstring);
	
	//parray_for_view = check_diversity(parray_for_view, 5)
	parray_for_view = remove_adjacent(parray_for_view)
	parray_for_view = remove_adjacent(parray_for_view)
	
	// We don't want to re-render what is already on the page. We check here what has already been rendered.
	div_individual_proposals = jq_sequence_main_container.find('.sequence_individual_proposal')
	previous_ids = [] // previous_ids will contain the IDs of the sequence_individual_proposal that were already
					  // displayed before this call.
	div_individual_proposals.each(function(idx, d) {
		previous_ids.push(d.id);
	});
	
	

	
	new_ids = []
	previous_id = null
	
	current_master_arrangement = null
	master_count = 0
	sub_count = 0
	// We iterate on each arrangement
	while (sub_count < parray_for_view.length){
		// We have found a new master_arrangement.
		if ((current_master_arrangement == null) || !(is_similar(current_master_arrangement, parray_for_view[sub_count]['arrangement']))){
			if (master_count >= NUMBER_OF_PROPOSALS_SHOWN){
				break;
			}
			else {
				master_count += 1
				current_master_arrangement = parray_for_view[sub_count]['arrangement']
				additional_class = 'master_arrangement'
			}
		}
		else {
			additional_class = 'sub_arrangement'
		}
	
	//for (i = 0; i < Math.min(parray_for_view.length, NUMBER_OF_PROPOSALS_SHOWN); i++) { //TODO: don't hard code the number of proposals
		
		i = sub_count
		
		jq_proposal_element = null
		
		larrangement = parray_for_view[i]['arrangement']
		
		// We construct the ID for the arrangement (it contains the sequence_id and provider_id/ts_id for each event).
		id_buffer = ''
		for (j = 0; j < larrangement.length; j++) {
			id_buffer = id_buffer + '__' + 'sequence__' + psequence_id + '__event__'+ larrangement[j]['event_id'] + '__provider__' + larrangement[j]['provider_id'] + '__time_slot__' + larrangement[j]['time_slot_id']
		}
		
		// We check whether this arrangement has already been rendered.
		if (previous_ids.indexOf(id_buffer) >= 0) {
			
			// If it was already on the page, we may have to update its ranking. We do it here.
			temp_selector = '#' + id_buffer
			jq_proposal_element = $(temp_selector)
			
			// Cleaning
			jq_proposal_element.removeClass('sub_arrangement')
			jq_proposal_element.removeClass('master_arrangement')
			
			html_classes = (jq_proposal_element.first().attr('class')).split(" ")
			temp = jQuery.grep(html_classes, function(el,idx){return (el.search(/rank__/)>=0)})
			if (temp.length > 0) {
				previous_rank = parseInt(temp[0].replace(/rank__/,''))
				// The rank has changed. We treat this proposal as a new one (i.e. we remove it from the view
				// and we will re-add it with the new rank).
				if (previous_rank != master_count){
					// In case this proposal had a "show sub arrangements button" we remove it here (we don't know)
					// at this stage whether it will still need one - will be re-added later if need be.
					jq_proposal_element.find('.sub_arrangement_toggle_button').first().remove();
					jq_proposal_element.removeClass(temp[0])
					/////$(proposal_element).find('proposal').removeClass(temp[0])
					//$(proposal_element).next('.sequence_proposals_spacer').remove()
					jq_proposal_element.detach(); // Attention : use detach here and not remove! We need to keep the binding in place
												  // since we will re-add this element later.
					previous_ids.splice(previous_ids.indexOf(id_buffer),1)
				}
				else {
					////container.removeClass(temp[0])
					////container.addClass('rank__' + i)
					jq_proposal_element.addClass(additional_class)
					
					// Remove or add "show sub arrangements button"
					if ((additional_class == 'sub_arrangement') || 
					   (i == parray_for_view.length - 1) || //Master but Last element
					   !(is_similar(larrangement, parray_for_view[i+1]['arrangement']))){ // Master and Next one is different
						jq_proposal_element.find('.sub_arrangement_toggle_button').first().remove();
					}
					else if ((additional_class == 'master_arrangement') && (i != parray_for_view.length - 1) && (is_similar(larrangement, parray_for_view[i+1]['arrangement']))){
						if (jq_proposal_element.find('.sub_arrangement_toggle_button').length == 0){
							// Add "show sub arrangements button"
							sub_arrangement_toggle_button = $("<a/>", {'class': 'sub_arrangement_toggle_button', 'href':'#'}).html("Alternate times")
							sub_arrangement_toggle_button.click(function(){
								$(this).parent().nextUntil('.master_arrangement').each(function(idx,el){
								//$(this).parent().next('.sequence_proposals_spacer').nextUntil('.master_arrangement').each(function(idx,el){
									if ($(el).hasClass('sub_arrangement')){
										$(el).toggle();
										//$(el).next().toggle();//spacer
									}
								})
								
								resize_timing_bars($(this).parents('.proposals_area').first())
								return false;

							})
							jq_proposal_element.append(sub_arrangement_toggle_button)
						}
					}
					
					previous_id = id_buffer;
					new_ids.push(id_buffer);
					
					//$(proposal_element).find('proposal').each(function(idx, element){
					//	element.className.replace(/rank__[0-9]*/,'rank__' + i)
					//})
					sub_count += 1
					continue; // We are done with this arrangement, we skip to the next one.
				}		
			}			
			
		}
		
		

		
		// For each nuplet, we create a sequence_individual_proposal container
		jq_proposal_element = ((jq_proposal_element != null) && (jq_proposal_element.length > 0)) ? jq_proposal_element : generate_proposal(jq_sequence_main_container, i, parray_for_view[i]['arrangement'], parray_for_view[i]['score'], parray_for_view[i]['price']['value'], parray_for_view[i]['price']['currency'], shown_proposals_infos)
		// Add a "show sub arrangements button" if necessary.
		if ((additional_class != 'sub_arrangement') && (i != parray_for_view.length - 1) && (is_similar(larrangement, parray_for_view[i+1]['arrangement']))){ // It is a meta_arrangement
			sub_arrangement_toggle_button = $("<a/>", {'class': 'sub_arrangement_toggle_button', 'href':'#'}).html("Alternate times")
			sub_arrangement_toggle_button.click(function(){
				$(this).parent().nextUntil('.master_arrangement').each(function(idx,el){
				//$(this).parent().next('.sequence_proposals_spacer').nextUntil('.master_arrangement').each(function(idx,el){
					if ($(el).hasClass('sub_arrangement')){
						$(el).toggle();
						//$(el).next().toggle();//spacer
					}
				})
				resize_timing_bars($(this).parents('.proposals_area').first())
				return false;
					
			})
			jq_proposal_element.append(sub_arrangement_toggle_button)
		}
		
		

		
		// We have created the html element for the arrangement. We can't just append
		// to the main container: it might contain some lower ranked arrangements that won't
		// be removed. So we need to fund where to insert the newly created arrangement.
		if ((jq_proposal_element != null) && (jq_proposal_element.length > 0)) {
			new_ids.push(jq_proposal_element.attr('id'))
			jq_proposal_element.addClass('rank__' + master_count)
			jq_proposal_element.addClass(additional_class)
			//$(proposal_element).find('.proposal').addClass('rank__' + i)
			if ((jq_sequence_main_container != null) && (jq_sequence_main_container.length > 0)){
				jq_sequence_proposals = jq_sequence_main_container.find('.sequence_proposals').first()
				if (previous_id != null) {
					jq_previous_proposal = jq_sequence_proposals.find("#"+previous_id).first()
					////previous_proposal.after(proposal_element)
					if (!(jq_proposal_element.hasClass('sub_arrangement'))){
						if (!IS_MOBILE_DEVICE){
							jq_proposal_element.hide().insertAfter(jq_previous_proposal).fadeIn('slow');
						}
						else {
							jq_proposal_element.insertAfter(jq_previous_proposal); // Faster
						}
					}
					else {
						jq_previous_proposal.after(jq_proposal_element)
					}
					
					// After each arrangement, we add a spacer.
					//previous_proposal.after($("<div/>", { 'class': 'sequence_proposals_spacer' }))
					
				}
				else {
					////sequence_proposals.prepend(proposal_element)
					if (!(jq_proposal_element.hasClass('sub_arrangement'))){
						if (!IS_MOBILE_DEVICE){
							jq_proposal_element.hide().prependTo(jq_sequence_proposals).fadeIn('slow')
						}
						else {
							jq_proposal_element.prependTo(jq_sequence_proposals) // Faster
						}
					}
					else {
						jq_sequence_proposals.prepend(jq_proposal_element)
					}
					
					
					// After each arrangement, we add a spacer.
					//new_proposal_container.after($("<div/>", { 'class': 'sequence_proposals_spacer' }))
					
				}
				
			}
			previous_id = jq_proposal_element.attr('id') //full_id
		}

		sub_count += 1

	}
	

	
	elements_to_be_removed = $()
	$(previous_ids).each(function(idx, e) {
		if (new_ids.indexOf(e) < 0) {
			temp_selector = '#' + e
			//temp = $(temp_selector).next('.sequence_proposals_spacer')
			//if (temp.length > 0) {
			//	temp.remove();
			//}
			
			elements_to_be_removed = elements_to_be_removed.add($(temp_selector))
			
			// Old method
			////$(temp_selector).remove_safe();
			
			// We also need to remove from the global shown_proposals_infos hash
			delete shown_proposals_infos[e];
		}
	});
	

	
	elements_to_be_removed.remove_safe()
	
	
	////mark_non_available_elements(psequence_id, parray)
	

	// In each proposal, the providers have not yet been "positioned" to match the timegrid. We do it now.
	position_possibilities(jq_sequence_main_container, shown_proposals_infos, min_time, max_time)
	
	//sequence_main_container.find('.vertical_line').css('height', sequence_main_container.find('.sequence_proposals').first().height() + 10)
	resize_timing_bars(jq_sequence_main_container)
	
	
	// Setup the href for the "show less proposals link", so that the automatic repositioning in the page makes sense.
	jq_visible_proposals = jq_sequence_main_container.find(".sequence_individual_proposal").filter(":visible")
	if (jq_visible_proposals.length > 2){
		jq_sequence_main_container.find(".show_less_proposals_button").attr('href', "#" + jq_visible_proposals[jq_visible_proposals.length - 2].id)
	}
	
	
	// Mobile specific
	if (IS_MOBILE_DEVICE){
		jq_ltimeline = jq_sequence_main_container.find('.proposals_area').find('.timeline_container').detach();
		jq_sequence_main_container.find('.proposals_area').find('.sequence_individual_proposal').find('.activities').before(jq_ltimeline)
	}
	
	
	do_updates_after_content_change();
	
	return true
	
}

// TODO: pass jq object as parameter. Where is this used?
do_manual_updates = function(proposal_element, parray_for_view_element){
	jq_proposal_element = $(proposal_element)
	jq_proposal_element.find('.sequence_individual_proposal_information_section_after').first().find('.pricing_info').first().html(format_price(parray_for_view_element['price']['value'], parray_for_view_element['price']['currency']))
}


// Input: an ordered by score array of arrangement
// Output:
// - master_arrangement XX%
//    - sub_arrangement aa% (aa < XX)
//    - sub_arrangement bb% (bb < XX)
//    - ...
// - master_arrangement YY%
//    - sub_arrangement cc% (cc < YY)
sorter = function(parray){
	
	new_array = []
	current_master = null
	while (parray.length > 0){
		if (current_master == null){
			current_master = (parray.splice(0,1))[0]
			new_array.push(current_master)
		}
		j = parray.length
		for (i = 0; i < j; i++){
			if (is_similar(parray[i]['arrangement'], current_master['arrangement'])){
				new_array.push((parray.splice(i,1))[0])
				j = j - 1;
				i = i - 1;
			}
		}
		
		current_master = null
		
	}
	
	return new_array
	
}

// Returns true if both arrangements feature the same providers in the same order.
is_similar = function(arr1, arr2){
	temp = true
	for (ii = 0; ii < arr1.length; ii++){
		if (arr1[ii]['provider_id'] != arr2[ii]['provider_id']){
			temp = false
			break;
		}
	}
	
	//if (temp)
	
	return temp
}

resize_timing_bars = function(jq_sequence_main_container){
	if (!(IS_MOBILE_DEVICE)){
		jq_sequence_main_container.find('.vertical_line').css('height', jq_sequence_main_container.find('.sequence_proposals').first().height() + 10)
	}
}

// Returns a JQuery object!
generate_proposal = function(jq_sequence_main_container, rank, larrangement, score, price, currency, ts_infos){

	psequence_id = jq_sequence_main_container.attr('id').replace(/sequence__/,'')
	
	new_proposal_container = $("<div/>", { 'class': 'sequence_individual_proposal' })	
	new_proposal_container.append($("<div/>", { 'class': 'activities' }))

	full_id = ''
	temp_info = {} // Will be used to store the time_slots info
	// We iterate on each element of the nuplet
	for (j = 0; j < larrangement.length; j++) {
		temp = 'sequence__' + psequence_id + '__event__'+ larrangement[j]['event_id'] + '__provider__' + larrangement[j]['provider_id']
		temp_selector = '#' + temp
	
		ldiv = $(temp_selector) // Here we locate the already created node.
		if (ldiv == null) {
			alert('Oups. Something went wrong. E735')
			//everything_fine = false
			return null;
		}
		else {
			
			cloned_div = ldiv.clone(); // We copy this node
			
			// In the proposal view, we only keep the right time_slot
			temp2 = temp + '__time_slot__' + larrangement[j]['time_slot_id']
			cloned_div.find('.time_slot.possibility').not('#' + temp2).remove();
			
			
			cloned_div.attr('id', cloned_div.attr('id') + "__duplicate") // In the proposal section,
																		 // the providers id end with __duplicate.
			// These IDs are used by AJAX - so we need to mark them as duplicates.
			cloned_div.find('.time_slots').attr('id', function(){
				return this.id + "__duplicate"
			});
			
			
			// Here we prepare the info linked to this time_slot
			temp_info[temp2] = {'start_date': time_slots_internals[larrangement[j]['time_slot_id']][0], 'end_date':time_slots_internals[larrangement[j]['time_slot_id']][1]}
			

			cloned_div.addClass('proposal')
			cloned_div.find('.possibility').addClass('proposal')
			

			full_id = full_id + '__' + temp2
			//new_proposal_container.find('.activities').append(cloned_div.css('display','none').fadeIn())
			new_proposal_container.find('.activities').append(cloned_div)

		}
	}
	new_proposal_container.attr('id', full_id)
	////new_ids.push(full_id)
	ts_infos[full_id] = temp_info


	// Score container
	if (IS_MOBILE_DEVICE){ // No tooltip
		proposal_container_info_section_before = $("<div/>", { 'class': 'sequence_individual_proposal_information_section_before', 'title': 'Score is a combination of distance, time, reviews and more.' }).html("<small>Score:</small>" + "<strong>"+Math.round(score*100)+"%</strong>")
	}	
	else {
		proposal_container_info_section_before = $("<div/>", { 'class': 'sequence_individual_proposal_information_section_before', 'title': 'Score is a combination of distance, time, reviews and more.' }).html("<small>Score:</small>" + "<strong>"+Math.round(score*100)+"%</strong>").tooltip({'tipClass': 'score_tooltip', 'position': 'bottom right', 'offset': [-20, -20]}).addClass('tooltip_trigger')
	}
	
	// Additional info container
	proposal_container_info_section_after = $("<div/>", { 'class': 'sequence_individual_proposal_information_section_after' })
	// Display Min Price for arrangement:
	pricing_div = $("<div/>", { 'class': 'pricing_info' }).html(format_price(price, currency))
	proposal_container_info_section_after.append(pricing_div)//("Starting at: " + format_price(price, currency))//get_proposal_minimum_price(new_proposal_container))
	// Select Button
	if (IS_MOBILE_DEVICE){
		additional_info_button = $("<a/>", {'href': '#', 'class': 'additional_info_test', 'data-role': 'button', 'data-icon': 'arrow-r', 'data-iconpos': 'notext'}).html("More info")
		proposal_container_info_section_after.prepend(additional_info_button)//.trigger( "create" ); // Trigger event is used to enhance the control once added dynamically.
		
		//additional_info_button.button()
		
		//additional_info_button = $("<a/>", {'class': 'additional_info_test'}).html("<button class='desktop_navigation' type='button'>Details & Booking</button><br><img class='mobile_navigation' src='/images/mobile_next_page.png'/>")
		//additional_info_button = $("<a/>", {'data-role': 'button', 'data-icon': 'geo_location_icon', 'data-iconpos': 'notext'})
	}
	else {
		additional_info_button = $("<a/>", {'class': 'additional_info_test'}).html("<button class='desktop_navigation' type='button'>Details & Booking</button>")
		proposal_container_info_section_after.prepend(additional_info_button);
	}
	
	//proposal_container_info_section_after.prepend(additional_info_button).trigger( "create" );
	//additional_info_button = $("<a/>", {'class': 'additional_info_test'}).html("Map")
	//proposal_container_info_section_after.append(additional_info_button)
	additional_info_button.click( function(event) {
		
		var lcontainer = $(this).parents('.sequence_individual_proposal').first()
		if (IS_MOBILE_DEVICE){
			if (USING_LOADING_MESSAGE){
				$.mobile.showPageLoadingMsg();
			}
			var lmap_container = $("#booking_options_page #map_canvas_container" + lcontainer.attr('id'))//.find('#map_canvas_container' + lcontainer.attr('id'))
		}
		else {
			var lmap_container = lcontainer.find('.map_canvas_container')
		}
	
	
		// If user clicks (first time), we create the div.
		if (lmap_container.length == 0) {
			
			lmap_container = $('.map_canvas_container').first().clone_safe();		
			lcontainer.append(lmap_container)
			update_map(lcontainer, lmap_container)
			
			lmap_container.attr('id', 'map_canvas_container' + lcontainer.attr('id'))
			lmap_container.addClass('hide'); // Just so that thet first toggle (below) works.
		
		}
				
		
		if (IS_MOBILE_DEVICE){
			
			// Hide everything
			$('#booking_options_page .map_canvas_container').addClass('hide')
			
			// Update booking options with the latest
			lmap_container.find('.booking_options').remove()	
			jq_booking_options = render_booking_options(lcontainer)
			////jq_booking_options.trigger( "create" )
			lmap_container.prepend(jq_booking_options)
			
			$('#booking_options_page .booking_options_content').append(lmap_container.removeClass('hide'))
			
			jq_booking_options.trigger( "create" )
			
			$.mobile.changePage($('#booking_options_page'))
		}
		else {	// Desktop	
			
			lcontainer.find('.booking_options').remove()
				
			// TODO: can't we just toggle?
			if (lmap_container.hasClass('hide')) {
		
				lmap_container.removeClass('hide');	
				jq_booking_options = render_booking_options(lcontainer)
				lmap_container.prepend(jq_booking_options)
			
				lheight = lcontainer.css('height')
				jq_booking_options.css('float', 'left')
				lcontainer.css('height', lheight)			
			
			}
			else {
		
				lmap_container.addClass('hide');
				//lcontainer.find('.booking_options').remove()
				lcontainer.css('height', 'auto')
			}
		}
		resize_timing_bars($(this).parents('.proposals_area').first());
		
		//refresh_scroller();
		
	
	});
	
	
	new_proposal_container.prepend(proposal_container_info_section_before)
	new_proposal_container.append(proposal_container_info_section_after)
	new_proposal_container.append($("<div/>", { 'class': 'clear_float' }))
	
	
	// In the mobile version, we actually show the activities twice:
	// - once in the timeline with limited info
	// - once after, with the details.
	if (IS_MOBILE_DEVICE){
		activities = new_proposal_container.find('.activities')
		activities2 = activities.clone_safe()
		activities2.removeClass('activities').addClass('activities_repeat')
		
		activities.find('.provider_type_icon').removeClass('black')
		
		if (false) {
			activities2.find('.provider').each(function(){
				//show_all_possibilities_link = $("<a/>", {'id': $(this).attr('id') + "_show_possibilities", 'href': '#', 'class': 'show_all_possibilities_link', 'data-role': 'button', 'data-icon': 'plus', 'data-iconpos': 'notext'}).html("Others")
				show_all_possibilities_link = $("<div/>", {'id': $(this).attr('id') + "_show_possibilities", 'class': 'show_all_possibilities_link'}).html("...")
			
			
				show_all_possibilities_link.click( function(event) {
					target_id = $(this).attr('id').match(/sequence__[0-9]*__event__[0-9]*/) + '__proposals'
					$('#' + target_id).dialog({ modal: true, draggable: false, resizable: false, dialogClass: 'all_possibilities_dialog'}).bind('close_all_possibilities_modal', function(){$(this).dialog('close')});
					return false;
				})
				//show_all_possibilities_link.trigger( "create" )
				$(this).after(show_all_possibilities_link)
			})
		}
		
		////activities2.trigger( "create" )  // Nothing needs to be enhanced in our current design.
		
		activities.after(activities2)
		
		section_after = new_proposal_container.find('.sequence_individual_proposal_information_section_after').detach()
		new_proposal_container.find('.sequence_individual_proposal_information_section_before').after(section_after)
		
	}
	
	return new_proposal_container // We return the jq element

}


generate_timeline = function(min_time, max_time){
	
	timeline_container = $("<div/>", { 'class': 'timeline_container' })
	timeline_container.append($("<div/>", { 'class': 'sequence_individual_proposal_information_section_before' }))
	
	timeline0 = $("<div/>", {'class': 'timeline'})
	timeline = $("<div/>", {'class': 'timeline_positioner'})
	timeline.css('position', 'relative')
	timeline.css('height', '30px')
	vertical_lines = $("<div/>", {'class': 'vertical_lines'})
	vertical_lines.css('position', 'relative')
	vertical_lines.css('height', '10px')
	
	timeline0.append(timeline)
	timeline0.append(vertical_lines)
	
	number_of_hours = parseInt((max_time - min_time)/3600000)
	
	//number_of_divisions = Math.min(number_of_hours, 5)
	//division_duration = parseInt(number_of_hours / number_of_divisions)
	
	
	division_duration = parseInt(number_of_hours / IDEAL_NUMBER_OF_TIMELINE_DIVS)
	if (division_duration == 0){
		division_duration = 1
	}
	number_of_divisions = parseInt(number_of_hours / division_duration)
	
	start_date = new Date(min_time)
	first_time_shown = start_date
	if (first_time_shown.getMinutes() != 0){
		first_time_shown.setHours(start_date.getHours() + 1)
		first_time_shown.setMinutes(0)
	}
	else {
		number_of_divisions += 1
	}
	
	
	
	for (i = 0; i < number_of_divisions; i++){
		marker_time_ms = first_time_shown.getTime() + i*division_duration*3600000 // in milliseconds
		marker_display_value = ((new Date(marker_time_ms)).getUTCHours())%24 + ':00'
		marker = $("<div/>", { 'class': 'time_marker' }).html(marker_display_value);
		marker.css('left', ((marker_time_ms - min_time)/(max_time - min_time)*100.0 - 2) + '%')
		timeline.append(marker)
		
		line = $("<div/>", { 'class': 'vertical_line' });
		line.css('left', ((marker_time_ms - min_time)/(max_time - min_time)*100.0) + '%')
		vertical_lines.append(line)
		
	}
	
	timeline_container.append(timeline0)
	timeline_container.append($("<div/>", { 'class': 'sequence_individual_proposal_information_section_after' }))
	timeline_container.append($("<div/>", { 'class': 'clear_float' }))
	
	return timeline_container[0]
}


position_possibilities = function(jq_sequence_main_container, shown_proposals_infos, min_time, max_time){	
	
	activities_size = $('.master_arrangement').first().find('.activities').first().innerWidth();
	
	// TODO: There is a "bug" in JQueryMobile: when the page is not displayed, its elements have a "random" size. The
	// is only right when the objects are actually shown on the page.
	// So we are just storing the real size here. Awful solution...
	if (IS_MOBILE_DEVICE){
		if ((ACTIVITIES_SIZE_TEMP == null) || (ACTIVITIES_SIZE_TEMP < 150)){
			ACTIVITIES_SIZE_TEMP = activities_size
		}
		if (activities_size < 100) {
			activities_size = ACTIVITIES_SIZE_TEMP
		}
	}
	
	
	
	$.each(shown_proposals_infos, function(key, sequence){
		container = jq_sequence_main_container.find('#' + key)
		//accu = 0
		previous_end = min_time
		$.each(sequence, function(key2, infos){
			//possibility = container.find('.activities').find('#' + key2.replace(/__time_slot__[0-9]*/,'__duplicate'))
			possibility = container.find('.activities ' + '#' + key2.replace(/__time_slot__[0-9]*/,'__duplicate'))
			lm = (infos['start_date'] - previous_end)/(max_time - min_time)
			//possibility.css('margin-left', lm + '%')
			possibility.css('margin-left', parseInt(lm * activities_size)) //TODO: this is slow!
			width = (infos['end_date'] - infos['start_date'])/(max_time - min_time)
			//possibility.css('width', width + '%')
			//possibility.css('width', parseInt(width/100 * activities_size))
			possibility.outerWidth(parseInt(width * activities_size))			
			
			previous_end = infos['end_date']
			//accu = lm + width
		})
	})
	
}


// Initialization stuff.
$(document).ready( function() {
	
	// Initialize selectBoxes
	create_selectBoxes();
	
	// Pre-set the correct dates for the dates choosers (the select has been rendered in JS,
	// we need to replicate in the input fields)
	$('select[name=when]').change()
	
	
	//initiate_geolocation()
	
	// We need to store the current value of type/sub_type for each event
	// to be able to roll back. Cf activity_type_change.
	$('.events').find('.event_type select').each(function(){
		$(this).data('current', $(this).val())
		$(this).data('current_sub_type', $(this).parents('.event_data').find('.event_sub_type select').val())
	})
	
	$(document).trigger('updated_fields')
	
	
	// Display error messages
	if (!(typeof error_message === 'undefined') && (error_message.length > 0)){
		$('#main_error').empty();
		$('#main_error').html(error_message).dialog({modal: true, resizable: false, draggable: false, buttons: { "OK": function() { $(this).dialog("close"); } }, dialogClass: 'message_dialog' })
	}
	
	
	//Placeholder script
	$('input[placeholder], textarea[placeholder]').placeholder();

		
});

$(document).bind("pagecreate", function(){ //After the initialization
    if (!(typeof in_results_view === 'undefined') && in_results_view){
		document.location.href="#results_page";
		//$('#results_page').append($('#waiting_screen'))
	}
	
	// Mobile specifics
	do_mobile_specific_updates()
	
});

$(document).bind('updated_fields', function(){
	if (!(IS_MOBILE_DEVICE)){
		$('.sequence').each(function(){
			if ($(this).find('.event:visible').length <= 2){
				$(this).find('.event_add_link').css('margin-top', 50)
			}
			else {
				$(this).find('.event_add_link').css('margin-top', 0)
			}
		 
		})
	}
});


$(document).bind('parameters_got_updated', function(){
	
	if (IS_MOBILE_DEVICE) {
	
		if (SUBMIT_TOOLTIP_HAS_BEEN_SHOWN || (typeof in_results_view === 'undefined') || in_results_view == false) { return }
	
		if (IS_MOBILE_DEVICE){
			submit_button = $('#results_page .go_button')
			submit_button.show()
			lmy = 'right top'
			lat = 'right bottom'
		}
		else {
			submit_button = $('#solution_submit')
			lmy = 'center top'
			lat = 'center bottom'	
		}
	
		go_position = submit_button.position()
		//$('#submit_tooltip').dialog({ position: [go_position.left + 70, go_position.top + 100], draggable: false, resizable: false, dialogClass: "submit_button_dialog"});
		$('#submit_tooltip').dialog({ draggable: false, resizable: false, width: 50, height: 90, dialogClass: "submit_button_dialog"}).dialog('widget').position({ my: lmy, at: lat, of: submit_button });
		
		if (IS_ANDROID_DEVICE){
			$('#submit_tooltip').dialog().click(function(){$(this).close()})
		}
	
	
		SUBMIT_TOOLTIP_HAS_BEEN_SHOWN = true
		submit_button = null
	
	}
	
});


show_inline_tips = function(){
	
	if ($('.post_it').length > 0) {return;}
	
	var lmy = 'center top'
	var lat = 'center bottom'
	var target = $('.show_all_possibilities_link').first()
	$('.inline_tips .show_all_possibilities_tip').dialog({ draggable: false, resizable: false, dialogClass: "post_it rotate_right", height: 'auto', width: 200}).dialog('widget').position({ my: lmy, at: lat, of: target, offset:"0 0" });
	
	lmy = 'left top'
	lat = 'right bottom'
	target = $('.sequence_proposals .pre_select_button').first()
	$('.inline_tips .padlock_tip').dialog({ draggable: false, resizable: false, dialogClass: "post_it rotate_right", height: 'auto', width: 200}).dialog('widget').position({ my: lmy, at: lat, of: target, offset:"0 0" });
	
	//lmy = 'right center'
	//lat = 'left top'
	//target = $('.event').first()
	//$('.inline_tips .activities_type_tip').dialog({ draggable: false, resizable: false, dialogClass: "post_it", height: 'auto', width: 200}).dialog('widget').position({ my: lmy, at: lat, of: target, offset:"40 60" });
	
	//lmy = 'center top'
	//lat = 'center bottom'
	//target = $('.event_add_link').first()
	//$('.inline_tips .activities_tip').dialog({ draggable: false, resizable: false, dialogClass: "post_it rotate_left", height: 'auto', width: 100}).dialog('widget').position({ my: lmy, at: lat, of: target, offset:"0 40" });
	
	lmy = 'center top'
	lat = 'left bottom'
	target = $('.form_submit_button input').first()
	$('.inline_tips .submit_tip').dialog({ draggable: false, resizable: false, dialogClass: "post_it rotate_left", height: 'auto', width: 230}).dialog('widget').position({ my: lmy, at: lat, of: target, offset:"0 0" });	
	
	
}

do_mobile_specific_updates = function(){
	if (IS_MOBILE_DEVICE){
		//$('.input_for_datepicker').attr('readonly', 'readonly')
		
		/*
		setTimeout(function(){
		    // Hide the address bar!
		    window.scrollTo(0, 1);
		  }, 50);
		*/
	}
	
	IDEAL_NUMBER_OF_TIMELINE_DIVS = 3
	
}

create_selectBoxes = function (el){
	
	//return false
	
	if (USING_SELECTBOX){
		if (el === undefined){
			lselects = $("select")
			
			//lselects = lselects.not('#directions_travel_mode')
		}	
		else {
			lselects = $(el).find("select")
		}
		lselects.each(function(idx, lselect){
			$(lselect).selectBox() // Create a select box for each select
			
			//$(lselect).selectBox('control').attr('name', $(lselect).attr('name')) // Matching the names
			
			//$(lselect).selectBox('control').addClass($(lselect).attr('class')) // Gives to the select box 
													// (an anchor tag in fact) the same class as for the original select.
		
			// The role of the below is to ensure that when the real select is
			// changed (say via javascript), this is repercuted on the selectBox itself.
			//$(lselect).change(function(event){
			$(lselect).bind('update_dependents', function(event){
				try{$(lselect).selectBox('value', $(this).val());} // For SelectBox
				catch (err) {}
			})
		})
	}
	else {
		if (el === undefined){
			lselects = $("select")
			
			//lselects = lselects.not('#directions_travel_mode')
		}	
		else {
			lselects = $(el).find("select")
		}
		lselects.each(function(idx, lselect){
			$(lselect).bind('update_dependents', function(event){
				if (!($(lselect).is(':data(selectmenu)'))) {
					$(lselect).selectmenu();
				}
				$(lselect).selectmenu('refresh'); // For JqueryMobile
			})
		})
	}
}

update_select_box = function (jq_element){
	//return false
	
	if (USING_SELECTBOX){
		try {jq_element.selectBox('destroy');}
		catch (err){}
		jq_element.selectBox();
		//jq_element.selectBox('control').attr('name', jq_element.attr('name')) // Matching the names
	}
}


////////////


mark_non_available_elements = function (jq_sequence_main_container, pvalid_arrangements_array) {
	
	psequence_id = jq_sequence_main_container.attr('id').replace(/sequence__/,'')
	
	//sequence = $('#sequence__'+psequence_id)
	// We can't do sequence.find to find providers: when they are shown in a dialog, they are taken out of the normal DOM.
	providers = $('.provider[id*="sequence__' + psequence_id + '"]')
	providers.addClass('invalid').removeClass('valid')
	
	already_done = []
	$(pvalid_arrangements_array).each(function(idx, lhash){ //TODO: instead of going through all arrangements, we should first
		// return only the different ones in the hash.
		$(lhash['arrangement']).each(function(idx, a){
			if (already_done.indexOf(a['event_id'] + '__' + a['provider_id']) < 0) {
				//$('.provider[id*="sequence__' + psequence_id + '__event__' + a['event_id'] + '__provider__' + a['provider_id'] + '"]').addClass('valid').removeClass('invalid')
				
				//providers.filter(function(){ return (this.id.search('sequence__' + psequence_id + '__event__' + a['event_id'] + '__provider__' + a['provider_id']) >= 0) }).addClass('valid').removeClass('invalid')
				providers.filter('[id*="sequence__' + psequence_id + '__event__' + a['event_id'] + '__provider__' + a['provider_id'] + '"]').addClass('valid').removeClass('invalid')
				
				already_done.push(a['event_id'] + '__' + a['provider_id'])
			}
		})
		
	})
	
}


update_map = function(pproposal_container, pmap_container) {
	//////////////////////////////////////////////////////////////////////////////
	// Maps representation
	vproviders_list = pproposal_container.find('.activities').first().find('.provider')
	maps_points = []
	vproviders_list.each(function(idx, e) {
		llatitude = $(e).find('.latitude').first().html()
		llongitude = $(e).find('.longitude').first().html()
		lhtml_data = $(e).find('.provider_name').first().clone().html() //e.down('.provider_data')//e.down('.address').innerHTML
		//alert(laddress);
		maps_points.push({'latitude': llatitude, 'longitude': llongitude, 'html_data': lhtml_data})
	});
	//prepare_map(maps_points, pmap_container)
	
	if (maps_points.length <= 1){
		pmap_container.find('.travel_mode').first().hide()
		pmap_container.find('.directionsPanel').first().hide()
	}
	
	//render_map(maps_points, pmap_container.find('#map_canvas')[0], pmap_container.find('#directions_travel_mode').first().val())
	render_map_generic(pmap_container.find('.map_canvas')[0], null, maps_points, true, pmap_container.find('.directionsPanel')[0], pmap_container.find('.directions_travel_mode').first().val())
}

display_map_with_marker = function(el, pmap_container) {
	//alert(el)
	
	vprovider = el//.parents('.provider').first()
	
	//alert(vprovider)
	
	llatitude = vprovider.find('.latitude').first().html()
	llongitude = vprovider.find('.longitude').first().html()
	lhtml_data = vprovider.clone().html()
	
	larray = [];
	larray.push({'latitude': llatitude, 'longitude': llongitude, 'html_data': lhtml_data})
	
	////display_markers(larray, pmap_container[0]);
	render_map_generic(pmap_container[0], null, larray, false)
}

//attach_tip = function(el,target_content) {
//	ltip = new Tip(el, $(target_content), {
//					//title: "Click",
//					closeButton: true,
//					showOn: 'click',
//					hideOn: { element: 'closeButton', event: 'click'},
//					//stem: 'bottomMiddle',
//					//hook: { target: 'topMiddle', tip: 'bottomMiddle' },
//					viewport: true,
//					offset: { x: 0, y: -2 },
//					width: 'auto'
//				});
//	el.prototip.show();
//}


// This function takes our available_arrangements hash and removes duplicates - duplicates being two proposed arrangements
// with the same Prov/TS. Indeed, they might only differ by their AIs, but for the view we sometimes need to remove 
// these duplicates at Prov/TS level.
// Example: parray = [
// [{"time_slot_id":269983,"provider_id":51636,"event_id":3257,"availability_information_id":684057},{"time_slot_id":510786,"provider_id":52862,"event_id":3258,"availability_information_id":659899}],
// [{"time_slot_id":269986,"provider_id":51636,"event_id":3257,"availability_information_id":684057},{"time_slot_id":510786,"provider_id":52862,"event_id":3258,"availability_information_id":659899}]
// ];
clean_duplicates = function(parray) {
	
	for (i = 0; i < parray.length; i++) {
		for (j = i + 1; j < parray.length; j++) {
			is_equal = true
			array_i = parray[i]['arrangement']
			array_j = parray[j]['arrangement']
			for (k = 0; k < array_i.length; k++) {
				if (array_j[k]['time_slot_id'] != array_i[k]['time_slot_id']) {
					is_equal = false
				}
			}
			if (is_equal) {
				
				// We assign the minimum price to the arrangement we keep
				if (parray[i]['price']['value'] > parray[j]['price']['value']){
					parray[i]['price']['value'] = parray[j]['price']['value']
				}
				
				
				parray.splice(j,1)
				j--
			}
		}
	}
	return parray
	
}


pre_selection_button_touched_tentative = function(el, is_initiator, update_pre_selection) {
	if (IS_MOBILE_DEVICE){
		$.mobile.showPageLoadingMsg();
		pre_selection_button_touched_s(el, is_initiator, update_pre_selection)
		$.mobile.hidePageLoadingMsg();
	}
	else {
		pre_selection_button_touched_s(el, is_initiator, update_pre_selection)
	}
}

pre_selection_button_touched = function (el, is_initiator, update_pre_selection) {
	if (is_initiator === undefined) {
		is_initiator = false
	}
	else {
		
	}
	
	if (update_pre_selection === undefined) {
		update_pre_selection = true
	}	
	if (el[0].tagName.toLowerCase() == 'input') {
		if (!is_initiator) {
			el[0].checked = !(el[0].checked)
		}
		//temp = el.next()
		temp = el.parents('.pre_select_button')
		if (temp.attr('class').match('padlock_open') != null){
			temp.removeClass('padlock_open').addClass('padlock_close')
		}
		else {
			temp.removeClass('padlock_close').addClass('padlock_open')
		}
		
		if (update_pre_selection) {
			pre_selection_change(el);
		}
	}
	if ((el.attr('class') != undefined) && (el.attr('class').match('pre_select_button') != null)) {
		//pre_selection_button_touched(el.prev())
		pre_selection_button_touched(el.find('input'))
	}
	
	
	//if (IS_MOBILE_DEVICE){
	//	$.mobile.changePage( "#results_page");
	//}
	if (is_initiator == true){
		$(document).trigger('parameters_got_updated')
		if (IS_MOBILE_DEVICE){
			mobile_scroll_to_top($('#results_page_content'))		
		}
	}
	
}

mobile_scroll_to_top = function(jqel){
	if ($.support.touchOverflow){
		jqel.animate({scrollTop:40}, 1000)
	}
	else {
		$.mobile.silentScroll(40)
	}
}

pre_selection_change = function(el, called_recursively) {	
	
	//////////////////////////////////////////////////////////////////////////////
	// In this first part, we simply make sure that a user can't select
	// several concurrent proposals (e.g. 2 different providers for a same event)
	elements = el[0].name.match(/(\w+)/g)
	caller_type = elements[0]
	
	
	// TODO: Not used for now but KEEP. Needs optimisation, cf case provider just below
	/*
	if (caller_type == "time_slot") {
		caller = el.parents('.time_slot.possibility').first()
		vtime_slots = []
		
		vtime_slots = el.parents('.provider.possibility').first().find('.time_slot.possibility')
		//alert(vtime_slots.length)
		for(i=0; i < vtime_slots.length; i++) {
			//alert(4)
			if ($(vtime_slots[i]) != caller) {
				//alert(5)
				pre_select_buttons = []
				pre_select_buttons = $(vtime_slots[i]).find('.pre_select_button')
				for (j=0; j < pre_select_buttons.length; j++) { //TODO: there should be only one. No need for loop.
				//	alert(6)
				//	alert(pre_select_buttons[j])
				//	alert(pre_select_buttons[j].id)
					$(pre_select_buttons[j]).find('input').first().attr('checked', false)
					// If the checkbox is followed by an image lock.
					img = $(pre_select_buttons[j]).find('input').first().next()
					if ((img != null) && (img[0].tagName.toLowerCase() == 'img')) {
						img[0].src = '/images/padlock_open.png'
					}
					
				}
			}
		}
		caller_type = "provider"
	}
	*/
	
	caller_id = el.parents('.provider.possibility').first().attr('id')
	if (caller_type == "provider") {
		//caller_id = el.parents('.provider.possibility').first().attr('id')
		event_proposals_container = el.parents('.event_proposals').first()
		event_proposals_container.find('.provider.possibility:not(#'+caller_id+') .pre_select_button input').attr('checked', false)
		event_proposals_container.find('.provider.possibility:not(#'+caller_id+') .pre_select_button').removeClass('padlock_close').addClass('padlock_open')
	}
		// Old method
		/*
		if (caller_type == "provider") {
			//alert(1)
			caller = el.parents('.provider.possibility').first()
			vproviders = []
			event_proposals_container = el.parents('.event_proposals').first()
			if (event_proposals_container.length > 0) { // If the pre-select happened in a sequence_individual_proposal, there is no
				// need to "un-check" other providers - there is only one per proposal and event.
				vproviders = event_proposals_container.find('.provider.possibility')// CHANGED FOR AJAX vproviders = el.up('.event_proposals').select('.provider.possibility')
			}
			for (i=0; i < vproviders.length; i++) {
				//alert(2)
				if (vproviders[i] != caller[0]) {
					//alert(3)
					pre_select_buttons = []
					pre_select_buttons = $(vproviders[i]).find('.pre_select_button')
					for (j=0; j < pre_select_buttons.length; j++) {
						$(pre_select_buttons[j]).find('input').first().attr('checked', false)
						// If the checkbox is followed by an image lock.
						//img = $(pre_select_buttons[j]).find('input').first().next()
						img = $(pre_select_buttons[j]).find('input').first().parents('.pre_select_button').find('img')
						if ((img[0] != null) && (img[0].tagName.toLowerCase() == 'img')) {
							img[0].src = '/images/padlock_open.png'
						}
					}
				}
			}
		}
		*/
	
	
	
	// In this second part, we deal with the following problem:
	// a vprovider can be in two different places in the page: in event_proposals and in
	// sequence_proposals.
	// So if the user pre-select a provider or ts in one of the sections, we need to replicate
	// in the other section.
	//need_visual_and_model_update = false
	if (called_recursively === undefined) {  // There are two sections. So we set this to true when
											// calling the method after having set the elements of one of the two sections.
		called_recursively = false
		//need_visual_and_model_update = true
	}
	
	if (!called_recursively) {
		//caller_id = el.parents('.possibility').first().attr('id')
		
		// In the proposals section, the provider possibility IDs have been appended with __duplicate.
		caller_id = caller_id.replace(/__duplicate/,'')
		
		
		switch(elements[0]){
			case "time_slot":
				comparables = $(".time_slot[id^='" + caller_id + "']")
				break;
			case "provider":
				comparables = $(".provider[id^='" + caller_id + "']")
				comparables.each(function(idx, e){
					temp = $(e).find('input').first()
					if ((temp.length > 0) && (temp[0] != el[0])){
						temp.attr('checked', !(temp.attr('checked')))
						img = temp.parents('.pre_select_button')
						if (img.attr('class').match('padlock_open') != null){
							img.removeClass('padlock_open').addClass('padlock_close')
						}
						else {
							img.removeClass('padlock_close').addClass('padlock_open')
						}
						pre_selection_change(temp, true)
					}
				})
				break;
		}
		
		// Old method
		/*
		if (elements[0] == "time_slot") {
			comparables = $('.time_slot') // We can't search by ID since several elements can
			// have the same id. It seems to break the $$ function.
			// (TODO: several divs with the same ID is probably bad...)
		}
		else {
			comparables = $('.provider')
		}
		
		comparables.each(function(idx, e) {
			temp = $(e).find('input').first()
			if ( (temp.length != 0) && (e.id.replace(/__duplicate/,'') == caller_id) && (temp[0] != el[0]) ) {
				if ((temp.attr('checked') == true) || (temp.attr('checked') == 'checked')) {
					temp.attr('checked', false)
					// If the checkbox is followed by an image lock.
					//if (((temp.next()).length > 0) && ((temp.next())[0].tagName.toLowerCase() == 'img')) {
					//	(temp.next())[0].src = '/images/padlock_open.png'
					//}
					temp.parents('.pre_select_button').find('img').attr('src', '/images/padlock_open.png')
					
				}
				else {
					temp.attr('checked', true)
					// If the checkbox is followed by an image lock.
					//if (((temp.next()).length > 0 ) && ((temp.next())[0].tagName.toLowerCase() == 'img')) {
					//	(temp.next())[0].src = '/images/padlock_close.png'
					//}
					temp.parents('.pre_select_button').find('img').attr('src', '/images/padlock_close.png')
				}
				pre_selection_change($(e).find('input').first(), true)
			}
		});
		*/
		
		//////////////////////////////////////////////////////////////////////////////
		// In this part, we update the corresponding real models accordingly.
		update_corresponding_model(el)
		////visual_update(el)
		
		// Find sequence_id
		sequence_id = caller_id.match(/sequence__[0-9]*/g)[0].replace('sequence__','')	
			// Old method
			/*
			full_id = el.parents('.possibility').first().attr('id')
			id_infos = full_id.split(/__/g) // [sequence,1545,event,3461,provider,51636]
			sequence_id = null
			for (i=0; i < id_infos.length; i++) {
				if (id_infos[i] == "sequence") {
					sequence_id = id_infos[i+1]
					break;
				}
			}
			*/
		
		parray = proposed_arrangements[sequence_id]
		
		render_sequence_proposals(sequence_id, parray)
			
	}
	
	
}

// This is called from pre_selection_change: the user has pre-selected a VProvider or VTime_Slot.
// We need to make sure it is reflected in the real_event of the solution.
// For example: if a VProvider is pre-selected, we need to ensure that real_event.provider_id is set
// to the VProvider id.
update_corresponding_model = function(el) {
	
	//alert(el.name + ' '+ el.id)
	
	// Ex: sequence__1545__event__3461__provider__51636__time_slot__269986
	// or: sequence__1545__event__3461__provider__51636
	full_id = el.parents('.possibility').first().attr('id')
	id_infos = full_id.split(/__/g) // [sequence,1545,event,3461,provider,51636]
	sequence_id = null
	event_id = null
	for (i=0; i < id_infos.length; i++) {
		if (id_infos[i] == "sequence") {
			sequence_id = id_infos[i+1]
		}
		if (id_infos[i] == "event") {
			event_id = id_infos[i+1]
			break;
		}
	}
	
	//temp = "sequence__"+sequence_id
	//temp3 = $(temp)
	real_event = $("#sequence__"+sequence_id + " .events #sequence__" + sequence_id+"__event__"+event_id).first()
		// Old method
		//real_event = $("#sequence__"+sequence_id).first().find('.events').first().find("#sequence__"+sequence_id+"__event__"+event_id).first()
	
	// First we "clean" the real event
	real_event.find('.event_provider_id input').first().val('')
	real_event.find('.event_start_date input').first().val('')
	real_event.find('.event_end_date input').first().val('')
		// Old method
		//real_event.find('.event_provider_id').first().find('input').first().val('')
		//real_event.find('.event_start_date').first().find('input').first().val('')
		//real_event.find('.event_end_date').first().find('input').first().val('')
	
	
	
	// The "caller" el might be in any of the two sections. But we will always use its occurence
	// in the event_proposals section. Indeed, we know it will always be present at least in this section. On the other side
	//  we can't guarantee that it will be present in the sequence_individual_proposals section.
	selected_vprovider = $(".event_proposals" + "#sequence__"+sequence_id+"__event__"+event_id+"__proposals " + ".provider.possibility .from_provider.pre_select_button input:checked").parents('.provider.possibility')
	if (selected_vprovider.length > 0){
		real_event.find('.event_provider_id input').first().val(selected_vprovider.find('.provider_id').first().html())
	}
	
	selected_vtime_slot = $(".event_proposals" + "#sequence__"+sequence_id+"__event__"+event_id+"__proposals " + ".time_slot.possibility .from_time_slot.pre_select_button input:checked").parents('.time_slot.possibility')
	if (selected_vtime_slot.length > 0) {
		real_event.find('.event_start_date input').first().attr('value', selected_vtime_slot.find('.start_date').first().html())
		real_event.find('.event_end_date input').first().attr('value', selected_vtime_slot.find('.end_date').first().html())
	}
	
		// Old method
		/*
		event_proposals_containers = $('.event_proposals')
		event_proposals_containers.each(function(idx, e) {
			if (e.id == ("sequence__"+sequence_id+"__event__"+event_id+"__proposals") ) {
				event_proposals_container = e
				return false // break
			}
		}); 
	
		// TODO handle case event_proposals_container is null
		if (event_proposals_container == null) {
			return
		}
	
		vproviders = null
		vproviders = $(event_proposals_container).find('.provider.possibility')
		selected_vprovider = null
		vproviders.each(function(idx, e) {
			if (($(e).find('.from_provider.pre_select_button').first().find('input').first().attr('checked') == true) || ($(e).find('.from_provider.pre_select_button').first().find('input').first().attr('checked') == 'checked')) {
				selected_vprovider = e
				return false // break
			}
		});
		if (selected_vprovider != null) {
			real_event.find('.event_provider_id').first().find('input').first().val($(selected_vprovider).find('.provider_id').first().html())
		}
	
		vtime_slots = null
		vtime_slots = $(event_proposals_container).find('.time_slot.possibility')
		selected_vtime_slot = null
		vtime_slots.each(function(idx, e) {
			if (($(e).find('.from_time_slot.pre_select_button').first().find('input').first().attr('checked') == true) || ($(e).find('.from_time_slot.pre_select_button').first().find('input').first().attr('checked') == 'checked')) {
				selected_vtime_slot = e
				return false
			}
		});
		if (selected_vtime_slot != null) {
			real_event.find('.event_start_date').first().find('input').first().attr('value', $(selected_vtime_slot).find('.start_date').first().html())
			real_event.find('.event_end_date').first().down('input').first().attr('value', $(selected_vtime_slot).find('.end_date').first().html())
		}
		*/
	
}


adapt_available_arrangements_based_on_preselections = function(sequence) {
	
	// NOTE: sequence is a JQ object (the jq_main_sequence_container)
	sequence_id = sequence.attr('id').replace(/sequence__/,'')
	
	// Here, we scan the page to find all the event_proposals containers corresponding to the sequence 
	// being dealt with.
	events_proposals = []
	temp = $('.event_proposals')
	i = 0
	temp.each(function(idx, e) { // We can't just do events = sequence.select('.event_proposals') since the overlay
	// takes the event_proposals div outside of the normal DOM
		if ((e.id.match(/sequence__[0-9]*/g) != null) && (e.id.match(/sequence__[0-9]*/g)[0].replace(/sequence__/,'') == sequence_id)) {
			events_proposals.push(e)
		}
	});	
	
	currently_pre_selected = []
	currently_disliked_providers = []
	for (i=0; i < events_proposals.length; i++) {
		
		event_id = null
		event_type = null
		event_sub_type = null // not used yet
		event_sub_sub_type = null // not used yet
		provider_id = null
		time_slot_id = null
		new_hash = {};
		
		levent = $(events_proposals[i])
		
		event_id = levent.attr('id').replace(/.*event__/,'').replace(/__proposals/,'')
		
		// The event_proposals container doesn't have the event_type, sub_type etc information.
		// This info is only with the "real event". Therefore here we look in the page for the
		// real event that corresponds to the event_proposals curently worked on.
		real_event = sequence.find('.events').first().find("#sequence__"+sequence_id+"__event__"+event_id).first()
		real_event_type = real_event.find('.event_type').first().find('select').first().val()
		if ((real_event_type == (100000+'')) || (real_event_type == (-10+''))) { // 100000 is "Any", -10 is any "live show"
			event_type = null
		}
		else {
			event_type = real_event_type
		}
		// TODO: sub_sub_type - but it is not yet implemented
		real_event_sub_type = real_event.find('.event_sub_type').first().find('select').first().val()
		if ((real_event_sub_type == 'Any') || (real_event_sub_type == (-10+'')) || (real_event_sub_type == '') || (real_event_sub_type == '-')) { // 100000 is "Any"
			event_sub_type = null
		}
		else {
			event_sub_type = real_event_sub_type
		}
				
		
		lpre_selected_providers = levent.find('.provider.possibility .pre_select_button.from_provider input:checked')
		if (lpre_selected_providers.length > 0){ // There should be only one. TODO: handle case several - i.e. error!
			provider_id = lpre_selected_providers.first().parents('.provider.possibility').first().attr('id').replace(/.*provider__/,'')
		}
		
		new_hash['event_id'] = event_id;
		new_hash['event_type'] = event_type;
		new_hash['event_sub_type'] = event_sub_type;
		new_hash['event_sub_sub_type'] = event_sub_sub_type;
		new_hash['provider_id'] = provider_id;
		new_hash['time_slot_id'] = time_slot_id;
		
		currently_pre_selected.push(new_hash);

	}
	
	
	//TODO remove DEBUG
	//TODO remove DEBUG
	if (false){
		disp = ''
		$(currently_pre_selected).each(function(idx, e) {
			disp += '['
			//e.each(function(e2) {
				disp += '{ '+debug_display_hash(e)+' }'
			//});
			disp += ']'
		});
		disp2 = '[ '
		$(currently_disliked_providers).each(function(idx, e) {
			disp2 += e
			disp2 += ' '
		});
		disp2 += ']'
		//alert(disp + disp2)
	}
	/////////////////
	
	//for (i=0; i < currently_pre_selected.length; i++) {
	//	alert(debug_display_hash(currently_pre_selected[i]))
	//}
	
	//////////////////////////////////////////////////////////////////////////////
	// In this 3rd part, we compare the current pre-selection to the recommended assortments
	// proposed_arrangements = {'seq_id': [ [{ev_id, prov_id, ts_id, ai_id, bl}, {}...], [], ... ], 'seq_id' : ...}
	keep_available = []
	l_proposed_arrangements = proposed_arrangements[sequence_id]
	
	// Debug
	if (false){
		dbg_string = ""
		$(l_proposed_arrangements).each(function(idx, elem) {
			dbg_string += ("//" + elem['arrangement'][0]['provider_id'] + "--" + elem['arrangement'][1]['provider_id'])
		});
		alert(dbg_string); 
	}
	
	
	$(l_proposed_arrangements).each(function(idx, elem) {
		//alert(elem)
		if (is_included(elem['arrangement'], currently_pre_selected) && does_not_contain_a_disliked_provider(elem['arrangement'], currently_disliked_providers)) {
			keep_available.push(elem)
		}
	});
	
	//alert('1081')
	
	global_pre_selected_elements[sequence_id] = currently_pre_selected
	
	//alert('1085')
	
	return keep_available
}



// Internal method
is_included = function(elem, target) { // Returns true if elem 'is included in' target.
// By 'is included' we mean: {1, 2, 3} is included in {1, null, null} but it is not included in {2, 2, null}
// Note that in fact, elem and target are arrays of hashes.
// NOTE/TODO: elem and target are both supposed to be hashes. For some reasons, elem is not a 'real' prototype hash, i.e.
// it doesn't respond to get and set. I guess it comes from the fact elems are in fact trasnfered from the server, using to_json.
// In the process it becomes an array [ 'bla': 'tata'] instead of a real hash.

	if (target.length == 0) {
		return true
	}
	
	to_return = false
	
	// We don't use iterators here because they are difficult to "break" properly.
	for (i=0; i < elem.length; i++) {
		elem_h = elem[i]
				
	//	internals = $$('.provider__' + elem_h['provider_id'] + '__internals').first()
	//	elem_h['event_type'] = internals.getAttribute('name').match(/provider_type__[0-9]*/g)[0].gsub(/provider_type__/,'')
	//	elem_h['event_sub_type'] = internals.getAttribute('name').match(/provider_sub_type__[0-9]*/g)[0].gsub(/provider_sub_type__/,'')
	//	elem_h['event_sub_sub_type'] = internals.getAttribute('name').match(/provider_sub_sub_type__[0-9]*/g)[0].gsub(/provider_sub_sub_type__/,'')
		
		elem_h['event_type'] = providers_internals[elem_h['provider_id'] + ''][0]
		elem_h['event_sub_type'] = providers_internals[elem_h['provider_id'] + ''][1]
		
		
		for (j=0; j < target.length; j++) {
			target_h = target[j]
			if (elem_h['event_id'] == target_h['event_id']) {
				if ( 
					(target_h['event_type'] == null || target_h['event_type'] == elem_h['event_type'] )
				&& (target_h['event_sub_type'] == null || target_h['event_sub_type'] == elem_h['event_sub_type'] )
				&& (target_h['event_sub_sub_type'] == null || target_h.get('event_sub_sub_type') == elem_h['event_sub_sub_type'] )
				&& (target_h['time_slot_id'] == null || target_h['time_slot_id'] == elem_h['time_slot_id'] ) 
				&& (target_h['provider_id'] == null || target_h['provider_id'] == elem_h['provider_id']) ) {
					to_return = true
					break;
				}
				else {
					return false
				}
			}
		}
		
	}
	
	return to_return
	
}

// Internal method
does_not_contain_a_disliked_provider = function(elem, currently_disliked_providers) {
	for (i=0; i < elem.length; i++) {
		elem_h = elem[i]
		if (currently_disliked_providers.indexOf(elem_h['provider_id']+"") >= 0) {
			return false; // This elem contains a provider that is disliked
		}
	}
	return true;
} 

dislike_status_change = function(el) {
	
	full_id = el.parents('.possibility').first().id
	id_infos = full_id.split(/__/g) // [sequence,1545,event,3461,provider,51636]
	sequence_id = null
	event_id = null
	for (i=0; i < id_infos.length; i++) {
		if (id_infos[i] == "sequence") {
			sequence_id = id_infos[i+1]
		}
		if (id_infos[i] == "event") {
			event_id = id_infos[i+1]
			break;
		}
		//if (id_infos[i] == "provider") {
		//	provider_id = id_infos[i+1]
		//	break;
		//}
	}
	
	sequence = $('#sequence__'+sequence_id) // We need to scan the entire document because anything that is
	// in an overlay is taken out of the standard DOM structure (i.e. up, down do not work).
	
	events = []
	temp = $('.event_proposals')
	temp.each(function(idx, e) { // We can't just do events = sequence.select('.event_proposals') since the overlay
	// takes the event_proposals div outside of the normal DOM
		if (e.id.match(/sequence__[0-9]*/g)[0].replace(/sequence__/,'') == sequence_id) {
			events.push(e)
		}
	});
	//alert('events '+ events.length)
	
	provider = el.parents('.provider.possibility').first()
	dislike_button = provider.find('.dislike_button.from_provider').first().find('input').first()
	if (dislike_button == true) {
		provider.removeClass('valid')
		provider.addClass('invalid')
	}
	else {
		provider.removeClass('invalid')
		provider.addClass('valid')
	}
	
	
	
	provider_id = provider.id.replace(/__duplicate/,'') // We can keep the full DOM id here
	////display_containers = (sequence.select('.sequence_individual_proposal')).add(events)
	//alert(display_containers.length)
	ip = sequence.find('.sequence_individual_proposal').first()
	for (i = 0; i < events.length; i++){
		ip.add(events[i]);
	}
	
	
	display_containers.each(function(idx, e) {
		comparable_provider = e.find("#"+provider_id)[0] || e.find("#"+provider_id + "__duplicate")[0]
		if ((comparable_provider != null) && (comparable_provider != provider)) {
			dislike_button = comparable_provider.find('.dislike_button.from_provider').first().find('input').first()
			if (dislike_button.checked == true) {
				dislike_button.checked = false
				comparable_provider.removeClass('invalid')
				comparable_provider.addClass('valid')
				
			}
			else {
				dislike_button.checked = true
				comparable_provider.addClass('invalid')
				comparable_provider.removeClass('valid')
			}
		
		}
	});
	
	//visual_update(el)
	parray = proposed_arrangements.get(sequence_id)
	render_sequence_proposals(sequence_id, parray)

}

generate_options_for_type_select = function (el, default_value) {
	//alert('coucou')
	//options = el.childElements();
	//options.each(function(loption) {
	//	loption.remove();
	//});
	
	//el.children().each( function(idx,loption){
	//	loption.remove();
	//});
	el.children().remove()
	
	$(available_provider_types).each(function(idx, ltype) {
		
		el.append($('<option/>', {'value': ltype[1]}).html(ltype[0]));
	});
	
	
}

generate_options_for_sub_type_select = function (el,parent_type) {
	//options = el.childElements();
	//options.each(function(loption) {
	//	loption.remove();
	//});
	el.children().remove()
	
	corresponding_sub_types = available_provider_sub_types[parent_type]
	$(corresponding_sub_types).each(function(idx, ltype) {
		temp = $('<option/>', {'value': ltype[1]}).html(ltype[0])
		//alert(temp.value)
		if (temp.html() == 'Any') {
			temp.attr('selected', true); // We pre-select "Any".
		}
		el.append(temp);
			
	});
	
	//el.selectBox('destroy');
	//el.selectBox();
	update_select_box(el);
	el.trigger('update_dependents') //ADDED with jquerymobile
	
	
	if (USING_SELECTBOX){
		el.data('current', el.selectBox('value'));
	}
	else {
		el.data('current', el.val());
	}
	
}

sort_by_score = function(a,b) {
	//return ((b['score'] + 0.0) - (a['score'] + 0.0))
	return b['score'] - a['score']
}

sort_by_starting_time = function(a,b) {
	//return ((a['begin_time'] + 0.0) - (b['begin_time'] + 0.0))
	return a['begin_time'] - b['begin_time']
}

sort_by_price = function(a,b) {
	//return ((a['price']['value'] + 0.0) - (b['price']['value'] + 0.0))
	return a['price']['value'] - b['price']['value']
}

date_range_preselected = function(el) {
	
	today = new Date()
	switch (el.value) {
		case 'tonight':
			start = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 18, 0, 0, 0)
			end = new Date()
			end.setHours(start.getHours() + 14)
			end.setMinutes(0)
			end.setSeconds(0)
			end.setMilliseconds(0)
			break;
		case 'friday_evening':
			start = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 18, 0, 0, 0)
			start.setDate( start.getDate() + (7+5-start.getDay())%7 ) // 5 for Friday
			end = new Date(start)
			end.setHours(start.getHours() + 10)
			break;
		case 'saturday':
			start = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 10, 0, 0, 0)
			start.setDate( start.getDate() + (7+6-start.getDay())%7 ) 
			end = new Date(start)
			end.setHours(start.getHours() + 16)
			break;
		case 'sunday':
			start = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 10, 0, 0, 0)
			start.setDate( start.getDate() + (7+0-start.getDay())%7 )
			end = new Date(start)
			end.setHours(start.getHours() + 16)
			break;		
		default:
		 	//TODO error. Default to friday evening
			start = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 18, 0, 0, 0)
			start.setDate( start.getDate() + (7+5-start.getDay())%7 ) // 5 for Friday
			end = new Date(start)
			end.setHours(start.getHours() + 10)
		 
	}
	
	hidden_dates_selector = el.parents('.sequence_parameters').first().find('[type="hidden"]')
	hidden_dates_selector.each(function(idx, input) {
		if (input.id.include('start_date')) {
			input.value = start.toISOString();
			return
		}
		if (input.id.include('end_date')) {
			input.value = end.toISOString();
			return
		}
	});
	
	date_picker_values = el.parents('.sequence_parameters').first().find('.manual_dates_selector').first().find('[type="text"]')
	date_picker_values.each(function(idx, input) {
		if (input.id.search(/start_date/) >= 0) {
			temp = start.toISOString().split("T")[0].split("-")
			//input.value = start.getDate() + "-" + (start.getMonth() + 1) + "-" + start.getFullYear()
			input.value = temp[2] + "-" + temp[1] + "-" + temp[0]
			return
		}
		if (input.id.search(/end_date/)) {
			temp = end.toISOString().split("T")[0].split("-")
			//input.value = start.getDate() + "-" + (start.getMonth() + 1) + "-" + start.getFullYear()
			input.value = temp[2] + "-" + temp[1] + "-" + temp[0]
			return
		}
	}); 
	
	time_picker_selects = el.parents('.sequence_parameters').first().find('.manual_dates_selector').first().find('select')
	time_picker_selects.each(function(idx, lselect) {
		if (lselect.id.search(/start_date/) >= 0) {
			options = lselect.find('option')
			options.each(function(idx2, loption) {
				ltime = start.toISOString().split("T")[1].replace(/[A-Za-z]/,"").substring(0, 5)
				if (loption.attr('value') == ltime) {
					loption.selected = true;
					return false;
				}
			});
			return
		}
		if (lselect.id.search(/end_date/) >= 0) {
			options = lselect.find('option')
			options.each(function(idx2, loption) {
				ltime = end.toISOString().split("T")[1].replace(/[A-Za-z]/,"").substring(0, 5)
				if ($(loption).attr('value') == ltime) {
					loption.selected = true;
					return false;
				}
			});
			return
		}	
			
		
	});
	
}



generate_sorter_selector = function(sequence_id, pre_selected){
	
}

generate_date_preselectors = function(sequence_id, pre_selected){
	possibilities = ["now","tonight","tomorrow","friday night","saturday lunch","saturday night","sunday lunch"]
	selectors_and_time_values = {};
	now = new Date();
	startHourTonight = 0;
	endHourTonight = 0;
	selectors = []
	
	
	switch(now.getDay()){
		case 1: case 2:
			selectors.push(possibilities[0],possibilities[1], possibilities[2], possibilities[3], possibilities[4], possibilities[5], possibilities[6]);
			startHourTonight = 18;
			endHourTonight = 2;
			startHourTomorrow = 18;
			endHourTomorrow = 2;
			break;
		case 3:
			selectors.push(possibilities[0],possibilities[1], possibilities[2], possibilities[3], possibilities[4], possibilities[5], possibilities[6]);
			startHourTonight = 18;
			endHourTonight = 2;
			startHourTomorrow = 18;
			endHourTomorrow = 5;
			break;
		case 4: // thursday
			selectors.push(possibilities[0],possibilities[1], possibilities[3], possibilities[4], possibilities[5], possibilities[6]);
			startHourTonight = 18;
			endHourTonight = 5;
			startHourTomorrow = 18;
			endHourTomorrow = 5;
			break;
		case 5: // friday
			selectors.push(possibilities[0],possibilities[1], possibilities[4], possibilities[5], possibilities[6]);
			startHourTonight = 18;
			endHourTonight = 5;
			startHourTomorrow = 18;
			endHourTomorrow = 5;
			break;
		case 6: // saturday
			selectors.push(possibilities[0],possibilities[1], possibilities[6], possibilities[3]);
			startHourTonight = 18;
			endHourTonight = 5;
			startHourTomorrow = 18;
			endHourTomorrow = 2;
			break;
		case 0: // sunday
			selectors.push(possibilities[0],possibilities[1], possibilities[2], possibilities[3], possibilities[4], possibilities[5]);
			startHourTonight = 18;
			endHourTonight = 2;
			startHourTomorrow = 18;
			endHourTomorrow = 2;
			break;
			
	}
	
	$(possibilities).each(function (idx, p) {
		switch (p) {
			case 'tonight':	
				if (now.getHours() < 3) { // before 3am in the morning, we consider tonight as now
					start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), Math.ceil((now.getMinutes() + 15) / 30) * 30, 0, 0);					
					end = new Date(start);
					end.setMinutes(start.getMinutes() + 180);
				}
				else {
					start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), startHourTonight, 0, 0, 0);
					thresholdStart = new Date(start.getTime() - 30 * 60000);
					if (now > thresholdStart) {start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), Math.ceil((now.getMinutes() + 15) / 30) * 30, 0, 0);} // we need 30 minutes before 1st event
					end = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, endHourTonight, 0, 0, 0);
					thresholdEnd = new Date(start.getTime() + 180 * 60000); // at least 3 hours
					if (thresholdEnd > end) {end = new Date(thresholdEnd);}
				}
				break;
			case 'tomorrow':
				start = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, startHourTomorrow, 0, 0, 0)
				end = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 2, endHourTomorrow, 0, 0, 0)
				break;
			case 'friday night':
				start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 18, 0, 0, 0)
				start.setDate( start.getDate() + (7+5-start.getDay())%7 ) // 5 for Friday
				if (start.getDate() == now.getDate()) {
					start.setDate(start.getDate() + 7)
				}
				end = new Date(start)
				end.setHours(start.getHours() + 11) // 6pm to 5am
				break;
			case 'saturday lunch':
				start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 11, 0, 0, 0)
				start.setDate( start.getDate() + (7+6-start.getDay())%7 ) // 6 for Saturday
				if (start.getDate() == now.getDate()) {
					start.setDate(start.getDate() + 7)
				}
				end = new Date(start)
				end.setHours(start.getHours() + 4) // 11am to 3pm
				break;
			case 'saturday night':
				start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 18, 0, 0, 0)
				start.setDate( start.getDate() + (7+6-start.getDay())%7 ) // 6 for Saturday
				if (start.getDate() == now.getDate()) {
					start.setDate(start.getDate() + 7)
				}

				end = new Date(start)
				end.setHours(start.getHours() + 11) // 6pm to 5am
				break;
			case 'sunday lunch':
			start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 11, 0, 0, 0)
			start.setDate( start.getDate() + (7+0-start.getDay())%7 ) // 0 for Sunday
			if (start.getDate() == now.getDate()) {
				start.setDate(start.getDate() + 7)
			}

			end = new Date(start)
			end.setHours(start.getHours() + 4) // 11am to 3pm
			break;
			case 'now':
				start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), Math.ceil((now.getMinutes() + 15) / 30) * 30, 0, 0);
				end = new Date(start)
				end.setMinutes(start.getMinutes() + 420)				
				break;
			default:
				start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 18, 0, 0, 0)
				start.setDate( start.getDate() + (7+5-start.getDay())%7 ) // 5 for Friday
				end = new Date(start)
				end.setHours(start.getHours() + 11) // 6pm to 5am


		}
		

		selectors_and_time_values[p] = [start, end]
		

	})
	

	
	

	
	sequence = $('#' + sequence_id)
	container = sequence.find('.time_frame').first()
	if (false){ // case individual buttons
		$(selectors).each (function(idx, sel){
			element = $('<span/>').html(sel + ": ")
			//button = $('<input/>', { 'type': 'radio', 'name': 'when', 'value': sel, 'onclick' : "javascript:update_time_date_picker($(this), '"+ selectors_and_time_values[sel][0].toISOString()+"', '" + selectors_and_time_values[sel][1].toISOString()+ "')" })
			button = $('<input/>', { 'type': 'radio', 'name': 'when', 'value': sel, 'click': function(){update_time_date_picker($(this), selectors_and_time_values[sel][0], selectors_and_time_values[sel][1])} })
			//button = $('<input/>', { 'type': 'radio', 'name': 'when', 'value': sel, 'click': function(){blabla($(this).attr("value"))} })
		

			if ((pre_selected != null) && (pre_selected == sel)) {
				button.attr('checked', true);
			}

		
			//button.onclick = update_time_date_picker(element, selectors_and_time_values.get(sel)[0], selectors_and_time_values.get(sel)[1])
			element.append(button)//.observe('onClick', update_time_date_picker(element, selectors_and_time_values.get(sel)[0], selectors_and_time_values.get(sel)[1])))
		

			element.append($('<span>&nbsp;&nbsp;</span>'))
			//element.observe('onClick', update_time_date_picker(element, selectors_and_time_values.get(sel)[0], selectors_and_time_values.get(sel)[1]))
			container.append(element);
			//button.onclick = update_time_date_picker(element, selectors_and_time_values.get(sel)[0], selectors_and_time_values.get(sel)[1])
		
		})
	}
	else { // case select
		select_element = $('<select/>', {'name': 'when'})
		
		
		temp1 = {} // Careful here: if you call this variable temp, it seems it is being overwritten by another one
		// (cf the fact we use this temp variable in the .change below.). Not sure how to avoid this in JS.
		$(selectors).each(function(idx, sel){
			//loption = $('<option/>',{ 'value': sel, 'click': function(){update_time_date_picker($(this), selectors_and_time_values[sel][0], selectors_and_time_values[sel][1])} }).html(sel)
			
			loption = $('<option/>',{ 'value': sel}).html(sel)
			temp1[sel] = [selectors_and_time_values[sel][0], selectors_and_time_values[sel][1]]
			
			if ((pre_selected != null) && (pre_selected == sel)) {
				loption.attr('selected', true);
			}
			select_element.append(loption)
		})
		
		if (pre_selected == "manual"){
			$('<option/>',{ 'value': pre_selected}).html(pre_selected).appendTo(select_element).attr('selected', true)
			select_element.addClass('greyed_out')
		}
		
		select_element.change(function(event){
			lval = $(this).children(":selected").attr('value')
			if (lval != "manual"){
				update_time_date_picker($(this), temp1[lval][0], temp1[lval][1])
				$(this).removeClass('greyed_out')
				$(this).find("option[value=manual]").remove()
			}
		
			update_select_box($(this))
			$(this).trigger('update_dependents') //ADDED with jquerymobile
			
			render_default_sequence(sequence)
			
		})
		
		select_element.bind('deactivateWhenSelect', function(event){	
			if ($(this).find("option[value=manual]").length == 0){
				$("<option value='manual'>manual</option>").appendTo($(this))
			}
			$(this).addClass('greyed_out').find("option[value=manual]").first().attr('selected', true)

			////update_select_box(when_select)
			update_select_box($(this))	
			$(this).trigger('update_dependents') //ADDED with jquerymobile
			
			render_default_sequence(sequence)
		})
		
		
		container.append(select_element)
		
	}
	
	
}

generate_date_preselectorsORIGINAL = function(sequence_id, pre_selected){
	possibilities = ["tonight","tomorrow night","friday night","saturday","sunday","tomorrow","now", "next friday", "next saturday", "next sunday"]
	selectors_and_time_values = {};
	now = new Date();
	$(possibilities).each(function (idx, p) {
		switch (p) {
			case 'tonight':				
				start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 18, 0, 0, 0)
				if ((now > start) || (now.getHours() < 3)) { // between 0 and 3h in the morning, we consider that
											// tonight is the same night.
					start = new Date(); // now
					if (start.getMinutes() <= 30) {
						start.setMinutes(30)
					}
					else {
						start.setMinutes(0)
						start.setHours(start.getHours() + 1)
					}
					start.setSeconds(0)
					start.setMilliseconds(0)
				}
				end = new Date()
				if (start.getHours() > 3){ // between 0 and 3h in the morning, we consider that
											// tonight is the same night.
					end.setDate(start.getDate() + 1)
				}
				else if ((now.getHours() == 23) && (now.getMinutes() > 30)) { // This second case applies if now is between 23.30 and midnight. If it is the case
				// it means "tonight" will start at midnight the "day after".
					end.setDate(start.getDate())
				}
				end.setHours(8)
				end.setMinutes(0)
				end.setSeconds(0)
				end.setMilliseconds(0)
				break;
			case 'tomorrow night':
				start = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 18, 0, 0, 0)
				end = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 2, 8, 0, 0, 0)
				break;
			case 'friday night': case 'next friday':
				start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 18, 0, 0, 0)
				start.setDate( start.getDate() + (7+5-start.getDay())%7 ) // 5 for Friday
				if (start.getDate() == now.getDate()) {
					start.setDate(start.getDate() + 7)
				}
				end = new Date(start)
				end.setHours(start.getHours() + 10)
				break;
			case 'saturday': case 'next saturday':
				start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 13, 0, 0, 0)
				start.setDate( start.getDate() + (7+6-start.getDay())%7 )
				if (start.getDate() == now.getDate()) {
					start.setDate(start.getDate() + 7)
				} 
				end = new Date(start)
				end.setHours(start.getHours() + 16)
				break;
			case 'sunday': case 'next sunday':
				start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 13, 0, 0, 0)
				start.setDate( start.getDate() + (7+0-start.getDay())%7 )
				if (start.getDate() == now.getDate()) {
					start.setDate(start.getDate() + 7)
				}
				end = new Date(start)
				end.setHours(start.getHours() + 16)
				break;
			case 'tomorrow':
				start = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 10, 0, 0, 0)
				end = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 2, 8, 0, 0, 0)
				break;
			case 'now':
				start = new Date()
				if (start.getMinutes() <= 20) {
					start.setMinutes(30)
				}
				else {
					start.setMinutes(0)
					start.setHours(start.getHours() + 1)
				}
				start.setSeconds(0)
				start.setMilliseconds(0)
				
				end = new Date()
				if (end.getHours() > 2) {
					end.setDate(end.getDate() + 1)
				}
				end.setHours(8)
				end.setMinutes(0)
				end.setSeconds(0)
				end.setMilliseconds(0)
				break;
			default:
			 	//TODO error. Default to friday evening
				start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 18, 0, 0, 0)
				start.setDate( start.getDate() + (7+5-start.getDay())%7 ) // 5 for Friday
				end = new Date(start)
				end.setHours(start.getHours() + 10)

		}
		
		selectors_and_time_values[p] = [start, end]
		
	})
	
	
	
	//now = new Date();
	selectors = []
	switch(now.getDay()){
		case 1: case 2: case 3: case 4:
			selectors.push(possibilities[6],possibilities[0], possibilities[2], possibilities[3], possibilities[4]);
			break;
		case 5: // friday
			selectors.push(possibilities[0], possibilities[5], possibilities[4]);
			break;
		case 6: // saturday
			selectors.push(possibilities[6], possibilities[0], possibilities[5]);
			break;
		case 0: // sunday
			selectors.push(possibilities[6], possibilities[0],possibilities[7],possibilities[8],possibilities[9]);
			break;
			
	}
	
	sequence = $('#' + sequence_id)
	container = sequence.find('.time_frame').first()
	if (false){ // case individual buttons
		$(selectors).each (function(idx, sel){
			element = $('<span/>').html(sel + ": ")
			//button = $('<input/>', { 'type': 'radio', 'name': 'when', 'value': sel, 'onclick' : "javascript:update_time_date_picker($(this), '"+ selectors_and_time_values[sel][0].toISOString()+"', '" + selectors_and_time_values[sel][1].toISOString()+ "')" })
			button = $('<input/>', { 'type': 'radio', 'name': 'when', 'value': sel, 'click': function(){update_time_date_picker($(this), selectors_and_time_values[sel][0], selectors_and_time_values[sel][1])} })
			//button = $('<input/>', { 'type': 'radio', 'name': 'when', 'value': sel, 'click': function(){blabla($(this).attr("value"))} })
		
			if ((pre_selected != null) && (pre_selected == sel)) {
				button.attr('checked', true);
			}
		
			//button.onclick = update_time_date_picker(element, selectors_and_time_values.get(sel)[0], selectors_and_time_values.get(sel)[1])
			element.append(button)//.observe('onClick', update_time_date_picker(element, selectors_and_time_values.get(sel)[0], selectors_and_time_values.get(sel)[1])))
		
			element.append($('<span>&nbsp;&nbsp;</span>'))
			//element.observe('onClick', update_time_date_picker(element, selectors_and_time_values.get(sel)[0], selectors_and_time_values.get(sel)[1]))
			container.append(element);
			//button.onclick = update_time_date_picker(element, selectors_and_time_values.get(sel)[0], selectors_and_time_values.get(sel)[1])
		
		})
	}
	else { // case select
		select_element = $('<select/>', {'name': 'when'})
		
		
		temp1 = {} // Careful here: if you call this variable temp, it seems it is being overwritten by another one
		// (cf the fact we use this temp variable in the .change below.). Not sure how to avoid this in JS.
		$(selectors).each(function(idx, sel){
			//loption = $('<option/>',{ 'value': sel, 'click': function(){update_time_date_picker($(this), selectors_and_time_values[sel][0], selectors_and_time_values[sel][1])} }).html(sel)
			
			loption = $('<option/>',{ 'value': sel}).html(sel)
			temp1[sel] = [selectors_and_time_values[sel][0], selectors_and_time_values[sel][1]]
			
			if ((pre_selected != null) && (pre_selected == sel)) {
				loption.attr('selected', true);
			}
			select_element.append(loption)
		})
		
		if (pre_selected == "manual"){
			$('<option/>',{ 'value': pre_selected}).html(pre_selected).appendTo(select_element).attr('selected', true)
			select_element.addClass('greyed_out')
		}
		
		select_element.change(function(event){
			lval = $(this).children(":selected").attr('value')
			if (lval != "manual"){
				update_time_date_picker($(this), temp1[lval][0], temp1[lval][1])
				$(this).removeClass('greyed_out')
				$(this).find("option[value=manual]").remove()
			}
		
			update_select_box($(this))
			$(this).trigger('update_dependents') //ADDED with jquerymobile
			
			render_default_sequence(sequence)
			
		})
		
		select_element.bind('deactivateWhenSelect', function(event){	
			if ($(this).find("option[value=manual]").length == 0){
				$("<option value='manual'>manual</option>").appendTo($(this))
			}
			$(this).addClass('greyed_out').find("option[value=manual]").first().attr('selected', true)
			////update_select_box(when_select)
			update_select_box($(this))	
			$(this).trigger('update_dependents') //ADDED with jquerymobile
			
			render_default_sequence(sequence)
		})
		
		
		container.append(select_element)
		
	}
	
	
}

// This function take a JS Date and make it a Rails comptible date without the time zone.
date_formatter = function(datetime) {
	day = datetime.getDate()
	if (day < 10){day = "0" + day}
	month = (datetime.getMonth() + 1)
	if (month < 10){ month = "0" + month}
	hour = datetime.getHours()
	if (hour < 10){ hour = "0" + hour}
	minutes = datetime.getMinutes()
	if (minutes < 10){minutes = "0" + minutes}
	
	return day + "-" + month + "-" + datetime.getFullYear() + " "  + hour + ":" + minutes
}

update_time_date_picker = function(el, start, end) {
	//return function(){
	
		start = date_formatter(start)
		end = date_formatter(end)
	
		hidden_dates_selector = el.parents('.sequence_parameters').first().find('[type="hidden"]')
		hidden_dates_selector.each(function(idx, input) {
			if (input.id.search(/start_date/) >= 0) {
				input.value = start;
				return
			}
			if (input.id.search(/end_date/) >= 0) {
				input.value = end;
				return
			}
		});
	
		date_picker_values = el.parents('.sequence_parameters').first().find('.manual_dates_selector').first().find('[type="text"]')
		date_picker_values.each(function(idx, input) {
			if (input.id.search(/start_date/) >= 0) {
				//temp = start.split("T")[0].split("-")
				//input.value = temp[2] + "-" + temp[1] + "-" + temp[0]
				input.value = start.split(" ")[0]
				return
			}
			if (input.id.search(/end_date/) >= 0) {
				//temp = end.split("T")[0].split("-")
				//input.value = temp[2] + "-" + temp[1] + "-" + temp[0]
				input.value = end.split(" ")[0]
				return
			}
		}); 
	
		time_picker_selects = el.parents('.sequence_parameters').first().find('.manual_dates_selector').first().find('select')
		time_picker_selects.each(function(idx, lselect) {
			if (lselect.id.search(/start_date/) >= 0) {
				options = $(lselect).find('option')
				options.each(function(idx2, loption) {
					//ltime = start.split("T")[1].replace(/[A-Za-z]/,"").substring(0, 5)
					ltime = start.split(" ")[1]
					if ($(loption).attr('value') == ltime) {
						loption.selected = true;
						return false;
					}
				});
				
				// Just to signal the change on elements that depend on the "onChange" event
				// of this select, e.g. selectBox.
				//$(lselect).change();
				$(lselect).trigger('update_dependents')
				
				return
			}
			if (lselect.id.search(/end_date/) >= 0) {
				options = $(lselect).find('option')
				options.each(function(idx2, loption) {
					//ltime = end.split("T")[1].replace(/[A-Za-z]/,"").substring(0, 5)
					ltime = end.split(" ")[1]
					if ($(loption).attr('value') == ltime) {
						loption.selected = true;
						return false;
					}
				});
				
				// Just to signal the change on elements that depend on the "onChange" event
				// of this select, e.g. selectBox.
				//$(lselect).change();
				$(lselect).trigger('update_dependents')
				
				return
			}	
			
		
		});
		
	
	//}
}

deactivate_preselectors = function(el) {
	//el.parents('.sequence_parameters').first().find("input[name='when']").each(function(idx, e){
		//e.checked = false
	//})
	
	when_select = el.parents('.sequence_parameters').first().find("select[name='when']").first()	
	when_select.trigger('deactivateWhenSelect')		
	
}


get_proposal_minimum_price = function(proposal){
	providers = proposal.find('.provider')
	prices = [];
	providers.each(function(idx, provider) {
		ais = $(provider).find('.availability_information')
		min_price = null
		currency = null
		ais.each(function(idx2, ai){
			if (currency == null) {
				currency = $(ai).find('.availability_information_currency').first().html()
				// Note we assume that for a same provider, all the ais use the same currency.
			}
			ai_price = $(ai).find('.availability_information_price').first().html()
			if (min_price == null || ai_price < min_price) {
				min_price = ai_price
			}
			
		})
		
		temp = {};
		temp['currency'] = currency
		temp['price'] = min_price
		prices.push(temp)
		
	})
	
	
	concat = {}
	$(prices).each(function(idx, p) {
		currency = p['currency']
		if (concat[currency]) {
			concat[currency] = parseFloat(concat[currency]) + parseFloat(p['price'])
		}
		else {
			concat[currency] = parseFloat(p['price'])
		}		
	})
	
	to_display = ""
	number_of_elements = 1
	$(concat).each(function(idx, e){
		if (number_of_elements > 1) {
			to_display += " and "
		}
		for (var i in e){
			to_display += i + e[i]
		}
		number_of_elements += 1
	})
	
	return to_display
}


format_price = function(price, currency){
	return "<p>Starting at:</p>" +  "<span>" + "£" + price + "</span>"
}

prepare_global_pre_selected_elements = function(){
	temp = $('#global_pre_selected_elements')
	if (temp[0] != null){
		temp.val(JSON.stringify(global_pre_selected_elements));
	}
}

restore_preselections = function() {
	$.each(global_pre_selected_elements, function(idx, sequ_lvl) {
		$.each(sequ_lvl, function(idx2, h){
			//if (!(Object.isHash(h))){
			//	h = new Hash(h);
			//}
			if (h == null) { //TODO ???
				h = {}
			}
			if (h['provider_id'] != null){
				provider = $('#sequence__'+idx+'__event__'+h['event_id']+'__provider__'+h['provider_id'])
				if (provider != null) {
					el = provider.find('.pre_select_button input').first();
					el.attr('checked', true);
					provider.find('.pre_select_button').removeClass('padlock_open').addClass('padlock_close')
					//pre_selection_button_touched(link,false,false);
				}
			}
		})
	})
}


string_to_date_conversion = function(pstring){
	temp = pstring.match(/[0-9]+/g)
	if (pstring.search(/ /) >= 0){ // Case: 01-12-2010 00:30
		return new Date(temp[2], temp[1]-1, temp[0], temp[3], temp[4])
	}
	else { // Case 2010-12-04T10:00:00.000Z
		return new Date(temp[0], temp[1]-1, temp[2], temp[3], temp[4], temp[5])
	}
}


// FORM VALIDATION
// This is a bit complex: 
// - we need Google to geocode the address, and this feature is asynchronous.
// - we may have several sequences, i.e. several successive async calls to geocoding.
// So we:
// - call the geocoder with a callback function, "targeting" one sequence
// - the callback function actually validates the form for the seqeunce
// - if there are more sequence, this callback actually call the geocoder as well, passing "itself" (i.e. it is
//	kind of recursive).
validate_main_form = function() {
	//return true;
	
	$('.form_error').removeClass('form_error')
	
	remaining_sequences = []
	$('.sequence').each(function(idx, s){
		remaining_sequences.push(s)
	})
	sequ = remaining_sequences.shift();
	destination_container = $(sequ).find('.destination').first()
	destination_container.find('input').first().each(function(idx, input){
		// Careful of the order of the ifs here!
		if (input.id.search(/destination_longitude/) >= 0){
			//destination_longitude = input
			return;
		}
		if (input.id.search(/destination_latitude/) >= 0){
			//destination_latitude = input
			return;
		}
		if (input.id.search(/destination/) >= 0){
			destination = input
			return;
		}
	})
	message = ""
	
	geocoder.geocode( { 'address': destination.value}, recursive_form_validation(sequ, remaining_sequences, message));	
	
}

// The geocoder call back, which is going to call itself if there are several sequences.
recursive_form_validation = function(sequ, remaining_sequences, message) {
	return function(results, status){
	
		// Check Time Frame
		$(sequ).find('.manual_dates_selector').first().find("input[type=hidden]").each(function(idx, linput){
			if (linput.id.search(/start_date/) >= 0){
				start_date = string_to_date_conversion(linput.value)
			}
			else if (linput.id.search(/end_date/) > 0){
				end_date = string_to_date_conversion(linput.value)
			}
		})
		now = new Date();
		if (start_date < now.setMinutes(now.getMinutes() - 30)) {
			message += "It seems that the start date has not been setup correctly.<br>"
			$(sequ).find('.time_frame').addClass('form_error')
		}
		if (end_date - start_date <= 0) {
			message += "It seems you haven't specified a correct time frame.<br>"
			$(sequ).find('.time_frame').addClass('form_error')
		}
		
		if (end_date - start_date > 25*60*60*1000){ // More than 25 hours time frame
			message += "At this stage, we can't search over long periods. Sorry...<br>"
			$(sequ).find('.time_frame').addClass('form_error')
		}
		if (start_date >= now.setDate(now.getDate() + 30)){ // We can search over one month only
			message += "At this stage, we can only search for the coming month. Sorry...<br>"
			$(sequ).find('.time_frame').addClass('form_error')
		}
		// End Check Time Frame
	
		// Check number of people
		number_of_invitees = 0
		number_of_invitees_selector = $(sequ).find('#number_of_invitees2').first() // In the edit view, the selector is a mirror,
		// and its id is number_of_invitees2.
		if (number_of_invitees_selector.length == 0){
			number_of_invitees_selector = $(sequ).find('#number_of_invitees').first()
		}
		number_of_invitees_selector.find('option').each(function(idx, opt){
			if (opt.selected == true){
				number_of_invitees = parseInt(opt.value)
				return false;
			}
		})
		if ((number_of_invitees <= 0) || (number_of_invitees > 8)){
			message += "It seems the number of people coming is wrong?<br>"
			////number_of_invitees_selector.addClass('form_error')
			$(sequ).find('.number_of_users_select').addClass('form_error')
		}
		// End Check number of people
	
		// Check number of real events
		number_of_events = $(sequ).find('.event.fields.real_event')
		number_of_deleted_events = 0
		$(sequ).find('.event_remove_link.real_event').each(function(idx, el){
			hidden_field = $(el).find("input[type=hidden]")[0]
			if ((hidden_field != undefined) && (hidden_field.value != "")) {
				number_of_deleted_events += parseInt(hidden_field.value)
			}
		})

		if (number_of_events.length == number_of_deleted_events){
			message += "It seems you haven't selected any activities.<br>"
			$(sequ).find('.add_fields_link_for_event').first().addClass('form_error')
		}
		// End Check number of real events
	
		// Check timeframe vs number of events
		// Min 2x hour per event
		number_of_active_events = number_of_events.length - number_of_deleted_events 
		
		if (end_date - start_date < number_of_active_events*2*60*60*1000){
			message += "It seems you only have roughly " + (end_date - start_date)/(60*60*1000)  +" hours for " + number_of_active_events + " activities. It probably won't be enough.<br>"
			$(sequ).find('.time_frame').addClass('form_error')
		}
		
		if (end_date - start_date > 25*60*60*1000){ // More than 25 hours time frame
			message += "At this stage, we can't search over long periods. Sorry...<br>"
			$(sequ).find('.time_frame').addClass('form_error')
		}
		// End check timeframe vs number of events	
	
	
		// Check location
		destination_container = $(sequ).find('.destination').first()
		
		destination_container.find('input').each(function(idx, input){
			// Careful of the order of the ifs here!
			if (input.id.search(/destination_longitude/) >= 0){
				destination_longitude = input
				return;
			}
			if (input.id.search(/destination_latitude/) >= 0){
				destination_latitude = input
				return;
			}
			if (input.id.search(/destination/) >= 0){
				destination = input
				return;
			}
		})
		// Case Anywhere
		if (destination.value.search(/Anywhere/) >= 0){ //TODO don't hard code!
			destination_longitude.value = -1000
			destination_latitude.value = -1000
		}
		
		
		else if (status == google.maps.GeocoderStatus.OK) {
			//destination_container.select('input').each(function(input){
				// Careful of the order of the ifs here!
				//if (input.id.include('destination_longitude')){
				//	destination_longitude = input
				//	return;
				//}
				//if (input.id.include('destination_latitude')){
				//	destination_latitude = input
				//	return;
				//}
				//if (input.id.include('destination')){
				//	destination = input
				//	return;
				//}
			//})
			
			if (!is_in_area("London", results[0].geometry.location.lat(), results[0].geometry.location.lng())){
				message += "When we locate this destination, it seems outside of London."
				//destination_container.addClass('form_error')
				$(sequ).find('.destination').addClass('form_error')
			}
			else {
				destination_longitude.value = results[0].geometry.location.lng()
				destination_latitude.value = results[0].geometry.location.lat()
			}
		} 
		else {
			
			// TODO: OVER_QUERY_LIMIT problem!!! 3 lines below should not be commented.
			message += "We could not locate your destination.<br>"
			//message += status
			//destination_container.addClass('form_error')
			$(sequ).find('.destination').addClass('form_error')
			
			
			
		}
	 	// End check location

		if (remaining_sequences.length > 0){
			next_sequ = remaining_sequences.shift();
			destination_container = $(next_sequ).find('.destination').first()
			destination_container.find('input').each(function(idx, input){
				// Careful of the order of the ifs here!
				if (input.id.search(/destination_longitude/) >= 0){
					//destination_longitude = input
					return;
				}
				if (input.id.search(/destination_latitude/) >= 0){
					//destination_latitude = input
					return;
				}
				if (input.id.search(/destination/) >= 0){
					destination = input
					return;
				}
			})
		
		
			geocoder.geocode( { 'address': destination.value}, recursive_form_validation(next_sequ, remaining_sequences, message));
		
		}
		else {
			if (message != ""){
				$('#main_form_error').find('.error_message').first().html(message)
				////Lightview.show('#main_form_error')
				//(new Control.Modal($('main_form_error_link'),{  
				//          className: 'modal',  
				//          closeOnClick: true, 
				//	        overlayOpacity: 0.75 
				//          })).open();
				
				//open_livepipe_window('modal', $('main_form_error'),{  
				//		          className: 'modal',  
				//		          closeOnClick: true, 
				//			       overlayOpacity: 0.75 
				//		          })
						
				
				if (USING_LOADING_MESSAGE){
					$.mobile.hidePageLoadingMsg();
				}
				
				$('#main_form_error').dialog({resizable: false, draggable: false, buttons: { "OK": function() { $(this).dialog("close"); } }, dialogClass: 'message_dialog' })
					
				return false
			}
			
			// We are done, everything is fine, we can submit the form.
			form_is_validated = true
			$('form').first().submit();
			//return true;
		
		}
	

	}
}


is_in_area = function(area_as_string, lat, lon){

	if ((area_as_string == null) || (area_as_string == "")){
		return false
	}
	
	switch (area_as_string){
		case "London":
			lat_max = 51.643590
			lat_min = 51.360634
			lon_max = 0.137329
			lon_min = -0.379028
			if ((lat <= lat_max) && (lat >= lat_min) && (lon <= lon_max) && (lon >= lon_min)){
				return true;
			}
			break;
		default:
			return false;
	}
	return false;
	
}

display_debug_string = function(debug_string){
	$('#debug_area').text(debug_string)
}

// Below is some LivePipe setup stuff:

// Wrapper from PJ: there is a bug with the library. When you
// click a link, it opens the window but also sends it a click notification. When
// closeOnClick is set to true, it then closes the window instantly (i.e. you don't see the
// window at all).
open_livepipe_window = function(type, target_el, options){
	return true;
	switch (type) {
		case 'modal':
			setTimeout(function() {Control.Modal.open(target_el,options);}, 0);
		case 'window':
			setTimeout(function() {Control.Window.open(target_el,options);}, 0);
		break;
	}
}

//styled examples use the window factory for a shared set of behavior  
window_factory = function(container,target_link,options){  
    return true
	var window_header = new Element('div',{  
        className: 'window_header'  
    });  
    var window_title = new Element('div',{  
        className: 'window_title'  
    });  
    var window_close = new Element('div',{  
        className: 'window_close'  
    });  
    var window_contents = new Element('div',{  
        className: 'window_contents'  
    });  
    var w = new Control.Window(container,Object.extend({  
        className: 'window',  
        closeOnClick: window_close,  
        draggable: window_header,
  		//position: 'mouse',
		indicator: $('spinner'),
        insertRemoteContentAt: window_contents//,  
        //afterOpen: function(){  
        //    window_title.update(container.readAttribute('title'))  
        //}  
    },options || {}));  
    w.container.insert(window_header);  
    window_header.insert(window_title);  
    window_header.insert(window_close);  
    w.container.insert(window_contents);  
    return w;  
};


sort_it = function(TheArr, us, u, vs, v, ws, w, xs, x, ys, y, zs, z) {
// From: http://javascript.keithjoubert.com/sorting.htm    
// us-zs: 1=asc, -1=desc.  u-z: column-numbers.  See example
    if (u == undefined) {
        TheArr.sort(Sortsingle);
    }
    // if this is a simple array, not multi-dimensional, ie, SortIt(TheArr,1): ascending.
    else {
        TheArr.sort(Sortmulti);
    }

    function Sortsingle(a, b) {
        var swap = 0;
        if (isNaN(a - b)) {
            if ((isNaN(a)) && (isNaN(b))) {
                swap = (b < a) - (a < b);
            }
            else {
                swap = (isNaN(a) ? 1: -1);
            }
        }
        else {
            swap = (a - b);
        }
        return swap * us;
    }

    function Sortmulti(a, b) {
        var swap = 0;
        
		//TODO: this is dirty. I am just taking into account
		// that price is a two-level hash: a[price][value].
		// We should find a better way to do this. This function is
		// not generic anymore...
		if (u == 'price'){
			a_u = a[u]['value']
			b_u = b[u]['value']
		}
		else {
			a_u = a[u]
			b_u = b[u]
		}
		

		if (isNaN(a_u - b_u)) {
            if ((isNaN(a_u)) && (isNaN(b_u))) {
                swap = (b_u < a_u) - (a_u < b_u);
            }
            else {
                swap = (isNaN(a_u) ? 1: -1);
            }
        }
        else {
            swap = (a_u - b_u);
        }
        if ((v == undefined) || (swap != 0)) {
            return swap * us;
        }
        else {
            if (isNaN(a[v] - b[v])) {
                if ((isNaN(a[v])) && (isNaN(b[v]))) {
                    swap = (b[v] < a[v]) - (a[v] < b[v]);
                }
                else {
                    swap = (isNaN(a[v]) ? 1: -1);
                }
            }
            else {
                swap = (a[v] - b[v]);
            }
            if ((w == undefined) || (swap != 0)) {
                return swap * vs;
            }
            else {
                if (isNaN(a[w] - b[w])) {
                    if ((isNaN(a[w])) && (isNaN(b[w]))) {
                        swap = (b[w] < a[w]) - (a[w] < b[w]);
                    }
                    else {
                        swap = (isNaN(a[w]) ? 1: -1);
                    }
                }
                else {
                    swap = (a[w] - b[w]);
                }
                if ((x == undefined) || (swap != 0)) {
                    return swap * ws;
                }
                else {
                    if (isNaN(a[x] - b[x])) {
                        if ((isNaN(a[x])) && (isNaN(b[x]))) {
                            swap = (b[x] < a[x]) - (a[x] < b[x]);
                        }
                        else {
                            swap = (isNaN(a[x]) ? 1: -1);
                        }
                    }
                    else {
                        swap = (a[x] - b[x]);
                    }
                    if ((y == undefined) || (swap != 0)) {
                        return swap * xs;
                    }
                    else {
                        if (isNaN(a[y] - b[y])) {
                            if ((isNaN(a[y])) && (isNaN(b[y]))) {
                                swap = (b[y] < a[y]) - (a[y] < b[y]);
                            }
                            else {
                                swap = (isNaN(a[y]) ? 1: -1);
                            }
                        }
                        else {
                            swap = (a[y] - b[y]);
                        }
                        if ((z = undefined) || (swap != 0)) {
                            return swap * ys;
                        }
                        else {
                            if (isNaN(a[z] - b[z])) {
                                if ((isNaN(a[z])) && (isNaN(b[z]))) {
                                    swap = (b[z] < a[z]) - (a[z] < b[z]);
                                }
                                else {
                                    swap = (isNaN(a[z]) ? 1: -1);
                                }
                            }
                            else {
                                swap = (a[z] - b[z]);
                            }
                            return swap * zs;
                        }
                    }
                }
            }
        }
    }
}


activate_waiting_splash_screen = function(paction){
	
	if (IS_MOBILE_DEVICE){
		if (paction){
			$('#waiting_screen').dialog({modal: true, resizable: false, dialogClass: 'waiting_screen_dialog'})
			//$('#footer_bar').hide()
		}
		else
		{
			$('#waiting_screen').dialog('close')
			//$('#footer_bar').show()
			
			
		}
		return;
	}
	
	if (paction){
		//$('.sequence').children().not('.sequence_parameters').hide()
		$('.sequence').children().not('.sequence_parameters_no_edit').hide()
		$('.form_submit_button').hide()
		$('#waiting_screen').show()
	}
	else
	{
		//$('.sequence').children().not('.sequence_parameters_dialog').show()
		
		////$('.sequence').children().not('.sequence_parameters').not('.sequence_parameters_dialog').show()
		////$('.form_submit_button').show()
		$('.sequence').children().not('.sequence_parameters').not('.sequence_parameters_dialog').not('.form_submit_button').show()
		
		$('#waiting_screen').hide()
		$(document).trigger('updated_fields')
	}
	
}

/**** Geo-location ***/

initiate_geolocation = function() {  
	navigator.geolocation.getCurrentPosition(handle_geolocation_query, handle_geolocation_errors, {enableHighAccuracy: false, timeout: 3000});  
}  

  
handle_geolocation_errors = function(error)  
{  
    alert("Sorry, we could not locate you. Please enter an address manually or use the map.");

	//switch(error.code)  
    //{  
    //    case error.PERMISSION_DENIED: alert("user did not share geolocation data");  
    //    break;  

    //    case error.POSITION_UNAVAILABLE: alert("could not detect current position");  
    //    break;  

    //    case error.TIMEOUT: alert("retrieving position timed out");  
    //    break;  

    //    default: alert("unknown error");  
    //    break;  
    //}

	$('.target_for_geolocation').removeClass('target_for_geolocation')
}  
  
function handle_geolocation_query(position){  
	//alert('Lat: ' + position.coords.latitude +  
    //     ' Lon: ' + position.coords.longitude);

	var latlng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
	geocoder.geocode({'latLng': latlng}, function(results, status) {
    	if (status == google.maps.GeocoderStatus.OK) {
       		i = 0
			while ((results[i] == null) && (i<7)){
				i++;
			}
         	if (results[i]){
				//alert(results[i].formatted_address)
				$('.target_for_geolocation').first().val(results[i].formatted_address).removeClass('target_for_geolocation')
			}
			else {
				alert("Sorry, we could not locate you. Please enter an address manually or use the map.");
			}
     	} 
		else {
       		alert("Sorry, we could not locate you. Please enter an address manually or use the map.");
     	}
	});
	
	//$('.target_for_geolocation').removeClass('target_for_geolocation')
}

create_auto_locator_button = function(psequence_id){
	if (IS_MOBILE_DEVICE){
		destination_div = $("#" + psequence_id).find(".destination").first()
		target_icon = $("<a/>", {'class': 'geo-location', 'data-role': 'button', 'data-icon': 'geo_location_icon', 'data-iconpos': 'notext'})
		//target_icon = $("<a/>", {'class': 'geo-location'}).html("<img src='/images/geolocation_icon.png' />")
		
		target_icon.appendTo(destination_div).click(function(event){
			destination_div.find("input[type=text]").addClass('target_for_geolocation')
			initiate_geolocation();
		})
		
		$("<div/>", { 'class': 'clear_float' }).appendTo(destination_div)
		
	}
}


// The standard clone() seems to break the selectBoxes. 
// safe_clone first remove the selectBoxes, then clone and add them
// back on both the original and the copy. So it is transparent.
$.fn.clone_safe = function(){
	try {
		$(this).find('select').selectBox('destroy')
	}
	catch (err) {
		
	}
	cloned_container = $(this).clone()
	create_selectBoxes($(this))
	create_selectBoxes(cloned_container)
	return cloned_container
}

// The standard remove() combined with clone() seems to break the selectBoxes. 
// remove_safe first remove the selectBoxes, then remove the element.
// It is transparent.
$.fn.remove_safe = function(){
	if (($(this).length > 0) && (!IS_MOBILE_DEVICE)) {
		$(this).find('select').selectBox('destroy')
		//$('.score_tooltip').mouseout()
		$(this).find('.tooltip_trigger').data('tooltip').hide()
	}
	
	return $(this).remove()
}

// Check for value of fields marked with class "check_present" in form.
check_presence = function(jq_form, jq_error_container){
	
	form_is_valid = true
	
	jq_form.find('.check_present').each(function(idx){
		$(this).removeClass('form_error') // Cleaning
		if (($(this).val() == null) || ($(this).val() == "")){
			form_is_valid = false
			$(this).addClass('form_error')
		}
	})
	
	if (!form_is_valid){
		jq_error_container.html("Some information is missing. Please check the form.")
	}
	else {
		jq_error_container.html("")
	}
	
	return form_is_valid	
	
}


rearrange_availability_informations = function(jq_provider){
	ais = jq_provider.find('.availability_information')
	ais_hash = {}
	ais.each(function(idx){
		source_name = get_availability_information_source_name($(this))
		// First we create a hash of ais infos
		if (ais_hash[source_name] == null){
			ais_hash[source_name] = {}
			ais_hash[source_name]['booking_link'] = get_availability_information_booking_link($(this))
			ais_hash[source_name]['availability_informations'] = []
			ais_hash[source_name]['availability_informations'].push($(this))
			return true;
		}
		for (i = 0; i < ais_hash[source_name]['availability_informations'].length; i++){ // Order by price
			if (get_availability_information_price(ais_hash[source_name]['availability_informations'][i]) > get_availability_information_price($(this))){
				ais_hash[source_name]['availability_informations'].splice(i, 0, $(this))
				return true;
			}
		}
		ais_hash[source_name]['availability_informations'].push($(this))
	})
	
	return ais_hash
	
}

get_availability_information_price = function(jq_ai){
	return parseFloat(jq_ai.find('.availability_information_price').html())
}
get_availability_information_source_name = function(jq_ai){
	return jq_ai.find('.source_name').first().html()
}
get_availability_information_booking_link = function(jq_ai){
	return jq_ai.find('.booking_link').first().find('a')
}


render_booking_options = function(jq_sequence_individual_proposal){
	booking_options_div = $("<div/>", {'class': 'booking_options'}).html("<h3>Booking options:</h3>")
	jq_sequence_individual_proposal.find('.activities .provider').each(function(idx){
		provider = $(this).clone()
		provider.css('margin-left', 'auto')
		provider.css('width', 'auto')
		
		provider.find('.pre_select_button').remove()
		provider.find('.time_slots').removeClass('hide')
		provider.find('.time_slots .availability_informations').addClass('hide')
		
		ais_hash = rearrange_availability_informations(provider)
		for (var key in ais_hash){
			source_name = $("<div/>", {'class': 'source_name'}).html(key)
			provider.append(source_name)
			ais_hash[key]['booking_link'].html("<button type='button' class='booking_link'>Book!</button>")//.click(function(event){event.stopPropagation();})
			
			if (!IS_MOBILE_DEVICE){
				ais_hash[key]['booking_link'].click(function(event){event.stopPropagation();})
			}
			
			
			provider.append(ais_hash[key]['booking_link'])
			$(ais_hash[key]['availability_informations']).each(function(idx2){
				ai = $(this).clone()
				get_availability_information_booking_link(ai).remove()
				provider.append(ai)
			})
		}
		
		if (IS_MOBILE_DEVICE){
			provider.attr('onclick','').unbind('click');
		}
		
		
		booking_options_div.append(provider)
	})
	
	return booking_options_div
	//jq_sequence_individual_proposal.find('.map_canvas_container').prepend(booking_options_div)
}

display_booking_optionsOLD = function(jq_sequence_individual_proposal){
	booking_options_div = $("<div/>", {'class': 'booking_options'}).html("<h3>Booking options:</h3>")
	jq_sequence_individual_proposal.find('.provider').each(function(idx){
		provider = $(this).clone()
		provider.css('margin-left', 'auto')
		provider.css('width', 'auto')
		
		provider.find('.pre_select_button').remove()
		provider.find('.time_slots').removeClass('hide')
		provider.find('.time_slots').find('.availability_informations').addClass('hide')
		//provider.find('.time_slot_data').show()
		ais_hash = rearrange_availability_informations(provider)
		for (var key in ais_hash){
			source_name = $("<div/>", {'class': 'source_name'}).html(key)
			provider.append(source_name)
			ais_hash[key]['booking_link'].html("<button type='button' class='booking_link'>Book!</button>")
			provider.append(ais_hash[key]['booking_link'])
			$(ais_hash[key]['availability_informations']).each(function(idx2){
				ai = $(this).clone()
				get_availability_information_booking_link(ai).remove()
				provider.append(ai)
			})
		}
		booking_options_div.append(provider)
	})
	
	//booking_options_div.dialog({ width: 300 })
	jq_sequence_individual_proposal.find('.map_canvas_container').prepend(booking_options_div)
}


render_default_sequence = function(jq_sequence){
	if (sequence_details_were_revealed[jq_sequence.attr('id')] || in_results_view){
		return; // The user saw the default sequence or is in the results view - so we should not 
				// modify it programatically now.
	}
	else {

		$(jq_sequence).find('.manual_dates_selector').first().find("input[type=hidden]").each(function(idx, linput){
			if (linput.id.search(/start_date/) >= 0){
				pstart = string_to_date_conversion(linput.value)
			}
			else if (linput.id.search(/end_date/) > 0){
				pend = string_to_date_conversion(linput.value)
			}
		})
		
		default_sequence = fetch_default_sequence(pstart, pend)
		preset_activities(jq_sequence, default_sequence)
		
		if (IS_MOBILE_DEVICE){
			$('.events').trigger("create")
		}
		
	}
}


fetch_default_sequence = function(pstart, pend){
	start_hour = pstart.getHours()
	duration = (pend - pstart)/(1000 * 3600) // In hours
	
	// Late night
	if (start_hour <= 5) {
		if (duration <= 5){ // Really a late night thing
			return [6] // [Resto]
		}
		else { // Could be an ealy morning thing?
			return [6] //return [5, -10]
		}
	}
	// Morning
	if (start_hour < 11) {
		if (duration < 5){ // Very short
			return [-10] // [Any event]
		}
		else {
			return [-10, 5]
		}
	}
	// Lunch time
	if (start_hour < 14) {
		if (duration < 5){ // Short
			return [5] // [Resto]
		}
		else {
			return [5, -10]
		}
	}
	// Afternoon / Evening
	if (start_hour <= 20) {
		if (duration < 5){ // Very short
			return [-10] // [Any event]
		}
		//else if (duration <= 4){ // Short
		//	return [-10, 5]
		//}
		else {
			return [-10, 5] //TEMPORARY return [-10, 5, 6]
		}
	}
	if (start_hour <= 21) {
		if (duration < 5){ // Very short
			return [-10]
		}
		else {
			return [-10, 6]
		}
	}
	else {
		return [6]
	}
	
	
}

preset_activities = function (jq_sequence, default_sequence){
	if (jq_sequence == null){
		return
	}

	jq_sequence.find('.real_event').find('.remove_fields_link_for_event').click()
	
	add_activity_link = jq_sequence.find('.add_fields_link_for_event').first()
	$(default_sequence).each(function(idx){
		add_activity_link.click()
		levent = jq_sequence.find('.event').addClass('hidden').last()
		//levent.addClass('hidden')
		lselect = levent.find('select').first()
		lselect.val(default_sequence[idx] + "")
		lselect.change()
		lselect.trigger('update_dependents')
	})
	
}

set_advanced_search_link_flag = function(jq_advanced_search_link){
	sequence_id = jq_advanced_search_link.parents('.sequence').first().attr('id')
	sequence_details_were_revealed[sequence_id] = true
}

$.fn.modal_datepicker = function(options){
	
		////modal_dialog_div = $("<div />", {'class': 'modal_datepicker_dialog'})
		modal_dialog_div = $("<div />")
		modal_datepicker_div = $("<div />", {'class': 'modal_datepicker_datepicker'})	
		modal_dialog_div.append(modal_datepicker_div);
		
			
	
		
		jqready_target_id = "#" + $(this).attr('id')
		all_options = {altField: jqready_target_id, onSelect: function(dateText, inst) {$(jqready_target_id).change(); modal_dialog_div.dialog('destroy');modal_dialog_div.remove();opening_modal_window(false);}, defaultDate: $(this).val()}
		
		
		
		for (attr in options) { all_options[attr] = options[attr]; }

		modal_datepicker_div.datepicker(all_options)
		
		
		
		//modal_dialog_div.dialog({modal: true, resizable: false, draggable: false, position: 'center', dialogClass: 'modal_datepicker_dialog'})
		modal_dialog_div.dialog({modal: true, resizable: false, draggable: false, position: 'center', dialogClass: 'modal_datepicker_dialog', open: opening_modal_window(true)})
	
	
}

// Android bug: when taping in a modal window, the event is passed to selects "behind"
// and it does not seem possible to prevent propagation. In the workaround below we
// deactivate the selects entirely.
opening_modal_window = function(bool){
	if (!IS_ANDROID_DEVICE) { return }
	
	if (bool){
		$('select').selectmenu('disable');
	}
	else {
		$('select').selectmenu('enable');
	}
}

remap_image = function(image_container, original_width){
	
	return;
	
	img = image_container.find('img').first()
	map = image_container.find('map').first()
	
	new_width = img.width()
	
	if (new_width != original_width){
		ratio = (new_width + 0.0) / original_width
		
		map.find('area').each(function(){
		      var coord_vals = $(this).attr('coords').split(',');
		      var new_vals = [];
		      for(var i=0; i<coord_vals.length; i++) {
		          new_vals[i] = Math.round(coord_vals[i] * ratio);
		      }
		      new_vals = new_vals.join(",");
		      $(this).attr('coords', new_vals);
		 });
		
	}
	
	
}

/*refresh_scroller = function(){
	if (typeof scrollers === 'undefined') { return; } 
	
	setTimeout(function () {
		$(scrollers).each(function(idx){
			this.refresh();
			this.refresh();
		})
		
	}, 0);
}*/

refresh_jquerymobile_widgets = function(jq_element){
	
	if ((IS_MOBILE_DEVICE) || (IS_TABLET_DEVICE)){ 
		jq_element.find('select').selectmenu();
	}
	
}


jquery_mobile_custom_init = function(){
	$(document).bind('mobileinit', function(){ 
		
		$.mobile.defaultPageTransition = 'none'; // When transitions are "on", pages keep scrolling
		// up before transitioning which is very ennoying. Should get fixed with iOS 5.
		////$.mobile.fixedToolbars.setTouchToggleEnabled(false); USED Pre 1.1
		
		//$.mobile.selectmenu.prototype.options.nativeMenu = false;
		
		//$.mobile.nativeSelectMenu = true;
		////$.mobile.touchOverflowEnabled = true; USED Pre 1.1
		
		
		if (USING_LOADING_MESSAGE){
			//$("div[data-role='navbar'] a").live('click', function(){$.mobile.showPageLoadingMsg();})
			//$('#sequence_parameters_page').live('pageshow',function(event, ui){$.mobile.hidePageLoadingMsg();});
			//$('#activities_page').live('pageshow',function(event, ui){$.mobile.hidePageLoadingMsg();});
			//$('#results_page').live('pageshow',function(event, ui){$.mobile.hidePageLoadingMsg();});
			
			$('#booking_options_page').live('pageshow',function(event, ui){$.mobile.hidePageLoadingMsg();});
			
			$('#results_pageBK').live('pageshow',function(event, ui){
				$('.sequence').each(function(){
					$(this).find('.events').hide().insertAfter($(this).find('.show_all_possibilities_link').last()).before(
							$("<div/>", {'class': 'activities_toggle_link'}).html("Activities").click(function(){
								exclusive_toggle($(this).parents('.sequence').find('.events').addClass('exclusive_toggle'), $(this).parents('.individual_proposals'));
							})
						);
				})
			});
			
			$('#results_page').live('pageshow',function(event, ui){
			
				if ((ui.prevPage == null) || (ui.prevPage.attr('id') == 'sequence_parameters_page')){
				
					$('.sequence').each(function(){
						levents = $(this).find('.events')
						levents.hide()
						levents.appendTo($(this).find('.individual_proposals'))	
						$(this).find('.show_all_possibilities_link').last().after(
								$("<div/>", {'class': 'activities_toggle_link'}).html("Activities").click(function(){
									exclusive_toggle($(this).parents('.sequence').find('.events').addClass('exclusive_toggle'), $(this).parents('.individual_proposals'));
								})
						)
					})
				}
			})
			
			$('#sequence_parameters_page').live('pageshow',function(event, ui){
				$('.sequence').each(function(){
					$(this).find('.activities_toggle_link').remove()
					$(this).find('.events').removeClass('exclusive_toggle').insertBefore($(this).find('#sequence_parameters_page .skyline_mobile').first()).show();
				})
			});
		}
		
	});
	
}

jquery_mobile_custom_init_tablet = function(){
	$(document).bind('mobileinit', function(){ 
		//$.mobile.ns = "tablet-";
		$.mobile.ajaxEnabled = false;
		$.mobile.linkBindingEnabled = false;
		$.mobile.hashListeningEnabled = false;
		$.mobile.pushStateEnabled = false;
		$.mobile.defaultPageTransition = "none";
		$.mobile.touchOverflowEnabled = false;
		//$.mobile.nativeSelectMenu = true;
		//$.mobile.page.prototype.options.keepNative = "input";
	});
		
}

do_updates_after_content_change = function() {
	if (IS_MOBILE_DEVICE){ 
		$('.provider.possibility').bind('vmousedown', function(){$(this).addClass('pressed')}).bind('vmouseup', function(){$(this).removeClass('pressed')}).find('.pre_select_button').bind('vmousedown', function(e){$(this).addClass('pressed'); e.stopPropagation();}).bind('vmouseup', function(){$(this).removeClass('pressed')});	
	
		$('.sequence_individual_proposal_information_section_after').trigger("create")
	}
	
}

generate_provider_additional_info_dialog = function(jq_caller){
	
	additional_info_container = $('#individual_map_container')
	
	additional_info_container.dialog({ dialogClass: 'provider_additional_info_dialog', resizable: false, title: jq_caller.find('.provider_name').html()});
	if (IS_MOBILE_DEVICE) {
		additional_info_container.dialog("option", { modal: true, draggable: false});
	}
	
	display_map_with_marker(jq_caller, $('#individual_map_canvas'));
	additional_info_container.find('.provider_description').first().html(jq_caller.find('.description').first().clone(true).removeClass('hide'));
	//additional_info_container.find('.provider_name').first().html(jq_caller.find('.provider_name').first().clone(true));
	$('.spinner2_reviews').show();
	
	
}

activity_type_changing = function(el, pcontext){
	
	type_input = el.parents('.event_type').first().find('input').first()
	sub_type_input = el.parents('.event_data').first().find('.event_sub_type').first().find('input').first()
	
	previous_type = type_input.val()
	previous_sub_type = sub_type_input.val()
	
	select = el.parents('.event_type').find('select')
	
	type_input.val(el.val());
	sub_type_input.val(-10);
	
	el.parents('.event_type').first().find('.event_type_icon').first().removeClass(function(idx, klass){return klass.match(/type_[-]*[0-9]+/g)[0];}).addClass('type_' + el.val())
	
	//alert(el.parents('.event_type').first().find('.event_type_icon').first().attr('class'))
	
	generate_options_for_sub_type_select(el.parents('.event_data').first().find('#event_sub_type2').first(),el.val());
	//sub_type_select = el.parents('.event_data').first().find('.event_sub_type').find('select')
	
	if (pcontext) {
		if (!render_sequence_proposals(el.parents('.sequence').first().attr('id').match(/sequence__[0-9]*/g)[0].replace(/sequence__/,''))){
			// Something wrong happened, we roll back.
			//type_input.val(previous_type)
			//sub_type_input.val(previous_sub_type)
			//if (USING_SELECTBOX){
			//	select.selectBox('value', select.data('current'));
			//}
			//else {
			//	select.val(select.data('current'));
			//}
			return
		}
	}
	
	// Everything OK, we update the "current" values
	if (USING_SELECTBOX){
		select.data('current', select.selectBox('value'));
		//sub_type_select.data('current', sub_type_select.selectBox('value'))
	}
	else {
		select.data('current', select.val());
		//sub_type_select.data('current', sub_type_select.val())
	}
	
	$(document).trigger('parameters_got_updated')
	if (IS_MOBILE_DEVICE){
		mobile_scroll_to_top($('#results_page_content'))		
	}
	
}


activity_sub_type_changing = function(el, pcontext){
	
	sub_type_input = el.parents('.event_sub_type').first().find('input').first()
	previous_sub_type = sub_type_input.val()
	
	select = el.parents('.event_sub_type').find('select')
	
	sub_type_input.val(el.val());
	if (pcontext) {
		if (!render_sequence_proposals(el.parents('.sequence').first().attr('id').match(/sequence__[0-9]*/g)[0].replace(/sequence__/,''))){
			//sub_type_input.val(previous_sub_type)
			//if (USING_SELECTBOX){
			//	select.selectBox('value', select.data('current'));
			//}
			//else {
			//	select.val(select.data('current'));
			//}
			return
		}
	}
	
	type_select = el.parents('.event_data').find('.event_type select').first()
	if (USING_SELECTBOX){
		type_select.data('current_sub_type', select.selectBox('value'));
	}
	else {
		type_select.data('current_sub_type', select.val());
	}
	
	$(document).trigger('parameters_got_updated')
	if (IS_MOBILE_DEVICE){
		mobile_scroll_to_top($('#results_page_content'))		
	}
	
}

roll_back_select_change = function(jq_sequence_main_container){
	
	jq_sequence_main_container.find('.events .event_type select').each(function(){
		
		// First we set all type selects to their previous value
		if (USING_SELECTBOX){
			$(this).selectBox('value', $(this).data('current'));
		}
		else {
			$(this).val($(this).data('current'));
		}
		if (IS_MOBILE_DEVICE) {
			$(this).selectmenu("refresh"); // Needed for the MUI to update
		}
		
		// Then we store the sub_type previous value (they will be overriden when we call activity_type_changing)
		sub_type_select = $(this).parents('.event_data').find('.event_sub_type select').first()
		previous_sub_type_value = $(this).data('current_sub_type')
		
		// Then we trigger the updates required by the type change
		activity_type_changing($(this), false)
		
		
		// We manually set the sub_type selects to their previous value
		if (USING_SELECTBOX){
			sub_type_select.selectBox('value', previous_sub_type_value);
		}
		else {
			sub_type_select.val(previous_sub_type_value);
		}
		if (IS_MOBILE_DEVICE) {
			sub_type_select.selectmenu("refresh"); // Needed for the MUI to update
		}
		// We trigger the dependent updates
		activity_sub_type_changing(sub_type_select, false)
		
	})
	
	
	$('.submit_button_dialog').hide()
	
}


remove_adjacent = function(parray) {
	if (parray.length > 1) {		
		nEvents = parray[0]['arrangement'].length
		
		// 1st part: remove adjacent duplicate
		for (i = 0; i < nEvents; i++) {
			// check if the event has been locked
			currentProvider = parray[0]['arrangement'][i]['provider_id']
			last_j = 0
			j = 1
			while (j < parray.length){
				
				while (j < parray.length && currentProvider == parray[j]['arrangement'][i]['provider_id']){
					j++
				}

				if (j < parray.length){
					parray.splice(last_j + 1,j - last_j - 1)
					last_j++
					j = last_j + 1
					currentProvider = parray[last_j]['arrangement'][i]['provider_id']
				}
			}
		}
	}
	return parray
}


check_diversity = function(parray, n) {
	if (parray.length > 1) {		
		nEvents = parray[0]['arrangement'].length

		// 1st part: remove adjacent duplicate
		for (i = 0; i < nEvents; i++) {
			// check if the event has been locked
			currentProvider = parray[0]['arrangement'][i]['provider_id']
			last_j = 0
			j = 1
			while (j < parray.length){
				
				while (j < parray.length && currentProvider == parray[j]['arrangement'][i]['provider_id']){
					j++
				}

				if (j < parray.length){
					parray.splice(last_j + 1,j - last_j - 1)
					last_j++
					j = last_j + 1
					currentProvider = parray[last_j]['arrangement'][i]['provider_id']
				}
			}
		}

		for (i = 0; i < nEvents; i++) {
			currentProvider = parray[0]['arrangement'][i]['provider_id']
			last_j = 0
			j = 2
			while (j < parray.length){
				if (parray[last_j + 1]['arrangement'][i]['provider_id'] != currentProvider) {
					while (j < last_j + n && parray.length) {
						if (parray[j]['arrangement'][i]['provider_id'] == currentProvider) {
							parray.splice(j,1)
						} else {
							j++
						}
					}
					last_j++
					j = last_j + 2;
				} else {
					lastj++
					j = last_j + 2
				}
			} 

		}

	}
	return parray
}


// With this method, we make sure only one element in the container can be visible among the exclusive one.
exclusive_toggle = function(jq_visible_el, jq_container){
	if (jq_visible_el.hasClass('visible')){
		jq_visible_el.removeClass('visible')
		return
	}
	
	jq_container.find('.exclusive_toggle').removeClass('visible')
	jq_visible_el.addClass('visible')
}

var providers_positioning_in_view = {}
position_html = function(html_object, container_id, object_parameters_as_hash){
	container_references = providers_positioning_in_view[container_id]
	if (container_references === undefined){
		container_references = []
		providers_positioning_in_view[container_id] = container_references
	}
	container_references.push(object_parameters_as_hash)
//	if (object_parameters_as_hash['provider_type'] == 5) { // Restaurant
//		sort_it(container_references,1,'provider_type',-1,'rating')//, 1, 'name')
//	}
//	else {
//		//sort_it(container_references,1,'provider_type',1,'provider_sub_type',-1,'rating')
//		sort_it(container_references,1,'provider_type',-1,'rating')//, 1, 'additional_name')
//	}
	sort_it(container_references, -1,'rating')
	
	el_index = $.inArray(object_parameters_as_hash, container_references)
	
	if ((el_index == -1) || (el_index == 0)){
		$('#'+container_id).prepend(html_object) //That's the standard behavior, no ranking.
	}
	else {
		$('#'+container_references[el_index - 1]['html_id']).after(html_object)
	}
	
}