PageSourceSearch

https://citoyen.vandoeuvre.fr/static/js/combo.public.js?20260924.2241

js vandoeuvre.fr collected 2026-09-24 20:53:03 UTC 18,165 bytes, 516 lines download raw bytes

1function combo_load_cell(element, display_function) {
2  window.console && console.warn('combo_load_cell is deprecated, use combo_cell_loader instead');
3  combo_cell_loader({"element": element, "display_function": display_function});
4}
5
6function combo_cell_url({base_url=null, element=null, dismiss_location_search=false} = {}) {
7  const elem = element.nodeType ? element : element[0];
8  let current_url = new URL(window.location.href);
9  let url = base_url || elem.dataset.ajaxCellUrl;
10
11  // we need scheme://domain to use URL()
12  url = url[0] == '/' ? current_url.origin + url : url;
13  url = new URL(url);
14
15  // parameters to skip
16  const matches = ['cwcs', 'offset'];
17  if (elem.id) {
18    matches.push(elem.id);
19  }
20  const matcher = new RegExp('^(' + matches.join('|') + ')');
21
22  // copy current URL parameters to ajax URL
23  current_url.searchParams.forEach((value, key) => {
24    if (matcher.exec(key) && dismiss_location_search) {
25      return;
26    }
27    url.searchParams.append(key, value)
28  });
29
30  url = url.toString();
31  url = url.includes('?') ? url + '&' : url + '?';
32  const extra_url_params = elem.dataset.extraUrlParams || '';
33  url = extra_url_params ? `${url}${extra_url_params}&` : url;
34  const extra_context = elem.dataset.extraContext;
35  url = extra_context ? `${url}ctx=${extra_context}` : url;
36  return url;
37}
38
39function combo_cell_loader({element=null, display_function=null, dismiss_location_search=false, tries=0} = {}) {
40  const elem = element.nodeType ? element : element[0];
41  // clear existing timeout if any
42  clearTimeout(elem.timeout);
43  // abort previous request if one exists
44  if (elem.xhr !== undefined) {
45      elem.xhr.abort();
46  }
47  // abort after 10 tries
48  if (tries > 10) {
49      return;
50  }
51  let url = combo_cell_url({"element": elem, "dismiss_location_search": dismiss_location_search})
52  var $elem = $(element);
53  var display_function = display_function || function($element, data) {
54    $element.find('> div').html(data);
55  }
56  $.support.cors = true; /* IE9 */
57  elem.xhr = $.ajax({url: url,
58          xhrFields: { withCredentials: true },
59          async: true,
60          dataType: 'html',
61          crossDomain: true,
62          success: function(data, text_status, xhr) {
63              if (data == 'retry') {
64                let timeout = Math.min(30 * 1000, 1000 * Math.pow(2, tries));
65                elem.timeout = setTimeout(
66                  function() {
67                    combo_cell_loader({"element": elem, tries: tries + 1});
68                  },
69                  timeout);
70                return;
71              }
72              var page_title = xhr.getResponseHeader('X-Page-Title');
73              if (page_title) {
74                  page_title = JSON.parse(page_title)
75                  if (document.original_title === undefined) {
76                      document.original_title = document.title
77                  }
78                  document.title = page_title + ' | ' + document.original_title;
79              }
80              $elem.addClass('ajax-loaded');
81              $elem.find('> div').nextAll().remove();
82              if (data == '') {
83                  $elem.find('> div').remove();
84              } else {
85                  display_function($elem, data);
86              }
87              if (! $elem.find('> div').children().length) {
88                $elem.addClass('empty-cell');
89              } else {
90                $elem.removeClass('empty-cell')
91              }
92              // Scroll to currently selected anchor
93              if (window.location.hash) {
94                var $anchor_element = $(window.location.hash);
95                if ($anchor_element.length) {
96                  $('html').scrollTop($anchor_element.offset().top);
97                }
98              }
99              $(document).trigger('combo:cell-loaded', $elem);
100          },
101          error: function(error) {
102              var msg = $elem.data('ajax-cell-error-message');
103              if (!msg) msg = 'Unknown error (code: ' + error.status + ')';
104              $elem
105                  .removeClass('empty-cell')
106                  .find('.loading')
107                  .addClass('error-loading')
108                  .html('<span class="loading-message">' + msg + '</span>');
109              window.console && console.log(':(', error);
110          }
111  });
112}
113
114function combo_modify_query_string(name, value) {
115    var search = window.location.search.substring(1);
116    var parts = search.split('&');
117    var newparts = [];
118    var modified = 0;
119    value = encodeURIComponent(value);
120    for (var i = 0; i < parts.length; i++) {
121        if (! parts[i]) {
122            continue;
123        }
124        var pair = parts[i].split('=');
125        if (pair[0] == name) {
126            if (value) {
127                newparts.push(name + '=' + value);
128            }
129            modified = 1;
130        } else {
131            newparts.push(parts[i]);
132        }
133    }
134    if (! modified && value) {
135        newparts.push(name + '=' + value);
136    }
137    if (newparts) {
138        search = '?' + newparts.join('&');
139    } else {
140        search = '';
141    }
142    return search;
143}
144
145/*  Launch callbacks when scroll move above and below a limit
146 *     new ComboScrollY({
147 *       limit: int,
148 *       below: callback function,
149 *       above: callback function
150 *     })
151 */
152function ComboScrollY(options) {
153  this.defaults = {
154    limit: 0
155  };
156  this.options = options;
157  this.init();
158};
159ComboScrollY.prototype.init = function(){
160  this.options = $.extend({}, this.defaults, this.options);
161  this.callbacks();
162  window.addEventListener('scroll', this.callbacks.bind(this));
163};
164ComboScrollY.prototype.position = function(){
165    return (window.pageYOffset <= this.options.limit) ? "above" : "below";
166};
167ComboScrollY.prototype.update = function(){
168    return (this.position() === this.last_callback_position) ? false : true;
169};
170ComboScrollY.prototype.callbacks = function() {
171  if (this.update()) {
172    if (this.position() === "below") {
173      this.options.below();
174      this.last_callback_position = "below";
175    } else {
176      this.options.above();
177      this.last_callback_position = "above";
178    }
179  }
180};
181
182/* Make table widget responsive
183 *    new Combo_Responsive_table_widget(table)
184 */
185const Combo_Responsive_table_widget = function (table) {
186    'use strict';
187    this.table = table;
188    this.col_headers = table.querySelectorAll('thead th, tr th');
189    this.col_headers_text = [];
190    this.body_rows = table.querySelectorAll('tbody tr');
191    this.init();
192};
193Combo_Responsive_table_widget.prototype.storeHeaders = function () {
194    'use strict';
195    let _self = this;
196    $(this.col_headers).each(function (i, header) {
197        _self.col_headers_text.push(header.innerText);
198    });
199    $(this.body_rows).each(function (i, tr) {
200        $(tr.querySelectorAll('td')).each(function (i, td) {
201            td.dataset.colHeader = _self.col_headers_text[i];
202        });
203    });
204};
205Combo_Responsive_table_widget.prototype.init = function () {
206    'use strict';
207    this.table.classList.add('responsive-tableWidget');
208    this.storeHeaders();
209};
210
211$(function() {
212
213  function pagination_update_page(items, max_page_index, step, paginate_by, $pagination, focus_first_item) {
214    var page_index = $pagination.data('page_index') + step;
215    if (page_index == 0) {
216      $pagination.find('.cell-items-pagination-prev').prop('disabled', true);
217    } else {
218      $pagination.find('.cell-items-pagination-prev').prop('disabled', null);
219    }
220    if (page_index == max_page_index - 1) {
221      $pagination.find('.cell-items-pagination-next').prop('disabled', true);
222    } else {
223      $pagination.find('.cell-items-pagination-next').prop('disabled', null);
224    }
225    start_item = paginate_by * page_index;
226    items.hide();
227    items.slice(start_item, start_item + paginate_by).show();
228    $pagination.data('page_index', page_index);
229    if (focus_first_item) {
230      $pagination.parent().focus();
231    }
232  };
233
234  function paginate($pagination, items, must_focus) {
235    $pagination.data('page_index', 0);
236    var paginate_by = parseInt($pagination.data('paginate-by'));
237    var max_page_index = Math.ceil(items.length / paginate_by);
238    if (items.length <= paginate_by) {
239      // hide pagination
240      $pagination.prop('hidden', true);
241      return;
242    }
243
244    function update_page(step, focus_first_item) {
245      if (! must_focus) {
246        focus_first_item = false;
247      }
248      pagination_update_page(items, max_page_index, step, paginate_by, $pagination, focus_first_item);
249    }
250
251    $pagination.find('.cell-items-pagination-prev').off("click");
252    $pagination.find('.cell-items-pagination-next').off("click");
253    $pagination.find('.cell-items-pagination-prev').click(function() { update_page(-1, true); });
254    $pagination.find('.cell-items-pagination-next').click(function() { update_page(1, true); });
255    update_page(0, false);
256    $pagination.prop('hidden', null);
257  };
258
259  $('.cell-items-pagination').each(function(idx, elem) {
260    var $cells = $(elem).parents('.combo-cell');
261    if ($cells.length) {
262      $(document).trigger('combo:cell-loaded', $cells.first());
263    }
264  });
265
266  $('[data-ajax-cell-refresh]').each(function(idx, elem) {
267    var $elem = $(elem);
268    function refresh() {
269      combo_cell_loader({"element": $elem});
270    }
271    $elem.timeout_id = setInterval(refresh, $elem.data('ajax-cell-refresh')*1000);
272  });
273  $(".combo-cell:not('.card') [data-ajax-cell-must-load]").each(function(idx, elem) {
274    combo_cell_loader({"element": $(elem).parents('.combo-cell')[0]});
275  });
276  $('div.combo-cell > div').each(function(idx, elem) {
277    if (! $('[data-ajax-cell-must-load]', $(elem)).length && ! $(elem).children().length) {
278      $(elem).parent().addClass('empty-cell');
279    }
280  });
281
282  /* utility functions and events, for themes */
283  $('.togglable').on('click', function() {
284    $(this).toggleClass('toggled');
285  });
286
287  $(document).on('click keypress', '.add-more-items--button', function(e) {
288    if (e.type === 'keypress' && !(e.key === ' ' || e.key === 'Enter'))
289      return;
290
291    e.preventDefault();
292    $(this).attr('aria-expanded', 'true');
293    $(this).parent('.add-more-items').hide();
294    $('#' + $(this).attr('aria-controls')).show().focus();
295  });
296
297
298  var menu_page_ids = $.makeArray($('[data-menu-page-id]').map(function() { return $(this).data('menu-page-id'); }));
299  if (menu_page_ids.length && $('body').data('check-badges') === true) {
300    $.ajax({url: $('body').data('api-root') + 'menu-badges/',
301            xhrFields: { withCredentials: true },
302            async: true,
303            dataType: 'json',
304            data: {'page': menu_page_ids},
305            crossDomain: true,
306            success: function(data) {
307              $(document).trigger('combo:menu-badges-loaded', data);
308    }});
309  }
310  $(document).on('combo:menu-badges-loaded', function(ctx, data) {
311    for (var page_id in data) {
312      var badge = data[page_id];
313      if (badge.badge) {
314        $('[data-menu-page-id=' + page_id + '] > a > span').append(' <span class="badge">' + badge.badge + '</span>');
315      }
316      if (badge.klass) {
317        $('[data-menu-page-id=' + page_id + ']').addClass(badge.klass);
318      }
319    }
320  });
321  $('.selfdeclaredinvoicepayment form').on('combo:load-invoice', window.displayPopup);
322  $('.selfdeclaredinvoicepayment form').on('submit', function(event) {
323    var url = $(this).attr('action');
324    var data = $(this).serialize();
325    var $form = $(this);
326    $.ajax({url: url + '?ajax=on',
327          xhrFields: { withCredentials: true },
328          data: data,
329          async: true,
330          dataType: 'json',
331          success: function(data) {
332              if (data.msg) {
333                  var err_msg = $('<p></p>');
334                  err_msg.text(data.msg);
335                  $(err_msg).dialog({
336                     dialogClass: 'alert',
337                     modal: true,
338                     width: 'auto',
339                     buttons: {
340                       '×': function() {
341                          $(this).dialog('destroy');
342                       }
343                     },
344                  });
345                  return;
346              }
347              $form.data('url', data.url);
348              $form.trigger('combo:load-invoice', event);
349          },
350          error: function(error) {
351              window.console && console.log(':(', error);
352          }
353    });
354    return false;
355  });
356
357  function set_booking_calendar_sensitivity(table) {
358    if ($(table).find('input:checked').length == 0) {
359      /* no checked box, enable them all */
360      $(table).find('input').prop('disabled', false);
361      return;
362    }
363
364    /* disable every thing */
365    var column_index = $(table).find('input:checked').parents('td').index();
366    $(table).find('td').removeClass('active-column').removeClass('clickable');
367    $(table).find('input').prop('disabled', true);
368
369    /* enable checkboxes from the active column */
370    $(table).find('td:nth-child(' + (column_index+1) + ') input').prop('disabled', false);
371    /* mark active column */
372    $(table).find('td:nth-child(' + (column_index+1) + ')').addClass('active-column');
373
374    /* enable contiguous checkboxes; this is done by adding a clickable class
375     * to the approriate checkboxes, as all of them need to be kept as enabled
376     * HTML-wise to be transmitted on POST.
377     */
378    var checkboxes = $(table).find('td:nth-child(' + (column_index+1) + ') input:checked');
379
380    /* enable the preceding one */
381    $(checkboxes[0]).parents('tr').prev().find('td:nth-child(' + (column_index+1) + ')').addClass('clickable');
382    /* enable first one, so it can be unchecked */
383    $(checkboxes[0]).parents('td').addClass('clickable');
384    /* enable last one, so it can be unchecked */
385    $(checkboxes[checkboxes.length-1]).parents('td').addClass('clickable');
386    /* enable the following one */
387    $(checkboxes[checkboxes.length-1]).parents('tr').next().find('td:nth-child(' + (column_index+1) + ')').addClass('clickable');
388  }
389
390  const foldableOptions = {
391    titleClass: 'cell--title',
392    pictureClass: 'cell--picture',
393  }
394  if ("gadjo_js" in window) {
395    gadjo_js.Foldable.initFoldableQuery('.cell.foldable', foldableOptions)
396    $(document).on('combo:cell-loaded', (_, cell) => {
397      if ('gadjoFoldable' in cell) {
398        cell.gadjoFoldable.prepareFoldable(cell)
399      } else if (cell.classList.contains('foldable')) {
400        gadjo_js.Foldable.initFoldableQuery(cell, foldableOptions)
401      }
402    })
403  }
404
405  /* add a scrolled class to body once the user scrolled the page a bit */
406  var body_is_scrolled = new ComboScrollY({
407    above: function(){
408      $('body').removeClass('scrolled');
409    },
410    below: function(){
411      $('body').addClass('scrolled');
412    }
413  });
414
415  $('body').on('click', 'a.calchunk', function(event){
416      event.preventDefault();
417      var $elem = $(this);
418      var url = $elem.data('content-url');
419      $.ajax({
420          url: url,
421          async: true,
422          dataType: 'html',
423          crossDomain: true,
424          success: function(data){
425              $elem.closest('div.calcontent').html(data);
426          },
427          error: function(error){
428              console.log(':(', error);
429          }
430      });
431  });
432
433  $('.bookingcalendar table').each(function(idx, elem) { set_booking_calendar_sensitivity(elem); });
434
435  $('body').on('change', '.bookingcalendar input', function() {
436     set_booking_calendar_sensitivity($(this).parents('table'));
437  });
438
439  $(document).on('focusin', 'input:not([type=checkbox]):not([type=radio]), textarea', function() {
440    $('body').addClass('focus-in');
441  })
442  $(document).on('focusout', 'input:not([type=checkbox]):not([type=radio]), textarea', function() {
443    $('body').removeClass('focus-in');
444  })
445
446  /* notifications */
447  $('div.cell').delegate('li.combo-notification', 'click', function() {
448    var target_url = $(this).find('a').attr('href');
449    if ($(this).hasClass('combo-notification-acked')) {
450      if (target_url != '#') {
451        window.location.href = target_url;
452      }
453    } else {
454      var ack_url = $('body').data('api-root') + 'notification/ack/' + $(this).data('combo-notification-id');
455      $.ajax({
456         url: ack_url,
457         success: function(html) {
458           $(this).addClass('combo-notification-acked');
459           if (target_url != '#') {
460             window.location.href = target_url;
461           }
462         }
463      });
464    }
465    return false;
466  });
467  $(document).on('combo:cell-loaded', function(ev, cell) {
468    var $pagination = $(cell).find('.cell-items-pagination');
469    if ($pagination.length == 0) return;
470    // Get all <li> inside the same div as us, ignoring whether they are part of
471    // different <ul>
472    var wrapper = $pagination.parent();
473    wrapper.attr('tabindex', -1)
474    var items = $pagination.parent().find('.links-list li, tbody tr');
475    paginate($pagination, items, true);
476  });
477  // accessibililty enhancements:
478  // support for hiding some elements (such as submenus)
479  // when the escape key is pressed
480  $(document).keydown(function(e) {
481    if (e.key === "Escape") {
482      var $elt = $(document.activeElement);
483      if($elt.parent('.hide-on-escape')) {
484        $elt.trigger('blur');
485      }
486    }
487  });
488  // and ensure the elements can be viewed again
489  // on subsequent hover/focus
490  function reset_hidden_elements(event) {
491    var root_element = $(event.target).closest('.contains-hidden-elements')
492    $(root_element).find('.hide-on-escape').css({display: ''})
493  }
494  $('.contains-hidden-elements').mouseenter(reset_hidden_elements, null)
495  $('.contains-hidden-elements').focusin(reset_hidden_elements)
496
497  $(document).on('combo:cell-loaded', function(e, cell) {
498    if (typeof multiSelectWidget !== 'undefined') {
499      multiSelectWidget.init(cell);
500    }
501  });
502
503  function init_select2() {
504    $('select[data-combo-autocomplete]').each(function(idx, elem) {
505      $(elem).select2()
506    });
507  }
508  init_select2();
509  $(document).on('combo:cell-loaded', init_select2);
510
511  $(document).on('combo:cell-loaded', function(ev, cell) {
512    $(cell).find('.combo-cell:not(".pk-table-no-responsive") table:not(".pk-table-no-responsive")').has('th').each(function(i, table) {
513      new Combo_Responsive_table_widget(table);
514    });
515  });
516});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.