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