442 lines
15 KiB
JavaScript
442 lines
15 KiB
JavaScript
function combo_load_cell(elem) {
|
||
var $elem = $(elem);
|
||
var url = $elem.data('ajax-cell-url');
|
||
var extra_context = $elem.data('extra-context');
|
||
$.support.cors = true; /* IE9 */
|
||
var qs;
|
||
if (url.includes('?')) {
|
||
qs = '&';
|
||
if (window.location.search) {
|
||
qs += window.location.search.slice(1) + '&';
|
||
}
|
||
} else if (window.location.search) {
|
||
qs = window.location.search + '&';
|
||
} else {
|
||
qs = '?';
|
||
}
|
||
if (extra_context) {
|
||
qs += 'ctx=' + extra_context;
|
||
}
|
||
$.ajax({url: url + qs,
|
||
xhrFields: { withCredentials: true },
|
||
async: true,
|
||
dataType: 'html',
|
||
crossDomain: true,
|
||
success: function(data) {
|
||
$elem.addClass('ajax-loaded');
|
||
$elem.find('> div').nextAll().remove();
|
||
if (data == '') {
|
||
$elem.find('> div').remove();
|
||
} else {
|
||
$elem.find('> div').html(data);
|
||
}
|
||
if (! $elem.find('> div').children().length) {
|
||
$elem.addClass('empty-cell');
|
||
} else {
|
||
$elem.removeClass('empty-cell')
|
||
}
|
||
$(document).trigger('combo:cell-loaded', $elem);
|
||
},
|
||
error: function(error) {
|
||
var msg = $(elem).data('ajax-cell-error-message');
|
||
if (!msg) msg = 'Unknown error (code: ' + error.status + ')';
|
||
$elem
|
||
.removeClass('empty-cell')
|
||
.find('.loading')
|
||
.addClass('error-loading')
|
||
.html('<span class="loading-message">' + msg + '</span>');
|
||
window.console && console.log(':(', error);
|
||
}
|
||
});
|
||
}
|
||
|
||
function combo_modify_query_string(name, value) {
|
||
var search = window.location.search.substring(1);
|
||
var parts = search.split('&');
|
||
var newparts = [];
|
||
var modified = 0;
|
||
value = encodeURIComponent(value);
|
||
for (var i = 0; i < parts.length; i++) {
|
||
if (! parts[i]) {
|
||
continue;
|
||
}
|
||
var pair = parts[i].split('=');
|
||
if (pair[0] == name) {
|
||
if (value) {
|
||
newparts.push(name + '=' + value);
|
||
}
|
||
modified = 1;
|
||
} else {
|
||
newparts.push(parts[i]);
|
||
}
|
||
}
|
||
if (! modified && value) {
|
||
newparts.push(name + '=' + value);
|
||
}
|
||
if (newparts) {
|
||
search = '?' + newparts.join('&');
|
||
} else {
|
||
search = '';
|
||
}
|
||
return search;
|
||
}
|
||
|
||
/* Launch callbacks when scroll move above and below a limit
|
||
* new ComboScrollY({
|
||
* limit: int,
|
||
* below: callback function,
|
||
* above: callback function
|
||
* })
|
||
*/
|
||
function ComboScrollY(options) {
|
||
this.defaults = {
|
||
limit: 0
|
||
};
|
||
this.options = options;
|
||
this.init();
|
||
};
|
||
ComboScrollY.prototype.init = function(){
|
||
this.options = $.extend({}, this.defaults, this.options);
|
||
this.callbacks();
|
||
window.addEventListener('scroll', this.callbacks.bind(this));
|
||
};
|
||
ComboScrollY.prototype.position = function(){
|
||
return (window.pageYOffset <= this.options.limit) ? "above" : "below";
|
||
};
|
||
ComboScrollY.prototype.update = function(){
|
||
return (this.position() === this.last_callback_position) ? false : true;
|
||
};
|
||
ComboScrollY.prototype.callbacks = function() {
|
||
if (this.update()) {
|
||
if (this.position() === "below") {
|
||
this.options.below();
|
||
this.last_callback_position = "below";
|
||
} else {
|
||
this.options.above();
|
||
this.last_callback_position = "above";
|
||
}
|
||
}
|
||
};
|
||
|
||
|
||
$(function() {
|
||
$('[data-ajax-cell-refresh]').each(function(idx, elem) {
|
||
var $elem = $(elem);
|
||
function refresh() {
|
||
combo_load_cell($elem);
|
||
}
|
||
$elem.timeout_id = setInterval(refresh, $elem.data('ajax-cell-refresh')*1000);
|
||
});
|
||
$('[data-ajax-cell-must-load]').each(function(idx, elem) {
|
||
combo_load_cell($(elem).parents('div.cell')[0]);
|
||
});
|
||
$('div.cell > div').each(function(idx, elem) {
|
||
if (! $('[data-ajax-cell-must-load]', $(elem)).length && ! $(elem).children().length) {
|
||
$(elem).parent().addClass('empty-cell');
|
||
}
|
||
});
|
||
|
||
/* utility functions and events, for themes */
|
||
$('.togglable').on('click', function() {
|
||
$(this).toggleClass('toggled');
|
||
});
|
||
/* reload cells when parameters changes */
|
||
function combo_refresh_ajax_cells() {
|
||
$('[data-ajax-cell-url]').each(function(idx, elem) {
|
||
var $elem = $(elem);
|
||
var msg = $(elem).data('ajax-cell-loading-message');
|
||
$elem.prepend('<div><div class="loading"><span class="loading-message">' + msg + '</span></div></div>');
|
||
combo_load_cell($elem);
|
||
});
|
||
}
|
||
|
||
$(document).on('click keypress', '.add-more-items--button', function(e) {
|
||
if (e.type === 'keypress' && !(e.key === ' ' || e.key === 'Enter'))
|
||
return;
|
||
|
||
e.preventDefault();
|
||
$(this).attr('aria-expanded', 'true');
|
||
$(this).parent('.add-more-items').hide();
|
||
$('#' + $(this).attr('aria-controls')).show().focus();
|
||
});
|
||
|
||
|
||
var menu_page_ids = $.makeArray($('[data-menu-page-id]').map(function() { return $(this).data('menu-page-id'); }));
|
||
if (menu_page_ids.length && $('body').data('check-badges') === true) {
|
||
$.ajax({url: $('body').data('api-root') + 'menu-badges/',
|
||
xhrFields: { withCredentials: true },
|
||
async: true,
|
||
dataType: 'json',
|
||
data: {'page': menu_page_ids},
|
||
crossDomain: true,
|
||
success: function(data) {
|
||
$(document).trigger('combo:menu-badges-loaded', data);
|
||
}});
|
||
}
|
||
$(document).on('combo:menu-badges-loaded', function(ctx, data) {
|
||
for (var page_id in data) {
|
||
var badge = data[page_id];
|
||
if (badge.badge) {
|
||
$('[data-menu-page-id=' + page_id + '] > a > span').append(' <span class="badge">' + badge.badge + '</span>');
|
||
}
|
||
if (badge.klass) {
|
||
$('[data-menu-page-id=' + page_id + ']').addClass(badge.klass);
|
||
}
|
||
}
|
||
});
|
||
$('.selfdeclaredinvoicepayment form').on('combo:load-invoice', window.displayPopup);
|
||
$('.selfdeclaredinvoicepayment form').on('submit', function(event) {
|
||
var url = $(this).attr('action');
|
||
var data = $(this).serialize();
|
||
var $form = $(this);
|
||
$.ajax({url: url + '?ajax=on',
|
||
xhrFields: { withCredentials: true },
|
||
data: data,
|
||
async: true,
|
||
dataType: 'json',
|
||
success: function(data) {
|
||
if (data.msg) {
|
||
var err_msg = $('<p></p>');
|
||
err_msg.text(data.msg);
|
||
$(err_msg).dialog({
|
||
dialogClass: 'alert',
|
||
modal: true,
|
||
width: 'auto',
|
||
buttons: {
|
||
'×': function() {
|
||
$(this).dialog('destroy');
|
||
}
|
||
},
|
||
});
|
||
return;
|
||
}
|
||
$form.data('url', data.url);
|
||
$form.trigger('combo:load-invoice', event);
|
||
},
|
||
error: function(error) {
|
||
window.console && console.log(':(', error);
|
||
}
|
||
});
|
||
return false;
|
||
});
|
||
|
||
function set_booking_calendar_sensitivity(table) {
|
||
if ($(table).find('input:checked').length == 0) {
|
||
/* no checked box, enable them all */
|
||
$(table).find('input').prop('disabled', false);
|
||
return;
|
||
}
|
||
|
||
/* disable every thing */
|
||
var column_index = $(table).find('input:checked').parents('td').index();
|
||
$(table).find('td').removeClass('active-column').removeClass('clickable');
|
||
$(table).find('input').prop('disabled', true);
|
||
|
||
/* enable checkboxes from the active column */
|
||
$(table).find('td:nth-child(' + (column_index+1) + ') input').prop('disabled', false);
|
||
/* mark active column */
|
||
$(table).find('td:nth-child(' + (column_index+1) + ')').addClass('active-column');
|
||
|
||
/* enable contiguous checkboxes; this is done by adding a clickable class
|
||
* to the approriate checkboxes, as all of them need to be kept as enabled
|
||
* HTML-wise to be transmitted on POST.
|
||
*/
|
||
var checkboxes = $(table).find('td:nth-child(' + (column_index+1) + ') input:checked');
|
||
|
||
/* enable the preceding one */
|
||
$(checkboxes[0]).parents('tr').prev().find('td:nth-child(' + (column_index+1) + ')').addClass('clickable');
|
||
/* enable first one, so it can be unchecked */
|
||
$(checkboxes[0]).parents('td').addClass('clickable');
|
||
/* enable last one, so it can be unchecked */
|
||
$(checkboxes[checkboxes.length-1]).parents('td').addClass('clickable');
|
||
/* enable the following one */
|
||
$(checkboxes[checkboxes.length-1]).parents('tr').next().find('td:nth-child(' + (column_index+1) + ')').addClass('clickable');
|
||
}
|
||
|
||
/* foldable/folded support */
|
||
function prepare_foldable(cell) {
|
||
var $cell = $(cell);
|
||
if (! $cell.is('.foldable')) {
|
||
return;
|
||
}
|
||
var $cell_title = $cell.find('> div > h2:first-child');
|
||
if ($cell_title.attr('aria-expanded') !== undefined) { // already prepared
|
||
return;
|
||
}
|
||
$cell_title.attr('tabindex', '0');
|
||
$cell_title.attr('role', 'button');
|
||
function set_aria_expanded() {
|
||
if ($cell.is('.folded')) {
|
||
$cell_title.attr('aria-expanded', 'false');
|
||
} else {
|
||
$cell_title.attr('aria-expanded', 'true');
|
||
}
|
||
}
|
||
if (document.location.hash && document.location.hash.substr(1) == $cell.attr('id')) {
|
||
// always unfold targeted cell
|
||
$cell.removeClass('folded');
|
||
}
|
||
set_aria_expanded();
|
||
$cell.find('> div > h2:first-child').on('keydown', function(ev) {
|
||
if (ev.keyCode == 13 || ev.keyCode == 32) { // enter || space
|
||
$(this).trigger('click');
|
||
return false;
|
||
}
|
||
});
|
||
$cell.find('> div > h2:first-child, > div > picture').on('click', function() {
|
||
$cell.toggleClass('folded');
|
||
set_aria_expanded();
|
||
return false;
|
||
});
|
||
}
|
||
|
||
$('div.cell.foldable').each(function(i, cell) { prepare_foldable(cell); });
|
||
$(document).on('combo:cell-loaded', function(ev, cell) { prepare_foldable(cell); });
|
||
|
||
/* add a scrolled class to body once the user scrolled the page a bit */
|
||
var body_is_scrolled = new ComboScrollY({
|
||
above: function(){
|
||
$('body').removeClass('scrolled');
|
||
},
|
||
below: function(){
|
||
$('body').addClass('scrolled');
|
||
}
|
||
});
|
||
|
||
$('body').on('click', 'a.calchunk', function(event){
|
||
event.preventDefault();
|
||
var $elem = $(this);
|
||
var url = $elem.data('content-url');
|
||
$.ajax({
|
||
url: url,
|
||
async: true,
|
||
dataType: 'html',
|
||
crossDomain: true,
|
||
success: function(data){
|
||
$elem.closest('div.calcontent').html(data);
|
||
},
|
||
error: function(error){
|
||
console.log(':(', error);
|
||
}
|
||
});
|
||
});
|
||
|
||
$('.bookingcalendar table').each(function(idx, elem) { set_booking_calendar_sensitivity(elem); });
|
||
|
||
$('body').on('change', '.bookingcalendar input', function() {
|
||
set_booking_calendar_sensitivity($(this).parents('table'));
|
||
});
|
||
|
||
$('input:not([type=checkbox]):not([type=radio]), textarea').focusin(function() {
|
||
$('body').addClass('focus-in');
|
||
}).focusout(function() {
|
||
$('body').removeClass('focus-in');
|
||
});
|
||
|
||
/* notifications */
|
||
$('div.cell').delegate('li.combo-notification', 'click', function() {
|
||
var target_url = $(this).find('a').attr('href');
|
||
if ($(this).hasClass('combo-notification-acked')) {
|
||
if (target_url != '#') {
|
||
window.location.href = target_url;
|
||
}
|
||
} else {
|
||
var ack_url = $('body').data('api-root') + 'notification/ack/' + $(this).data('combo-notification-id');
|
||
$.ajax({
|
||
url: ack_url,
|
||
success: function(html) {
|
||
$(this).addClass('combo-notification-acked');
|
||
if (target_url != '#') {
|
||
window.location.href = target_url;
|
||
}
|
||
}
|
||
});
|
||
}
|
||
return false;
|
||
});
|
||
|
||
function pagination_update_page(items, max_page_index, step, paginate_by, $pagination, focus_first_item) {
|
||
var page_index = $pagination.data('page_index') + step;
|
||
if (page_index == 0) {
|
||
$pagination.find('.cell-items-pagination-prev').prop('disabled', true);
|
||
} else {
|
||
$pagination.find('.cell-items-pagination-prev').prop('disabled', null);
|
||
}
|
||
if (page_index == max_page_index - 1) {
|
||
$pagination.find('.cell-items-pagination-next').prop('disabled', true);
|
||
} else {
|
||
$pagination.find('.cell-items-pagination-next').prop('disabled', null);
|
||
}
|
||
start_item = paginate_by * page_index;
|
||
items.hide();
|
||
items.slice(start_item, start_item + paginate_by).show();
|
||
$pagination.data('page_index', page_index);
|
||
if (focus_first_item) {
|
||
$pagination.parent().focus();
|
||
}
|
||
};
|
||
|
||
function paginate($pagination, items, must_focus) {
|
||
$pagination.data('page_index', 0);
|
||
var paginate_by = parseInt($pagination.data('paginate-by'));
|
||
var max_page_index = Math.ceil(items.length / paginate_by);
|
||
if (items.length <= paginate_by) {
|
||
return;
|
||
}
|
||
|
||
function update_page(step, focus_first_item) {
|
||
if (! must_focus) {
|
||
focus_first_item = false;
|
||
}
|
||
pagination_update_page(items, max_page_index, step, paginate_by, $pagination, focus_first_item);
|
||
}
|
||
|
||
$pagination.find('.cell-items-pagination-prev').click(function() { update_page(-1, true); });
|
||
$pagination.find('.cell-items-pagination-next').click(function() { update_page(1, true); });
|
||
update_page(0, false);
|
||
$pagination.prop('hidden', null);
|
||
};
|
||
|
||
function paginate_cards($pagination) {
|
||
var items = $pagination.parent().find('.card.' + $pagination.data('cell-reference'));
|
||
paginate($pagination, items, false);
|
||
};
|
||
|
||
// pagination for cells with list of items or for card cells
|
||
$(document).on('combo:cell-loaded', function(ev, cell) {
|
||
var $pagination = $(cell).find('.cell-items-pagination');
|
||
if ($pagination.length == 0) return;
|
||
// Get all <li> inside the same div as us, ignoring whether they are part of
|
||
// different <ul>
|
||
var wrapper = $pagination.parent();
|
||
wrapper.attr('tabindex', -1)
|
||
var items = $pagination.parent().find('li');
|
||
paginate($pagination, items, true);
|
||
});
|
||
|
||
$('.cell-items-pagination').each(function(idx, elem) {
|
||
var $cells = $(elem).parents('.cell');
|
||
if ($cells.length) {
|
||
$(document).trigger('combo:cell-loaded', $cells.first());
|
||
} else if ($(elem).data('cell-reference')) {
|
||
paginate_cards($(elem));
|
||
}
|
||
});
|
||
// accessibililty enhancements:
|
||
// support for hiding some elements (such as submenus)
|
||
// when the escape key is pressed
|
||
$(document).keydown(function(e) {
|
||
if (e.key === "Escape") {
|
||
$(".hide-on-escape:visible").hide()
|
||
}
|
||
});
|
||
// and ensure the elements can be viewed again
|
||
// on subsequent hover/focus
|
||
function reset_hidden_elements(event) {
|
||
var root_element = $(event.target).closest('.contains-hidden-elements')
|
||
$(root_element).find('.hide-on-escape').css({display: ''})
|
||
}
|
||
$('.contains-hidden-elements').mouseenter(reset_hidden_elements, null)
|
||
$('.contains-hidden-elements').focusin(reset_hidden_elements)
|
||
});
|