PluginProbe
Booking Calendar / 11.9
Booking Calendar v11.9
11.9 11.8.4 11.8.3 11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 All 205 releases
booking / includes / page-availability-general / _src / availability_general_page.js

availability_general_page.js in Booking Calendar 11.9, at includes/page-availability-general/_src/availability_general_page.js

1,330 lines 48.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * General Availability UI.
3 */
4 ( function ( $, w ) {
5 'use strict';
6
7 var cfg = w.wpbc_availability_general_page || {};
8 var preview_timer = 0;
9 var preview_frame = 0;
10 var preview_ajax = null;
11 var observer = null;
12 var preview_unavailable_classes = 'wpbc_ag_preview_unavailable wpbc_ag_preview_weekday_unavailable wpbc_ag_preview_from_today_unavailable wpbc_ag_preview_limit_available_from_today wpbc_ag_preview_buffer_unavailable weekday_unavailable from_today_unavailable limit_available_from_today buffer_unavailable';
13
14 function trim_text( value ) {
15 return String( value || '' ).trim();
16 }
17
18 function switch_panel( $tab ) {
19 var panel_id = $tab.attr( 'aria-controls' );
20 var $tabs = $tab.closest( '.wpbc_ag_rightbar_tabs' ).find( '[role="tab"]' );
21 var $panels = $( '.wpbc_ag_rightbar_panels [role="tabpanel"]' );
22
23 $tabs.attr( 'aria-selected', 'false' );
24 $tab.attr( 'aria-selected', 'true' );
25
26 $panels.attr( 'hidden', 'hidden' ).attr( 'aria-hidden', 'true' );
27 $( '#' + panel_id ).removeAttr( 'hidden' ).attr( 'aria-hidden', 'false' );
28 }
29
30 function toggle_group( $button ) {
31 var $group = $button.closest( '.wpbc_ui__collapsible_group' );
32 var $fields = $group.find( '> .group__fields' );
33 var is_open = $group.hasClass( 'is-open' );
34
35 $group.toggleClass( 'is-open', ! is_open );
36 $button.attr( 'aria-expanded', is_open ? 'false' : 'true' );
37 $fields.prop( 'hidden', is_open ).attr( 'aria-hidden', is_open ? 'true' : 'false' );
38 }
39
40 function close_group( $group ) {
41 var $button = $group.find( '> .group__header' );
42 var $fields = $group.find( '> .group__fields' );
43
44 $group.removeClass( 'is-open' );
45 $button.attr( 'aria-expanded', 'false' );
46 $fields.prop( 'hidden', true ).attr( 'aria-hidden', 'true' );
47 }
48
49 function open_group( group_name, is_exclusive ) {
50 var $group = $( '.wpbc_ui__collapsible_group[data-group="' + group_name + '"]' );
51 var $button = $group.find( '> .group__header' );
52 var $fields = $group.find( '> .group__fields' );
53
54 if ( ! $group.length ) {
55 return;
56 }
57
58 if ( is_exclusive ) {
59 $group.siblings( '.wpbc_ui__collapsible_group' ).each( function () {
60 close_group( $( this ) );
61 } );
62 }
63
64 $group.addClass( 'is-open' );
65 $button.attr( 'aria-expanded', 'true' );
66 $fields.prop( 'hidden', false ).attr( 'aria-hidden', 'false' );
67 }
68
69 function scroll_to_group( group_name ) {
70 var $group = $( '.wpbc_ui__collapsible_group[data-group="' + group_name + '"]' );
71 var $scroll_parent = $();
72 var scroll_parent_el;
73 var group_el;
74 var scroll_top;
75 var parent_rect;
76 var group_rect;
77
78 if ( ! $group.length ) {
79 return;
80 }
81
82 $group.parents().each( function () {
83 var $candidate = $( this );
84 var overflow_y = $candidate.css( 'overflow-y' );
85
86 if (
87 $candidate.hasClass( 'simplebar-content-wrapper' )
88 || (
89 /(auto|scroll)/.test( overflow_y )
90 && this.scrollHeight > this.clientHeight
91 )
92 ) {
93 $scroll_parent = $candidate;
94 return false;
95 }
96 } );
97
98 if ( ! $scroll_parent.length ) {
99 $scroll_parent = $group.closest( '.wpbc_ui_el__vert_right_bar__content' ).find( '.simplebar-content-wrapper' ).first();
100 }
101
102 if ( ! $scroll_parent.length ) {
103 $group.get( 0 ).scrollIntoView( { block: 'nearest' } );
104 return;
105 }
106
107 scroll_parent_el = $scroll_parent.get( 0 );
108 group_el = $group.get( 0 );
109 parent_rect = scroll_parent_el.getBoundingClientRect();
110 group_rect = group_el.getBoundingClientRect();
111 scroll_top = $scroll_parent.scrollTop() + group_rect.top - parent_rect.top - 10;
112
113 $scroll_parent.stop().animate( { scrollTop: Math.max( 0, scroll_top ) }, 180 );
114 }
115
116 function apply_open_section_from_url() {
117 var section_groups = {
118 weekdays: 'general-availability-weekdays',
119 from_today: 'general-availability-from-today',
120 buffer: 'general-availability-buffer',
121 working_time: 'general-availability-working-time'
122 };
123 var group_name = section_groups[ cfg.open_section ] || '';
124
125 if ( '' === group_name ) {
126 return;
127 }
128
129 open_group( group_name, true );
130 $.each( [ 120, 650 ], function ( index, delay ) {
131 setTimeout( function () {
132 scroll_to_group( group_name );
133 }, delay );
134 } );
135 }
136
137 function refresh_buffer_fields() {
138 var value = $( 'input[name="booking_unavailable_extra_in_out"]:checked' ).val() || '';
139
140 $( '.wpbc_ag_buffer_fields' ).each( function () {
141 var $panel = $( this );
142 $panel.toggleClass( 'is-visible', $panel.data( 'buffer-panel' ) === value );
143 } );
144 }
145
146 function is_buffer_available() {
147 return ! ( cfg.is_buffer_available === false || cfg.is_buffer_available === 'false' || cfg.is_buffer_available === 0 || cfg.is_buffer_available === '0' );
148 }
149
150 function is_available_limit_available() {
151 return ! ( cfg.is_available_limit_available === false || cfg.is_available_limit_available === 'false' || cfg.is_available_limit_available === 0 || cfg.is_available_limit_available === '0' );
152 }
153
154 function sync_range_from_select( select ) {
155 var $select = $( select );
156 var name = $select.attr( 'name' );
157 var selected_index = $select.prop( 'selectedIndex' );
158 var selected_text = trim_text( $select.find( 'option:selected' ).text() );
159 var $range = $( '[data-wpbc-ag-range-for="' + name + '"]' );
160 var $value = $( '[data-wpbc-ag-range-value-for="' + name + '"]' );
161
162 if ( ! $range.length ) {
163 return;
164 }
165
166 $range.val( selected_index < 0 ? 0 : selected_index );
167 $value.text( selected_text );
168 }
169
170 function sync_select_from_range( range ) {
171 var $range = $( range );
172 var name = $range.attr( 'data-wpbc-ag-range-for' );
173 var $select = $( '[name="' + name + '"]' );
174 var index = parseInt( $range.val(), 10 ) || 0;
175
176 if ( ! $select.length ) {
177 return;
178 }
179
180 $select.prop( 'selectedIndex', index );
181 sync_range_from_select( $select );
182 }
183
184 function sync_all_ranges() {
185 $( '[data-wpbc-ag-range-for]' ).each( function () {
186 var name = $( this ).attr( 'data-wpbc-ag-range-for' );
187 sync_range_from_select( $( '[name="' + name + '"]' ).first() );
188 } );
189 }
190
191 function step_select_value( button ) {
192 var $button = $( button );
193 var name = $button.attr( 'data-wpbc-ag-stepper' );
194 var step = parseInt( $button.attr( 'data-step' ), 10 ) || 0;
195 var $select = $( '[name="' + name + '"]' ).first();
196 var current_index;
197 var next_index;
198
199 if ( ! $select.length || $select.prop( 'disabled' ) ) {
200 return;
201 }
202
203 current_index = $select.prop( 'selectedIndex' );
204 next_index = Math.max( 0, Math.min( $select.find( 'option' ).length - 1, current_index + step ) );
205
206 if ( next_index === current_index ) {
207 return;
208 }
209
210 $select.prop( 'selectedIndex', next_index ).trigger( 'change' );
211 }
212
213 function get_form() {
214 return $( '[data-wpbc-ag-settings-form="1"]' ).first();
215 }
216
217 function collect_settings() {
218 var $form = get_form();
219 var weekdays = [];
220
221 $form.find( 'input[name="booking_unavailable_days[]"]:checked' ).each( function () {
222 weekdays.push( parseInt( this.value, 10 ) );
223 } );
224
225 return {
226 weekdays: weekdays,
227 booking_unavailable_days_num_from_today: $form.find( '[name="booking_unavailable_days_num_from_today"]' ).val() || '0',
228 booking_available_days_num_from_today: $form.find( '[name="booking_available_days_num_from_today"]' ).val() || '',
229 booking_unavailable_extra_in_out: $form.find( '[name="booking_unavailable_extra_in_out"]:checked' ).val() || '',
230 booking_unavailable_extra_minutes_in: $form.find( '[name="booking_unavailable_extra_minutes_in"]' ).val() || '',
231 booking_unavailable_extra_minutes_out: $form.find( '[name="booking_unavailable_extra_minutes_out"]' ).val() || '',
232 booking_unavailable_extra_days_in: $form.find( '[name="booking_unavailable_extra_days_in"]' ).val() || '',
233 booking_unavailable_extra_days_out: $form.find( '[name="booking_unavailable_extra_days_out"]' ).val() || '',
234 working_time: get_working_time_settings_from_form()
235 };
236 }
237
238 function set_select_value( name, value ) {
239 var $field = get_form().find( '[name="' + name + '"]' ).first();
240
241 if ( ! $field.length ) {
242 return;
243 }
244
245 $field.val( value );
246 if ( String( $field.val() ) !== String( value ) ) {
247 $field.prop( 'selectedIndex', 0 );
248 }
249 }
250
251 function apply_settings_to_form( settings ) {
252 var $form = get_form();
253 var weekdays = settings && settings.weekdays ? settings.weekdays : [];
254
255 if ( ! $form.length || ! settings ) {
256 return;
257 }
258
259 $form.find( 'input[name="booking_unavailable_days[]"]' ).prop( 'checked', false );
260 $.each( weekdays, function ( index, day_num ) {
261 $form.find( 'input[name="booking_unavailable_days[]"][value="' + parseInt( day_num, 10 ) + '"]' ).prop( 'checked', true );
262 } );
263
264 set_select_value( 'booking_unavailable_days_num_from_today', settings.booking_unavailable_days_num_from_today || '0' );
265 set_select_value( 'booking_available_days_num_from_today', settings.booking_available_days_num_from_today || '' );
266 set_select_value( 'booking_unavailable_extra_minutes_in', settings.booking_unavailable_extra_minutes_in || '' );
267 set_select_value( 'booking_unavailable_extra_minutes_out', settings.booking_unavailable_extra_minutes_out || '' );
268 set_select_value( 'booking_unavailable_extra_days_in', settings.booking_unavailable_extra_days_in || '' );
269 set_select_value( 'booking_unavailable_extra_days_out', settings.booking_unavailable_extra_days_out || '' );
270
271 $form.find( 'input[name="booking_unavailable_extra_in_out"]' ).prop( 'checked', false );
272 $form.find( 'input[name="booking_unavailable_extra_in_out"][value="' + ( settings.booking_unavailable_extra_in_out || '' ) + '"]' ).prop( 'checked', true );
273 apply_working_time_settings_to_form( settings.working_time || {}, $( '#wpbc_ag_resource_id' ).val() );
274
275 refresh_buffer_fields();
276 sync_all_ranges();
277 schedule_preview_refresh();
278 }
279
280 function date_from_sql( sql_date ) {
281 var parts = String( sql_date || '' ).split( '-' );
282 if ( parts.length !== 3 ) {
283 return null;
284 }
285 return new Date( parseInt( parts[0], 10 ), parseInt( parts[1], 10 ) - 1, parseInt( parts[2], 10 ), 0, 0, 0 );
286 }
287
288 function get_today_date() {
289 var arr = w._wpbc && typeof w._wpbc.get_other_param === 'function' ? w._wpbc.get_other_param( 'today_arr' ) : null;
290 if ( arr && arr.length >= 3 ) {
291 return new Date( parseInt( arr[0], 10 ), parseInt( arr[1], 10 ) - 1, parseInt( arr[2], 10 ), 0, 0, 0 );
292 }
293 var now = new Date();
294 return new Date( now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0 );
295 }
296
297 function get_real_today_date() {
298 var arr = w._wpbc && typeof w._wpbc.get_other_param === 'function' ? w._wpbc.get_other_param( 'time_local_arr' ) : null;
299 if ( arr && arr.length >= 3 ) {
300 return new Date( parseInt( arr[0], 10 ), parseInt( arr[1], 10 ) - 1, parseInt( arr[2], 10 ), 0, 0, 0 );
301 }
302 return get_today_date();
303 }
304
305 function days_between( date_a, date_b ) {
306 return Math.floor( ( date_a.getTime() - date_b.getTime() ) / 86400000 );
307 }
308
309 function add_days( date, days ) {
310 var shifted_date = new Date( date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0 );
311 shifted_date.setDate( shifted_date.getDate() + days );
312 return shifted_date;
313 }
314
315 function date_to_sql( date ) {
316 var month = String( date.getMonth() + 1 );
317 var day = String( date.getDate() );
318
319 if ( month.length < 2 ) {
320 month = '0' + month;
321 }
322 if ( day.length < 2 ) {
323 day = '0' + day;
324 }
325
326 return date.getFullYear() + '-' + month + '-' + day;
327 }
328
329 function get_sql_date_from_cell( cell ) {
330 var classes = String( cell.className || '' ).split( /\s+/ );
331 var i;
332 for ( i = 0; i < classes.length; i++ ) {
333 if ( classes[i].indexOf( 'sql_date_' ) === 0 ) {
334 return classes[i].replace( 'sql_date_', '' );
335 }
336 }
337 return '';
338 }
339
340 function unavailable_from_today_applies( cell_date, today_date, value ) {
341 var minutes;
342 var now;
343 var unavailable_until;
344
345 if ( ! value || value === '0' ) {
346 return false;
347 }
348
349 if ( /m$/.test( value ) ) {
350 minutes = parseInt( value, 10 );
351 if ( ! minutes ) {
352 return false;
353 }
354 now = new Date();
355 unavailable_until = new Date( now.getTime() + ( ( minutes - 1 ) * 60000 ) );
356 unavailable_until = new Date( unavailable_until.getFullYear(), unavailable_until.getMonth(), unavailable_until.getDate(), 0, 0, 0 );
357 return cell_date.getTime() <= unavailable_until.getTime();
358 }
359
360 return days_between( cell_date, today_date ) < parseInt( value, 10 );
361 }
362
363 function get_option_text( selector ) {
364 var text = $( selector ).find( 'option:selected' ).text();
365 return trim_text( text );
366 }
367
368 function get_days_value( value ) {
369 if ( ! value || ! /d$/.test( value ) ) {
370 return 0;
371 }
372 return parseInt( value, 10 ) || 0;
373 }
374
375 function time_to_seconds( time ) {
376 var parts = String( time || '' ).split( ':' );
377 var hours;
378 var mins;
379
380 if ( parts.length < 2 ) {
381 return 0;
382 }
383
384 hours = Math.max( 0, Math.min( 24, parseInt( parts[0], 10 ) || 0 ) );
385 mins = hours === 24 ? 0 : Math.max( 0, Math.min( 59, parseInt( parts[1], 10 ) || 0 ) );
386
387 return Math.min( 86400, ( hours * 3600 ) + ( mins * 60 ) );
388 }
389
390 function seconds_to_time( seconds ) {
391 var hours;
392 var mins;
393
394 seconds = Math.max( 0, Math.min( 86400, parseInt( seconds, 10 ) || 0 ) );
395 hours = Math.floor( seconds / 3600 );
396 mins = Math.floor( ( seconds % 3600 ) / 60 );
397
398 return ( hours < 10 ? '0' : '' ) + hours + ':' + ( mins < 10 ? '0' : '' ) + mins;
399 }
400
401 /**
402 * Return the bounded interval limit for one Working Time editor.
403 *
404 * @param {jQuery} $weekdays Working Time weekday wrapper.
405 * @return {number} Maximum intervals per weekday.
406 */
407 function get_max_working_time_intervals( $weekdays ) {
408 var configured_limit = parseInt( $weekdays.attr( 'data-max-intervals' ), 10 );
409 var localized_limit = parseInt( cfg.max_working_time_intervals, 10 );
410
411 return Math.max( 1, Math.min( 48, configured_limit || localized_limit || 8 ) );
412 }
413
414 /**
415 * Read all visible interval controls for one weekday.
416 *
417 * @param {jQuery} $day_row Weekday row.
418 * @return {Object[]} Canonical interval records.
419 */
420 function collect_working_time_day_intervals( $day_row ) {
421 var intervals = [];
422
423 $day_row.find( '[data-wpbc-working-time-interval="1"]' ).each( function () {
424 var $interval = $( this );
425
426 intervals.push( {
427 start_second: time_to_seconds( $interval.find( '.wpbc_ag_working_time_start' ).val() ),
428 end_second: time_to_seconds( $interval.find( '.wpbc_ag_working_time_end' ).val() )
429 } );
430 } );
431
432 return intervals;
433 }
434
435 /**
436 * Rebuild dynamic names, IDs, and label associations after a row mutation.
437 *
438 * @param {jQuery} $day_row Weekday row.
439 * @return {void}
440 */
441 function reindex_working_time_day( $day_row ) {
442 var $weekdays = $day_row.closest( '[data-wpbc-working-time-weekdays]' );
443 var prefix = String( $weekdays.attr( 'data-wpbc-working-time-weekdays' ) || '' );
444 var day = parseInt( $day_row.attr( 'data-wpbc-working-time-day' ), 10 );
445
446 $day_row.find( '[data-wpbc-working-time-interval="1"]' ).each( function ( interval_index ) {
447 var $interval = $( this );
448 var start_id = prefix + '_start_' + day + '_' + interval_index;
449 var end_id = prefix + '_end_' + day + '_' + interval_index;
450 var $labels = $interval.find( 'label' );
451
452 $interval.attr( 'data-interval-index', interval_index );
453 $interval.find( '.wpbc_ag_working_time_start' )
454 .attr( 'id', start_id )
455 .attr( 'name', prefix + '_start[' + day + '][]' );
456 $interval.find( '.wpbc_ag_working_time_end' )
457 .attr( 'id', end_id )
458 .attr( 'name', prefix + '_end[' + day + '][]' );
459 $labels.eq( 0 ).attr( 'for', start_id );
460 $labels.eq( 1 ).attr( 'for', end_id );
461 } );
462 }
463
464 /**
465 * Refresh one weekday's enabled controls and interval-limit state.
466 *
467 * @param {jQuery} $day_row Weekday row.
468 * @return {void}
469 */
470 function refresh_working_time_day( $day_row ) {
471 var $weekdays = $day_row.closest( '[data-wpbc-working-time-weekdays]' );
472 var $toggle = $day_row.find( '[data-wpbc-working-time-day-toggle="1"]' ).first();
473 var $resource_custom = $day_row.closest( '[data-wpbc-working-time-resource-custom="1"]' );
474 var is_block_disabled = $day_row.closest( '.wpbc_ag_working_time_block' ).hasClass( 'is-disabled' )
475 || ( $resource_custom.length && ! $resource_custom.hasClass( 'is-visible' ) );
476 var is_day_enabled = ! is_block_disabled && $toggle.prop( 'checked' );
477 var interval_count = $day_row.find( '[data-wpbc-working-time-interval="1"]' ).length;
478 var max_intervals = get_max_working_time_intervals( $weekdays );
479
480 $day_row.toggleClass( 'is-enabled', $toggle.prop( 'checked' ) );
481 $toggle.prop( 'disabled', is_block_disabled );
482 $day_row.find( '.wpbc_ag_working_time_start, .wpbc_ag_working_time_end, [data-wpbc-remove-working-time-interval]' ).prop( 'disabled', ! is_day_enabled );
483 $day_row.find( '[data-wpbc-add-working-time-interval]' ).prop( 'disabled', ! is_day_enabled || interval_count >= max_intervals );
484 }
485
486 /**
487 * Find a non-overlapping one-hour interval proposal.
488 *
489 * @param {Object[]} intervals Current weekday intervals.
490 * @return {Object|null} Proposed interval, or null when the day has no gap.
491 */
492 function find_working_time_interval_proposal( intervals ) {
493 var sorted_intervals = intervals.slice().sort( function ( first_interval, second_interval ) {
494 return first_interval.start_second - second_interval.start_second;
495 } );
496 var preferred_start = sorted_intervals.length ? sorted_intervals[ sorted_intervals.length - 1 ].end_second : 9 * 3600;
497 var candidates = [];
498 var candidate_start;
499
500 for ( candidate_start = 0; candidate_start <= 23 * 3600; candidate_start += 1800 ) {
501 if ( candidate_start >= preferred_start ) {
502 candidates.push( candidate_start );
503 }
504 }
505 for ( candidate_start = 0; candidate_start < preferred_start && candidate_start <= 23 * 3600; candidate_start += 1800 ) {
506 candidates.push( candidate_start );
507 }
508
509 candidate_start = null;
510 candidates.some( function ( proposed_start ) {
511 var proposed_end = proposed_start + 3600;
512 var overlaps = sorted_intervals.some( function ( interval ) {
513 return proposed_start < interval.end_second && proposed_end > interval.start_second;
514 } );
515
516 if ( proposed_end <= 86400 && ! overlaps ) {
517 candidate_start = proposed_start;
518 return true;
519 }
520 return false;
521 } );
522
523 return null === candidate_start ? null : {
524 start_second: candidate_start,
525 end_second: candidate_start + 3600
526 };
527 }
528
529 /**
530 * Append an interval by cloning the server-rendered allow-listed controls.
531 *
532 * @param {jQuery} $day_row Weekday row.
533 * @param {Object} interval Canonical interval record.
534 * @return {jQuery} Appended interval node.
535 */
536 function append_working_time_interval( $day_row, interval ) {
537 var $container = $day_row.find( '[data-wpbc-working-time-intervals="1"]' ).first();
538 var $prototype = $container.find( '[data-wpbc-working-time-interval="1"]' ).first();
539 var $interval = $prototype.clone( false, false );
540
541 $interval.find( '.wpbc_ag_working_time_start' ).val( seconds_to_time( interval.start_second ) );
542 $interval.find( '.wpbc_ag_working_time_end' ).val( seconds_to_time( interval.end_second ) );
543 $container.append( $interval );
544 reindex_working_time_day( $day_row );
545
546 return $interval;
547 }
548
549 /**
550 * Add the next available one-hour interval to a weekday.
551 *
552 * @param {HTMLElement} button Add-interval button.
553 * @return {void}
554 */
555 function add_working_time_interval( button ) {
556 var $day_row = $( button ).closest( '[data-wpbc-working-time-day]' );
557 var $weekdays = $day_row.closest( '[data-wpbc-working-time-weekdays]' );
558 var intervals = collect_working_time_day_intervals( $day_row );
559 var max_intervals = get_max_working_time_intervals( $weekdays );
560 var proposal;
561 var $interval;
562
563 if ( intervals.length >= max_intervals ) {
564 show_message( ( cfg.i18n && cfg.i18n.interval_limit ) || 'No additional one-hour interval is available for this weekday.', 'error', 6000 );
565 return;
566 }
567
568 proposal = find_working_time_interval_proposal( intervals );
569 if ( ! proposal ) {
570 show_message( ( cfg.i18n && cfg.i18n.interval_limit ) || 'No additional one-hour interval is available for this weekday.', 'error', 6000 );
571 return;
572 }
573
574 $interval = append_working_time_interval( $day_row, proposal );
575 $day_row.find( '[data-wpbc-working-time-day-toggle="1"]' ).prop( 'checked', true );
576 refresh_working_time_day( $day_row );
577 $interval.find( '.wpbc_ag_working_time_start' ).trigger( 'focus' );
578 schedule_preview_refresh();
579 }
580
581 /**
582 * Remove one weekday interval, closing the weekday when it is the last one.
583 *
584 * Keeping one disabled interval in the DOM preserves an allow-listed control
585 * prototype for reopening the day or adding a later interval.
586 *
587 * @param {HTMLElement} button Remove-interval button.
588 * @return {void}
589 */
590 function remove_working_time_interval( button ) {
591 var $button = $( button );
592 var $day_row = $button.closest( '[data-wpbc-working-time-day]' );
593 var $intervals = $day_row.find( '[data-wpbc-working-time-interval="1"]' );
594 var $focus_target;
595
596 if ( $intervals.length > 1 ) {
597 $button.closest( '[data-wpbc-working-time-interval="1"]' ).remove();
598 $focus_target = $day_row.find( '[data-wpbc-add-working-time-interval="1"]' ).first();
599 } else {
600 $day_row.find( '[data-wpbc-working-time-day-toggle="1"]' ).prop( 'checked', false );
601 $focus_target = $day_row.find( '[data-wpbc-working-time-day-toggle="1"]' ).first();
602 }
603
604 reindex_working_time_day( $day_row );
605 refresh_working_time_day( $day_row );
606 $focus_target.trigger( 'focus' );
607 schedule_preview_refresh();
608 }
609
610 function collect_working_time_weekdays( prefix ) {
611 var $form = get_form();
612 var weekdays = {};
613
614 $form.find( '[data-wpbc-working-time-weekdays="' + prefix + '"] .wpbc_ag_working_time_row' ).each( function () {
615 var $row = $( this );
616 var day = parseInt( $row.attr( 'data-wpbc-working-time-day' ), 10 );
617
618 weekdays[ day ] = [];
619 if ( ! $row.find( '[data-wpbc-working-time-day-toggle="1"]' ).prop( 'checked' ) ) {
620 return;
621 }
622
623 weekdays[ day ] = collect_working_time_day_intervals( $row );
624 } );
625
626 return weekdays;
627 }
628
629 function set_working_time_weekdays( prefix, weekdays ) {
630 var $wrap = get_form().find( '[data-wpbc-working-time-weekdays="' + prefix + '"]' );
631
632 $wrap.find( '.wpbc_ag_working_time_row' ).each( function () {
633 var $row = $( this );
634 var day = parseInt( $row.attr( 'data-wpbc-working-time-day' ), 10 );
635 var intervals = weekdays && $.isArray( weekdays[ day ] ) ? weekdays[ day ] : [];
636 var display_intervals = intervals.length ? intervals : [ { start_second: 32400, end_second: 64800 } ];
637 var $container = $row.find( '[data-wpbc-working-time-intervals="1"]' ).first();
638 var $prototype = $container.find( '[data-wpbc-working-time-interval="1"]' ).first().clone( false, false );
639
640 $container.empty().append( $prototype );
641 $prototype.find( '.wpbc_ag_working_time_start' ).val( seconds_to_time( display_intervals[0].start_second ) );
642 $prototype.find( '.wpbc_ag_working_time_end' ).val( seconds_to_time( display_intervals[0].end_second ) );
643 display_intervals.slice( 1 ).forEach( function ( interval ) {
644 append_working_time_interval( $row, interval );
645 } );
646 $row.find( '[data-wpbc-working-time-day-toggle="1"]' ).prop( 'checked', intervals.length > 0 );
647 reindex_working_time_day( $row );
648 } );
649 }
650
651 function refresh_working_time_panels() {
652 var is_enabled = get_form().find( 'input[name="booking_working_time_enabled"]' ).prop( 'checked' );
653 var mode = get_form().find( 'input[name="booking_working_time_resource_mode"]:checked' ).val() || 'inherit';
654 var $resource_custom = get_form().find( '[data-wpbc-working-time-resource-custom]' );
655
656 $resource_custom.toggleClass( 'is-visible', mode === 'custom' );
657
658 get_form()
659 .find( '.wpbc_ag_working_time_block' )
660 .toggleClass( 'is-disabled', ! is_enabled );
661
662 get_form().find( '.wpbc_ag_working_time_block' ).each( function () {
663 $( this ).find( 'input, select, button' ).prop( 'disabled', ! is_enabled );
664 } );
665 get_form().find( '[data-wpbc-working-time-day]' ).each( function () {
666 refresh_working_time_day( $( this ) );
667 } );
668 }
669
670 function get_working_time_settings_from_form() {
671 var $form = get_form();
672 var resourceId = parseInt( $form.find( '[data-wpbc-working-time-resource-id]' ).val(), 10 ) || parseInt( $( '#wpbc_ag_resource_id' ).val(), 10 ) || 0;
673 var workingTime = $.extend( true, {}, cfg.settings && cfg.settings.working_time ? cfg.settings.working_time : ( cfg.default_settings && cfg.default_settings.working_time ? cfg.default_settings.working_time : {} ) );
674
675 if ( ! workingTime.default ) {
676 workingTime.default = {};
677 }
678 if ( ! workingTime.resources ) {
679 workingTime.resources = {};
680 }
681
682 workingTime.enabled = $form.find( 'input[name="booking_working_time_enabled"]' ).prop( 'checked' ) ? 'On' : 'Off';
683 workingTime.default.weekdays = collect_working_time_weekdays( 'booking_working_time_default' );
684
685 if ( resourceId > 0 ) {
686 workingTime.resources[ resourceId ] = {
687 mode: $form.find( 'input[name="booking_working_time_resource_mode"]:checked' ).val() || 'inherit',
688 weekdays: collect_working_time_weekdays( 'booking_working_time_resource' )
689 };
690 }
691
692 return workingTime;
693 }
694
695 function apply_working_time_settings_to_form( workingTime, resourceId ) {
696 var resourceSettings;
697
698 workingTime = workingTime || {};
699 resourceId = parseInt( resourceId, 10 ) || parseInt( $( '#wpbc_ag_resource_id' ).val(), 10 ) || 0;
700 resourceSettings = workingTime.resources && workingTime.resources[ resourceId ] ? workingTime.resources[ resourceId ] : {
701 mode: 'inherit',
702 weekdays: workingTime.default && workingTime.default.weekdays ? workingTime.default.weekdays : {}
703 };
704
705 get_form().find( 'input[name="booking_working_time_enabled"]' ).prop( 'checked', workingTime.enabled === 'On' );
706 get_form().find( '[data-wpbc-working-time-resource-id]' ).val( resourceId );
707 set_working_time_weekdays( 'booking_working_time_default', workingTime.default && workingTime.default.weekdays ? workingTime.default.weekdays : {} );
708 get_form().find( 'input[name="booking_working_time_resource_mode"][value="' + ( resourceSettings.mode || 'inherit' ) + '"]' ).prop( 'checked', true );
709 set_working_time_weekdays( 'booking_working_time_resource', resourceSettings.weekdays || ( workingTime.default ? workingTime.default.weekdays : {} ) );
710 refresh_working_time_panels();
711 }
712
713 function append_working_time_payload( payload ) {
714 var $form = get_form();
715 var defaultDays = [];
716 var resourceDays = [];
717 var defaultStart = {};
718 var defaultEnd = {};
719 var resourceStart = {};
720 var resourceEnd = {};
721
722 $form.find( '[data-wpbc-working-time-weekdays="booking_working_time_default"] .wpbc_ag_working_time_row' ).each( function () {
723 var $row = $( this );
724 var day = parseInt( $row.attr( 'data-wpbc-working-time-day' ), 10 );
725
726 defaultStart[ day ] = [];
727 defaultEnd[ day ] = [];
728 $row.find( '[data-wpbc-working-time-interval="1"]' ).each( function () {
729 defaultStart[ day ].push( $( this ).find( '.wpbc_ag_working_time_start' ).val() || '09:00' );
730 defaultEnd[ day ].push( $( this ).find( '.wpbc_ag_working_time_end' ).val() || '18:00' );
731 } );
732 if ( $row.find( '[data-wpbc-working-time-day-toggle="1"]' ).prop( 'checked' ) ) {
733 defaultDays.push( day );
734 }
735 } );
736
737 $form.find( '[data-wpbc-working-time-weekdays="booking_working_time_resource"] .wpbc_ag_working_time_row' ).each( function () {
738 var $row = $( this );
739 var day = parseInt( $row.attr( 'data-wpbc-working-time-day' ), 10 );
740
741 resourceStart[ day ] = [];
742 resourceEnd[ day ] = [];
743 $row.find( '[data-wpbc-working-time-interval="1"]' ).each( function () {
744 resourceStart[ day ].push( $( this ).find( '.wpbc_ag_working_time_start' ).val() || '09:00' );
745 resourceEnd[ day ].push( $( this ).find( '.wpbc_ag_working_time_end' ).val() || '18:00' );
746 } );
747 if ( $row.find( '[data-wpbc-working-time-day-toggle="1"]' ).prop( 'checked' ) ) {
748 resourceDays.push( day );
749 }
750 } );
751
752 payload.booking_working_time_enabled = $form.find( 'input[name="booking_working_time_enabled"]' ).prop( 'checked' ) ? 'On' : '';
753 payload.booking_working_time_resource_id = $form.find( '[data-wpbc-working-time-resource-id]' ).val() || $( '#wpbc_ag_resource_id' ).val() || '';
754 payload.booking_working_time_resource_mode = $form.find( 'input[name="booking_working_time_resource_mode"]:checked' ).val() || 'inherit';
755 payload.booking_working_time_default_days = defaultDays;
756 payload.booking_working_time_default_start = defaultStart;
757 payload.booking_working_time_default_end = defaultEnd;
758 payload.booking_working_time_resource_days = resourceDays;
759 payload.booking_working_time_resource_start = resourceStart;
760 payload.booking_working_time_resource_end = resourceEnd;
761 }
762
763 function update_wpbc_preview_params( settings ) {
764 if ( ! w._wpbc || typeof w._wpbc.set_other_param !== 'function' ) {
765 return;
766 }
767
768 w._wpbc.set_other_param( 'availability__week_days_unavailable', settings.weekdays.concat( [ 999 ] ) );
769 w._wpbc.set_other_param( 'availability__available_from_today', is_available_limit_available() ? ( settings.booking_available_days_num_from_today || '' ) : '' );
770 w._wpbc.set_other_param( 'availability__unavailable_from_today', settings.booking_unavailable_days_num_from_today || '0' );
771 }
772
773 function update_buffer_preview_note( settings ) {
774 var $notes = $( '[data-wpbc-ag-calendar-notes="1"]' ).first();
775 var $calendar = $( '[data-wpbc-ag-calendar-panel="1"]' ).first();
776 var $note = $notes.find( '.wpbc_ag_buffer_preview_note' );
777 var type = settings.booking_unavailable_extra_in_out || '';
778 var before_text = '';
779 var after_text = '';
780 var message = '';
781
782 if ( ! $notes.length || ! $calendar.length ) {
783 return;
784 }
785
786 if ( ! $note.length ) {
787 $note = $( '<div class="wpbc_ag_buffer_preview_note" aria-live="polite"></div>' );
788 $notes.append( $note );
789 }
790
791 if ( ! is_buffer_available() ) {
792 $calendar.removeClass( 'wpbc_ag_preview_buffer_active' );
793 $note.attr( 'hidden', 'hidden' );
794 return;
795 }
796
797 $calendar.toggleClass( 'wpbc_ag_preview_buffer_active', !! type );
798
799 if ( type === 'm' ) {
800 before_text = get_option_text( '[name="booking_unavailable_extra_minutes_in"]' ) || '-';
801 after_text = get_option_text( '[name="booking_unavailable_extra_minutes_out"]' ) || '-';
802 } else if ( type === 'd' ) {
803 before_text = get_option_text( '[name="booking_unavailable_extra_days_in"]' ) || '-';
804 after_text = get_option_text( '[name="booking_unavailable_extra_days_out"]' ) || '-';
805 }
806
807 if ( type ) {
808 message = '<strong>' + ( cfg.i18n && cfg.i18n.buffer_preview ? cfg.i18n.buffer_preview : 'Buffer preview' ) + ':</strong> ' +
809 ( cfg.i18n && cfg.i18n.before_booking ? cfg.i18n.before_booking : 'Before booking' ) + ' ' + before_text +
810 ' / ' + ( cfg.i18n && cfg.i18n.after_booking ? cfg.i18n.after_booking : 'After booking' ) + ' ' + after_text;
811 if ( $note.html() !== message ) {
812 $note.html( message );
813 }
814 if ( $note.attr( 'hidden' ) ) {
815 $note.removeAttr( 'hidden' );
816 }
817 } else {
818 message = cfg.i18n && cfg.i18n.no_buffer ? cfg.i18n.no_buffer : 'No booking buffer is selected.';
819 if ( $note.text() !== message ) {
820 $note.text( message );
821 }
822 if ( ! $note.attr( 'hidden' ) ) {
823 $note.attr( 'hidden', 'hidden' );
824 }
825 }
826 }
827
828 function apply_buffer_days_preview( settings ) {
829 var $calendar = $( '[data-wpbc-ag-calendar-panel="1"]' );
830 var before_days = get_days_value( settings.booking_unavailable_extra_days_in );
831 var after_days = get_days_value( settings.booking_unavailable_extra_days_out );
832 var date_cells = {};
833 var booked_dates = [];
834
835 if ( ! is_buffer_available() ) {
836 return;
837 }
838
839 if ( settings.booking_unavailable_extra_in_out !== 'd' || ( ! before_days && ! after_days ) ) {
840 return;
841 }
842
843 $calendar.find( '.datepick-days-cell' ).each( function () {
844 var $cell = $( this );
845 var sql_date = get_sql_date_from_cell( this );
846 var cell_date;
847
848 if ( ! sql_date ) {
849 return;
850 }
851
852 date_cells[ sql_date ] = $cell;
853
854 if ( $cell.hasClass( 'date_approved' ) || $cell.hasClass( 'date2approve' ) ) {
855 cell_date = date_from_sql( sql_date );
856 if ( cell_date ) {
857 booked_dates.push( cell_date );
858 }
859 }
860 } );
861
862 $.each( booked_dates, function ( index, booked_date ) {
863 var offset;
864 var target_sql_date;
865 var $target_cell;
866
867 for ( offset = before_days * -1; offset < 0; offset++ ) {
868 target_sql_date = date_to_sql( add_days( booked_date, offset ) );
869 $target_cell = date_cells[ target_sql_date ];
870 if ( $target_cell && ! $target_cell.hasClass( 'date_approved' ) && ! $target_cell.hasClass( 'date2approve' ) ) {
871 remember_preview_origin( $target_cell );
872 $target_cell.addClass( 'date_user_unavailable wpbc_ag_preview_unavailable wpbc_ag_preview_buffer_unavailable buffer_unavailable' );
873 $target_cell.attr( 'data-wpbc-ag-preview-reason', 'wpbc_ag_preview_buffer_unavailable' );
874 }
875 }
876
877 for ( offset = 1; offset <= after_days; offset++ ) {
878 target_sql_date = date_to_sql( add_days( booked_date, offset ) );
879 $target_cell = date_cells[ target_sql_date ];
880 if ( $target_cell && ! $target_cell.hasClass( 'date_approved' ) && ! $target_cell.hasClass( 'date2approve' ) ) {
881 remember_preview_origin( $target_cell );
882 $target_cell.addClass( 'date_user_unavailable wpbc_ag_preview_unavailable wpbc_ag_preview_buffer_unavailable buffer_unavailable' );
883 $target_cell.attr( 'data-wpbc-ag-preview-reason', 'wpbc_ag_preview_buffer_unavailable' );
884 }
885 }
886 } );
887 }
888
889 function remember_preview_origin( $cell ) {
890 if ( typeof $cell.attr( 'data-wpbc-ag-original-date-user-unavailable' ) === 'undefined' ) {
891 $cell.attr( 'data-wpbc-ag-original-date-user-unavailable', $cell.hasClass( 'date_user_unavailable' ) ? '1' : '0' );
892 }
893 }
894
895 function clear_preview_cell( $cell ) {
896 var had_date_user_unavailable = $cell.attr( 'data-wpbc-ag-original-date-user-unavailable' ) === '1';
897
898 $cell.removeClass( preview_unavailable_classes );
899 $cell.removeAttr( 'data-wpbc-ag-preview-reason' );
900 $cell.removeAttr( 'data-wpbc-ag-original-date-user-unavailable' );
901
902 if ( ! had_date_user_unavailable ) {
903 $cell.removeClass( 'date_user_unavailable' );
904 }
905 }
906
907 function apply_calendar_preview() {
908 var settings = collect_settings();
909 var today_date = get_real_today_date();
910 var available_limit = is_available_limit_available() ? parseInt( settings.booking_available_days_num_from_today || '0', 10 ) : 0;
911 var $calendar = $( '[data-wpbc-ag-calendar-panel="1"]' );
912
913 update_wpbc_preview_params( settings );
914 update_buffer_preview_note( settings );
915
916 $calendar.find( '.datepick-days-cell' ).each( function () {
917 var cell = this;
918 var $cell = $( cell );
919 var sql_date = get_sql_date_from_cell( cell );
920 var cell_date = date_from_sql( sql_date );
921 var make_unavailable = false;
922 var reason_class = '';
923 var previous_reason = $cell.attr( 'data-wpbc-ag-preview-reason' ) || '';
924 var had_general_preview = !! previous_reason || $cell.hasClass( 'wpbc_ag_preview_unavailable' ) || $cell.hasClass( 'weekday_unavailable' ) || $cell.hasClass( 'from_today_unavailable' ) || $cell.hasClass( 'limit_available_from_today' ) || $cell.hasClass( 'buffer_unavailable' );
925
926 if ( ! cell_date ) {
927 return;
928 }
929
930 if ( settings.weekdays.indexOf( cell_date.getDay() ) > -1 ) {
931 make_unavailable = true;
932 reason_class = 'wpbc_ag_preview_weekday_unavailable';
933 }
934
935 if ( unavailable_from_today_applies( cell_date, today_date, settings.booking_unavailable_days_num_from_today ) ) {
936 make_unavailable = true;
937 reason_class = 'wpbc_ag_preview_from_today_unavailable';
938 }
939
940 if ( available_limit > 0 && days_between( cell_date, today_date ) >= available_limit ) {
941 make_unavailable = true;
942 reason_class = 'wpbc_ag_preview_limit_available_from_today';
943 }
944
945 if ( make_unavailable ) {
946 if ( previous_reason !== reason_class ) {
947 if ( had_general_preview ) {
948 clear_preview_cell( $cell );
949 }
950 remember_preview_origin( $cell );
951 $cell.addClass( 'date_user_unavailable wpbc_ag_preview_unavailable ' + reason_class );
952 $cell.attr( 'data-wpbc-ag-preview-reason', reason_class );
953 }
954 } else if ( had_general_preview ) {
955 clear_preview_cell( $cell );
956 }
957 } );
958
959 apply_buffer_days_preview( settings );
960 }
961
962 function queue_preview_refresh() {
963 if ( preview_frame ) {
964 return;
965 }
966
967 if ( w.requestAnimationFrame ) {
968 preview_frame = w.requestAnimationFrame( function () {
969 preview_frame = 0;
970 apply_calendar_preview();
971 } );
972 } else {
973 preview_frame = w.setTimeout( function () {
974 preview_frame = 0;
975 apply_calendar_preview();
976 }, 0 );
977 }
978 }
979
980 function schedule_preview_refresh( delay ) {
981 clearTimeout( preview_timer );
982 delay = parseInt( delay, 10 ) || 0;
983
984 if ( delay > 0 ) {
985 preview_timer = setTimeout( queue_preview_refresh, delay );
986 return;
987 }
988
989 queue_preview_refresh();
990 }
991
992 function update_hints( hints ) {
993 if ( ! hints ) {
994 return;
995 }
996
997 if ( typeof hints.booking_unavailable_days_num_from_today__hint !== 'undefined' ) {
998 $( '[data-wpbc-ag-hint="booking_unavailable_days_num_from_today"]' ).html(
999 '<span class="wpbc_ag_hint_unavailable">' + $( '[data-wpbc-ag-hint="booking_unavailable_days_num_from_today"] .wpbc_ag_hint_unavailable' ).first().text() + '</span>' +
1000 hints.booking_unavailable_days_num_from_today__hint
1001 );
1002 }
1003
1004 if ( typeof hints.booking_available_days_num_from_today__hint !== 'undefined' ) {
1005 $( '[data-wpbc-ag-hint="booking_available_days_num_from_today"]' ).html(
1006 '<span class="wpbc_ag_hint_available">' + $( '[data-wpbc-ag-hint="booking_available_days_num_from_today"] .wpbc_ag_hint_available' ).first().text() + '</span>' +
1007 hints.booking_available_days_num_from_today__hint
1008 );
1009 }
1010 }
1011
1012 function show_message( message, type, duration ) {
1013 if ( typeof w.wpbc_admin_show_message === 'function' ) {
1014 w.wpbc_admin_show_message( message, type || 'info', duration || 2000, false );
1015 } else {
1016 w.alert( message );
1017 }
1018 }
1019
1020 function set_busy( $button, busy ) {
1021 var busy_text;
1022
1023 if ( ! $button || ! $button.length ) {
1024 return;
1025 }
1026
1027 if ( busy ) {
1028 if ( ! $button.data( 'wpbc-ag-original-html' ) ) {
1029 $button.data( 'wpbc-ag-original-html', $button.html() );
1030 }
1031 busy_text = $button.data( 'wpbc-u-busy-text' ) || ( cfg.i18n && cfg.i18n.saving ) || 'Saving...';
1032 $button.addClass( 'wpbc_ag_is_saving' ).attr( 'aria-busy', 'true' ).html( '<i class="menu_icon icon-1x wpbc_icn_rotate_right wpbc_spin"></i><span class="in-button-text">&nbsp;&nbsp;' + busy_text + '</span>' );
1033 } else {
1034 $button.removeClass( 'wpbc_ag_is_saving' ).removeAttr( 'aria-busy' );
1035 if ( $button.data( 'wpbc-ag-original-html' ) ) {
1036 $button.html( $button.data( 'wpbc-ag-original-html' ) );
1037 }
1038 }
1039 }
1040
1041 function replace_calendar_panel( html ) {
1042 var $holder = $( '<div />' ).append( $.parseHTML( html, document, true ) );
1043 var $new_panel = $holder.find( '[data-wpbc-ag-calendar-panel="1"]' ).first();
1044 var $old_panel = $( '[data-wpbc-ag-calendar-panel="1"]' ).first();
1045 var $scripts;
1046
1047 if ( ! $new_panel.length || ! $old_panel.length ) {
1048 return;
1049 }
1050
1051 $scripts = $new_panel.find( 'script' ).remove();
1052 $old_panel.replaceWith( $new_panel );
1053
1054 $scripts.each( function () {
1055 var code = this.text || this.textContent || this.innerHTML || '';
1056 var src = this.src || '';
1057
1058 if ( src ) {
1059 $.ajax( {
1060 url: src,
1061 dataType: 'script',
1062 cache: true
1063 } );
1064 } else if ( code ) {
1065 $.globalEval( code );
1066 }
1067 } );
1068 }
1069
1070 function set_calendar_loading( is_loading ) {
1071 var $calendar = $( '[data-wpbc-ag-calendar-panel="1"]' ).first();
1072 var loading_text = cfg.i18n && cfg.i18n.loading ? cfg.i18n.loading : 'Loading';
1073 var $loading;
1074
1075 if ( ! $calendar.length ) {
1076 return;
1077 }
1078
1079 if ( is_loading ) {
1080 $loading = $calendar.find( '.wpbc_calendar_loading' ).first();
1081 if ( ! $loading.length ) {
1082 $loading = $(
1083 '<div class="wpbc_calendar_loading wpbc_ag_calendar_loading">' +
1084 '<span class="wpbc_icn_autorenew wpbc_spin"></span>&nbsp;&nbsp;' +
1085 '<span></span>' +
1086 '</div>'
1087 );
1088 $loading.find( 'span' ).last().text( loading_text + '...' );
1089 $calendar.append( $loading );
1090 }
1091 $calendar.addClass( 'is-loading' ).attr( 'aria-busy', 'true' );
1092 } else {
1093 $calendar.removeClass( 'is-loading' ).removeAttr( 'aria-busy' );
1094 $calendar.find( '.wpbc_calendar_loading' ).remove();
1095 }
1096 }
1097
1098 function get_preview_payload() {
1099 return {
1100 action: cfg.preview_action || 'WPBC_AJX_AVAILABILITY_GENERAL_PREVIEW',
1101 nonce: cfg.nonce || '',
1102 resource_id: $( '#wpbc_ag_resource_id' ).val() || '',
1103 months_count: $( '#wpbc_ag_months_count' ).val() || ''
1104 };
1105 }
1106
1107 function load_calendar_preview() {
1108 var $calendar = $( '[data-wpbc-ag-calendar-panel="1"]' ).first();
1109 var current_preview_ajax;
1110
1111 if ( ! cfg.ajax_url ) {
1112 show_message( ( cfg.i18n && cfg.i18n.preview_failed ) || 'Unable to refresh calendar preview.', 'error', 10000 );
1113 return;
1114 }
1115
1116 if ( preview_ajax && preview_ajax.readyState !== 4 ) {
1117 preview_ajax.abort();
1118 }
1119
1120 set_calendar_loading( true );
1121
1122 current_preview_ajax = $.ajax( {
1123 url: cfg.ajax_url,
1124 method: 'POST',
1125 dataType: 'json',
1126 data: get_preview_payload()
1127 } );
1128 preview_ajax = current_preview_ajax;
1129
1130 current_preview_ajax.done( function ( response ) {
1131 if ( ! response || ! response.success || ! response.data || ! response.data.html ) {
1132 show_message( ( response && response.data && response.data.message ) || ( cfg.i18n && cfg.i18n.preview_failed ) || 'Unable to refresh calendar preview.', 'error', 10000 );
1133 return;
1134 }
1135
1136 replace_calendar_panel( response.data.html );
1137 $( '[data-wpbc-ag-page="1"]' ).attr( 'data-wpbc-ag-resource-id', response.data.resource_id || '' );
1138 observe_calendar_changes();
1139 schedule_preview_refresh();
1140 setTimeout( schedule_preview_refresh, 600 );
1141 } ).fail( function ( jq_xhr, text_status ) {
1142 if ( text_status !== 'abort' ) {
1143 show_message( ( cfg.i18n && cfg.i18n.preview_failed ) || 'Unable to refresh calendar preview.', 'error', 10000 );
1144 }
1145 } ).always( function () {
1146 if ( preview_ajax === current_preview_ajax ) {
1147 set_calendar_loading( false );
1148 }
1149 } );
1150 }
1151
1152 function save_settings( button ) {
1153 var $button = $( button );
1154 var settings = collect_settings();
1155 var payload = $.extend( {}, settings, {
1156 action: cfg.action || 'WPBC_AJX_AVAILABILITY_GENERAL_SAVE',
1157 nonce: cfg.nonce || '',
1158 booking_unavailable_days: settings.weekdays
1159 } );
1160
1161 append_working_time_payload( payload );
1162
1163 if ( ! cfg.ajax_url ) {
1164 show_message( ( cfg.i18n && cfg.i18n.save_failed ) || 'Unable to save general availability settings.', 'error', 10000 );
1165 return;
1166 }
1167
1168 set_busy( $button, true );
1169
1170 $.ajax( {
1171 url: cfg.ajax_url,
1172 method: 'POST',
1173 dataType: 'json',
1174 data: payload
1175 } ).done( function ( response ) {
1176 if ( ! response || ! response.success ) {
1177 show_message( ( response && response.data && response.data.message ) || ( cfg.i18n && cfg.i18n.save_failed ) || 'Unable to save general availability settings.', 'error', 10000 );
1178 return;
1179 }
1180
1181 if ( response.data && response.data.settings && response.data.settings.hints ) {
1182 update_hints( response.data.settings.hints );
1183 }
1184 if ( response.data && response.data.settings ) {
1185 cfg.settings = response.data.settings;
1186 if ( response.data.settings.working_time ) {
1187 apply_working_time_settings_to_form( response.data.settings.working_time, $( '#wpbc_ag_resource_id' ).val() );
1188 }
1189 }
1190
1191 load_calendar_preview();
1192 document.dispatchEvent( new CustomEvent( 'wpbc:availability-general:settings-saved', {
1193 detail: {
1194 settings: response.data && response.data.settings ? response.data.settings : {}
1195 }
1196 } ) );
1197 show_message( ( response.data && response.data.message ) || ( cfg.i18n && cfg.i18n.saved ) || 'General availability settings updated.', 'success', 2000 );
1198 } ).fail( function ( jq_xhr ) {
1199 var response_message = jq_xhr && jq_xhr.responseJSON && jq_xhr.responseJSON.data && jq_xhr.responseJSON.data.message
1200 ? jq_xhr.responseJSON.data.message
1201 : '';
1202
1203 show_message( response_message || ( cfg.i18n && cfg.i18n.save_failed ) || 'Unable to save general availability settings.', 'error', 10000 );
1204 } ).always( function () {
1205 set_busy( $button, false );
1206 } );
1207 }
1208
1209 function reset_settings( button ) {
1210 var confirm_message = ( cfg.i18n && cfg.i18n.reset_confirm ) || 'Reset general availability settings to default values?';
1211 var default_settings = cfg.default_settings || {
1212 weekdays: [],
1213 booking_unavailable_days_num_from_today: '0',
1214 booking_available_days_num_from_today: '',
1215 booking_unavailable_extra_in_out: '',
1216 booking_unavailable_extra_minutes_in: '',
1217 booking_unavailable_extra_minutes_out: '',
1218 booking_unavailable_extra_days_in: '',
1219 booking_unavailable_extra_days_out: '',
1220 working_time: {}
1221 };
1222
1223 if ( ! w.confirm( confirm_message ) ) {
1224 return;
1225 }
1226
1227 apply_settings_to_form( default_settings );
1228 load_calendar_preview();
1229 show_message( ( cfg.i18n && cfg.i18n.reset_applied ) || 'Default availability settings are ready for preview. Click Save Changes to apply them.', 'success', 4000 );
1230 }
1231
1232 function observe_calendar_changes() {
1233 var target = document.querySelector( '[data-wpbc-ag-calendar-panel="1"]' );
1234
1235 if ( ! target || ! w.MutationObserver ) {
1236 return;
1237 }
1238
1239 if ( observer ) {
1240 observer.disconnect();
1241 }
1242
1243 observer = new MutationObserver( schedule_preview_refresh );
1244 observer.observe( target, {
1245 childList: true,
1246 subtree: true
1247 } );
1248 }
1249
1250 $( document ).on( 'click', '.wpbc_ag_rightbar_tabs [role="tab"]', function () {
1251 switch_panel( $( this ) );
1252 } );
1253
1254 $( document ).on( 'click', '.wpbc_ag_rightbar_panels .group__header', function () {
1255 toggle_group( $( this ) );
1256 } );
1257
1258 $( document ).on( 'submit', '[data-wpbc-ag-preview-toolbar="1"]', function ( event ) {
1259 event.preventDefault();
1260 load_calendar_preview();
1261 } );
1262
1263 $( document ).on( 'change', '#wpbc_ag_resource_id, #wpbc_ag_months_count', load_calendar_preview );
1264
1265 $( document ).on( 'change', '#wpbc_ag_resource_id', function () {
1266 cfg.settings = cfg.settings || {};
1267 cfg.settings.working_time = get_working_time_settings_from_form();
1268 apply_working_time_settings_to_form( cfg.settings.working_time, $( this ).val() );
1269 } );
1270
1271 $( document ).on( 'input change', '[data-wpbc-ag-range-for]', function () {
1272 sync_select_from_range( this );
1273 schedule_preview_refresh();
1274 } );
1275
1276 $( document ).on( 'change', '[data-wpbc-ag-settings-form="1"] select', function () {
1277 sync_range_from_select( this );
1278 } );
1279
1280 $( document ).on( 'click', '[data-wpbc-ag-stepper]', function () {
1281 step_select_value( this );
1282 } );
1283
1284 $( document ).on( 'change', 'input[name="booking_unavailable_extra_in_out"]', function () {
1285 refresh_buffer_fields();
1286 schedule_preview_refresh();
1287 } );
1288
1289 $( document ).on( 'change', 'input[name="booking_working_time_enabled"]', refresh_working_time_panels );
1290
1291 $( document ).on( 'change', 'input[name="booking_working_time_resource_mode"]', refresh_working_time_panels );
1292
1293 $( document ).on( 'change', '[data-wpbc-working-time-day-toggle="1"]', function () {
1294 refresh_working_time_day( $( this ).closest( '[data-wpbc-working-time-day]' ) );
1295 } );
1296
1297 $( document ).on( 'click', '[data-wpbc-add-working-time-interval="1"]', function () {
1298 add_working_time_interval( this );
1299 } );
1300
1301 $( document ).on( 'click', '[data-wpbc-remove-working-time-interval="1"]', function () {
1302 remove_working_time_interval( this );
1303 } );
1304
1305 $( document ).on( 'change', '[data-wpbc-ag-settings-form="1"] input, [data-wpbc-ag-settings-form="1"] select', schedule_preview_refresh );
1306
1307 $( document ).on( 'submit', '[data-wpbc-ag-settings-form="1"]', function ( event ) {
1308 event.preventDefault();
1309 save_settings( $( '[data-wpbc-ag-save="1"]' ).first() );
1310 } );
1311
1312 $( document ).on( 'click', '[data-wpbc-ag-save="1"]', function () {
1313 save_settings( this );
1314 } );
1315
1316 $( document ).on( 'click', '[data-wpbc-ag-reset="1"]', function () {
1317 reset_settings( this );
1318 } );
1319
1320 $( document ).ready( function () {
1321 apply_working_time_settings_to_form( ( cfg.settings && cfg.settings.working_time ) || ( cfg.default_settings && cfg.default_settings.working_time ) || {}, $( '#wpbc_ag_resource_id' ).val() );
1322 apply_open_section_from_url();
1323 refresh_buffer_fields();
1324 sync_all_ranges();
1325 observe_calendar_changes();
1326 schedule_preview_refresh();
1327 setTimeout( schedule_preview_refresh, 600 );
1328 } );
1329 }( jQuery, window ) );
1330