PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / 1.2.21
VikAppointments Services Booking Calendar v1.2.21
1.2.21 1.2.20 trunk 1.2.17 1.2.18 1.2.19
vikappointments / admin / assets / js / vikappointments.js
vikappointments / admin / assets / js Last commit date
adapter 2 days ago index.html 2 days ago inspector.js 2 days ago mediamanager.js 2 days ago vikappointments.js 2 days ago wizard.js 2 days ago
vikappointments.js
615 lines
1 (function($) {
2 'use strict';
3
4 /**
5 * VikAppointments Card
6 */
7
8 $.fn.vapcard = function(key, value) {
9 if (!key) {
10 throw 'Missing key';
11 }
12
13 if (value !== undefined) {
14 // setter
15 switch (key) {
16 case 'image':
17 if (value && value.match(/^<img/)) {
18 $(this).find('.vap-card-image img').replaceWith(value);
19 } else {
20 $(this).find('.vap-card-image img').attr('src', value);
21 }
22
23 if (value) {
24 $(this).find('.vap-card-image').show();
25 } else {
26 $(this).find('.vap-card-image').hide();
27 }
28 break;
29
30 case 'badge':
31 $(this).find('.card-badge-icon').html(value);
32 break;
33
34 case 'primary':
35 $(this).find('.card-text-primary').html(value);
36 break;
37
38 case 'secondary':
39 $(this).find('.card-text-secondary').html(value);
40 break;
41
42 case 'edit':
43 if (value && (typeof value === 'number' || value.match(/^\d+$/))) {
44 $(this).find('button.card-edit').attr('data-id', value).attr('onclick', '');
45 } else {
46 $(this).find('button.card-edit').attr('onclick', value).attr('data-id', '');
47 }
48 break;
49
50 default:
51 throw 'Unsupported parameter ' + key;
52 }
53 } else {
54 // getter
55 switch (key) {
56 case 'image':
57 return $(this).find('.vap-card-image img').attr('src');
58
59 case 'badge':
60 return $(this).find('.card-badge-icon').html();
61
62 case 'primary':
63 return $(this).find('.card-text-primary').html();
64
65 case 'secondary':
66 return $(this).find('.card-text-secondary').html();
67
68 case 'edit':
69 return $(this).find('button.card-edit');
70
71 default:
72 throw 'Unsupported parameter ' + key;
73 }
74 }
75 }
76
77 /**
78 * Percentage circle.
79 */
80
81 $.fn.percentageCircle = function(method, data) {
82 if (typeof method !== 'string') {
83 // we probably have data as first argument
84 data = typeof method === 'object' ? method : {};
85 // auto-create percentage circle
86 method = 'create';
87 }
88
89 // define internal function to validate progress
90 var getProgress = function(progress) {
91 // validate progress
92 progress = parseInt(progress);
93 return isNaN(progress) ? 0 : Math.min(100, Math.abs(progress));
94 };
95
96 var animationTimeout = null;
97 var _this = this;
98
99 // define function to set and animate progress
100 var animateProgress = function() {
101 // get current progress
102 var progress = getProgress($(_this).data('tmp'));
103 var ceil = getProgress($(_this).data('progress'));
104 var factor = progress <= ceil ? 1 : -1;
105 var updated = progress + 1 * factor;
106
107 // fetch animation steps timer
108 var timer = parseInt($(_this).data('timer'));
109 timer = isNaN(timer) ? 16 : Math.abs(timer);
110
111 if (timer == 0) {
112 // bypass progress animation
113 updated = ceil;
114 }
115
116 // make sure the progress didn't exceed the maximum/minimum amount
117 if (factor == 1) {
118 updated = Math.min(updated, ceil);
119 } else {
120 updated = Math.max(updated, ceil);
121 }
122
123 // go to next animation step
124 $(_this).removeClass('p' + progress).addClass('p' + updated);
125 // update percentage text
126 $(_this).find('.amount').text(updated + '%');
127
128 // update progress
129 $(_this).data('tmp', updated);
130
131 // re-launch animation recursively, in case it didn't end
132 if ((factor == 1 && updated < ceil) || (factor == -1 && updated > ceil)) {
133
134 // register timeout
135 animationTimeout = setTimeout(animateProgress, timer);
136 } else {
137 // trigger done event
138 $(_this).trigger('done');
139
140 if (ceil == 100) {
141 // trigger complete event
142 $(_this).trigger('complete');
143 }
144 }
145 };
146
147 if (method.match(/^create$/i)) {
148 // create default properties
149 data = $.extend({
150 progress: 0,
151 size: null,
152 color: null,
153 darkMode: false,
154 timer: 16,
155 }, data);
156
157 // validate progress
158 data.progress = getProgress(data.progress);
159
160 // get original classes
161 var classes = $(this).attr('class');
162
163 // instantiate only once
164 if (!$(this).hasClass('c100')) {
165 $(this).addClass('c100 p' + data.progress)
166 .data('progress', data.progress)
167 .data('tmp', data.progress)
168 .data('class', classes)
169 .data('size', data.size)
170 .data('color', data.color)
171 .data('darkMode', data.darkMode)
172 .data('timer', data.timer)
173 .html('<span class="amount">' + data.progress + '%</span><div class="slice">\n<div class="bar"></div>\n<div class="fill"></div>\n</div>');
174
175 if (data.size) {
176 $(this).addClass(data.size);
177 }
178
179 if (data.color) {
180 $(this).addClass(data.color);
181 }
182
183 if (data.darkMode) {
184 $(this).addClass('dark');
185 }
186 }
187 } else if (method.match(/^destroy$/i)) {
188 // make sure the progress exists
189 if ($(this).hasClass('c100')) {
190 $(this).html('').attr('class', $(this).data('class'));
191 }
192 } else if (method.match(/^progress$/i)) {
193 // look for getter or setter
194 if (typeof data !== 'undefined') {
195 // get previous
196 var prev = $(this).data('progress');
197 // setter
198 $(this).data('progress', getProgress(data));
199
200 // make sure something has changed
201 if ($(this).data('progress') != prev) {
202 // trigger change event
203 $(this).trigger('change');
204 // animate progress
205 animateProgress();
206 }
207 } else {
208 // getter
209 return getProgress($(this).data('progress'));
210 }
211 } else if (method.match(/^color$/i)) {
212 // look for getter or setter
213 if (typeof data !== 'undefined') {
214 // get previous
215 var prev = $(this).data('color');
216 // setter
217 $(this).data('color', data);
218 // refresh color
219 $(this).removeClass(prev).addClass(data);
220 } else {
221 // getter
222 var color = $(this).data('color');
223 return color ? color : 'blue';
224 }
225 } else if (method.match(/^size$/i)) {
226 // look for getter or setter
227 if (typeof data !== 'undefined') {
228 // get previous
229 var prev = $(this).data('size');
230 // setter
231 $(this).data('size', data);
232 // refresh size
233 $(this).removeClass(prev).addClass(data);
234 } else {
235 // getter
236 var size = $(this).data('size');
237 return size ? size : 'normal';
238 }
239 } else if (method.match(/^darkMode$/i)) {
240 // look for getter or setter
241 if (typeof data !== 'undefined') {
242 // setter
243 $(this).data('darkMode', data ? true : false);
244 // refresh darkMode
245 if (data) {
246 // enable
247 $(this).addClass('dark');
248 } else {
249 // disable
250 $(this).removeClass('dark');
251 }
252 } else {
253 // getter
254 return $(this).data('darkMode') ? true : false;
255 }
256 } else if (method.match(/^timer$/i)) {
257 // look for getter or setter
258 if (typeof data !== 'undefined') {
259 // get previous
260 var prev = $(this).data('timer');
261 // setter
262 $(this).data('timer', data);
263 // refresh timer
264 $(this).removeClass(prev).addClass(data);
265 } else {
266 // getter
267 var timer = parseInt($(this).data('timer'));
268 return timer && !isNaN(timer) ? timer : 16;
269 }
270 }
271
272 return $(this);
273 }
274
275 /*
276 * OVERLAYS
277 */
278
279 window['openLoadingOverlay'] = (lock, message) => {
280 var _html = '';
281
282 if (message !== undefined) {
283 _html += '<div class="vap-loading-box-message">' + message + '</div>';
284 }
285
286 $('#content').append('<div class="vap-loading-overlay' + (lock ? ' lock' : '') + '">' + _html + '<div class="vap-loading-box"></div></div>');
287 }
288
289 window['closeLoadingOverlay'] = () => {
290 $('.vap-loading-overlay').remove();
291 }
292
293 /*
294 * SYSTEM UTILS
295 */
296
297 $.fn.updateChosen = function(value, active) {
298 $(this).val(value).trigger('chosen:updated').trigger('liszt:updated');
299
300 if (active) {
301 $(this).next().addClass('active');
302 } else {
303 $(this).next().removeClass('active');
304 }
305 }
306
307 window['debounce'] = (func, wait, immediate) => {
308 var timeout;
309 return function() {
310 var context = this, args = arguments;
311 var later = function() {
312 timeout = null;
313 if (!immediate) {
314 func.apply(context, args);
315 }
316 };
317 var callNow = immediate && !timeout;
318 clearTimeout(timeout);
319 timeout = setTimeout(later, wait);
320 if (callNow) {
321 func.apply(context, args);
322 }
323 };
324 }
325
326 window['vapToggleSearchToolsButton'] = (btn, suffix) => {
327 if (suffix === undefined) {
328 suffix = '';
329 } else {
330 suffix = '-' + suffix;
331 }
332
333 if ($(btn).hasClass('btn-primary')) {
334 $('#vap-search-tools' + suffix).slideUp('fast');
335
336 $(btn).removeClass('btn-primary');
337
338 $('#vap-tools-caret' + suffix).removeClass('fa-caret-up').addClass('fa-caret-down');
339 } else {
340 $('#vap-search-tools' + suffix).slideDown('fast');
341
342 $(btn).addClass('btn-primary');
343
344 $('#vap-tools-caret' + suffix).removeClass('fa-caret-down').addClass('fa-caret-up');
345 }
346 }
347
348 /*
349 * LEFTBOARD MENU
350 */
351
352 window['leftBoardMenuItemClicked'] = (elem, callee) => {
353 var wrapper = $(elem).next('.wrapper');
354
355 if (!wrapper.length) {
356 // find wrapper within the container
357 wrapper = $(elem).find('.wrapper');
358 }
359
360 var has = !wrapper.hasClass('collapsed');
361
362 if (has && callee == 'out')
363 {
364 // do not proceed as we are facing a loading delay,
365 // because the 'hover' event wasn't yet ready
366 return;
367 }
368
369 $('#vap-main-menu .parent .wrapper').removeClass('collapsed');
370
371 $('.vap-angle-dir').removeClass('fa-angle-up');
372 $('.vap-angle-dir').addClass('fa-angle-down');
373 $('#vap-main-menu .parent .title').removeClass('focus');
374
375 if (has) {
376 wrapper.addClass('collapsed');
377 var angle = $(elem).find('.vap-angle-dir');
378 angle.addClass('fa-angle-up');
379 angle.removeClass('fa-angle-down');
380 wrapper.closest('.title').addClass('focus');
381 }
382 }
383
384 window['leftBoardMenuToggle'] = () => {
385
386 // restore arrows
387 $('.vap-angle-dir').removeClass('fa-angle-up');
388 $('.vap-angle-dir').addClass('fa-angle-down');
389 $('#vap-main-menu .parent .title').removeClass('focus');
390
391 var status;
392
393 if (isLeftBoardMenuCompressed()) {
394 $('.vap-leftboard-menu').removeClass('compressed');
395 $('.vap-task-wrapper').removeClass('extended');
396 status = 1;
397 } else {
398 $('.vap-leftboard-menu').addClass('compressed');
399 $('.vap-task-wrapper').addClass('extended');
400
401 $('.vap-leftboard-menu.compressed .parent .title.selected').removeClass('collapsed');
402 $('.vap-leftboard-menu.compressed .parent .wrapper.collapsed').removeClass('collapsed');
403
404 status = 2;
405 }
406
407 leftBoardMenuRegisterStatus(status);
408 $(window).trigger('resize');
409
410 }
411
412 window['leftBoardMenuRegisterStatus'] = (status) => {
413 /**
414 * Store the main menu status with the browser cookie,
415 * so that each administrator will be able to use its
416 * preferred layout.
417 *
418 * Keep the main menu status for 1 year.
419 *
420 * @since 1.7
421 */
422 var date = new Date();
423 date.setYear(date.getFullYear() + 1);
424
425 document.cookie = 'vikappointments.mainmenu.status=' + status + '; expires=' + date.toUTCString() + '; path=/';
426 }
427
428 window['isLeftBoardMenuCompressed'] = () => {
429 return $('.vap-leftboard-menu').hasClass('compressed') && $(window).width() >= 768;
430 }
431
432 $(function() {
433 if (typeof VIKAPPOINTMENTS_MENU_INIT === 'undefined') {
434 // avoid to re-init menu again
435 window['VIKAPPOINTMENTS_MENU_INIT'] = true;
436
437 if (isLeftBoardMenuCompressed()) {
438 $('.vap-leftboard-menu.compressed .parent .title.selected').removeClass('collapsed');
439 $('.vap-leftboard-menu.compressed .parent .wrapper.collapsed').removeClass('collapsed');
440 }
441
442 $('#vap-main-menu .parent .title').disableSelection();
443
444 $('#vap-main-menu .parent .title').on('click', function() {
445 if (!isLeftBoardMenuCompressed()) {
446 leftBoardMenuItemClicked(this, 'click');
447 }
448 });
449
450 $('#vap-main-menu .parent .title').hover(function() {
451 if (isLeftBoardMenuCompressed() && !$(this).hasClass('collapsed')) {
452 leftBoardMenuItemClicked(this, 'hover');
453
454 $('#vap-main-menu.compressed .parent .title').removeClass('collapsed');
455 $(this).addClass('collapsed');
456 }
457
458 if ($(this).hasClass('has-href') && $(this).find('.wrapper').length) {
459 leftBoardMenuItemClicked(this, 'hover');
460
461 $('#vap-main-menu .parent .title').removeClass('collapsed');
462 $(this).addClass('collapsed');
463 }
464 }, function() {
465 if ($(this).hasClass('has-href') && $(this).find('.wrapper').length) {
466 leftBoardMenuItemClicked(this, 'out');
467
468 $('#vap-main-menu .parent .title').removeClass('collapsed');
469 }
470 });
471
472 $('.vap-leftboard-menu').hover(function() {
473
474 }, function() {
475 if ($(window).width() >= 768) {
476 $('.vap-leftboard-menu.compressed .parent .title').removeClass('collapsed');
477 $('.vap-leftboard-menu.compressed .parent .wrapper').removeClass('collapsed');
478 }
479 });
480
481 $('.vap-leftboard-menu .custom').hover(function() {
482 if ($(window).width() >= 768) {
483 $('.vap-leftboard-menu.compressed .parent .title').removeClass('collapsed');
484 $('.vap-leftboard-menu.compressed .parent .wrapper').removeClass('collapsed');
485 }
486 }, function() {
487
488 });
489
490 $('#vap-menu-toggle-phone').on('click', function() {
491 $('.vap-leftboard-menu').slideToggle();
492 });
493 }
494 });
495
496 /*
497 * DOCUMENT CONTENT RESIZE
498 */
499
500 $(function() {
501 // Statement to quickly disable doc resizing.
502 // Do not proceed in case of small devices (< 768).
503 if (true && $(window).width() >= 768) {
504 var task = $('.vap-task-wrapper');
505 var lfb_menu = $('.vap-leftboard-menu');
506 var _margin = 15;
507
508 $(window).resize(function() {
509 // var p = (lfb_menu.width() + _margin) * 100 / $(document).width();
510 // task.css('width', (100 - Math.ceil(p + 1)) + '%');
511 let p = lfb_menu.outerWidth() + _margin * 2;
512 task.css('width', 'calc(100% - ' + p + 'px)');
513 });
514 }
515
516 $(window).trigger('resize');
517 });
518 })(jQuery);
519
520 class VikTimer {
521 static debounce(key, func, wait, immediate) {
522 if (!VikTimer.debounceLookup) {
523 VikTimer.debounceLookup = {};
524 }
525
526 return function() {
527 var context = this, args = arguments;
528 var later = function() {
529 VikTimer.debounceLookup[key] = null;
530 if (!immediate) func.apply(context, args);
531 };
532 var callNow = immediate && !VikTimer.debounceLookup[key];
533 clearTimeout(VikTimer.debounceLookup[key]);
534 VikTimer.debounceLookup[key] = setTimeout(later, wait);
535 if (callNow) func.apply(context, args);
536 };
537 }
538
539 static isRunning(key) {
540 return VikTimer.debounceLookup && VikTimer.debounceLookup[key];
541 }
542 }
543
544 /**
545 * @deprecated 1.8 Use onInstanceReady() instead.
546 */
547 function callbackOn(on, callback) {
548 onInstanceReady(on).then(callback);
549 }
550
551 /*
552 * SEARCH BAR - editconfig
553 */
554
555 function SearchBar(matches) {
556 this.setMatches(matches);
557 }
558
559 SearchBar.prototype.setMatches = function(matches) {
560 this.matches = matches;
561 this.currIndex = 0;
562 }
563
564 SearchBar.prototype.clear = function() {
565 this.setMatches(false);
566 }
567
568 SearchBar.prototype.isNull = function() {
569 return this.matches === false;
570 }
571
572 SearchBar.prototype.isEmpty = function() {
573 return !this.isNull() && this.matches.length == 0;
574 }
575
576 SearchBar.prototype.getElement = function() {
577 if (this.matches === false) {
578 return null;
579 }
580 return this.matches[this.currIndex];
581 }
582
583 SearchBar.prototype.getCurrentIndex = function() {
584 return this.currIndex;
585 }
586
587 SearchBar.prototype.size = function() {
588 if (this.matches === false) {
589 return 0;
590 }
591 return this.matches.length;
592 }
593
594 SearchBar.prototype.next = function() {
595 if (this.matches === false) {
596 return null;
597 }
598 this.currIndex++;
599 if (this.currIndex >= this.matches.length) {
600 this.currIndex = 0;
601 }
602 return this.matches[this.currIndex];
603 }
604
605 SearchBar.prototype.previous = function() {
606 if (this.matches === false) {
607 return null;
608 }
609 this.currIndex--;
610 if (this.currIndex < 0) {
611 this.currIndex = this.matches.length-1;
612 }
613 return this.matches[this.currIndex];
614 }
615