PluginProbe
Orderable – Restaurant & Food Ordering System / 1.19.0
Orderable – Restaurant & Food Ordering System v1.19.0
0.1.4 0.1.5 0.2.0 1.0.0 1.1.0 1.1.1 1.10.0 1.10.1 1.11.0 1.12.0 1.12.1 1.12.2 1.13.0 1.14.0 1.15.0 1.16.0 1.17.0 1.17.1 1.18.0 1.19.0 1.19.1 1.19.2 1.2.0 1.20.0 1.20.1 All 44 releases
orderable / assets / frontend / js / main.js

main.js in Orderable – Restaurant & Food Ordering System 1.19.0, at assets/frontend/js/main.js

1,522 lines 54.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1
2 (function ($, document) {
3 'use strict';
4
5 var orderable_accordion = {
6 /**
7 * On doc ready.
8 */
9 on_ready() {
10 orderable_accordion.cache();
11 orderable_accordion.watch();
12 },
13 /**
14 * Cache.
15 */
16 cache() {
17 orderable_accordion.vars = {
18 classes: {
19 parent: 'orderable-accordion',
20 link: 'orderable-accordion__item-link',
21 content: 'orderable-accordion__item-content',
22 link_active: 'orderable-accordion__item-link--active',
23 content_active: 'orderable-accordion__item-content--active'
24 }
25 };
26 },
27 /**
28 * Watch.
29 */
30 watch() {
31 /**
32 * When click accordion link.
33 */
34 $(document.body).on('click', '.' + orderable_accordion.vars.classes.link, function (e) {
35 e.preventDefault();
36 const $link = $(this),
37 $parent = $link.closest('.' + orderable_accordion.vars.classes.parent),
38 content_id = $link.attr('href'),
39 $content = $(content_id),
40 is_active = $link.hasClass(orderable_accordion.vars.classes.link_active);
41 $parent.find('.' + orderable_accordion.vars.classes.link).removeClass(orderable_accordion.vars.classes.link_active);
42 $parent.find('.' + orderable_accordion.vars.classes.content).removeClass(orderable_accordion.vars.classes.content_active);
43 if (!is_active) {
44 $link.addClass(orderable_accordion.vars.classes.link_active);
45 $content.addClass(orderable_accordion.vars.classes.content_active);
46 }
47 $(document.body).trigger('orderable-accordion.toggled', {
48 link: $link,
49 content: $content
50 });
51 });
52
53 /**
54 * When drawer is opened.
55 */
56 $(document.body).on('orderable-scrollbar.created', function (e, args) {
57 const $active_accordion = $('.orderable-drawer .' + orderable_accordion.vars.classes.link_active);
58 if ($active_accordion.length <= 0) {
59 return;
60 }
61 const $scroll_content = args.content,
62 scroll_position = $scroll_content.scrollTop() - $scroll_content.offset().top + $active_accordion.offset().top;
63 $scroll_content.scrollTop(scroll_position);
64 });
65 }
66 };
67 $(document).ready(orderable_accordion.on_ready);
68 })(jQuery, document);
69 (function ($, document) {
70 'use strict';
71
72 var orderable_drawer = {
73 /**
74 * Delays invoking function
75 *
76 * @param {Function} func The function to debounce.
77 * @param {number} timeout The number of milliseconds to delay.
78 * @return {Function} Returns the new debounced function.
79 */
80 debounce(func, timeout = 700) {
81 let timer;
82 return (...args) => {
83 clearTimeout(timer);
84 timer = setTimeout(() => {
85 func.apply(this, args);
86 }, timeout);
87 };
88 },
89 /**
90 * Allow only number for events like keypress
91 *
92 * @param {Event} event
93 */
94 allow_only_numbers(event) {
95 const value = String.fromCharCode(event.which);
96 if (!/^\d+$/.test(value)) {
97 event.preventDefault();
98 }
99 },
100 /**
101 * Send a request to change the quantity.
102 *
103 * @param {Event} event
104 */
105 on_change_quantity(event) {
106 const quantityElement = $(event.currentTarget);
107 const product_id = quantityElement.data('orderable-product-id');
108 const cart_item_key = quantityElement.data('orderable-cart-item-key');
109 const quantity = parseInt(quantityElement.text());
110 const data = {
111 action: 'orderable_cart_quantity',
112 cart_item_key,
113 product_id,
114 quantity
115 };
116 jQuery.post(orderable_vars.ajax_url, data, function (response) {
117 if (!response) {
118 return;
119 }
120 $(document.body).trigger('added_to_cart', [response.fragments, response.cart_hash, quantityElement]);
121 $(document.body).trigger('orderable-drawer.quantity-updated');
122 });
123 },
124 /**
125 * On doc ready.
126 */
127 on_ready() {
128 orderable_drawer.cache();
129 orderable_drawer.watch();
130
131 /**
132 * Handle manually changing the quantity of a product.
133 */
134 $(document.body).on('keypress', '.orderable-quantity-roller__quantity', orderable_drawer.allow_only_numbers);
135 $('.orderable-drawer__cart, .orderable-product--add-to-cart, .orderable-products-list').on('input', '.orderable-quantity-roller__quantity', orderable_drawer.debounce(orderable_drawer.on_change_quantity));
136 $(document.body).on('click', '.orderable-quantity-roller__quantity', function (event) {
137 event.stopPropagation();
138 });
139
140 /**
141 * We turn off the click event for .add_to_cart_button elements
142 * to keep the AJAX behaviour only on Mini cart when the option
143 * "Enable AJAX add to cart buttons on archives" is disabled.
144 */
145 if (orderable_vars && !orderable_vars.woocommerce_enable_ajax_add_to_cart) {
146 $(document.body).off('click', '.add_to_cart_button');
147 }
148 },
149 /**
150 * Cache.
151 */
152 cache() {
153 orderable_drawer.vars = {
154 classes: {
155 overlay: 'orderable-drawer-overlay',
156 drawer: 'orderable-drawer',
157 drawer_cart: 'orderable-drawer__cart',
158 drawer_html: 'orderable-drawer__html',
159 overlay_open: 'orderable-drawer-overlay--open',
160 drawer_open: 'orderable-drawer--open',
161 drawer_open_body: 'orderable-drawer-open'
162 }
163 };
164 orderable_drawer.elements = {
165 body: $('body'),
166 overlay: $('.' + orderable_drawer.vars.classes.overlay),
167 drawer: $('.' + orderable_drawer.vars.classes.drawer),
168 drawer_cart: $('.' + orderable_drawer.vars.classes.drawer_cart),
169 drawer_html: $('.' + orderable_drawer.vars.classes.drawer_html),
170 floating_cart_button_class: '.orderable-floating-cart__button'
171 };
172 },
173 /**
174 * Watch for trigger events.
175 */
176 watch() {
177 if (typeof orderable_drawer.elements.drawer === 'undefined') {
178 return;
179 }
180 $(document.body).on('orderable-drawer.open', orderable_drawer.open);
181 $(document.body).on('orderable-drawer.close', orderable_drawer.close);
182 $(document.body).on('click', orderable_drawer.elements.floating_cart_button_class, function () {
183 $(document.body).trigger('orderable-drawer.open', {
184 show_cart: true
185 });
186 });
187 $(document.body).on('orderable-increase-quantity', orderable_drawer.cart.handle_quantity_change_by_button);
188 $(document.body).on('orderable-decrease-quantity', orderable_drawer.cart.handle_quantity_change_by_button);
189 const updateQuantityRequest = orderable_drawer.debounce(orderable_drawer.cart.click_increase_decrease_quantity);
190 $(document.body).on('orderable-increase-quantity', updateQuantityRequest);
191 $(document.body).on('orderable-decrease-quantity', updateQuantityRequest);
192 const drawer = document.querySelector('body:not( .rtl ) .orderable-drawer');
193 const drawer_rtl = document.querySelector('body.rtl .orderable-drawer');
194 if (drawer) {
195 drawer.addEventListener('swiped-right', function (e) {
196 orderable_drawer.close();
197 });
198 }
199 if (drawer_rtl) {
200 drawer_rtl.addEventListener('swiped-left', function (e) {
201 orderable_drawer.close();
202 });
203 }
204 },
205 /**
206 * Open the drawer.
207 * @param event
208 * @param args
209 */
210 open(event, args) {
211 args.html = args.html || false;
212 args.show_cart = args.show_cart || false;
213 orderable_drawer.elements.drawer_html.hide();
214 orderable_drawer.elements.drawer_cart.hide();
215 if (args.html) {
216 orderable_drawer.elements.drawer_html.html(args.html);
217 orderable_drawer.elements.drawer_html.show();
218 }
219 if (args.show_cart) {
220 // Empty drawer HTML before showing cart. Prevents options
221 // interfering with subsequent cart additions.
222 orderable_drawer.elements.drawer_html.html('');
223 orderable_drawer.elements.drawer_cart.show();
224 }
225 orderable_drawer.elements.overlay.addClass(orderable_drawer.vars.classes.overlay_open);
226 orderable_drawer.elements.drawer.addClass(orderable_drawer.vars.classes.drawer_open);
227 orderable_drawer.elements.body.addClass(orderable_drawer.vars.classes.drawer_open_body);
228 $(document.body).trigger('orderable-drawer.opened', args);
229 },
230 /**
231 * Close the drawer.
232 */
233 close() {
234 orderable_drawer.elements.overlay.removeClass(orderable_drawer.vars.classes.overlay_open);
235 orderable_drawer.elements.drawer.removeClass(orderable_drawer.vars.classes.drawer_open);
236 orderable_drawer.elements.body.removeClass(orderable_drawer.vars.classes.drawer_open_body);
237 orderable_drawer.elements.drawer_html.html('');
238 $(document.body).trigger('orderable-drawer.closed');
239 },
240 /**
241 * Mini cart related functions.
242 */
243 cart: {
244 /**
245 * When increase qty is clicked.
246 *
247 * @param e
248 * @param $button
249 */
250 click_increase_decrease_quantity(e, $button) {
251 const direction = $button.data('orderable-trigger');
252 const product_id = $button.attr('data-orderable-product-id'),
253 cart_item_key = $button.attr('data-orderable-cart-item-key'),
254 quantity = $button.attr('data-orderable-quantity');
255 const siblingButtonName = 'increase-quantity' === direction ? 'decrease' : 'increase';
256 const $siblingButton = $button.siblings(`.orderable-quantity-roller__button--${siblingButtonName}`);
257 const $quantityElement = $button.siblings('.orderable-quantity-roller__quantity');
258 const data = {
259 action: 'orderable_cart_quantity',
260 cart_item_key,
261 product_id,
262 quantity
263 };
264 if (this.currentRequest) {
265 this.currentRequest.abort();
266 this.currentRequest = undefined;
267 }
268 $button.addClass('orderable-button--loading');
269 $button.attr('disabled', true);
270 $siblingButton.attr('disabled', true);
271 $quantityElement.attr('contenteditable', false);
272 this.currentRequest = jQuery.post(orderable_vars.ajax_url, data, function (response) {
273 if (!response) {
274 return;
275 }
276 const $quantityElement = $button.siblings('.orderable-quantity-roller__quantity');
277 if (response && response.fragments && response.fragments['.orderable-mini-cart__notices']) {
278 $(document.body).trigger('orderable-drawer.open', {
279 show_cart: true
280 });
281 }
282 switch (data.quantity) {
283 case '0':
284 $(document.body).trigger('removed_from_cart', [response.fragments, response.cart_hash, $button]);
285 break;
286 case $quantityElement.attr('data-orderable-updating-quantity'):
287 $(document.body).trigger('added_to_cart', [response.fragments, response.cart_hash, $button]);
288 $(document.body).trigger('orderable-drawer.quantity-updated');
289 break;
290 default:
291 break;
292 }
293 }.bind(this)).always(function () {
294 this.currentRequest = undefined;
295 $button.removeClass('orderable-button--loading');
296 $button.attr('disabled', false);
297 $siblingButton.attr('disabled', false);
298 $quantityElement.attr('contenteditable', true);
299 }.bind(this));
300 },
301 handle_quantity_change_by_button(e, $button) {
302 const direction = $button.data('orderable-trigger');
303 const quantity = parseInt($button.attr('data-orderable-quantity'));
304 const siblingButtonName = 'increase-quantity' === direction ? 'decrease' : 'increase';
305 const $siblingButton = $button.siblings(`.orderable-quantity-roller__button--${siblingButtonName}`);
306 const $quantityElement = $button.siblings('.orderable-quantity-roller__quantity');
307 const newQuantity = 'increase-quantity' === direction ? quantity + 1 : Math.max(0, quantity - 1);
308 const $parent = $button.parents('.orderable-product__actions-button');
309 if (0 === newQuantity && $parent.length) {
310 const $addToCartButton = $parent.find('button.orderable-button[data-orderable-trigger]');
311 const $quantityRoller = $parent.find('.orderable-quantity-roller');
312 if ($quantityRoller.length) {
313 $addToCartButton.removeClass('orderable-button--product-in-the-cart');
314 $quantityRoller.removeClass('orderable-quantity-roller--is-active');
315 }
316 }
317 $button.attr('data-orderable-quantity', newQuantity);
318 $siblingButton.attr('data-orderable-quantity', newQuantity);
319 $quantityElement.attr('data-orderable-updating-quantity', newQuantity);
320 $quantityElement.text(newQuantity);
321 $quantityElement.attr('contenteditable', false);
322 }
323 }
324 };
325 $(document).ready(orderable_drawer.on_ready);
326 })(jQuery, document);
327 (function ($, document) {
328 'use strict';
329
330 var orderable_products = {
331 /**
332 * On doc ready.
333 */
334 on_ready() {
335 orderable_products.cache();
336 orderable_products.watch();
337 },
338 /**
339 * Cache.
340 */
341 cache() {
342 orderable_products.vars = {
343 classes: {
344 clickable_product: 'orderable-product--add-to-cart ',
345 add_to_order_button: 'orderable-product__add-to-order',
346 product_messages: 'orderable-product__messages',
347 product_price: 'orderable-product__actions-price',
348 invalid_field: 'orderable-field--invalid',
349 option_select_td: 'orderable-product__option-select',
350 button_loading: 'orderable-button--loading',
351 out_of_stock: 'orderable-button--out-of-stock'
352 },
353 parent_price: null,
354 variable_product_types: ['variable', 'variable-subscription']
355 };
356 orderable_products.elements = {};
357 },
358 /**
359 * Watch for trigger events.
360 */
361 watch() {
362 $(document.body).on('orderable-drawer.opened', orderable_products.init_product_options);
363 $(document.body).on('orderable-add-to-cart', orderable_products.click_add_to_order);
364 $(document.body).on('orderable-product-options', orderable_products.click_add_to_order);
365 $(document.body).on('orderable-view-product', orderable_products.view_product);
366 $(document.body).on('mouseenter mouseleave', '.' + orderable_products.vars.classes.clickable_product, orderable_products.simulate_add_to_order_hover);
367 $(document.body).on('orderable-edit-cart-item', orderable_products.edit_cart_item);
368 $(document.body).on('orderable-update-cart-item', orderable_products.update_cart_item);
369 $(document.body).on('orderable-show-cart', orderable_products.show_cart);
370 $(document.body).on('orderable-add-to-cart-without-side-drawer', orderable_products.click_add_to_order);
371 $(document.body).on('added_to_cart', orderable_products.remove_fragments);
372 $(document.body).on('added_to_cart', orderable_products.remove_animation);
373 $(document.body).on('added_to_cart', orderable_products.shake_floating_cart);
374 $(document.body).on('removed_from_cart', orderable_products.hide_quantity_roller);
375 $(document.body).on('wc_cart_button_updated', orderable_products.remove_view_cart_link);
376 },
377 /**
378 * Simulate hover on add to order button.
379 *
380 * @param event
381 */
382 simulate_add_to_order_hover(event) {
383 const $element = $(this),
384 $button = $element.find('.' + orderable_products.vars.classes.add_to_order_button);
385 $button.toggleClass('orderable-button--hover', 'mouseenter' === event.type);
386 },
387 /**
388 * Add to order click event.
389 *
390 * This event accounts for button clicks or card clicks.
391 * @param event
392 * @param $element
393 */
394 click_add_to_order(event, $element) {
395 // If undefined, it means it was triggered by a click
396 // event and not the `orderable-add-to-cart` trigger.
397 $element = typeof $element !== 'undefined' ? $element : $(this);
398
399 // The button is either the clicked element, or the
400 // add to order button within the clicked element.
401 const $button = $element.is('button') ? $element : $element.find('.' + orderable_products.vars.classes.add_to_order_button),
402 action = $button.data('orderable-trigger'),
403 product_id = $button.data('orderable-product-id'),
404 variation_id = $button.data('orderable-variation-id'),
405 attributes = $button.data('orderable-variation-attributes'),
406 args = {
407 action
408 };
409 if ($button.hasClass(orderable_products.vars.classes.button_loading) || $button.hasClass(orderable_products.vars.classes.out_of_stock)) {
410 return;
411 }
412 $button.addClass(orderable_products.vars.classes.button_loading);
413 switch (action) {
414 case 'add-to-cart':
415 orderable_products.add_to_cart({
416 product_id,
417 variation_id,
418 attributes,
419 thisbutton: $element
420 }, function (response) {
421 args.show_cart = true;
422 args.response = response;
423 $(document.body).trigger('orderable-drawer.open', args);
424 $button.removeClass(orderable_products.vars.classes.button_loading);
425 const $addToCartButtonOutsideDrawer = $('.orderable-product .orderable-product__actions-button button.orderable-product__add-to-order[data-orderable-product-id=' + product_id + ']');
426 if ($addToCartButtonOutsideDrawer.siblings('.orderable-quantity-roller').length) {
427 $addToCartButtonOutsideDrawer.addClass('orderable-button--product-in-the-cart');
428 }
429 });
430 break;
431 case 'add-to-cart-without-side-drawer':
432 orderable_products.add_to_cart({
433 product_id,
434 variation_id,
435 attributes
436 }, function (response) {
437 args.response = response;
438 $button.addClass('orderable-button--product-in-the-cart');
439 $button.removeClass(orderable_products.vars.classes.button_loading);
440 });
441 break;
442 case 'product-options':
443 orderable_products.get_product_options({
444 product_id,
445 focus: $button.data('orderable-focus')
446 }, function (response) {
447 args.html = response.html;
448 $(document.body).trigger('orderable-drawer.open', args);
449 $button.removeClass(orderable_products.vars.classes.button_loading);
450 });
451 break;
452 default:
453 break;
454 }
455 },
456 /**
457 * Show the cart.
458 */
459 show_cart() {
460 $(document.body).trigger('orderable-drawer.open', {
461 show_cart: true
462 });
463 },
464 /**
465 * View product.
466 *
467 * @param event
468 * @param $element
469 */
470 view_product(event, $element) {
471 const product_id = $element.data('orderable-product-id'),
472 args = {
473 action: 'product-options'
474 };
475 orderable_products.get_product_options({
476 product_id,
477 focus: $element.data('orderable-focus')
478 }, function (response) {
479 args.html = response.html;
480 $(document.body).trigger('orderable-drawer.open', args);
481 });
482 },
483 /**
484 * Ajax add to cart.
485 * @param args
486 * @param callback
487 */
488 add_to_cart(args, callback) {
489 if (typeof args.product_id === 'undefined') {
490 return;
491 }
492 let data = {
493 action: 'orderable_add_to_cart',
494 product_id: args.product_id,
495 variation_id: args.variation_id || false,
496 attributes: args.attributes || false
497 };
498
499 // Prepare addons data.
500 if ($('.orderable-product-fields-group').length) {
501 let inputs = jQuery('.orderable-product-fields-group :input').serializeArray();
502 inputs = orderable_products.add_unchecked_checkbox_fields(inputs);
503 const addons_data = orderable_products.convert_to_flat_object(inputs);
504 if (!jQuery.isEmptyObject(addons_data)) {
505 data = Object.assign(data, addons_data); // Merge objects.
506 }
507 }
508 jQuery.post(orderable_vars.ajax_url, data, function (response) {
509 if (!response) {
510 return;
511 }
512
513 // Trigger event so themes can refresh other areas.
514 $(document.body).trigger('added_to_cart', [response.fragments, response.cart_hash, args.thisbutton]);
515 if (typeof callback === 'function') {
516 callback(response);
517 }
518 });
519 },
520 /**
521 * Edit cart item.
522 *
523 * @param event
524 * @param $element
525 */
526 edit_cart_item(event, $element) {
527 const cart_item_key = $element.data('orderable-cart-item-key');
528 $element.addClass(orderable_products.vars.classes.button_loading);
529 orderable_products.get_cart_item_options({
530 cart_item_key
531 }, function (response) {
532 const args = {
533 html: response.html,
534 action: 'update-cart-item'
535 };
536 $(document.body).trigger('orderable-drawer.open', args);
537 $element.removeClass(orderable_products.vars.classes.button_loading);
538 });
539 },
540 /**
541 * Update cart item.
542 *
543 * @param event
544 * @param $element
545 */
546 update_cart_item(event, $element) {
547 const cart_item_key = $element.data('orderable-cart-item-key');
548 const product_id = $element.data('orderable-product-id');
549 const variation_id = $element.data('orderable-variation-id');
550 const attributes = $element.data('orderable-variation-attributes');
551 $element.addClass(orderable_products.vars.classes.button_loading);
552 orderable_products.update_cart_item_options({
553 cart_item_key,
554 product_id,
555 variation_id,
556 attributes
557 }, function (response) {
558 const args = {
559 show_cart: true,
560 response
561 };
562 $(document.body).trigger('added_to_cart', [response.fragments, response.cart_hash, $element]);
563 $(document.body).trigger('orderable-drawer.open', args);
564 $element.removeClass(orderable_products.vars.classes.button_loading);
565 });
566 },
567 /**
568 * Convert [{name:x, value:y }] to {x:y} format.
569 * @param inputs
570 */
571 convert_to_flat_object(inputs) {
572 const data = {};
573 inputs.forEach(function (input) {
574 const is_array = '[]' === input.name.substr(-2) || Array.isArray(input.name);
575 // If last 2 chars are '[]', remove them.
576 const key = is_array ? input.name.substr(0, input.name.length - 2) : input.name;
577 if (is_array) {
578 data[key] = typeof data[key] === 'undefined' ? [] : data[key];
579 data[key].push(input.value);
580 } else {
581 data[key] = input.value;
582 }
583 });
584 return data;
585 },
586 /**
587 * Get variable product options.
588 *
589 * @param args
590 * @param callback
591 */
592 get_product_options(args, callback) {
593 if (typeof args.product_id === 'undefined') {
594 return;
595 }
596 args.action = 'orderable_get_product_options';
597 jQuery.post(orderable_vars.ajax_url, args, function (response) {
598 if (!response.success) {
599 return;
600 }
601 if (typeof callback === 'function') {
602 callback(response.data);
603 }
604 });
605 },
606 /**
607 * Get variable product options.
608 *
609 * @param args
610 * @param callback
611 */
612 get_cart_item_options(args, callback) {
613 if (typeof args.cart_item_key === 'undefined') {
614 return;
615 }
616 args.action = 'orderable_get_cart_item_options';
617 jQuery.post(orderable_vars.ajax_url, args, function (response) {
618 if (!response.success) {
619 return;
620 }
621 if (typeof callback === 'function') {
622 callback(response.data);
623 }
624 });
625 },
626 /**
627 * Update variable product options.
628 *
629 * @param args
630 * @param callback
631 */
632 update_cart_item_options(args, callback) {
633 if (typeof args.cart_item_key === 'undefined') {
634 return;
635 }
636 args.action = 'orderable_update_cart_item_options';
637
638 // Prepare addons data.
639 if ($('.orderable-product-fields-group').length) {
640 let inputs = jQuery('.orderable-product-fields-group :input').serializeArray();
641 inputs = orderable_products.add_unchecked_checkbox_fields(inputs);
642 const addons_data = orderable_products.convert_to_flat_object(inputs);
643 if (!jQuery.isEmptyObject(addons_data)) {
644 args = Object.assign(args, addons_data); // Merge objects.
645 }
646 }
647 jQuery.post(orderable_vars.ajax_url, args, function (response) {
648 if (!response) {
649 return;
650 }
651 if (typeof callback === 'function') {
652 callback(response);
653 }
654 });
655 },
656 /**
657 * Init drawer product options.
658 *
659 * @param event
660 * @param args
661 */
662 init_product_options(event, args) {
663 if (typeof args.action === 'undefined' || 'product-options' !== args.action && 'update-cart-item' !== args.action) {
664 return;
665 }
666 const selectors = '.orderable-drawer .orderable-product__options input, .orderable-drawer .orderable-product__options select, .orderable-product__options textarea';
667 const $options = $(selectors);
668 orderable_products.vars.parent_price = $('.orderable-drawer .orderable-product__actions-price').html();
669 orderable_products.product_options_change($options);
670 orderable_products.update_button_state();
671 const debounced_update_button_state = orderable_products.debounce(orderable_products.update_button_state, 500);
672 const debounced_product_options_change = orderable_products.debounce(orderable_products.product_options_change, 500);
673 $(document).on('change keyup', selectors, function () {
674 debounced_product_options_change($options);
675 debounced_update_button_state();
676 });
677 },
678 /**
679 * On product options change.
680 *
681 * @param $options
682 */
683 product_options_change($options) {
684 const $add_to_order_button = $('.orderable-drawer .orderable-product__add-to-order, .orderable-drawer .orderable-product__update-cart-item'),
685 options_set = orderable_products.check_options($options),
686 product_type = $add_to_order_button.data('orderable-product-type');
687 if ('product-options' === $add_to_order_button.attr('data-orderable-trigger')) {
688 $add_to_order_button.attr('data-orderable-trigger', 'add-to-cart');
689 }
690 $('.' + orderable_products.vars.classes.product_messages).html('');
691 if (!orderable_products.vars.variable_product_types.includes(product_type)) {
692 return;
693 }
694 if (!options_set) {
695 orderable_products.clear_variation($add_to_order_button);
696 return;
697 }
698 const variation = orderable_products.check_variation($options);
699 orderable_products.set_variation($add_to_order_button, variation);
700 },
701 /**
702 * Check if all product options are set.
703 *
704 * @param $options
705 * @return {boolean}
706 */
707 check_options($options) {
708 if ($options.length <= 0) {
709 return false;
710 }
711 let all_set = true;
712 $options.each(function (index, option) {
713 // Only check attribute fields.
714 if (!$(option).hasClass('orderable-input--validate')) {
715 return;
716 }
717 if ('' === $(option).val()) {
718 $(option).addClass(orderable_products.vars.classes.invalid_field);
719 all_set = false;
720 } else {
721 $(option).removeClass(orderable_products.vars.classes.invalid_field);
722 }
723 });
724 return all_set;
725 },
726 /**
727 * Check if variation has been selected.
728 * @param $options
729 */
730 check_variation($options) {
731 const $product = $options.closest('.orderable-drawer');
732 let variations = $product.find('.orderable-product__variations').text();
733 variations = variations ? JSON.parse(variations) : '';
734 const selected_options = orderable_products.serialize_object($options),
735 matching_variations = orderable_products.find_matching_variations(variations, selected_options);
736 if (orderable_products.is_empty(matching_variations)) {
737 return false;
738 }
739 const variation = matching_variations.shift();
740 variation.attributes = selected_options;
741 variation.attributes_json = JSON.stringify(selected_options);
742 return typeof variation !== 'undefined' ? variation : false;
743 },
744 /**
745 * Set variation for add to cart button.
746 * @param $button
747 * @param variation
748 */
749 set_variation($button, variation) {
750 let variation_id = variation.variation_id || '',
751 attributes = variation.attributes_json || '',
752 price = variation.price_html || orderable_products.vars.parent_price,
753 message = '';
754 if (variation && '' !== variation.availability_html) {
755 message = variation.availability_html;
756 }
757 if (variation && !variation.is_in_stock) {
758 message = '<p>' + orderable_vars.i18n.out_of_stock + '</p>';
759 }
760 if (variation && !variation.is_purchasable) {
761 message = '<p>' + orderable_vars.i18n.unavailable + '</p>';
762 }
763 if (false === variation) {
764 message = '<p>' + orderable_vars.i18n.no_exist + '</p>';
765 }
766 if (variation && (!variation.is_purchasable || !variation.is_in_stock)) {
767 variation_id = '';
768 attributes = '';
769 }
770 if ('' !== message) {
771 $('.' + orderable_products.vars.classes.product_messages).html(message);
772 }
773 $button.data('orderable-variation-id', variation_id);
774 $button.data('orderable-variation-attributes', attributes);
775 $('.orderable-drawer .orderable-product__actions-price').html(price);
776 $button.trigger('orderable_variation_set', {
777 variation,
778 variation_id,
779 attributes,
780 price
781 });
782 },
783 /**
784 * Clear variation and disable add to order.
785 *
786 * @param $button
787 */
788 clear_variation($button) {
789 orderable_products.set_variation($button, '');
790 if (orderable_products.vars.parent_price) {
791 $('.orderable-drawer .orderable-product__actions-price').html(orderable_products.vars.parent_price);
792 }
793 },
794 /**
795 * Find matching variations for attributes.
796 * @param variations
797 * @param attributes
798 */
799 find_matching_variations(variations, attributes) {
800 const matching = [];
801 for (let i = 0; i < variations.length; i++) {
802 const variation = variations[i];
803 if (orderable_products.is_matching_variation(variation.attributes, attributes)) {
804 matching.push(variation);
805 }
806 }
807 return matching;
808 },
809 /**
810 * See if attributes match.
811 * @param variation_attributes
812 * @param attributes
813 * @return {boolean}
814 */
815 is_matching_variation(variation_attributes, attributes) {
816 let match = true;
817 for (const attr_name in variation_attributes) {
818 if (variation_attributes.hasOwnProperty(attr_name)) {
819 const val1 = variation_attributes[attr_name];
820 const val2 = attributes[attr_name];
821 if (val1 !== undefined && val2 !== undefined && val1.length !== 0 && val2.length !== 0 && val1 !== val2) {
822 match = false;
823 }
824 }
825 }
826 return match;
827 },
828 /**
829 * Is value empty?
830 *
831 * @param value
832 * @return {boolean}
833 */
834 is_empty(value) {
835 return typeof value === 'undefined' || false === value || value.length <= 0 || !value;
836 },
837 /**
838 * Serialize into a key/value object.
839 *
840 * @param $elements
841 * @return {{}}
842 */
843 serialize_object: function objectifyForm($elements) {
844 const serialized = $elements.serializeArray(),
845 return_object = {};
846 for (let i = 0; i < serialized.length; i++) {
847 return_object[serialized[i].name] = serialized[i].value;
848 }
849 return return_object;
850 },
851 /**
852 * Disable/Enable the 'Add to cart' button based on the presence of orderable-field--invalid class.
853 */
854 update_button_state() {
855 // Add delay to ensure invalid class has been assigned to inputs.
856 setTimeout(function () {
857 let $button = $('.orderable-drawer .orderable-product__add-to-order, .orderable-drawer .orderable-product__update-cart-item'),
858 invalid_fields_count = $('.orderable-drawer__html .' + orderable_products.vars.classes.invalid_field).length,
859 product_type = $button.data('orderable-product-type'),
860 has_variation_id = true;
861 if ('variable' === product_type) {
862 has_variation_id = '' !== $button.data('orderable-variation-id');
863 }
864 $button.prop('disabled', invalid_fields_count || !has_variation_id);
865 }, 50);
866 },
867 /**
868 * Debounce function.
869 *
870 * @param func Function to debounce.
871 * @param wait Time to wait in milliseconds.
872 * @param immediate Trigger the function on the leading edge, instead of the trailing.
873 *
874 * @return
875 */
876 debounce(func, wait, immediate) {
877 let timeout;
878 return function () {
879 const context = this,
880 args = arguments;
881 const later = function () {
882 timeout = null;
883 if (!immediate) {
884 func.apply(context, args);
885 }
886 };
887 const callNow = immediate && !timeout;
888 clearTimeout(timeout);
889 timeout = setTimeout(later, wait);
890 if (callNow) {
891 func.apply(context, args);
892 }
893 };
894 },
895 /**
896 * Remove the quantity roller fragments
897 *
898 * @param {Event} e
899 * @param {Object} fragments
900 * @return void
901 */
902 remove_fragments(e, fragments) {
903 if (!fragments || 'undefined' === typeof wc_cart_fragments_params || !wc_cart_fragments_params.fragment_name) {
904 return;
905 }
906 const regex_quantity_roller = /\.orderable-product\[data-orderable-product-id='[1-9][0-9]*'\] \.orderable-product__actions-button \.orderable-quantity-roller/;
907 const regex_product_in_the_cart_counter = /\.orderable-product\[data-orderable-product-id='[1-9][0-9]*'\] \.orderable-product__actions-button \.orderable-product__actions-counter/;
908 for (const key in fragments) {
909 if (!regex_quantity_roller.test(key) && !regex_product_in_the_cart_counter.test(key)) {
910 continue;
911 }
912 fragments[key] = undefined;
913 }
914 sessionStorage.setItem(wc_cart_fragments_params.fragment_name, JSON.stringify(fragments));
915 },
916 /**
917 * Remove animation.
918 */
919 remove_animation() {
920 setTimeout(function () {
921 $('.orderable-product__actions-counter').css('animation', '');
922 }, 1000);
923 },
924 /**
925 * Hide quantity roller element and show the Add to Cart button.
926 *
927 * @param {Event} e
928 * @param {Object} fragments
929 * @param {string} cart_hash
930 * @param {Element} $button
931 * @return
932 */
933 hide_quantity_roller(e, fragments, cart_hash, $button) {
934 const product_id = $button.attr('data-product_id') || $button.attr('data-orderable-product-id');
935 if (!product_id) {
936 return;
937 }
938 const $actions_button = $('.orderable-product[data-orderable-product-id=' + product_id + '] .orderable-product__actions-button');
939 if (!$actions_button.length) {
940 return;
941 }
942 const $quantity_roller = $actions_button.find('.orderable-quantity-roller');
943 if ($quantity_roller.length) {
944 $actions_button.find('button.orderable-product__add-to-order[data-orderable-trigger]').removeClass('orderable-button--product-in-the-cart');
945 $quantity_roller.addClass('orderable-button--hide');
946 }
947 },
948 /**
949 * Add unchecked checkboxs to the list of inputs
950 * sent to the request to add/update an item
951 *
952 * @param {Object} inputs
953 * @return {Object}
954 */
955 add_unchecked_checkbox_fields(inputs) {
956 jQuery('.orderable-product-fields-group :input[type="checkbox"]:not(:checked)').each(function (index, element) {
957 inputs.push({
958 name: element.getAttribute('name'),
959 value: ''
960 });
961 });
962 return inputs;
963 },
964 /**
965 * Shake the floating cart button.
966 *
967 * @return void
968 */
969 shake_floating_cart() {
970 $('.orderable-floating-cart__button').css('animation', 'wobble-hor-bottom .8s both');
971 },
972 /**
973 * Remove the view cart link.
974 *
975 * @param event
976 * @param $button
977 */
978 remove_view_cart_link(event, $button) {
979 if (!$button?.hasClass('orderable-product__add-to-order')) {
980 return;
981 }
982 $button?.siblings('.added_to_cart.wc-forward').remove();
983 }
984 };
985 $(document).ready(orderable_products.on_ready);
986 })(jQuery, document);
987 (function ($, document) {
988 'use strict';
989
990 var orderable_scrollbar = {
991 /**
992 * On doc ready.
993 */
994 on_ready() {
995 orderable_scrollbar.cache();
996 orderable_scrollbar.watch();
997 },
998 /**
999 * Cache.
1000 */
1001 cache() {
1002 orderable_scrollbar.vars = {
1003 top: {}
1004 };
1005 orderable_scrollbar.elements = {};
1006 },
1007 /**
1008 * Watch.
1009 */
1010 watch() {
1011 $(document.body).on('orderable-drawer.opened', orderable_scrollbar.trigger);
1012 $(document.body).on('orderable-tabs.changed', orderable_scrollbar.trigger);
1013 $(document.body).on('orderable-accordion.toggled', orderable_scrollbar.trigger);
1014 $(document.body).on('wc_fragments_loaded', orderable_scrollbar.trigger);
1015 },
1016 /**
1017 * Init or retrigger scrollbars.
1018 */
1019 trigger() {
1020 $('.orderable-sb-container').each(function (index, element) {
1021 const $element = $(element),
1022 scroll_id = $element.data('orderable-scroll-id');
1023 if (!orderable_scrollbar.has_scrollbar($element)) {
1024 $element.scrollBox({
1025 containerClass: 'orderable-sb-container',
1026 containerNoScrollClass: 'orderable-sb-container-noscroll',
1027 contentClass: 'orderable-sb-content',
1028 scrollbarContainerClass: 'orderable-sb-scrollbar-container',
1029 scrollBarClass: 'orderable-sb-scrollbar'
1030 });
1031 const $content = $element.find('.orderable-sb-content');
1032 if ($content.length > 0) {
1033 $content.on('scroll.scrollBox', orderable_scrollbar.log_top_position);
1034
1035 // Set scroll position.
1036 if (typeof orderable_scrollbar.vars.top[scroll_id] !== 'undefined') {
1037 $content.scrollTop(orderable_scrollbar.vars.top[scroll_id]);
1038 }
1039 }
1040 $(document.body).trigger('orderable-scrollbar.created', {
1041 element: $element,
1042 content: $content
1043 });
1044 }
1045 });
1046 $(window).trigger('resize.scrollBox');
1047 },
1048 /**
1049 * Has scrollbar already?
1050 *
1051 * @param $element
1052 * @return {boolean}
1053 */
1054 has_scrollbar($element) {
1055 return $element.find('.orderable-sb-content').length > 0;
1056 },
1057 /**
1058 * Set scrolltop position.
1059 *
1060 * @param e
1061 */
1062 log_top_position(e) {
1063 const $element = $(e.currentTarget),
1064 $container = $element.closest('.orderable-sb-container'),
1065 scroll_id = $container.data('orderable-scroll-id');
1066 orderable_scrollbar.vars.top[scroll_id] = $(e.currentTarget).scrollTop();
1067 }
1068 };
1069 $(document).ready(orderable_scrollbar.on_ready);
1070 })(jQuery, document);
1071 (function ($, document) {
1072 'use strict';
1073
1074 var orderable_tabs = {
1075 /**
1076 * On doc ready.
1077 */
1078 on_ready() {
1079 orderable_tabs.cache();
1080 orderable_tabs.watch();
1081 orderable_tabs.toggle_scroll();
1082 },
1083 /**
1084 * On resize.
1085 */
1086 on_resize() {
1087 if (typeof orderable_tabs.vars === 'undefined') {
1088 orderable_tabs.cache();
1089 }
1090 orderable_tabs.toggle_scroll();
1091 },
1092 /**
1093 * Cache.
1094 */
1095 cache() {
1096 orderable_tabs.vars = {
1097 classes: {
1098 tabs: 'orderable-tabs',
1099 tabs_list: 'orderable-tabs__list',
1100 tab_items: 'orderable-tabs__item',
1101 tab_item_active: 'orderable-tabs__item--active',
1102 tab_links: 'orderable-tabs__link',
1103 tab_arrow_right: 'orderable-tabs__arrow-right',
1104 tab_arrow_left: 'orderable-tabs__arrow-left'
1105 },
1106 dragging: false
1107 };
1108 orderable_tabs.elements = {};
1109 },
1110 /**
1111 * Watch.
1112 */
1113 watch() {
1114 $('body').on('touchstart', function () {
1115 orderable_tabs.vars.dragging = false;
1116 }).on('touchmove', function () {
1117 orderable_tabs.vars.dragging = true;
1118 });
1119 $(document.body).on('click mouseup touchend', '.' + orderable_tabs.vars.classes.tab_links, function (e) {
1120 if (orderable_tabs.vars.dragging) {
1121 return;
1122 }
1123 e.preventDefault();
1124 const $link = $(this),
1125 section_id = $link.attr('href'),
1126 $tab = $link.closest('.' + orderable_tabs.vars.classes.tab_items),
1127 $tabs = $link.closest('.' + orderable_tabs.vars.classes.tabs),
1128 $tabs_list = $tabs.find('.' + orderable_tabs.vars.classes.tabs_list),
1129 $tab_items = $tabs.find('.' + orderable_tabs.vars.classes.tab_items),
1130 tabs_args = $tabs.data('orderable-tabs'),
1131 $wrapper = $link.closest(tabs_args.wrapper),
1132 $sections = $wrapper.find(tabs_args.sections),
1133 $section = $wrapper.find(section_id);
1134 $sections.hide();
1135 $section.show();
1136 $tab_items.removeClass(orderable_tabs.vars.classes.tab_item_active);
1137 $tab.addClass(orderable_tabs.vars.classes.tab_item_active);
1138 $tabs_list.animate({
1139 scrollLeft: $tabs_list.scrollLeft() + $tab.position().left
1140 });
1141 $(document.body).trigger('orderable-tabs.changed', {
1142 tab: $tab
1143 });
1144 });
1145
1146 /**
1147 * Watch scroll position of tabs.
1148 */
1149 $('.' + orderable_tabs.vars.classes.tabs_list).on('scroll', function (e) {
1150 const $list = $(this),
1151 $wrapper = $list.parent('.' + orderable_tabs.vars.classes.tabs),
1152 $arrow_right = $list.siblings('.' + orderable_tabs.vars.classes.tab_arrow_right),
1153 $arrow_left = $list.siblings('.' + orderable_tabs.vars.classes.tab_arrow_left);
1154 if ($list[0].scrollWidth <= $wrapper.width() + $list.scrollLeft()) {
1155 $arrow_right.fadeOut();
1156 } else {
1157 $arrow_right.fadeIn();
1158 }
1159 if (0 >= $list.scrollLeft() - $arrow_left.width()) {
1160 $arrow_left.fadeOut();
1161 } else {
1162 $arrow_left.fadeIn();
1163 }
1164 });
1165
1166 /**
1167 * Stop animated scroll if user manually scrolls.
1168 */
1169 $('.' + orderable_tabs.vars.classes.tabs_list).on('wheel DOMMouseScroll mousewheel touchmove', function () {
1170 $(this).stop();
1171 });
1172
1173 /**
1174 * Click tab arrow right.
1175 */
1176 $(document).on('click', '.' + orderable_tabs.vars.classes.tab_arrow_right, function (e) {
1177 e.preventDefault();
1178 const $arrow = $(this),
1179 $wrapper = $arrow.parent(),
1180 $list = $wrapper.find('.' + orderable_tabs.vars.classes.tabs_list);
1181 $list.animate({
1182 scrollLeft: $list.scrollLeft() + $wrapper.width() * 0.5
1183 });
1184 });
1185
1186 /**
1187 * Click tab arrow left.
1188 */
1189 $(document).on('click', '.' + orderable_tabs.vars.classes.tab_arrow_left, function (e) {
1190 e.preventDefault();
1191 const $arrow = $(this),
1192 $wrapper = $arrow.parent(),
1193 $list = $wrapper.find('.' + orderable_tabs.vars.classes.tabs_list);
1194 $list.animate({
1195 scrollLeft: $list.scrollLeft() - $wrapper.width() * 0.5
1196 });
1197 });
1198 },
1199 /**
1200 * Toggle scroll arrow.
1201 */
1202 toggle_scroll() {
1203 $('.' + orderable_tabs.vars.classes.tabs).each(function (index, wrapper) {
1204 const $tabs = $(this),
1205 tabs_args = $tabs.data('orderable-tabs'),
1206 $wrapper = $tabs.closest(tabs_args.wrapper),
1207 $list = $wrapper.find('.' + orderable_tabs.vars.classes.tabs_list),
1208 $arrow_right = $wrapper.find('.' + orderable_tabs.vars.classes.tab_arrow_right),
1209 wrapper_width = $wrapper.outerWidth(),
1210 list_width = $list[0].scrollWidth;
1211 if (list_width > wrapper_width) {
1212 $arrow_right.show();
1213 } else {
1214 $arrow_right.hide();
1215 }
1216 });
1217 }
1218 };
1219 $(document).ready(orderable_tabs.on_ready);
1220 $(window).on('resize', orderable_tabs.on_resize);
1221 })(jQuery, document);
1222 let orderable_timings = {}; // Make this global so pro modules can access it.
1223
1224 (function ($, document) {
1225 'use strict';
1226
1227 orderable_timings = {
1228 /**
1229 * On doc ready.
1230 */
1231 on_ready() {
1232 orderable_timings.watch();
1233 },
1234 /**
1235 * Restore current timings.
1236 */
1237 restore() {
1238 const timings = orderable_timings.get_timings();
1239 if (!timings || !timings.date) {
1240 return;
1241 }
1242 const dateSelect = $('.orderable-order-timings__date');
1243 if (dateSelect.find('option[value="' + timings.date + '"]').length > 0) {
1244 dateSelect.val(timings.date);
1245 dateSelect.change();
1246 }
1247 if (!timings.time) {
1248 return;
1249 }
1250 const timeSelect = $('.orderable-order-timings__time');
1251 if (timeSelect.find('option[value="' + timings.time + '"]').length > 0) {
1252 timeSelect.val(timings.time);
1253 timeSelect.change();
1254 }
1255 },
1256 /**
1257 * Watch for trigger events.
1258 */
1259 watch() {
1260 $(document.body).on('wc_fragments_refreshed', function () {
1261 orderable_timings.restore();
1262 });
1263 $(document.body).on('updated_checkout', function () {
1264 orderable_timings.restore();
1265 });
1266 $(document.body).on('change', '.orderable-order-timings__date', function (event) {
1267 const $date_field = $(this),
1268 $selected = $date_field.find('option:selected'),
1269 slots = $selected.data('orderable-slots'),
1270 $time_field_wrap = $('.orderable-order-timings--time'),
1271 $time_field = $('.orderable-order-timings__time'),
1272 $first_option = $time_field.find('option').first(),
1273 $asap_option = $time_field.find('option[value="asap"]').first();
1274 const timings = orderable_timings.get_timings();
1275 timings.date = $('.orderable-order-timings__date').val();
1276 window.localStorage.setItem('orderable_timings', JSON.stringify(timings));
1277 $time_field.html($first_option);
1278 if ($asap_option) {
1279 $time_field.append($asap_option);
1280 }
1281 if (!slots) {
1282 $time_field.prop('disabled', true);
1283 $time_field_wrap.hide();
1284 return;
1285 }
1286 if ('all-day' === slots[0].value) {
1287 $time_field_wrap.hide();
1288 $time_field.prop('disabled', true);
1289 } else {
1290 $time_field.prop('disabled', false);
1291 $time_field_wrap.show();
1292 $.each(slots, function (index, slot) {
1293 $time_field.append($('<option />').attr('value', slot.value).attr('data-orderable-time-slot-id', slot?.setting_row?.time_slot_id).text(slot.formatted));
1294 });
1295 }
1296 });
1297 $(document.body).on('change', '.orderable-order-timings__time', function (event) {
1298 const timings = orderable_timings.get_timings();
1299 timings.time = $('.orderable-order-timings__time').val();
1300 window.localStorage.setItem('orderable_timings', JSON.stringify(timings));
1301 $(this).siblings('input[name="orderable_order_time_slot_id"]').val($(this).find(':selected').attr('data-orderable-time-slot-id'));
1302 });
1303 },
1304 get_timings() {
1305 return JSON.parse(window.localStorage.getItem('orderable_timings')) || {};
1306 }
1307 };
1308 $(document).ready(orderable_timings.on_ready);
1309 })(jQuery, document);
1310 (function ($, document) {
1311 'use strict';
1312
1313 var orderable_triggers = {
1314 /**
1315 * On doc ready.
1316 */
1317 on_ready() {
1318 orderable_triggers.watch();
1319 },
1320 /**
1321 * Watch for trigger events.
1322 */
1323 watch() {
1324 if ('undefined' !== typeof window.orderable_triggers_admin) {
1325 return;
1326 }
1327 $(document.body).on('click', '[data-orderable-trigger]', orderable_triggers.trigger);
1328 },
1329 /**
1330 * Fire trigger.
1331 * @param e
1332 */
1333 trigger(e) {
1334 // Prevent even bubbling up.
1335 e.stopImmediatePropagation();
1336 const $trigger_element = $(this),
1337 trigger = $trigger_element.data('orderable-trigger');
1338 if ($trigger_element.is('button') || $trigger_element.is('a')) {
1339 e.preventDefault();
1340 }
1341 $(document.body).trigger('orderable-' + trigger, [$trigger_element]);
1342 }
1343 };
1344 $(document).ready(orderable_triggers.on_ready);
1345 })(jQuery, document);
1346 /**
1347 * jQiery scrollBar Plugin
1348 * @author Falk Müller (www-falk-m.de)
1349 * Thankts to https://codepen.io/IliaSky/pen/obowmv
1350 */
1351 ;
1352 (function ($, window, document) {
1353 "use strict";
1354
1355 var pluginName = "scrollBox",
1356 defaults = {
1357 containerClass: "sb-container",
1358 containerNoScrollClass: "sb-container-noscroll",
1359 contentClass: "sb-content",
1360 scrollbarContainerClass: "sb-scrollbar-container",
1361 scrollBarClass: "sb-scrollbar"
1362 };
1363
1364 // plugin constructor
1365 function Plugin(element, options) {
1366 this.element = element;
1367 this.settings = $.extend({}, defaults, options);
1368 this._defaults = defaults;
1369 this._name = pluginName;
1370 this.init();
1371 }
1372
1373 // Avoid Plugin.prototype conflicts
1374 $.extend(Plugin.prototype, {
1375 init: function () {
1376 this.addScrollbar();
1377 this.addEvents();
1378 this.onResize();
1379 },
1380 addScrollbar: function () {
1381 $(this.element).addClass(this.settings.containerClass);
1382 this.wrapper = $("<div class='" + this.settings.contentClass + "' />");
1383 this.wrapper.append($(this.element).contents());
1384 $(this.element).append(this.wrapper);
1385 this.scollbarContainer = $("<div class='" + this.settings.scrollbarContainerClass + "' />");
1386 this.scrollBar = $("<div class='" + this.settings.scrollBarClass + "' />");
1387 this.scollbarContainer.append(this.scrollBar);
1388 $(this.element).prepend(this.scollbarContainer);
1389 },
1390 addEvents: function () {
1391 this.wrapper.on("scroll." + pluginName, $.proxy(this.onScroll, this));
1392 $(window).on("resize." + pluginName, $.proxy(this.onResize, this));
1393 this.scrollBar.on('mousedown.' + pluginName, $.proxy(this.onMousedown, this));
1394 this.scrollBar.on('touchstart.' + pluginName, $.proxy(this.onTouchstart, this));
1395 },
1396 onTouchstart: function (ev) {
1397 var me = this;
1398 ev.preventDefault();
1399 var y = me.scrollBar[0].offsetTop;
1400 var onMove = function (end) {
1401 var delta = end.touches[0].pageY - ev.touches[0].pageY;
1402 me.scrollBar[0].style.top = Math.min(me.scollbarContainer[0].clientHeight - me.scrollBar[0].clientHeight, Math.max(0, y + delta)) + 'px';
1403 me.wrapper[0].scrollTop = me.wrapper[0].scrollHeight * me.scrollBar[0].offsetTop / me.scollbarContainer[0].clientHeight;
1404 };
1405 $(document).on("touchmove." + pluginName, onMove);
1406 $(document).on("touchend." + pluginName, function () {
1407 $(document).off("touchmove." + pluginName);
1408 $(document).off("touchend." + pluginName);
1409 });
1410 },
1411 onMousedown: function (ev) {
1412 var me = this;
1413 ev.preventDefault();
1414 var y = me.scrollBar[0].offsetTop;
1415 var onMove = function (end) {
1416 var delta = end.pageY - ev.pageY;
1417 me.scrollBar[0].style.top = Math.min(me.scollbarContainer[0].clientHeight - me.scrollBar[0].clientHeight, Math.max(0, y + delta)) + 'px';
1418 me.wrapper[0].scrollTop = me.wrapper[0].scrollHeight * me.scrollBar[0].offsetTop / me.scollbarContainer[0].clientHeight;
1419 };
1420 $(document).on("mousemove." + pluginName, onMove);
1421 $(document).on("mouseup." + pluginName, function () {
1422 $(document).off("mousemove." + pluginName);
1423 $(document).off("mouseup." + pluginName);
1424 });
1425 },
1426 onResize: function () {
1427 this.wrapper.css("max-height", $(this.element).height());
1428 var wrapper_client_height = this.wrapper[0].clientHeight;
1429 this.scrollBar.css("height", this.scollbarContainer[0].clientHeight * wrapper_client_height / this.wrapper[0].scrollHeight + "px");
1430 if (this.scollbarContainer[0].clientHeight <= this.scrollBar[0].clientHeight) {
1431 $(this.element).addClass(this.settings.containerNoScrollClass);
1432 } else {
1433 $(this.element).removeClass(this.settings.containerNoScrollClass);
1434 }
1435 this.onScroll();
1436 },
1437 onScroll: function () {
1438 this.scrollBar.css("top", Math.min(this.scollbarContainer[0].clientHeight - this.scrollBar[0].clientHeight, this.scollbarContainer[0].clientHeight * this.wrapper[0].scrollTop / this.wrapper[0].scrollHeight) + "px");
1439 }
1440 });
1441
1442 // A really lightweight plugin wrapper around the constructor,
1443 // preventing against multiple instantiations
1444 $.fn[pluginName] = function (options) {
1445 return this.each(function () {
1446 if (!$.data(this, "plugin_" + pluginName)) {
1447 $.data(this, "plugin_" + pluginName, new Plugin(this, options));
1448 }
1449 });
1450 };
1451 })(jQuery, window, document);
1452 /*!
1453 * swiped-events.js - v1.1.6
1454 * Pure JavaScript swipe events
1455 * https://github.com/john-doherty/swiped-events
1456 * @inspiration https://stackoverflow.com/questions/16348031/disable-scrolling-when-touch-moving-certain-element
1457 * @author John Doherty <www.johndoherty.info>
1458 * @license MIT
1459 */
1460 !function (t, e) {
1461 "use strict";
1462
1463 "function" != typeof t.CustomEvent && (t.CustomEvent = function (t, n) {
1464 n = n || {
1465 bubbles: !1,
1466 cancelable: !1,
1467 detail: void 0
1468 };
1469 var a = e.createEvent("CustomEvent");
1470 return a.initCustomEvent(t, n.bubbles, n.cancelable, n.detail), a;
1471 }, t.CustomEvent.prototype = t.Event.prototype), e.addEventListener("touchstart", function (t) {
1472 if ("true" === t.target.getAttribute("data-swipe-ignore")) return;
1473 s = t.target, r = Date.now(), n = t.touches[0].clientX, a = t.touches[0].clientY, u = 0, i = 0;
1474 }, !1), e.addEventListener("touchmove", function (t) {
1475 if (!n || !a) return;
1476 var e = t.touches[0].clientX,
1477 r = t.touches[0].clientY;
1478 u = n - e, i = a - r;
1479 }, !1), e.addEventListener("touchend", function (t) {
1480 if (s !== t.target) return;
1481 var e = parseInt(l(s, "data-swipe-threshold", "20"), 10),
1482 o = parseInt(l(s, "data-swipe-timeout", "500"), 10),
1483 c = Date.now() - r,
1484 d = "",
1485 p = t.changedTouches || t.touches || [];
1486 Math.abs(u) > Math.abs(i) ? Math.abs(u) > e && c < o && (d = u > 0 ? "swiped-left" : "swiped-right") : Math.abs(i) > e && c < o && (d = i > 0 ? "swiped-up" : "swiped-down");
1487 if ("" !== d) {
1488 var b = {
1489 dir: d.replace(/swiped-/, ""),
1490 touchType: (p[0] || {}).touchType || "direct",
1491 xStart: parseInt(n, 10),
1492 xEnd: parseInt((p[0] || {}).clientX || -1, 10),
1493 yStart: parseInt(a, 10),
1494 yEnd: parseInt((p[0] || {}).clientY || -1, 10)
1495 };
1496 s.dispatchEvent(new CustomEvent("swiped", {
1497 bubbles: !0,
1498 cancelable: !0,
1499 detail: b
1500 })), s.dispatchEvent(new CustomEvent(d, {
1501 bubbles: !0,
1502 cancelable: !0,
1503 detail: b
1504 }));
1505 }
1506 n = null, a = null, r = null;
1507 }, !1);
1508 var n = null,
1509 a = null,
1510 u = null,
1511 i = null,
1512 r = null,
1513 s = null;
1514 function l(t, n, a) {
1515 for (; t && t !== e.documentElement;) {
1516 var u = t.getAttribute(n);
1517 if (u) return u;
1518 t = t.parentNode;
1519 }
1520 return a;
1521 }
1522 }(window, document);