1jQuery(function ($) { 2 var cart_button_selector = 3 ".wcpt-button-cart_ajax, .wcpt-button-cart_redirect, .wcpt-button-cart_refresh, .wcpt-button-cart_checkout"; 4 5 // local cache 6 window.wcpt_cache = { 7 data: {}, 8 remove: function (url) { 9 delete window.wcpt_cache.data[url]; 10 }, 11 exist: function (url) { 12 return ( 13 window.wcpt_cache.data.hasOwnProperty(url) && 14 window.wcpt_cache.data[url] !== null 15 ); 16 }, 17 get: function (url) { 18 return window.wcpt_cache.data[url]; 19 }, 20 set: function (url, cachedData, callback) { 21 window.wcpt_cache.remove(url); 22 window.wcpt_cache.data[url] = cachedData; 23 if ($.isFunction(callback)) callback(cachedData); 24 }, 25 }; 26 27 window.wcpt_product_form = {}; // product form cache 28 29 function get_device() { 30 // device 31 var device = "laptop"; // default 32 33 if ($(window).width() <= wcpt_params.breakpoints.phone) { 34 device = "phone"; 35 } else if ($(window).width() <= wcpt_params.breakpoints.tablet) { 36 device = "tablet"; 37 } 38 39 return device; 40 } 41 42 function get_child_row_device_runtime_config(sc_attrs, layoutMap, device) { 43 var raw_child_columns = {}; 44 ["laptop", "tablet", "phone"].forEach(function (dev) { 45 var raw = sc_attrs && sc_attrs[dev + "_child_row_columns"]; 46 raw_child_columns[dev] = raw ? String(raw).trim() : ""; 47 }); 48 49 var has_columns = {}; 50 ["laptop", "tablet", "phone"].forEach(function (dev) { 51 has_columns[dev] = get_layout_source_for_device(layoutMap, dev) === dev; 52 }); 53 54 var parent_of = { 55 phone: "tablet", 56 tablet: "laptop", 57 laptop: null, 58 }; 59 60 var parent_row_device = null; 61 var cursor = device; 62 while (cursor !== null) { 63 if (has_columns[cursor]) { 64 parent_row_device = cursor; 65 break; 66 } 67 cursor = parent_of[cursor]; 68 } 69 70 var settings_source = null; 71 if (raw_child_columns[device]) { 72 settings_source = device; 73 } else if (has_columns[device]) { 74 settings_source = null; 75 } else if (device === "phone" && raw_child_columns.tablet) { 76 // Keep parity with PHP: phone may inherit tablet child-row columns 77 // only when phone has no dedicated layout columns. 78 settings_source = "tablet"; 79 } 80 81 var trigger_position = device === "phone" ? "right" : "left"; 82 if (sc_attrs && sc_attrs[device + "_child_row_trigger_position"]) { 83 trigger_position = sc_attrs[device + "_child_row_trigger_position"]; 84 } 85 86 return { 87 enabled: !!(settings_source && parent_row_device), 88 parent_row_device: parent_row_device, 89 settings_source: settings_source, 90 trigger_position: trigger_position, 91 source_columns_raw: settings_source 92 ? raw_child_columns[settings_source] 93 : "", 94 }; 95 } 96 97 function child_row_device_signature(sc_attrs, layoutMap, device) { 98 var cfg = get_child_row_device_runtime_config(sc_attrs, layoutMap, device); 99 return [ 100 cfg.enabled ? "1" : "0", 101 cfg.parent_row_device || "", 102 cfg.settings_source || "", 103 cfg.trigger_position || "", 104 cfg.source_columns_raw || "", 105 ].join("|"); 106 } 107 108 function should_refetch_for_child_row_switch( 109 $container, 110 sc_attrs, 111 layoutMap, 112 current_device, 113 desired_device, 114 ) { 115 if (wcpt_is_module_disabled("child-row", $container)) { 116 return false; 117 } 118 119 var current_signature = child_row_device_signature( 120 sc_attrs, 121 layoutMap, 122 current_device, 123 ); 124 var desired_signature = child_row_device_signature( 125 sc_attrs, 126 layoutMap, 127 desired_device, 128 ); 129 130 return current_signature !== desired_signature; 131 } 132 133 // html entity encode 134 function htmlentity(string) { 135 return string.replace(/[\u00A0-\u9999<>\&]/gim, function (i) { 136 return "&#" + i.charCodeAt(0) + ";"; 137 }); 138 } 139 140 // layout handler 141 $("body").on("wcpt_layout", ".wcpt", function layout(e, data) { 142 var $wcpt = $(this), 143 $wrap = $wcpt.find(".wcpt-table-scroll-wrapper").first(), 144 id = $wcpt.attr("data-wcpt-table-id"), 145 sc_attrs = wcpt_util.get_sc_attrs($wcpt); 146 147 // Single device wrapper: if the initially rendered device doesn't match the 148 // current breakpoint, replace content with loader and fetch correct device. 149 // (We reuse the existing loader-icon â device_switch flow below.) 150 if ($wrap.length && !$wcpt.hasClass("wcpt-loading")) { 151 var desired_device = get_device(); 152 var current_device = wcpt_get_device_2($wcpt); 153 var layout_map = get_column_layout_source_map($wcpt); 154 var desired_src = get_layout_source_for_device( 155 layout_map, 156 desired_device, 157 ); 158 var current_src = get_layout_source_for_device( 159 layout_map, 160 current_device, 161 ); 162 var is_same_layout_source = 163 desired_src && current_src && desired_src === current_src; 164 var should_refetch_for_child_row = should_refetch_for_child_row_switch( 165 $wcpt, 166 sc_attrs, 167 layout_map, 168 current_device, 169 desired_device, 170 ); 171 172 if ( 173 desired_device && 174 current_device && 175 desired_device !== current_device && 176 !$(">.wcpt-device-view-loading-icon", $wrap).length 177 ) { 178 var $outer = $wrap.closest(".wcpt-table-scroll-wrapper-outer"); 179 180 // If both devices share the same effective column layout, we can usually 181 // skip a server fetch. Exception: child-row markup can still differ by 182 // device (enabled state, trigger side, source columns), so refetch when 183 // child-row signatures differ. 184 if (is_same_layout_source && !should_refetch_for_child_row) { 185 if ($outer.length) { 186 $outer 187 .removeClass("wcpt-device-" + current_device) 188 .addClass("wcpt-device-" + desired_device); 189 }
190 // Do not return: allow freeze-table, sidebar conversion, etc. to run. 191 } 192 // Only show loader + trigger device_switch when effective column 193 // layout differs (i.e. we truly need a fresh device markup). 194 if (!(is_same_layout_source && !should_refetch_for_child_row)) { 195 if ($outer.length) { 196 $outer 197 .removeClass("wcpt-device-" + current_device) 198 .addClass("wcpt-device-" + desired_device); 199 } 200 201 $wrap.html( 202 '<span class="wcpt-icon wcpt-icon-loader wcpt-device-view-loading-icon">' + 203 '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-loader">' + 204 '<line x1="12" y1="2" x2="12" y2="6"></line>' + 205 '<line x1="12" y1="18" x2="12" y2="22"></line>' + 206 '<line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line>' + 207 '<line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line>' + 208 '<line x1="2" y1="12" x2="6" y2="12"></line>' + 209 '<line x1="18" y1="12" x2="22" y2="12"></line>' + 210 '<line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line>' + 211 '<line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line>' + 212 "</svg>" + 213 "</span>", 214 ); 215 } 216 } 217 } 218 219 if ($(">.wcpt-device-view-loading-icon", $wrap).length) { 220 var url = window.location.href, 221 hash = window.location.hash, 222 query_exists = url.indexOf("?") !== -1, 223 query = "", 224 device = get_device(); 225 226 if (hash) { 227 url = url.replace(hash, ""); 228 } 229 230 if (query_exists) { 231 var replace = "&*" + id + "_device=(laptop|phone|tablet)"; 232 re = new RegExp(replace, "gm"); 233 234 url = (url.replace(re, "") + "&" + id + "_device=" + device).replace( 235 "?&", 236 "?", 237 ); 238 } else if (!sc_attrs.disable_url_update) { 239 url = url + "?" + id + "_device=" + device; 240 } 241 242 if (hash) { 243 url = url + hash; 244 } 245 246 // archive oveerride + compatible nav filter plugin = requires page refresh 247 if ($wcpt.attr("data-wcpt-sc-attrs").indexOf("_only_loop") !== -1) { 248 window.location = url; 249 return; 250 } 251 252 query = url.substr(url.indexOf("?") + 1); 253 254 if (hash) { 255 query = query.replace(hash, ""); 256 } 257 258 attempt_ajax($wcpt, query, false, "device_switch"); 259 260 return; // layout on AJAX response 261 } 262 263 // freeze tables 264 if (!wcpt_is_module_disabled("freeze-table", $wcpt)) { 265 if (!wcpt_util.get_freeze_table($wcpt)) { 266 var options = { breakpoint: {} }, 267 $table = $(".wcpt-table", $wcpt); 268 269 ["laptop", "tablet", "phone"].forEach((device) => { 270 var freeze_left_columns = sc_attrs[`${device}_freeze_left`], 271 freeze_right_columns = sc_attrs[`${device}_freeze_right`], 272 freeze_heading = sc_attrs[`${device}_freeze_heading`], 273 freeze_heading_offset = sc_attrs[`${device}_freeze_heading_offset`] 274 ? sc_attrs[`${device}_freeze_heading_offset`] 275 : sc_attrs[`${device}_scroll_offset`], 276 grab_and_scroll = sc_attrs.grab_and_scroll, 277 table_width = sc_attrs[`${device}_table_width`], 278 breakpoint_settings = { 279 // columns 280 left: freeze_left_columns ? parseInt(freeze_left_columns) : 0, 281 right: freeze_right_columns ? parseInt(freeze_right_columns) : 0, 282 283 // heading 284 // -- toggle 285 heading: !!(freeze_heading && freeze_heading !== "false"), 286 // -- offset 287 offset: freeze_heading_offset ? freeze_heading_offset : 0, 288 289 // grab and scroll 290 grab_and_scroll: !!grab_and_scroll, 291 292 // table width 293 tableWidth: !table_width ? false : table_width, 294 }; 295 296 if (device == "laptop") { 297 $.extend(options, breakpoint_settings); 298 } else { 299 options.breakpoint[wcpt_params.breakpoints[device]] = 300 breakpoint_settings; 301 } 302 }); 303 304 $table.freezeTable(options, data.source); 305 } 306 } 307 308 // convert sidebar to header 309 var device = get_device(), 310 $sidebar = $(".wcpt-left-sidebar, .wcpt-was-left-sidebar", $wcpt).not( 311 ".wcpt-nav-modal .wcpt-navigation", 312 ), 313 has_reveal_sidebar = !!$wcpt.find( 314 ".wcpt-header .wcpt-reveal-sidebar", 315 ).length; 316 317 // Reveal-sidebar keeps the left panel as an overlay â do not flatten it 318 // into the header strip on tablet/phone (that would also surface duplicates). 319 if (has_reveal_sidebar) { 320 if ($sidebar.hasClass("wcpt-was-left-sidebar")) { 321 $sidebar 322 .removeClass("wcpt-header wcpt-was-left-sidebar") 323 .addClass("wcpt-left-sidebar"); 324 } 325 } else if (device == "laptop" && $sidebar.hasClass("wcpt-was-left-sidebar")) { 326 $sidebar 327 .removeClass("wcpt-header wcpt-was-left-sidebar") 328 .addClass("wcpt-left-sidebar"); 329 } else if ( 330 device != "laptop" && 331 !$sidebar.hasClass("wcpt-was-left-sidebar") 332 ) { 333 // tablet / phone 334 $sidebar 335 .removeClass("wcpt-left-sidebar") 336 .addClass("wcpt-header wcpt-was-left-sidebar"); 337 } 338 339 // close open dropdowns 340 $(".wcpt-was-left-sidebar .wcpt-dropdown", $wcpt).removeClass("wcpt-open"); 341 342 // Hide duplicate filters when header + sidebar are flattened together 343 hide_responsive_duplicate_nav_filters($wcpt); 344 345 // checkboxes
346 var $table = wcpt_get_container_original_table($wcpt); 347 348 if ( 349 $table.data("wcpt_checked_rows") && 350 $table.data("wcpt_checked_rows").length 351 ) { 352 var $rows = $(".wcpt-row", $table); 353 354 $rows.each(function () { 355 var $this = $(this), 356 state = !!$this.data("wcpt_checked"); 357 358 $this.trigger("_wcpt_checkbox_change", state); 359 }); 360 } 361 362 // dropdown direction in single justified navigation header row 363 var $dropdown = $( 364 ".wcpt-filter-row.wcpt-ratio-flex_justified .wcpt-dropdown", 365 $wcpt, 366 ); 367 368 $dropdown.each(function () { 369 var $this = $(this), 370 $nav_row = $this.closest(".wcpt-filter-row"), 371 thisOffset = $this.offset().left, 372 navRowWidth = $nav_row.width(), 373 navRowOffset = $nav_row.offset().left, 374 relativePosition = thisOffset - navRowOffset; 375 376 if (relativePosition > navRowWidth / 2) { 377 $this.addClass("wcpt-dropdown-open-left"); 378 } 379 }); 380 }); 381 382 // resize 383 var resize_timer, 384 throttle = 250, 385 window_width; 386 387 // throttled window resize event listener 388 $(window).on("resize", window_resize); 389 390 function window_resize(e) { 391 var new_window_width = window.innerWidth; 392 if (new_window_width != window_width) { 393 window_width = new_window_width; 394 clearTimeout(resize_timer); 395 resize_timer = setTimeout(function () { 396 trigger_layout("resize"); 397 recent_orientationchange = false; 398 }, throttle); 399 } 400 } 401 402 function trigger_layout(source) { 403 $(".wcpt").trigger("wcpt_layout", { source: source }); 404 } 405 406 // every load - ajax or page load, needs this 407 function after_every_load($container) { 408 // cache new rows 409 var $new_rows = wcpt_util.get_uninit_rows($container, true); 410 411 // cache sc attrs 412 var sc_attrs = wcpt_util.get_sc_attrs($container, true); 413 414 // sortable column headings 415 // -- enable handler 416 wcpt_util.run_once_on_container( 417 $container, 418 "wcpt_sortable_headings_init", 419 function ($container) { 420 $container.on( 421 "click.wcpt_sort_by_column_headings", 422 ".wcpt-heading.wcpt-sortable", 423 window.wcpt_column_heading_sort_handler, 424 ); 425 }, 426 ); 427 // -- add wcpt-sortable class 428 $(".wcpt-heading", $container).each(function () { 429 var $this = $(this); 430 if ($this.find(".wcpt-sorting-icon").length) { 431 $this.addClass("wcpt-sortable"); 432 } 433 }); 434 435 // init cart form 436 $(".cart", $new_rows).each(function () { 437 var $this__form = $(this); 438 439 // init wc variation form 440 if ($this__form.hasClass("variations_form")) { 441 $this__form.wc_variation_form(); 442 } 443 444 // init product addons 445 if (typeof WC_PAO === "object") { 446 new WC_PAO.Form($this__form); 447 } else { 448 if ($.fn.init_addon_totals) { 449 $this__form.init_addon_totals(); 450 } 451 if (typeof wcPaoInitAddonTotals === "object") { 452 wcPaoInitAddonTotals.init($this__form); 453 } 454 } 455 456 // cart: action="*same page*" 457 $this__form.attr("action", window.location.href); 458 }); 459 460 // cart: wc measurement price calculator init 461 if (typeof wcpt_wc_mc_init_cart !== "undefined") { 462 $(".cart", $new_rows).each(wcpt_wc_mc_init_cart); 463 464 $(".wc-measurement-price-calculator-input-help", $new_rows).tipTip({ 465 attribute: "title", 466 defaultPosition: "left", 467 }); 468 } 469 470 // ultimate social media icons 471 if (typeof wcpt_sfsi_init !== "undefined") { 472 wcpt_sfsi_init(); 473 } 474 475 // snapshot / restore hooks (before prep resets variation + qty UI) 476 $container.trigger("wcpt_before_every_load"); 477 478 // trigger variation 479 prep_variation_options($new_rows); 480 481 // checkbox 482 // -- add heading cb 483 var $tables = wcpt_get_container_tables($container); 484 485 $tables.each(function () { 486 var $table = $(this), 487 $heading_row = wcpt_get_table_element( 488 ".wcpt-heading-row", 489 $table, 490 ).last(),
491 $cb = wcpt_get_table_element( 492 ".wcpt-cart-checkbox[data-wcpt-heading-enabled]", 493 $table, 494 ), 495 col_index = []; 496 497 $cb.each(function () { 498 var $this = $(this), 499 _col_index = $this.closest(".wcpt-cell").index(); 500 501 if (-1 == col_index.indexOf(_col_index)) { 502 col_index.push(_col_index); 503 } 504 }); 505 506 $.each(col_index, function (key, index) { 507 var $heading = $("th", $heading_row).eq(index); 508 $heading_row.removeClass("wcpt-hide"); // in case it was disabled: had no elements 509 if (!$(".wcpt-cart-checkbox-heading", $heading).length) { 510 $heading.prepend( 511 '<input type="checkbox" class="wcpt-cart-checkbox-heading" />', 512 ); 513 } 514 }); 515 }); 516 517 // background color 518 if (sc_attrs.checked_row_background_color) { 519 $("style", $container) 520 .first() 521 .append( 522 "#" + 523 $container.attr("id") + 524 " .wcpt-row--checked .wcpt-cell, #" + 525 $container.attr("id") + 526 " .wcpt-row--checked + .wcpt-child-row {background: " + 527 sc_attrs.checked_row_background_color + 528 "! important;}", 529 ); 530 } 531 532 // multirange 533 $(".wcpt-range-slider", $container).each(function () { 534 wcpt__multirange(this); 535 }); 536 537 $(".wcpt-range-input-min", $container).each(function () { 538 var $this = $(this), 539 val = $this.val(), 540 min = parseFloat($this.attr("min")); 541 542 if (val < min) { 543 $this.val(min); 544 } 545 }); 546 547 $(".wcpt-range-input-max", $container).each(function () { 548 var $this = $(this), 549 val = parseFloat($this.val()), 550 max = $this.attr("max"); 551 552 if (val > max) { 553 $this.val(max); 554 } 555 }); 556 557 // wpc smart compare 558 if (typeof wooscpGetCookie == "function" && typeof wooscpVars == "object") { 559 var compare_items__string = wooscpGetCookie( 560 "wooscp_products_" + wooscpVars.user_id, 561 ); 562 if (compare_items__string) { 563 var compare_items = compare_items__string.split(","); 564 compare_items.forEach(function (item) { 565 $(".wooscp-btn-" + item, $new_rows).each(function () { 566 var $this = $(this); 567 $this.addClass("wooscp-btn-added"); 568 $this.text(wooscpVars.button_text_added); 569 }); 570 }); 571 } 572 } 573 574 // @TODO: optimize for infinite scroll 575 576 // #start 577 578 // -- nav filter feedback 579 nav_filter_feedback($container.find(".wcpt-navigation")); 580 581 // -- sonaar integration 582 sonaar_player_auto_status(); 583 584 // -- hide empty columns 585 hide_empty_columns($container); 586 587 // -- initialize wp media player 588 if (window.wp && window.wp.mediaelement) { 589 window.wp.mediaelement.initialize(); 590 } 591 592 // #end 593 594 // url update 595 if (!sc_attrs.disable_url_update) { 596 wcpt_util.update_url( 597 $container.attr("data-wcpt-query-string"), 598 $container, 599 ); 600 } 601 602 // trigger after load event 603 $container.trigger("wcpt_after_every_load").trigger("wcpt_after_ajax"); 604 605 // register init rows 606 $new_rows.addClass("wcpt-row--init"); 607 608 // trigger cart and its view 609 if (window.wcpt_cart_result_cache) { 610 wcpt_cart({ 611 payload: { 612 use_cache: true, 613 }, 614 }); 615 } 616 617 // layout 618 $container.trigger("wcpt_layout", { source: "after_every_load" }); 619 } 620 621 function wcpt_get_container_tables($container) {
622 return wcpt_get_shell_element( 623 ".wcpt-table:not(.frzTbl-clone-table)", 624 ".wcpt", 625 $container, 626 ); 627 } 628 629 function wcpt_get_table_element(element_selector, $table) { 630 return wcpt_get_shell_element( 631 element_selector, 632 ".wcpt-table:not(.frzTbl-clone-table)", 633 $table, 634 ); 635 } 636 637 function wcpt_get_shell_element(element_selector, shell_selector, $shell) { 638 return $(element_selector, $shell).filter(function () { 639 var $this = $(this); 640 return $this.closest(shell_selector).is($shell); 641 }); 642 } 643 644 // hide empty columns 645 function hide_empty_columns($container) { 646 var sc_attrs = get_sc_attrs($container); 647 648 if (!sc_attrs.hide_empty_columns) { 649 return; 650 } 651 652 var $table = wcpt_get_container_original_table($container); 653 654 $(".wcpt-cell", $table).removeClass("wcpt-hide"); 655 $(".wcpt-heading", $table).removeClass("wcpt-hide"); 656 $(".wcpt-child-row__element", $table).removeClass("wcpt-hide"); 657 658 var column_indexes = []; 659 $table 660 .find(".wcpt-row:not(.wcpt-child-row)") 661 .first() 662 .find(".wcpt-cell") 663 .each(function () { 664 var col_index = $(this).attr("data-wcpt-column-index"); 665 if (typeof col_index !== "undefined") { 666 column_indexes.push(col_index); 667 } 668 }); 669 670 column_indexes.forEach(function (col_index) { 671 var $cells = $table.find( 672 '.wcpt-cell[data-wcpt-column-index="' + col_index + '"]', 673 ); 674 675 if ( 676 $cells.length && 677 $cells.filter(":empty").length === $cells.length 678 ) { 679 $cells 680 .add( 681 $table.find( 682 '.wcpt-heading[data-wcpt-column-index="' + col_index + '"]', 683 ), 684 ) 685 .add( 686 $table.find(".wcpt-child-row__element--column-" + col_index), 687 ) 688 .addClass("wcpt-hide"); 689 } 690 }); 691 692 // Child rows: hide elements with no cell content (headings alone don't count). 693 $table.find(".wcpt-child-row__element").each(function () { 694 var $element = $(this); 695 696 if (wcpt_child_row_element_content_is_empty($element)) { 697 $element.addClass("wcpt-hide"); 698 } 699 }); 700 } 701 702 function wcpt_child_row_element_content_is_empty($element) { 703 var $content = $element.find(".wcpt-child-row__element__content"); 704 705 if ($.trim($content.text())) { 706 return false; 707 } 708 709 return !$content.find( 710 "img, svg, video, iframe, input, button, select, textarea, table, ul, ol", 711 ).length; 712 } 713 714 // lazy load 715 function lazy_load_start() { 716 if (!window.wcpt_lazy_loaded) { 717 $(".wcpt-lazy-load").each(function () { 718 var $this = $(this), 719 table_id = $this.attr("data-wcpt-table-id"), 720 // Prefer data-wcpt-query-string (set by wcpt_get_container_attributes). 721 // Fall back to legacy data-wcpt-query for older markup. 722 query = 723 $this.attr("data-wcpt-query-string") || 724 $this.attr("data-wcpt-query") || 725 ""; 726 727 if (query.charAt(0) === "?") { 728 query = query.substring(1); 729 } 730 731 $this 732 .addClass("wcpt wcpt-" + table_id) 733 .removeClass("wcpt-lazy-load") 734 .attr("id", "wcpt-" + table_id); 735 window.wcpt_attempt_ajax($this, query, false, "lazy_load"); 736 }); 737 window.wcpt_lazy_loaded = true; 738 } 739 } 740 741 // get rows including freeze 742 function get_product_rows($elm) { 743 var $row = $elm.closest(".wcpt-row"), 744 product_id = $row.attr("data-wcpt-product-id"), 745 variation_id = $row.attr("data-wcpt-variation-id"), 746 $scroll_wrapper = $elm.closest(".wcpt-table-scroll-wrapper-outer"), 747 row_selector; 748 749 if (variation_id) { 750 row_selector = 751 '[data-wcpt-variation-id="' + 752 variation_id + 753 '"].wcpt-row.wcpt-product-type-variation'; 754 } else { 755 row_selector = 756 '[data-wcpt-product-id="' + 757 product_id + 758 '"].wcpt-row:not(.wcpt-product-type-variation)'; 759 } 760 761 if ($scroll_wrapper && $scroll_wrapper.length) { 762 return $scroll_wrapper.find(row_selector); 763 } 764 765 return $(row_selector); 766 } 767 768 // button click listener 769 $("body").on("click", ".wcpt-button", button_click_handler); 770 771 function button_click_handler(e) { 772 var $button = $(this), 773 link_code = $button.attr("data-wcpt-link-code"), 774 $product_rows = get_product_rows($button), 775 product_id = $product_rows.attr("data-wcpt-product-id"), 776 is_variable = $product_rows.hasClass("wcpt-product-type-variable"), 777 complete_match = $product_rows.data("wcpt_complete_match"), 778 is_variation = $product_rows.hasClass("wcpt-product-type-variation"), 779 is_composite = $product_rows.hasClass("wcpt-product-type-composite"), 780 is_bundle = $product_rows.hasClass("wcpt-product-type-woosb"), 781 has_addons = $product_rows.hasClass("wcpt-product-has-addons"), 782 has_measurement = $product_rows.hasClass("wcpt-product-has-measurement"), 783 has_nyp = $product_rows.hasClass("wcpt-product-has-name-your-price"), 784 qty = "", 785 params = { 786 payload: { 787 products: {}, 788 variations: {}, 789 attributes: {}, 790 addons: {}, 791 measurement: {}, 792 nyp: {}, // name your price 793 }, 794 }; 795 796 if ($("body").hasClass("wcpt-photoswipe-visible")) { 797 e.preventDefault(); 798 return; 799 } 800 801 if ( 802 -1 !== 803 $.inArray(link_code, [ 804 "product_link", 805 "external_link", 806 "custom_field", 807 "custom_field_media_id", 808 "custom_field_acf", 809 "custom", 810 ]) 811 ) { 812 return; 813 } 814 815 e.preventDefault(); 816 817 // validation 818 // -- variable product 819 if (is_variable) { 820 var variation_found = $product_rows.data("wcpt_variation_found"), 821 variation_selected = $product_rows.data("wcpt_variation_selected"), 822 variation_available = $product_rows.data("wcpt_variation_available"), 823 variation_ops = $product_rows.data("wcpt_variation_ops"); 824 825 // if row has variation selection options but customer did not make selections, show error 826 if ( 827 variation_ops && 828 (!variation_selected || !variation_found || !variation_available) 829 ) { 830 return; 831 } 832 } 833 834 // -- disabled 835 if (!is_variable && $button.hasClass("wcpt-disabled")) { 836 return; 837 } 838 839 // -- name your price 840 if (has_nyp) {
841 var $nyp = get_nyp_input_element($product_rows); 842 if ($nyp.length) { 843 var error = false, 844 name = $nyp.attr("data-wcpt-product-name"), 845 min = $nyp.attr("min"), 846 max = $nyp.attr("max"); 847 848 if (!$nyp.val()) { 849 error = wcpt_nyp_error_message_templates["empty"]; 850 } else if (min && $nyp.val() < parseFloat(min)) { 851 error = wcpt_nyp_error_message_templates["minimum_js"].replace( 852 "%%MINIMUM%%", 853 woocommerce_nyp_format_price( 854 min, 855 woocommerce_nyp_params.currency_format_symbol, 856 true, 857 ), 858 ); 859 } else if (max && $nyp.val() > parseFloat(max)) { 860 error = wcpt_nyp_error_message_templates["maximum_js"].replace( 861 "%%MAXIMUM%%", 862 woocommerce_nyp_format_price( 863 max, 864 woocommerce_nyp_params.currency_format_symbol, 865 true, 866 ), 867 ); 868 } 869 870 if (error) { 871 alert(error); 872 return; 873 } 874 } 875 } 876 877 // prepare params 878 879 // -- quantity 880 var $wcpt_qty = $( 881 ".wcpt-quantity input.qty, .wcpt-quantity > select.wcpt-qty-select", 882 $product_rows, 883 ), 884 $wc_qty = $(".cart .qty", $product_rows); 885 886 if ($wc_qty.length) { 887 // from WooCommerce form's qty field 888 qty = $wc_qty.val(); 889 } 890 891 if ($wcpt_qty.length) { 892 // from WCPT's own qty element 893 var val = parseFloat($wcpt_qty.val()); 894 if (isNaN(val) || !parseFloat($wcpt_qty.val())) { 895 $wcpt_qty 896 .filter("input") 897 .first() 898 .each(function () { 899 var $this = $(this), 900 min = $this.attr("data-wcpt-min"); 901 902 $this.val(min); 903 limit_qty_controller($this.parent("wcpt-quantity")); 904 val = $this.val(); 905 }); 906 } 907 908 qty = val; 909 } 910 911 params.payload.products[product_id] = qty; 912 913 // -- addons (only when the addon UI is actually printed & visible in the row; 914 // select variation "attribute dropdowns" reuses cart_form markup and must not 915 // count as having addons already collected)
916 var has_inline_addons_form = wcpt_row_has_inline_addons_form($product_rows), 917 addons = {}; 918 if (has_addons && has_inline_addons_form) { 919 addons = wcpt_get_addons($product_rows); 920 if (!$.isEmptyObject(addons)) { 921 params.payload.addons[product_id] = addons; 922 } 923 } 924 925 // -- measurement 926 if (has_measurement) { 927 var measurement = wcpt_get_measurement($product_rows); 928 if (!$.isEmptyObject(measurement)) { 929 params.payload.measurement[product_id] = measurement; 930 } 931 } 932 933 // -- name your price 934 if (has_nyp) { 935 var nyp = wcpt_get_nyp($product_rows); 936 if (nyp) { 937 params.payload.nyp[product_id] = nyp; 938 } 939 } 940 941 // -- variation 942 if (is_variation) { 943 var variation_id = $product_rows.attr("data-wcpt-variation-id"), 944 variation_attributes = JSON.parse( 945 $product_rows.attr("data-wcpt-variation-attributes"), 946 ), 947 $missing_attribute_select = $( 948 ".wcpt-select-variation-attribute-term", 949 $product_rows, 950 ); 951 952 if ($missing_attribute_select.length) { 953 $missing_attribute_select.each(function () { 954 var $this = $(this), 955 attribute = $this.attr("data-wcpt-attribute"), 956 term = $this.val(); 957 958 if (term) { 959 variation_attributes[attribute] = term; 960 } 961 }); 962 } 963 964 if (typeof params.payload.variations[product_id] === "undefined") { 965 params.payload.variations[product_id] = {}; 966 } 967 params.payload.variations[product_id][variation_id] = qty; 968 params.payload.attributes[variation_id] = variation_attributes; 969 } else if ($product_rows.hasClass("wcpt-product-type-variable")) { 970 var variation_id = $product_rows.data("wcpt_variation_id"), 971 variation_attributes = $product_rows.data("wcpt_attributes"); 972 973 if (variation_id) { 974 if (typeof params.payload.variations[product_id] === "undefined") { 975 params.payload.variations[product_id] = {}; 976 } 977 params.payload.variations[product_id][variation_id] = qty; 978 } 979 980 if (variation_attributes) { 981 params.payload.attributes[variation_id] = variation_attributes; 982 } 983 } 984 985 // prepare 'ajax_data' (required for non-AJAX req, submit over POST) 986 var ajax_data = { 987 action: "wcpt_add_to_cart", 988 "add-to-cart": $product_rows.attr("data-wcpt-product-id"), 989 product_id: product_id, 990 quantity: qty, 991 }; 992 993 // -- addons 994 if (has_addons && has_inline_addons_form && !$.isEmptyObject(addons)) { 995 $.extend(ajax_data, addons); 996 } 997 998 // -- measurement (submit via post) 999 if (has_measurement) { 1000 var measurement = wcpt_get_measurement($product_rows); 1001 if (!$.isEmptyObject(measurement)) { 1002 $.extend(ajax_data, measurement); 1003 } 1004 } 1005 1006 // -- name your price (submit via post) 1007 if (has_nyp) { 1008 var nyp = wcpt_get_nyp($product_rows); 1009 if (nyp) { 1010 ajax_data.nyp = nyp; 1011 } 1012 } 1013 1014 // -- variation (submit via post) 1015 if (is_variable || is_variation) { 1016 if (variation_id) { 1017 ajax_data.variation_id = variation_id; 1018 } 1019 1020 if (variation_attributes) { 1021 $.extend(ajax_data, variation_attributes); 1022 } 1023 } 1024 1025 // receive notices from server? 1026 ajax_data.return_notice = link_code == "cart_ajax"; 1027 1028 // modal required 1029 if ( 1030 is_composite || 1031 is_bundle || 1032 (is_variable && !complete_match) || 1033 (is_variation && is_incomplete_variation(variation_attributes)) || 1034 (has_addons && !has_inline_addons_form) || 1035 (has_measurement && !params.payload.measurement[product_id]) || 1036 (has_nyp && !params.payload.nyp[product_id]) 1037 ) { 1038 // deploy modal immediately if it's in cache 1039 if (typeof window.wcpt_product_form[product_id] !== "undefined") { 1040 deploy_product_form_modal( 1041 window.wcpt_product_form[product_id], 1042 $button, 1043 ajax_data, 1044 ); 1045 1046 // else fetch modal from server and deploy 1047 } else { 1048 ajax_data.action = "wcpt_get_product_form_modal";
1049 ajax_data.lang = wcpt_i18n.lang; 1050 delete ajax_data["add-to-cart"]; 1051 1052 $.ajax({ 1053 url: wcpt_params.wc_ajax_url.replace( 1054 "%%endpoint%%", 1055 "wcpt_get_product_form_modal", 1056 ), 1057 method: "POST", 1058 beforeSend: function () { 1059 window.wcpt_modal__last_requested_product_id = product_id; 1060 deploy_loading_modal(); 1061 }, 1062 data: ajax_data, 1063 }).done(function (response) { 1064 window.wcpt_product_form[product_id] = response; 1065 1066 if (product_id === window.wcpt_modal__last_requested_product_id) { 1067 // skip if req. superseded 1068 $(".wcpt-product-form-loading-modal").trigger("wcpt_close"); // close loading modal 1069 deploy_product_form_modal(response, $button, ajax_data); 1070 } 1071 }); 1072 } 1073 1074 return false; 1075 } 1076 1077 // all required info already available, no need for modal 1078 if (link_code == "cart_ajax") { 1079 wcpt_cart(params); 1080 } else { 1081 submit_via_post($button.attr("href"), ajax_data); 1082 } 1083 } 1084 1085 function deploy_product_form_modal(markup, $button, ajax_data) { 1086 var $modal = $(markup); 1087 $modal.appendTo("body"); 1088 $("body").addClass("wcpt-modal-on"); 1089 prep_product_form($modal, $button, ajax_data); 1090 $("body").trigger("wcpt_product_modal_ready"); 1091 } 1092 1093 function deploy_loading_modal() { 1094 var $loading_modal = $($("#tmpl-wcpt-product-form-loading-modal").html()); 1095 $("body").append($loading_modal); 1096 $loading_modal.on("wcpt_close", function () { 1097 $loading_modal.remove(); 1098 }); 1099 } 1100 1101 function is_incomplete_variation(variation_attributes) { 1102 var is_incomplete_variation = false; 1103 $.each(variation_attributes, function (key, value) { 1104 if (!value) { 1105 is_incomplete_variation = true; 1106 return false; 1107 } 1108 }); 1109 1110 return is_incomplete_variation; 1111 } 1112 1113 function submit_via_post(href, data) { 1114 // redirect by form 1115 var $form = $( 1116 '<form method="POST" action="' + 1117 href + 1118 '" style="display: none;"></form>', 1119 ); 1120 $.each(data, function (key, val) { 1121 if (key == "action") return; // continue 1122 var $input = $('<input type="hidden" name="' + key + '" value="" />'); 1123 $input.val(val); 1124 $form.append($input); 1125 }); 1126 $form.append('<input type="hidden" name="wcpt_request" value="true" />'); 1127 $form.appendTo($("body")).submit(); 1128 } 1129 1130 function prep_product_form($modal, $button, pre_select) { 1131 var link_code = $button.attr("data-wcpt-link-code"), 1132 href = link_code == "cart_ajax" ? "" : $button.attr("href"); 1133 1134 $modal.on("wcpt_close", function () { 1135 $modal.remove(); 1136 $("body").removeClass("wcpt-modal-on"); 1137 }); 1138 1139 $(".cart", $modal).each(function () { 1140 var $form = $(this); 1141 1142 if ($form.hasClass("variations_form")) { 1143 $form.wc_variation_form(); 1144 } else { 1145 // simple product (probably with addon or measurement) 1146 $form.append( 1147 '<input name="add-to-cart" type="hidden" value="' + 1148 pre_select["product_id"] + 1149 '">', 1150 ); 1151 } 1152 1153 // init addons 1154 if (typeof WC_PAO === "object") { 1155 new WC_PAO.Form($form); 1156 } else { 1157 if ($.fn.init_addon_totals) { 1158 $form.init_addon_totals(); 1159 } 1160 if (typeof wcPaoInitAddonTotals === "object") { 1161 wcPaoInitAddonTotals.init($form); 1162 } 1163 } 1164 1165 // init measurement 1166 if (typeof wcpt_wc_mc_init_cart !== "undefined") { 1167 $form.each(wcpt_wc_mc_init_cart); 1168 } 1169 1170 // cart: name your price 1171 if (typeof jQuery.fn.wc_nyp_form !== "undefined") { 1172 $form.wc_nyp_form(); 1173 } 1174 1175 $form.attr("action", href); 1176 1177 $(".qty", $form).attr("autocomplete", "off"); 1178 1179 if (pre_select) { 1180 $.each(pre_select, function (key, val) { 1181 var $control = $form.find("[name='" + key + "']"); 1182 if ($control.is("input.qty")) { 1183 // working on input 1184 val = parseFloat(val); 1185 var min = $control.attr("min") 1186 ? parseFloat($control.attr("min")) 1187 : 0; 1188 var max = $control.attr("max") 1189 ? parseFloat($control.attr("max")) 1190 : false; 1191 1192 // respect min 1193 if (val < min || isNaN(val)) { 1194 val = min; 1195 } 1196 1197 // respect max 1198 if (max && val > max) { 1199 val = max; 1200 } 1201 } 1202 $control.val(val); 1203 }); 1204 } 1205 1206 // try and apply quantity on default variation 1207 if (pre_select.quantity) { 1208 $form.one("show_variation", function () { 1209 var $form_qty = $(".qty", $form), 1210 min = $form_qty.attr("min"), 1211 max = $form_qty.attr("max"); 1212 if ( 1213 (!min || min <= pre_select.quantity) && 1214 (!max || max >= pre_select.quantity) 1215 ) { 1216 $form_qty.val(pre_select.quantity); 1217 } 1218 }); 1219 } 1220 1221 if (link_code == "cart_ajax") { 1222 $form.on("submit", function (e) { 1223 e.preventDefault(); 1224 1225 var external_payload = {}; 1226 1227 $.each($form.serializeArray(), function (i, field) { 1228 if (typeof external_payload[field.name] === "undefined") { 1229 external_payload[field.name] = field.value; 1230 } else { 1231 // should be array 1232 if (typeof external_payload[field.name] !== "object") { 1233 external_payload[field.name] = [external_payload[field.name]]; 1234 } 1235 external_payload[field.name].push(field.value); 1236 } 1237 }); 1238 1239 wcpt_cart({ 1240 external_payload: external_payload, 1241 payload: { variation_form: true }, 1242 }); 1243 1244 $modal.trigger("wcpt_close"); 1245 }); 1246 } 1247 1248 // reset qty in row 1249 var $rows = wcpt_get_sibling_rows($button.closest(".wcpt-row")); 1250 $rows 1251 .find(".qty[data-wcpt-return-to-initial=1]") 1252 .val(0) 1253 .first() 1254 .trigger("change"); 1255 });
1256 } 1257 1258 function disable_button($button, add_condition) { 1259 if (add_condition) { 1260 $button.addClass(add_condition); 1261 } 1262 1263 $button.addClass("wcpt-disabled"); 1264 } 1265 1266 function enable_button($button, clear_condition) { 1267 if (clear_condition) { 1268 $button.removeClass(clear_condition); 1269 } 1270 1271 if ( 1272 // list of conditions 1273 !$button.hasClass("wcpt-all-variations-out-of-stock") && 1274 !$button.hasClass("wcpt-variation-out-of-stock") && 1275 !$button.hasClass("wcpt-no-variation-selected") && 1276 !$button.hasClass("wcpt-quantity-input-error") && 1277 !$button.hasClass("wcpt-out-of-stock") 1278 ) { 1279 $button.removeClass("wcpt-disabled"); 1280 } 1281 } 1282 1283 function loading_badge_on_button($button) { 1284 disable_button($button); 1285 if (!$button.find(".wcpt-cart-badge-refresh").length) { 1286 var svg = 1287 '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-loader" color="#384047"><line x1="12" y1="2" x2="12" y2="6"></line><line x1="12" y1="18" x2="12" y2="22"></line><line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line><line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line><line x1="2" y1="12" x2="6" y2="12"></line><line x1="18" y1="12" x2="22" y2="12"></line><line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line><line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line></svg>'; 1288 $button.append('<i class="wcpt-cart-badge-refresh">' + svg + "</i>"); 1289 } 1290 } 1291 1292 function add_count_badge_to_button(in_cart, $button) { 1293 if (!parseFloat(in_cart)) { 1294 $(".wcpt-cart-badge-number, .wcpt-cart-badge-refresh", $button).remove(); 1295 return; 1296 } 1297 1298 // if( $button.closest('.wcpt-row').hasClass('wcpt-sold-individually') ){ 1299 // in_cart = 'â'; 1300 // } 1301 1302 if (!$button.find(".wcpt-cart-badge-number").length) { 1303 $button.append('<i class="wcpt-cart-badge-number">' + in_cart + "</i>"); 1304 } else { 1305 $button.find(".wcpt-cart-badge-number").html(in_cart); 1306 } 1307 1308 if ($button.find(".wcpt-cart-badge-refresh").length) { 1309 $button.find(".wcpt-cart-badge-refresh").remove(); 1310 } 1311 } 1312 1313 // search 1314 // -- submit 1315 $("body").on("click", ".wcpt-search-submit", search_submit); 1316 $("body").on("keydown", ".wcpt-search-input", search_submit); 1317 function search_submit(e) { 1318 var $this = $(this), 1319 $search = $this.closest(".wcpt-search"), 1320 $input = $search.find(".wcpt-search-input"), 1321 table_id = $search.attr("data-wcpt-table-id"), 1322 $container = $("#wcpt-" + table_id), 1323 $nav_modal = $this.closest(".wcpt-nav-modal"), 1324 $nav = $this.closest(".wcpt-navigation"), 1325 keywords = $input.val().trim(), 1326 previous_keywords = $input.attr("data-wcpt-value"), 1327 query = $input.attr("name") + "=" + encodeURIComponent(keywords), 1328 $wrapper = $input.closest(".wcpt-search-wrapper"), 1329 append = !$wrapper.hasClass("wcpt-search--reset-others"); 1330 1331 if (previous_keywords === keywords) { 1332 return; 1333 } 1334 1335 if ( 1336 // submit button is clicked 1337 ($(e.target).closest(".wcpt-search-submit").length && 1338 e.type == "click") || // enter key pressed on input 1339 ($(e.target).is(".wcpt-search-input") && 1340 e.type == "keydown" && 1341 (e.keyCode == 13 || e.which == 13)) 1342 ) { 1343 if ($nav_modal.length) { 1344 $(".wcpt-nm-apply").click(); 1345 return; 1346 } 1347 1348 if (append) { 1349 $nav.trigger("change"); 1350 } else { 1351 // reset other filters 1352 attempt_ajax($container, query, append, "filter"); 1353 } 1354 1355 if ($nav_modal.length) { 1356 $nav_modal.trigger("wcpt_close"); 1357 } 1358 } 1359 } 1360 // -- clear 1361 $("body").on("click", ".wcpt-search-clear", function (e) { 1362 var $this = $(this), 1363 $search = $this.closest(".wcpt-search"), 1364 $input = $search.find(".wcpt-search-input"), 1365 table_id = $search.attr("data-wcpt-table-id"), 1366 $container = $("#wcpt-" + table_id), 1367 $nav_modal = $this.closest(".wcpt-nav-modal"), 1368 query = "&" + $input.attr("name") + "=", 1369 append = true; 1370 $input.val(""); 1371 1372 if ($nav_modal.length) { 1373 $(".wcpt-nm-apply").click(); 1374 return; 1375 } 1376 1377 attempt_ajax($container, query, append, "filter"); 1378 1379 if ($nav_modal.length) { 1380 $nav_modal.trigger("wcpt_close"); 1381 } 1382 }); 1383 1384 // download button - responsive fix 1385 if (wcpt_params.initial_device !== "laptop") { 1386 $("body").on("click", ".wcpt-button[download]", function (e) { 1387 e.preventDefault(); 1388 var $this = $(this), 1389 url = $this.attr("href"); 1390 1391 if (!url) { 1392 return; 1393 } 1394 1395 // Masked stream URLs: download in a hidden iframe to avoid a blank tab 1396 // and keep the real remote URL off the address bar. 1397 if (url.indexOf("wcpt_stream=") !== -1) { 1398 var iframe = document.createElement("iframe"); 1399 iframe.style.display = "none"; 1400 iframe.setAttribute("aria-hidden", "true"); 1401 iframe.src = url; 1402 document.body.appendChild(iframe); 1403 setTimeout(function () { 1404 if (iframe.parentNode) { 1405 iframe.parentNode.removeChild(iframe); 1406 } 1407 }, 120000); 1408 return; 1409 } 1410 1411 window.open(url, "_blank", false); 1412 });
1413 } 1414 1415 // dropdown / tooltip 1416 1417 // currently assigned trigger ev 1418 // -- default 1419 window.wcpt_global_tooltip_trigger_mode = "hover"; // hover (default) | click 1420 // -- switch to click 1421 $(window).on("touchstart", function () { 1422 window.wcpt_global_tooltip_trigger_mode = "click"; 1423 }); 1424 // -- switch to hover 1425 $(window).on("resize", function () { 1426 window.wcpt_global_tooltip_trigger_mode = "hover"; 1427 }); 1428 1429 var target_selector = 1430 ".wcpt-dropdown, .wcpt-tooltip:not(.wcpt-tooltip--inside-portal)", 1431 $body = $("body"); 1432 1433 $body.on("mouseenter", target_selector, dropdown_hover_open); 1434 $body.on("mouseleave", target_selector, dropdown_hover_close); 1435 $body.on("click", dropdown_touch_toggle); 1436 1437 function get_event_type_for_dropdown($dropdown) { 1438 var is_tooltip = !!$dropdown.closest(".wcpt-tooltip").length, 1439 is_in_sidebar = !!$dropdown.closest(".wcpt-left-sidebar").length; 1440 1441 // click ev type is forced 1442 if ( 1443 // -- in dropdown options 1444 $dropdown.hasClass("wcpt-tooltip--open-on-click") || 1445 // -- we are on touchscreen 1446 wcpt_global_tooltip_trigger_mode == "click" 1447 ) { 1448 return "click"; 1449 } 1450 1451 // filters in sidebar alway use click 1452 if (!is_tooltip && is_in_sidebar) { 1453 return "click"; 1454 } 1455 1456 return "hover"; 1457 } 1458 1459 function dropdown_hover_open(e) { 1460 var $this = $(this); 1461 1462 if ("hover" !== get_event_type_for_dropdown($this)) { 1463 return; 1464 } 1465 1466 // hover intent 1467 // if( $this.hasClass('wcpt-tooltip--hover-intent-enabled') ){ 1468 var clear_timeout = setTimeout(function () { 1469 // if( ! $this.is(':hover') ) return; 1470 $this.addClass("wcpt-open"); 1471 fix_tooltip_position($this); 1472 }, 200); 1473 1474 $this.data("wcpt_hover_intent_clear_timeout", clear_timeout); 1475 } 1476 1477 function dropdown_hover_close(e) { 1478 var $this = $(this); 1479 1480 if ("hover" !== get_event_type_for_dropdown($this)) { 1481 return; 1482 } 1483 1484 // hover intent 1485 var clear_timeout = $this.data("wcpt_hover_intent_clear_timeout"); 1486 if (clear_timeout) { 1487 clearTimeout(clear_timeout); 1488 } 1489 1490 $this.removeClass("wcpt-open"); 1491 } 1492 1493 function dropdown_touch_toggle(e) { 1494 var $target = $(e.target), 1495 container_selector = ".wcpt-dropdown, .wcpt-tooltip", 1496 content_selector = 1497 "> .wcpt-dropdown-menu, > .wcpt-tooltip-content-wrapper > .wcpt-tooltip-content", 1498 $dropdown = $target.closest(container_selector), 1499 $content = $dropdown.find(content_selector), 1500 $body = $("body"); 1501 1502 // clicked outside any tootltip / filter 1503 if (!$dropdown.length) { 1504 // close all dropdowns or tooltips not in nav modal or sidebar 1505 if (!$target.closest(".wcpt-nav-modal").length) { 1506 $body.find(container_selector).each(function () { 1507 if ( 1508 $(this).hasClass("wcpt-tooltip") || 1509 (!$(this).closest(".wcpt-nav-modal").length && 1510 !$(this).closest(".wcpt-left-sidebar").length) 1511 ) { 1512 $(this).removeClass("wcpt-open"); 1513 } 1514 }); 1515 } 1516 1517 return; 1518 } 1519 1520 if ("click" !== get_event_type_for_dropdown($dropdown)) { 1521 return; 1522 } 1523 1524 // clicked in dropdown 1525 if ($dropdown.length) { 1526 // -- clicked in content 1527 if ($target.closest($content).length) { 1528 // return; 1529 // -- clicked on trigger 1530 } else { 1531 // close all others 1532 1533 // -- except parents and other open sidebar filters 1534 var $parents = $dropdown.parents(container_selector), 1535 $sidebar_filters = $(".wcpt-left-sidebar .wcpt-dropdown"); 1536 1537 $body 1538 .find(container_selector) 1539 .not($dropdown.add($parents).add($sidebar_filters)) 1540 .removeClass("wcpt-open"); 1541 1542 $dropdown.toggleClass("wcpt-open"); 1543 1544 // close all children 1545 if (!$dropdown.hasClass("wcpt-open")) { 1546 $dropdown.find(container_selector).removeClass("wcpt-open"); 1547 } 1548 1549 // popup enabled 1550 if ($dropdown.hasClass("wcpt-tooltip--popup-enabled")) { 1551 if ($dropdown.hasClass("wcpt-open")) { 1552 // need to run open logic earlier 1553 $body.addClass("wcpt-tooltip-popup-displayed"); 1554 } else { 1555 $body.removeClass("wcpt-tooltip-popup-displayed"); 1556 } 1557 } 1558 } 1559 1560 fix_tooltip_position($dropdown); 1561 } 1562 } 1563 1564 function fix_tooltip_position($tooltip) { 1565 // correct position 1566 var $content = $tooltip.find( 1567 " > .wcpt-dropdown-menu, > .wcpt-tooltip-content-wrapper > .wcpt-tooltip-content", 1568 ), 1569 content_width = $content.outerWidth(false), 1570 offset_left = $content.offset().left, 1571 page_width = $(window).width(); 1572 1573 if ($tooltip.hasClass("wcpt-tooltip")) { 1574 // tooltip 1575 1576 // if tooltip is already in portal, don't move it 1577 if ($tooltip.closest(".wcpt-tooltip-portal").length) { 1578 return; 1579 } 1580 1581 // narrow width 1582 $content.css("max-width", ""); 1583 var $container, margin; 1584 1585 if ($tooltip.hasClass("wcpt-tooltip--popup-enabled")) { 1586 $container = $("body"); 1587 margin = 40; 1588 } else { 1589 $container = $tooltip.closest(".wcpt-table-scroll-wrapper-outer").length 1590 ? $tooltip.closest(".wcpt-table-scroll-wrapper-outer") 1591 : $("body"); 1592 margin = 20; 1593 } 1594 1595 var container_rect = $.extend({}, $container[0].getBoundingClientRect()), 1596 content_rect = $content[0].getBoundingClientRect(); 1597 1598 var $freezeTable = $(".frzTbl-table", $container).not( 1599 ".frzTbl-clone-table", 1600 ); 1601 1602 if ($content.closest($freezeTable).length) {
1603 // target is inside the original freeze_table 1604 var $left_freeze_column = 1605 $freezeTable.data("freezeTable").el.$frozenColumnsLeft, 1606 $right_freeze_column = 1607 $freezeTable.data("freezeTable").el.$frozenColumnsRight; 1608 1609 container_rect.left += $left_freeze_column.width(); 1610 container_rect.right -= $right_freeze_column.width(); 1611 container_rect.width = 1612 container_rect.width - 1613 $left_freeze_column.width() - 1614 $right_freeze_column.width(); 1615 } 1616 1617 if (container_rect.width < parseInt(content_rect.width) + margin * 2) { 1618 $content.css("max-width", container_rect.width - margin * 2); 1619 } 1620 1621 // vertical position 1622 var $content_wrapper = $content.parent(), 1623 content_wrapper_width = $content_wrapper.width(), 1624 viewport_height = $(window).height(); 1625 1626 $content_wrapper.attr("data-wcpt-position", ""); 1627 content_rect = $content[0].getBoundingClientRect(); // refresh 1628 1629 // Check if tooltip would go above viewport or container top 1630 if (content_rect.top - 150 < 0) { 1631 // Position above if not enough space below 1632 } else { 1633 $content.parent().attr("data-wcpt-position", "above"); 1634 } 1635 1636 // // Check if tooltip would go below viewport or container bottom 1637 // if ( 1638 // content_rect.bottom + 30 > viewport_height || 1639 // container_rect.bottom < content_rect.bottom + 30 1640 // ) { 1641 // // Position above if not enough space below 1642 // $content.parent().attr("data-wcpt-position", "above"); 1643 // } 1644 1645 // horizontal position 1646 $content.css({ left: "", right: "" }); 1647 content_rect = $content[0].getBoundingClientRect(); // refresh 1648 1649 var arrow_margin = 20; // how close arrow gets to left/ right edge 1650 1651 if (content_rect.left - 15 < container_rect.left) { 1652 // excess left 1653 var left = 15 + container_rect.left - content_rect.left, 1654 limit = content_wrapper_width / 2 - arrow_margin; 1655 1656 if (left > limit) { 1657 left = limit; 1658 } 1659 1660 $content.css("left", left); 1661 } else if (content_rect.right + 15 > container_rect.right) { 1662 // excess right 1663 var right = content_rect.right - container_rect.right + 15, 1664 limit = content_wrapper_width / 2 - arrow_margin; 1665 1666 if (right > limit) { 1667 right = limit; 1668 } 1669 1670 $content.css("right", right); 1671 } 1672 } else { 1673 // dropdown 1674 if (content_width + 30 > page_width) { 1675 $content.outerWidth(page_width - 30); 1676 var content_width = $content.outerWidth(false); 1677 } 1678 1679 if ($content.offset().left + content_width > page_width) { 1680 // offscreen right 1681 var offset_required = 1682 $content.offset().left + content_width - page_width; 1683 $content.css("left", "-=" + (offset_required + 15)); 1684 } else if ($content.offset().left < 0) { 1685 // offscreen left 1686 $content.css("left", Math.abs($content.offset().left - 15)); 1687 } 1688 } 1689 1690 // tooltip arrow 1691 if ($tooltip.hasClass("wcpt-tooltip")) { 1692 var $label = $tooltip.find("> .wcpt-tooltip-label"), 1693 offset_left = $label.offset().left, 1694 width = $label.outerWidth(), 1695 $arrow = $("> .wcpt-tooltip-arrow", $content); 1696 1697 $arrow.css( 1698 "left", 1699 offset_left - $content.offset().left + width / 2 + "px", 1700 ); 1701 } 1702 1703 // place tooltip in portal 1704 if ( 1705 $tooltip.hasClass("wcpt-tooltip") && 1706 $container.hasClass("wcpt-table-scroll-wrapper-outer") 1707 ) { 1708 var container_rect = $container[0].getBoundingClientRect(); 1709 var content_rect = $content[0].getBoundingClientRect(); 1710 1711 var $row = $tooltip.closest(".wcpt-row, .wcpt-heading-row"), 1712 $cell = $tooltip.closest(".wcpt-cell, .wcpt-heading"); 1713 1714 $container.children(".wcpt-tooltip-portal").remove(); 1715 1716 // Get all data attributes from the original row 1717 var rowAttributes = ""; 1718 $.each($row[0].attributes, function (i, attr) { 1719 if (attr.name.startsWith("data-")) { 1720 rowAttributes += " " + attr.name + '="' + attr.value + '"'; 1721 } 1722 }); 1723 1724 var $portal = $( 1725 '<div class="wcpt-tooltip-portal">' + 1726 '<div class="wcpt-table">' + 1727 '<div class="' + 1728 $row.attr("class") + 1729 '"' + 1730 rowAttributes + 1731 ">" + 1732 '<div class="' + 1733 $cell.attr("class") + 1734 '">' + 1735 '<div class="wcpt-item-row"></div>' + 1736 "</div>" + 1737 "</div>" + 1738 "</div>" + 1739 "</div>", 1740 ); 1741 $portal 1742 .find(".wcpt-item-row") 1743 .html($tooltip.clone().addClass("wcpt-tooltip--inside-portal")); 1744 1745 $portal.find(".wcpt-row").data($row.data()); 1746 1747 var wrapper_rect = $content_wrapper[0].getBoundingClientRect(); 1748 $portal.find(".wcpt-tooltip-content-wrapper").css({ 1749 position: "fixed", 1750 top: wrapper_rect.top + "px", 1751 left: wrapper_rect.left + "px", 1752 }); 1753 1754 var content_rect = $content[0].getBoundingClientRect(); 1755 1756 var $portal_content = $portal.find(".wcpt-tooltip-content"); 1757 $portal_content.css({ 1758 width: content_rect.width + "px", 1759 transform: "translateY(4px)", 1760 opacity: 0, 1761 }); 1762 $content.css({ 1763 display: "none", 1764 }); 1765 $container.append($portal); 1766 1767 // animate 1768 setTimeout(function () { 1769 $portal_content.css({ 1770 transform: "translateY(0px)", 1771 opacity: 1, 1772 }); 1773 }, 1); 1774 1775 var $sticky_table_scroll_wrapper = $tooltip.closest( 1776 ".frzTbl-table-wrapper", 1777 ); 1778 1779 // Generate unique namespace for this tooltip instance 1780 var portalNamespace = 1781 "wcpt_portal_" + Math.random().toString(36).substr(2, 9); 1782 1783 function remove_portal() { 1784 // Remove all event handlers using unique namespace 1785 $container.off("mousemove." + portalNamespace); 1786 $(window).off("scroll." + portalNamespace); 1787 $container.off("scroll." + portalNamespace);
1788 $("body").off("click." + portalNamespace); 1789 $sticky_table_scroll_wrapper.off("scroll." + portalNamespace); 1790 1791 $portal.remove(); 1792 $content.css("display", ""); 1793 $tooltip.removeClass("wcpt-open"); 1794 } 1795 1796 // Store namespace on portal element for future reference 1797 $portal.data("portalNamespace", portalNamespace); 1798 1799 // remove portal based on 1800 // -- mouse hover 1801 if (!$tooltip.hasClass("wcpt-tooltip--open-on-click")) { 1802 $container.on("mousemove." + portalNamespace, function (e) { 1803 var $target = $(e.target); 1804 if ( 1805 !$target.closest($portal).length && 1806 !$target.closest($tooltip).length 1807 ) { 1808 remove_portal(); 1809 } 1810 }); 1811 } else { 1812 // -- click outside 1813 $("body").on("click." + portalNamespace, function (e) { 1814 if (!$(e.target).closest($portal).length) { 1815 remove_portal(); 1816 } 1817 }); 1818 } 1819 // -- scroll 1820 $(window).on("scroll." + portalNamespace, function () { 1821 remove_portal(); 1822 }); 1823 $container.on("scroll." + portalNamespace, function () { 1824 remove_portal(); 1825 }); 1826 $sticky_table_scroll_wrapper.on("scroll." + portalNamespace, function () { 1827 remove_portal(); 1828 }); 1829 } 1830 } 1831 1832 // tooltip with content hover disabled 1833 $("body").on("click mouseover", ".wcpt-tooltip-content", function () { 1834 var $this = $(this), 1835 $tooltip = $this.closest(".wcpt-tooltip"); 1836 1837 if ($tooltip.hasClass("wcpt-tooltip--hover-disabled")) { 1838 $tooltip.removeClass("wcpt-open"); 1839 } 1840 }); 1841 1842 // close dropdown when grab and scroll starts 1843 $("body").on("freeze_table__grab_and_scroll__start", function () { 1844 $( 1845 ".wcpt-navigation:not(.wcpt-left-sidebar) .wcpt-dropdown.wcpt-open", 1846 ).removeClass("wcpt-open"); 1847 }); 1848 1849 // Unique key for matching the same filter/search across header + sidebar 1850 function get_nav_filter_dedupe_key($el) { 1851 if ($el.hasClass("wcpt-search-wrapper") || $el.hasClass("wcpt-search")) { 1852 var $input = $el 1853 .find(".wcpt-search-input") 1854 .addBack(".wcpt-search-input") 1855 .first(); 1856 var name = $input.attr("name") || ""; 1857 return name ? "search:" + name : ""; 1858 } 1859 1860 if (!$el.hasClass("wcpt-filter") && !$el.is(".wcpt-filter")) { 1861 return ""; 1862 } 1863 1864 var filter = $el.attr("data-wcpt-filter"); 1865 if (!filter) { 1866 return ""; 1867 } 1868 1869 return ( 1870 filter + 1871 "|" + 1872 ($el.attr("data-wcpt-taxonomy") || "") + 1873 "|" + 1874 ($el.attr("data-wcpt-meta-key") || "") 1875 ); 1876 } 1877 1878 // Match other instances of the same filter across header / sidebar / etc. 1879 function find_duplicate_filters($filter, $nav) { 1880 var filter = $filter.attr("data-wcpt-filter"); 1881 if (!filter) { 1882 return $(); 1883 } 1884 1885 var selector = '.wcpt-filter[data-wcpt-filter="' + filter + '"]', 1886 taxonomy = $filter.attr("data-wcpt-taxonomy"), 1887 meta_key = $filter.attr("data-wcpt-meta-key"); 1888 1889 if (taxonomy) { 1890 selector += '[data-wcpt-taxonomy="' + taxonomy + '"]'; 1891 } 1892 if (meta_key) { 1893 selector += '[data-wcpt-meta-key="' + meta_key + '"]'; 1894 } 1895 1896 return $nav.find(selector); 1897 } 1898 1899 // When phone/tablet flattens header + sidebar into one strip, hide later 1900 // copies of the same filter so each filter only appears once. 1901 function hide_responsive_duplicate_nav_filters($wcpt) { 1902 $wcpt 1903 .find(".wcpt-nav-duplicate-hidden") 1904 .removeClass("wcpt-nav-duplicate-hidden"); 1905 1906 if (get_device() === "laptop") { 1907 return; 1908 } 1909 1910 // Only needed when the sidebar was converted into the combined header strip 1911 if (!$wcpt.find(".wcpt-was-left-sidebar").length) { 1912 return; 1913 } 1914 1915 var seen = {}; 1916 $wcpt 1917 .find(".wcpt-navigation-wrapper") 1918 .find(".wcpt-filter, .wcpt-search-wrapper") 1919 .each(function () { 1920 var $el = $(this); 1921 if ($el.closest(".wcpt-nav-modal-tpl, .wcpt-nav-modal").length) { 1922 return; 1923 } 1924 1925 var key = get_nav_filter_dedupe_key($el); 1926 if (!key) { 1927 return; 1928 } 1929 1930 if (seen[key]) { 1931 $el.addClass("wcpt-nav-duplicate-hidden"); 1932 } else { 1933 seen[key] = true; 1934 } 1935 }); 1936 } 1937 1938 // Keep first of each unique filter/search; drop later duplicates from a set 1939 function dedupe_nav_elements($set) { 1940 var seen = {}; 1941 return $set.filter(function () { 1942 var key = get_nav_filter_dedupe_key($(this)); 1943 if (!key) { 1944 return true; 1945 } 1946 if (seen[key]) { 1947 return false; 1948 } 1949 seen[key] = true; 1950 return true; 1951 });
1952 } 1953 1954 // Copy checked/value state from one filter instance onto its duplicates 1955 function sync_duplicate_filters_from($source_filter, $nav) { 1956 var $duplicates = find_duplicate_filters($source_filter, $nav).not( 1957 $source_filter, 1958 ); 1959 if (!$duplicates.length) { 1960 return; 1961 } 1962 1963 $source_filter 1964 .find('input[type="checkbox"], input[type="radio"]') 1965 .each(function () { 1966 var $src = $(this), 1967 value = $src.attr("value"), 1968 checked = $src.prop("checked"), 1969 type = $src.attr("type"); 1970 1971 $duplicates.each(function () { 1972 $(this) 1973 .find('input[type="' + type + '"]') 1974 .filter(function () { 1975 return $(this).attr("value") === value; 1976 }) 1977 .each(function () { 1978 var $input = $(this), 1979 $option = $input.closest(".wcpt-option, .wcpt-dropdown-option"), 1980 $label = $input.closest("label"); 1981 1982 $input.prop("checked", checked); 1983 1984 if ($option.length) { 1985 $option.toggleClass("wcpt-active", !!checked); 1986 } 1987 if ($label.length) { 1988 $label.toggleClass("wcpt-active", !!checked); 1989 } 1990 }); 1991 }); 1992 }); 1993 1994 // Range / date text inputs (same class names across copies) 1995 [ 1996 ".wcpt-range-input-min", 1997 ".wcpt-range-input-max", 1998 ".wcpt-filter-date-picker--start-date", 1999 ".wcpt-filter-date-picker--end-date", 2000 ].forEach(function (sel) { 2001 var $src_inputs = $source_filter.find(sel);
2002 if (!$src_inputs.length) { 2003 return; 2004 } 2005 2006 $src_inputs.each(function (i) { 2007 var val = $(this).val(); 2008 $duplicates.each(function () { 2009 var $dup_input = $(this).find(sel).eq(i); 2010 if ($dup_input.length) { 2011 $dup_input.val(val); 2012 } 2013 }); 2014 }); 2015 }); 2016 } 2017 2018 // apply nav filters 2019 $("body").on("change", ".wcpt-navigation", navigation_change); 2020 function navigation_change(e) { 2021 var $target = $(e.target), 2022 $container = $target.closest(".wcpt"), 2023 // Header + left sidebar are nested under .wcpt-navigation-wrapper / 2024 // .wcpt-nav-main, so they are no longer siblings â collect all of them. 2025 $nav = $( 2026 "> .wcpt-navigation, > .wcpt-navigation-wrapper .wcpt-navigation", 2027 $container, 2028 ); 2029 2030 // skip search filter options input 2031 if ($target.closest(".wcpt-search-filter-options").length) { 2032 return; 2033 } 2034 2035 // skip date pickers 2036 if ($target.closest(".wcpt-filter-date-picker").length) { 2037 return; 2038 } 2039 2040 // taxonomy hierarchy 2041 if ($target.closest(".wcpt-hierarchy").length) { 2042 var checked = $target.prop("checked"); 2043 2044 // effect on child terms 2045 if ($target.hasClass("wcpt-hr-parent-term")) { 2046 var ct_selector = "input[type=checkbox], input[type=radio]", 2047 $child_terms = $target 2048 .closest("label") 2049 .siblings(".wcpt-hr-child-terms-wrapper") 2050 .find(ct_selector); 2051 $child_terms.prop("checked", false); 2052 } 2053 2054 // effect on parent terms 2055 var $ancestors = $target.parents(".wcpt-hr-child-terms-wrapper"); 2056 if ($ancestors.length) { 2057 $ancestors.each(function () { 2058 var $parent_term = $(this) 2059 .siblings("label") 2060 .find(".wcpt-hr-parent-term"); 2061 $parent_term.prop("checked", false); 2062 }); 2063 } 2064 } 2065 2066 // range filter 2067 if ($target.closest(".wcpt-range-filter")) { 2068 // -- number and range inputs shouldn't cause filtering 2069 if ( 2070 $target.hasClass("wcpt-range-input-min") || 2071 $target.hasClass("wcpt-range-input-max") || 2072 $target.hasClass("wcpt-range-slider") 2073 ) { 2074 // Still sync typed values onto duplicate filter copies 2075 var $range_source = $target.closest(".wcpt-filter"); 2076 if ($range_source.length) { 2077 sync_duplicate_filters_from($range_source, $nav); 2078 } 2079 return; 2080 } 2081 2082 // apply selected radio range onto the range slider inputs 2083 if ($target.is("input[type=radio]")) { 2084 var min = $target.attr("data-wcpt-range-min") || "", 2085 max = $target.attr("data-wcpt-range-max") || "", 2086 $range_filter = $target.closest(".wcpt-range-filter"), 2087 $min = $range_filter.find(".wcpt-range-input-min"), 2088 $max = $range_filter.find(".wcpt-range-input-max"), 2089 $range_slider = $range_filter.find(".wcpt-range-slider.original"); 2090 2091 $min.val(min); 2092 $max.val(max); 2093 if (!min) { 2094 min = $range_slider.attr("min"); 2095 } 2096 if (!max) { 2097 max = $range_slider.attr("max"); 2098 } 2099 2100 $range_slider.val(min + "," + max); 2101 } 2102 } 2103 2104 // Keep duplicate copies of the same filter in sync (e.g. header + sidebar). 2105 // Without this, serialize() still picks up a checked option from the other 2106 // copy after deselection, so the filter stays applied. 2107 var $source_filter = $target.closest(".wcpt-filter"); 2108 if ($source_filter.length) { 2109 sync_duplicate_filters_from($source_filter, $nav); 2110 } 2111 2112 // return if current control doesn't trigger immediate filtering 2113 // -- search 2114 if ($target.closest(".wcpt-search").length) { 2115 return; 2116 } 2117 2118 // -- nav modal 2119 if ($target.closest(".wcpt-nav-modal").length) { 2120 return; 2121 } 2122 2123 // nav clone 2124 var table_id = $container.attr("id").substring(5), 2125 $nav_clone = $nav.clone(); 2126 2127 nav_clone_operations($nav_clone); 2128 2129 // build query 2130 var query = $("<form>").append($nav_clone).serialize(); 2131 2132 // include column sort 2133 if (!$(e.target).closest('[data-wcpt-filter="sort_by"]').length) {
2134 var $table = wcpt_get_container_original_table($container), 2135 $sortable_headings = $(".wcpt-heading.wcpt-sortable:visible", $table), 2136 $current_sort_col = $sortable_headings.filter(function () { 2137 return $(this).find( 2138 ".wcpt-sorting-icons.wcpt-sorting-asc, .wcpt-sorting-icons.wcpt-sorting-desc", 2139 ).length; 2140 }); 2141 if ($current_sort_col.length) { 2142 var sort_id = $current_sort_col.attr("data-wcpt-sort-id"), 2143 order = $current_sort_col.find(".wcpt-sorting-icons.wcpt-sorting-asc") 2144 .length 2145 ? "ASC" 2146 : "DESC", 2147 $sorting_icons = $current_sort_col 2148 .find(".wcpt-sorting-icons") 2149 .first(), 2150 column_orderby = $sorting_icons.attr("data-wcpt-orderby") || "", 2151 column_meta_key = $sorting_icons.attr("data-wcpt-meta-key") || "", 2152 column_orderby_attribute = 2153 $sorting_icons.attr("data-wcpt-orderby-attribute") || "", 2154 column_orderby_taxonomy = 2155 $sorting_icons.attr("data-wcpt-orderby-taxonomy") || ""; 2156 // Skip incomplete sort ids (e.g. "column_" with no index) â those 2157 // produce undefined orderby warnings on the server during filter AJAX. 2158 if ( 2159 sort_id && 2160 sort_id !== "column_" && 2161 sort_id.indexOf("column_") === 0 2162 ) { 2163 // Prefer explicit heading criteria when available 2164 if (column_orderby) { 2165 sort_id = "column_heading"; 2166 } 2167 2168 query += 2169 "&" + 2170 table_id + 2171 "_orderby=" + 2172 sort_id + 2173 "&" + 2174 table_id + 2175 "_order=" + 2176 order; 2177 2178 if (column_orderby) { 2179 query += 2180 "&" + 2181 table_id + 2182 "_column_orderby=" + 2183 encodeURIComponent(column_orderby); 2184 query += 2185 "&" + 2186 table_id + 2187 "_column_meta_key=" + 2188 encodeURIComponent(column_meta_key); 2189 if (column_orderby_attribute) { 2190 query += 2191 "&" + 2192 table_id + 2193 "_column_orderby_attribute=" + 2194 encodeURIComponent(column_orderby_attribute); 2195 } 2196 if (column_orderby_taxonomy) { 2197 query += 2198 "&" + 2199 table_id + 2200 "_column_orderby_taxonomy=" + 2201 encodeURIComponent(column_orderby_taxonomy); 2202 } 2203 } 2204 } 2205 } 2206 } 2207 2208 nav_filter_feedback($nav); 2209 2210 var data = { 2211 callback: function () { 2212 attempt_ajax($container, query, false, "filter", e); 2213 }, 2214 proceed: true, 2215 event: e, 2216 }; 2217 2218 $container.trigger("wcpt_after_navigation_change", data); 2219 2220 if (data.proceed) { 2221 data.callback(); 2222 } 2223 } 2224 2225 function nav_clone_operations($nav_clone) { 2226 var $reverse_check = $(); 2227 2228 $("[data-wcpt-reverse-value]:not(:checked)", $nav_clone).each(function () { 2229 var $this = $(this); 2230 $this.attr("value", $this.attr("data-wcpt-reverse-value")); 2231 $this.prop("checked", "checked"); 2232 $reverse_check = $reverse_check.add($this.clone()); 2233 }); 2234 $nav_clone = $nav_clone.add($reverse_check); 2235 2236 // get values from duplicate radio buttons and clean up the names 2237 $('input[type="radio"]', $nav_clone).each(function () { 2238 var $this = $(this), 2239 name = $this.attr("name"); 2240 2241 if (-1 !== name.indexOf("--")) { 2242 var is_array = name.indexOf("[]"); 2243 name = name.substr(0, name.indexOf("--")) + (is_array ? "[]" : ""); 2244 $this.attr("name", name); 2245 } 2246 }); 2247 2248 // remove redundant range values 2249 $nav_clone.find(".wcpt-range-options-wrapper").each(function () { 2250 var $this = $(this), 2251 $min_input = $this.find(".wcpt-range-input-min"), 2252 $max_input = $this.find(".wcpt-range-input-max"), 2253 $range_slider = $this.find(".wcpt-range-slider"), 2254 $filter = $this.closest(".wcpt-filter"), 2255 $selected_option = $filter.find("input[type=radio]:checked"); 2256 2257 // enforce option selected by user 2258 if ($selected_option.length && $selected_option.attr("value")) { 2259 var $selected_option_min = $selected_option.attr("data-wcpt-range-min"), 2260 $selected_option_max = $selected_option.attr("data-wcpt-range-max"); 2261 2262 $min_input.val($selected_option_min); 2263 $max_input.val($selected_option_max); 2264 return; 2265 } 2266 2267 var prev_min = $min_input.attr("value"), 2268 prev_max = $max_input.attr("value"); 2269 2270 $min_input.each(function () { 2271 var $this = $(this), 2272 min = $this.attr("min"), 2273 current_value = $this.val(); 2274 if (min == current_value) { 2275 $this.val(prev_min); 2276 } 2277 }); 2278 2279 $max_input.each(function () { 2280 var $this = $(this), 2281 max = $this.attr("max"), 2282 current_value = $this.val(); 2283 if (max == current_value) { 2284 $this.val(prev_max); 2285 } 2286 }); 2287 2288 $range_slider.remove(); 2289 });
2290 } 2291 2292 function nav_filter_feedback($nav) { 2293 // header nav 2294 $(".wcpt-filter", $nav.filter(".wcpt-header")).each(function () { 2295 var $this = $(this), 2296 filter = $this.attr("data-wcpt-filter"), 2297 $filter = $this.closest(".wcpt-filter"), 2298 format = $this.attr("data-wcpt-heading_format__op_selected"), 2299 radio = 2300 $this.find("input[type=radio]").length || 2301 $this.hasClass("wcpt-range-filter"), 2302 checkbox = $this.find("input[type=checkbox]").length, 2303 $selected = $this.find("input[type=radio]:checked"), 2304 $checked = $this.find("input[type=checkbox]:checked"), 2305 checked_count = $checked.length, 2306 $active_count = $this.find(".wcpt-active-count"), 2307 radio_permit = false, 2308 label_append = "", 2309 $multi_range = $(".wcpt-range-options-wrapper", $filter), 2310 $multi_range__min = $( 2311 ".wcpt-range-options-wrapper .wcpt-range-input-min", 2312 $filter, 2313 ), 2314 $multi_range__max = $( 2315 ".wcpt-range-options-wrapper .wcpt-range-input-max", 2316 $filter, 2317 ), 2318 in_filter_grid = !!$this.closest(".wcpt-ratio-filter_grid").length; 2319 2320 // Skip options-row filters outside filter grid (counts aren't used there) 2321 if ($this.hasClass("wcpt-options-row") && !in_filter_grid) { 2322 return; 2323 } 2324 2325 if ( 2326 -1 == 2327 $.inArray(filter, [ 2328 "custom_field", 2329 "attribute", 2330 "category", 2331 "taxonomy", 2332 "price_range", 2333 "rating", 2334 "sort_by", 2335 "results_per_page", 2336 "on_sale", 2337 "availability", 2338 ]) 2339 ) { 2340 return; 2341 } 2342 2343 // mark active filters 2344 if ( 2345 checked_count || 2346 ($selected.val() && 2347 !$selected.closest(".wcpt-default-option").length) || // sort by 2348 ($multi_range.length && 2349 ($multi_range__min.val() != $multi_range__min.attr("min") || 2350 $multi_range__max.val() != $multi_range__max.attr("max"))) 2351 ) { 2352 $this.closest(".wcpt-filter").addClass("wcpt-filter--active"); 2353 } else { 2354 $this.closest(".wcpt-filter").removeClass("wcpt-filter--active"); 2355 } 2356 2357 // modify dropdown heading 2358 2359 // -- filter grid: radio filters show (0)/(1) count like checkboxes 2360 if ( 2361 radio && 2362 in_filter_grid && 2363 filter !== "sort_by" && 2364 filter !== "results_per_page" 2365 ) { 2366 $this.find(".wcpt-radio-op-selected__heading-append").remove(); 2367 $active_count.remove(); 2368 2369 var radio_count = 0; 2370 if ($selected.length && $selected.attr("value")) { 2371 // Non-"show all" option selected 2372 radio_count = 1; 2373 } else if ( 2374 $multi_range.length && 2375 ($multi_range__min.val() != $multi_range__min.attr("min") || 2376 $multi_range__max.val() != $multi_range__max.attr("max")) 2377 ) { 2378 radio_count = 1; 2379 } 2380 2381 var radio_count_class = 2382 "wcpt-active-count wcpt-active-count--filter-grid"; 2383 if (!radio_count) { 2384 radio_count_class += " wcpt-active-count--zero"; 2385 } 2386 2387 $active_count = $( 2388 '<span class="' + radio_count_class + '">' + radio_count + "</span>", 2389 ); 2390 $( 2391 ".wcpt-filter-heading .wcpt-dropdown-label, .wcpt-filter-heading .wcpt-options-heading", 2392 $this, 2393 ).after($active_count); 2394 2395 // -- radio append option label 2396 } else if (radio && format !== "only_heading") { 2397 $this.find(".wcpt-radio-op-selected__heading-append").remove(); 2398 2399 if ( 2400 !$selected.length || 2401 !$selected.attr("value") // 'show all' op selected 2402 ) { 2403 $this.removeClass("wcpt-radio-op-selected"); 2404 } else { 2405 // selected and has value 2406 $this.addClass("wcpt-radio-op-selected"); 2407 label_append = $selected.next()[0].outerHTML; 2408 radio_permit = true; 2409 } 2410 2411 if (!$selected.length && filter == "price_range") { 2412 var min = 2413 wcpt_params.currency_symbol + 2414 $(".wcpt-range-input-min", $this).val() || 0, 2415 max = 2416 wcpt_params.currency_symbol + 2417 $(".wcpt-range-input-max", $this).val() || 0; 2418 2419 label_append = "<span>" + min + " - " + max + "<span>"; 2420 2421 if ( 2422 $(".wcpt-range-input-min", $this).val() != 2423 $(".wcpt-range-input-min", $this).attr("min") || 2424 $(".wcpt-range-input-max", $this).val() != 2425 $(".wcpt-range-input-max", $this).attr("max") 2426 ) { 2427 $this.addClass("wcpt-radio-op-selected"); 2428 radio_permit = true; 2429 } 2430 } else if (!$selected.length && filter == "custom_field") { 2431 var min = $(".wcpt-range-input-min", $this).val() || 0, 2432 max = $(".wcpt-range-input-max", $this).val() || 0; 2433 2434 label_append = "<span>" + min + " - " + max + "<span>"; 2435 2436 if ( 2437 min != $(".wcpt-range-input-min", $this).attr("min") || 2438 max != $(".wcpt-range-input-max", $this).attr("max") 2439 ) { 2440 $this.addClass("wcpt-radio-op-selected"); 2441 radio_permit = true; 2442 } 2443 } else if ($selected.length && filter == "sort_by") { 2444 var $icon = $this.find(".wcpt-dropdown-label > .wcpt-icon"); 2445 if ($icon.length) { 2446 label_append = 2447 $icon[0].outerHTML + " " + $selected.next()[0].outerHTML; 2448 2449 $icon.remove(); 2450 } 2451 } 2452 2453 if (radio_permit) { 2454 $this 2455 .find(".wcpt-dropdown-label") 2456 .append( 2457 '<div class="wcpt-radio-op-selected__heading-append">' + 2458 label_append + 2459 "</div>", 2460 ); 2461 } 2462 2463 // -- checkbox append selected option count 2464 } else if (checkbox) { 2465 $active_count.remove(); 2466 2467 // In filter grid rows, always show the count (including 0) 2468 // so empty checkbox dropdowns still read as (0). 2469 var always_show_count = in_filter_grid; 2470 2471 if (checked_count || always_show_count) { 2472 var active_count_class = "wcpt-active-count"; 2473 var active_count_text = checked_count; 2474 2475 if (always_show_count) { 2476 active_count_class += " wcpt-active-count--filter-grid"; 2477 2478 if (!checked_count) { 2479 active_count_class += " wcpt-active-count--zero"; 2480 } 2481 } 2482 2483 $active_count = $( 2484 '<span class="' + 2485 active_count_class + 2486 '">' + 2487 active_count_text + 2488 "</span>", 2489 ); 2490 $( 2491 ".wcpt-filter-heading .wcpt-dropdown-label, .wcpt-filter-heading .wcpt-options-heading", 2492 $this, 2493 ).after($active_count); 2494 } 2495 } 2496 });
2497 } 2498 2499 // submit range by enter 2500 $("body").on( 2501 "keyup", 2502 ".wcpt-range-input-min, .wcpt-range-input-max", 2503 function (e) { 2504 var $this = $(this), 2505 $filters = $this.closest(".wcpt-navigation"), 2506 code = e.keyCode ? e.keyCode : e.which; 2507 2508 if (code == 13) { 2509 var $filter = $this.closest(".wcpt-filter"), 2510 $container = $this.closest(".wcpt"), 2511 $nav = $( 2512 "> .wcpt-navigation, > .wcpt-navigation-wrapper .wcpt-navigation", 2513 $container, 2514 ); 2515 if ($filter.length) { 2516 sync_duplicate_filters_from($filter, $nav); 2517 } 2518 $filters.trigger("change"); 2519 } 2520 }, 2521 ); 2522 2523 // submit range 2524 $("body").on("click", ".wcpt-range-submit-button", function (e) { 2525 var $this = $(this), 2526 $range_wrapper = $this.closest(".wcpt-range-options-wrapper"), 2527 $min = $(".wcpt-range-input-min", $range_wrapper), 2528 $max = $(".wcpt-range-input-max", $range_wrapper), 2529 min_val = parseFloat($min.val()), 2530 max_val = parseFloat($max.val()); 2531 2532 // ensure max stays greater than min 2533 if (max_val < min_val) { 2534 $max.val(min_val); 2535 $min.val(max_val); 2536 } 2537 2538 $this.trigger("change"); 2539 }); 2540 2541 // date picker 2542 2543 // -- submit 2544 $("body").on("click", ".wcpt-date-picker-submit-button", function (e) { 2545 var $this = $(this), 2546 $filter = $this.closest('[data-wcpt-filter="date_picker"]'), 2547 $start_date = $(".wcpt-filter-date-picker--start-date", $filter), 2548 $end_date = $(".wcpt-filter-date-picker--end-date", $filter), 2549 $filters = $this.closest(".wcpt-navigation"), 2550 $container = $this.closest(".wcpt"), 2551 $nav = $( 2552 "> .wcpt-navigation, > .wcpt-navigation-wrapper .wcpt-navigation", 2553 $container, 2554 ); 2555 2556 if ($start_date.val() && $end_date.val()) { 2557 var start_date = new Date($start_date.val()), 2558 end_date = new Date($end_date.val()); 2559 2560 if (start_date > end_date) { 2561 var start_date_val = $start_date.val(), 2562 end_date_val = $end_date.val(); 2563 2564 $start_date.val(end_date_val); 2565 $end_date.val(start_date_val); 2566 } 2567 } 2568 2569 if ($filter.length) { 2570 sync_duplicate_filters_from($filter, $nav); 2571 } 2572 $filters.trigger("change"); 2573 }); 2574 // -- -- enter key 2575 $("body").on("keydown", ".wcpt-filter-date-picker", function (e) { 2576 if (e.keyCode === 13 || e.which === 13) { 2577 var $this = $(this), 2578 $filters = $this.closest(".wcpt-navigation"), 2579 $filter = $this.closest(".wcpt-filter"), 2580 $container = $this.closest(".wcpt"), 2581 $nav = $( 2582 "> .wcpt-navigation, > .wcpt-navigation-wrapper .wcpt-navigation", 2583 $container, 2584 ); 2585 2586 if ($filter.length) { 2587 sync_duplicate_filters_from($filter, $nav); 2588 } 2589 $filters.trigger("change"); 2590 } 2591 }); 2592 2593 // -- reset 2594 $("body").on("click", ".wcpt-date-picker-reset-button", function (e) { 2595 e.preventDefault(); 2596 2597 var $this = $(this), 2598 $filter = $this.closest('[data-wcpt-filter="date_picker"]'), 2599 $date_picker = $(".wcpt-filter-date-picker", $filter), 2600 $filters = $this.closest(".wcpt-navigation"), 2601 $container = $this.closest(".wcpt"), 2602 $nav = $( 2603 "> .wcpt-navigation, > .wcpt-navigation-wrapper .wcpt-navigation", 2604 $container, 2605 ); 2606 2607 $date_picker.val(""); 2608 if ($filter.length) { 2609 sync_duplicate_filters_from($filter, $nav); 2610 } 2611 $filters.trigger("change"); 2612 }); 2613 2614 // -- validate 2615 $("body").on("keyup", ".wcpt-filter-date-picker", function (e) { 2616 var $this = $(this); 2617 if ( 2618 $this.val().length > 10 || 2619 -1 == $.inArray($this.val()[0], ["1", "2"]) 2620 ) { 2621 $this.css("border-color", "red"); 2622 } else { 2623 $this.css("border-color", ""); 2624 } 2625 }); 2626 2627 // clear filter 2628 $("body").on("click", ".wcpt-clear-filter", function (e) { 2629 var $clear_filter = $(this), 2630 $target = $(e.target); 2631 2632 if ($target.closest(".wcpt-dropdown-menu")) { 2633 var $sub_option = $target.closest(".wcpt-dropdown-option"); 2634 } else { 2635 $sub_option = false; 2636 } 2637 2638 var $container = $clear_filter.closest(".wcpt"), 2639 filter = $clear_filter.attr("data-wcpt-filter"), 2640 $navs = $( 2641 "> .wcpt-navigation, >
2641 .wcpt-navigation-wrapper .wcpt-navigation", 2642 $container, 2643 ), 2644 $inputs = $(); 2645 2646 if (filter == "search") { 2647 var name = $clear_filter.attr("data-wcpt-search-name"), 2648 $inputs = $('.wcpt-search-input[name="' + name + '"]', $navs); 2649 } else if ( 2650 filter == "attribute" || 2651 filter == "category" || 2652 filter == "taxonomy" 2653 ) { 2654 var taxonomy = $clear_filter.attr("data-wcpt-taxonomy"), 2655 term = $clear_filter.attr("data-wcpt-value"), 2656 $inputs = $navs 2657 .find( 2658 '.wcpt-filter[data-wcpt-filter="' + 2659 filter + 2660 '"][data-wcpt-taxonomy="' + 2661 taxonomy + 2662 '"]', 2663 ) 2664 .find('input[value="' + term + '"]'); 2665 } else if (filter == "custom_field") { 2666 var meta_key = $clear_filter.attr("data-wcpt-meta-key"), 2667 value = $clear_filter.attr("data-wcpt-value"), 2668 $filter = $navs.find( 2669 '.wcpt-filter[data-wcpt-filter="' + 2670 filter + 2671 '"][data-wcpt-meta-key="' + 2672 meta_key + 2673 '"]', 2674 ); 2675 2676 if ($filter.hasClass("wcpt-range-filter")) { 2677 $inputs = $filter.find("input"); 2678 } else { 2679 $inputs = $navs 2680 .find( 2681 '.wcpt-filter[data-wcpt-filter="' + 2682 filter + 2683 '"][data-wcpt-meta-key="' + 2684 meta_key + 2685 '"]', 2686 ) 2687 .find('input[value="' + value + '"]'); 2688 } 2689 } else if (filter == "price_range") { 2690 $inputs = $navs 2691 .find( 2692 '.wcpt-filter[data-wcpt-filter="custom_field"][data-wcpt-meta-key="_price"]', 2693 ) 2694 .find("input"); 2695 } else if (filter == "search") { 2696 $inputs = $navs.find( 2697 'input[type=search][data-wcpt-value="' + 2698 htmlentity($clear_filter.attr("data-wcpt-value")) + 2699 '"]', 2700 ); 2701 } else if (filter == "rating") { 2702 $inputs = $navs 2703 .find('.wcpt-filter[data-wcpt-filter="rating"]') 2704 .find("input"); 2705 } else if (filter == "date_picker") { 2706 var val = $clear_filter.attr("data-wcpt-value"), 2707 $inputs = $navs 2708 .find('.wcpt-filter[data-wcpt-filter="date_picker"]') 2709 .find('input[value="' + val + '"]'); 2710 } else { 2711 var value = $clear_filter.attr("data-wcpt-value"), 2712 $inputs = $navs 2713 .find('.wcpt-filter[data-wcpt-filter="' + filter + '"]') 2714 .find('input[value="' + value + '"]'); 2715 } 2716 2717 $inputs 2718 .filter(":input[type=checkbox], :input[type=radio]") 2719 .prop("checked", false) 2720 .closest("label.wcpt-active") 2721 .removeClass("wcpt-active"); 2722 2723 $inputs 2724 .filter( 2725 ":input[type=text], :input[type=number], :input[type=search], :input[type=date]", 2726 ) 2727 .val(""); // search and range input 2728 2729 $navs.first().trigger("change"); 2730 2731 // remove clear filter 2732 if (!$clear_filter.siblings(".wcpt-clear-filter").length) { 2733 $clear_filter.closest(".wcpt-clear-filters-wrapper").remove(); 2734 } else { 2735 $clear_filter.remove(); 2736 } 2737 }); 2738 2739 // clear all filters 2740 $("body").on( 2741 "click", 2742 ".wcpt-clear-filters, .wcpt-clear-all-filters, .wcpt-reset", 2743 function (e) { 2744 e.preventDefault(); 2745 var $this = $(this), 2746 $container = $this.closest(".wcpt"), 2747 query = "", 2748 append = false, 2749 purpose = "filter"; 2750 2751 if (!$this.hasClass("wcpt-disabled")) { 2752 attempt_ajax($container, query, append, purpose); 2753 } 2754 }, 2755 ); 2756 2757 // sort by column heading 2758 window.wcpt_column_heading_sort_handler = function (e) { 2759 var $this = $(this), 2760 $sorting = $this.find(".wcpt-sorting-icons"); 2761 2762 if ($(e.target).closest(".wcpt-tooltip").length) { 2763 return; 2764 } 2765 2766 if (!$sorting.length) { 2767 return; 2768 } 2769 2770 // maintain scroll left upon column header sorting 2771 if ($this.closest(".frzTbl").length) { 2772 var $container = $this.closest(".wcpt"), 2773 $scrollOverlay = $this 2774 .closest(".frzTbl-content-wrapper") 2775 .siblings(".frzTbl-scroll-overlay"), 2776 scrollLeft = $scrollOverlay[0].scrollLeft; 2777 2778 $("body").one( 2779 "after_freeze_table_build", 2780 "#" + $container.attr("id") + " .frzTbl-table", 2781 function (e, frzTbl) { 2782 frzTbl.el.$scrollOverlay[0].scrollLeft = scrollLeft; 2783 }, 2784 ); 2785 } 2786 2787 var order = $sorting.hasClass("wcpt-sorting-asc") ? "desc" : "asc", 2788 sort_id = $this.attr("data-wcpt-sort-id") || "", 2789 $container = $this.closest(".wcpt"), 2790 table_id = $container.attr("id").substring(5), 2791 device = get_device(), 2792 // Explicit criteria from the heading icons â preferred server-side 2793 // source of truth (does not rely on column_{index} resolution). 2794 column_orderby = $sorting.attr("data-wcpt-orderby") || "", 2795 column_meta_key = $sorting.attr("data-wcpt-meta-key") || "", 2796 column_orderby_attribute = 2797 $sorting.attr("data-wcpt-orderby-attribute") || "", 2798 column_orderby_taxonomy = 2799 $sorting.attr("data-wcpt-orderby-taxonomy") || ""; 2800 2801 if ($(".wcpt-sorting-" + order + "-icon", $sorting).hasClass("wcpt-hide")) { 2802 if ( 2803 $(".wcpt-sorting-" + order + "-icon", $sorting) 2804 .siblings() 2805 .hasClass("wcpt-active") 2806 ) { 2807 return; 2808 } else { 2809 order = order == "asc" ? "desc" : "asc"; 2810 } 2811 } 2812 2813 // When the heading icons expose explicit sort criteria, use a stable 2814 // orderby token and send those criteria as the source of truth. This 2815 // avoids fragile column_{index} resolution (layout / original_index mismatch). 2816 if (column_orderby) { 2817 sort_id = "column_heading"; 2818 } 2819 2820 if (!sort_id || sort_id === "column_") { 2821 return; 2822 } 2823 2824 var query = 2825 table_id + 2826 "_orderby=" + 2827 encodeURIComponent(sort_id) + 2828 "&" + 2829 table_id + 2830 "_order=" + 2831 order + 2832 "&" + 2833 table_id + 2834 "_device=" + 2835 device + 2836 "&" + 2837 table_id + 2838 "_paged=1"; 2839 2840 if (column_orderby) { 2841 query += 2842 "&" + 2843 table_id + 2844 "_column_orderby=" + 2845 encodeURIComponent(column_orderby); 2846 // Always send meta_key key (even empty) so server can clear stale keys 2847 // when switching away from a custom-field sort. 2848 query += 2849 "&" + 2850 table_id + 2851 "_column_meta_key=" + 2852 encodeURIComponent(column_meta_key); 2853 if (column_orderby_attribute) { 2854 query += 2855 "&" + 2856 table_id + 2857 "_column_orderby_attribute=" + 2858 encodeURIComponent(column_orderby_attribute); 2859 } 2860 if (column_orderby_taxonomy) { 2861 query += 2862 "&" + 2863 table_id + 2864 "_column_orderby_taxonomy=" + 2865 encodeURIComponent(column_orderby_taxonomy); 2866 } 2867 } 2868 2869 attempt_ajax($container, query, true, "column_heading_sort"); 2870 }; 2871 2872 // ajax 2873 var attempt_ajax = (window.wcpt_attempt_ajax = ( 2874 $container, 2875 new_query, 2876 append, 2877 purpose, 2878 ) => { 2879 const query = build_ajax_query_string( 2880 $container, 2881 new_query, 2882 append, 2883 purpose, 2884 ); 2885 2886 // trigger body event to hook in 2887 var data = { 2888 query: query, 2889 $wcpt: $container, 2890 purpose: purpose, 2891 proceed: true, 2892 }; 2893 2894 var prev_nav_query = $container.attr("data-wcpt-nav-query"); 2895 2896 if (prev_nav_query === query) { 2897 data.proceed = false; 2898 } 2899 2900 $("body").trigger("wcpt_before_ajax_query", data); 2901 2902 $container.attr("data-wcpt-nav-query", data.query); 2903 2904 if (data.proceed) { 2905 fetch_markup_and_apply_callback( 2906 $container, 2907 data.query, 2908 purpose, 2909 ajax_success, 2910 ); 2911 } 2912 }); 2913 2914 function get_response_container(response) { 2915 var $response = $(response), 2916 $responseContainer = $response.filter(".wcpt").first(); 2917 2918 if (!$responseContainer.length) { 2919 $responseContainer = $response.find(".wcpt").first(); 2920 } 2921 2922 return $responseContainer.length ? $responseContainer : $response; 2923 } 2924 2925 function sync_container_attributes($container, $new_container) { 2926 if (!$new_container.length || !$new_container[0].attributes) { 2927 return; 2928 } 2929
2930 [...$new_container[0].attributes].forEach((attr) => { 2931 if ( 2932 attr.nodeName === "class" || 2933 attr.nodeName.substr(0, 10) === "data-wcpt-" 2934 ) { 2935 $container[0].setAttribute(attr.nodeName, attr.nodeValue); 2936 } 2937 }); 2938 } 2939 2940 function replace_paginated_content($container, $new_container) { 2941 var $target_scope = $container.find(".wcpt-nav-main").first(), 2942 $source_scope = $new_container.find(".wcpt-nav-main").first(), 2943 content_selector = [ 2944 ".wcpt-table-scroll-wrapper-outer", 2945 ".wcpt-pagination", 2946 ".wcpt-no-results", 2947 ".wcpt-in-footer", 2948 ".wcpt-infinite-scroll-dots", 2949 ".wcpt-add-selected-to-cart-with-pagination", 2950 ".wcpt-loading-screen", 2951 ].join(", "), 2952 previous_min_height, 2953 lock_height, 2954 $fragment; 2955 2956 if (!$target_scope.length) { 2957 $target_scope = $container; 2958 } 2959 2960 if (!$source_scope.length) { 2961 $source_scope = $new_container; 2962 } 2963 2964 // Keep height while swapping results so the page does not collapse and 2965 // jump the viewport to the top before the new table is inserted. 2966 previous_min_height = $target_scope[0].style.minHeight; 2967 lock_height = $target_scope.outerHeight(); 2968 if (lock_height) { 2969 $target_scope.css("min-height", lock_height + "px"); 2970 } 2971 2972 $fragment = $(document.createDocumentFragment()); 2973 $source_scope.children().each(function () { 2974 var $child = $(this); 2975 if ($child.is(content_selector)) { 2976 $fragment.append($child.clone(true, true)); 2977 } 2978 }); 2979 2980 $target_scope.children(content_selector).remove(); 2981 $target_scope.append($fragment); 2982 $target_scope.css("min-height", previous_min_height || ""); 2983 2984 // Paginate-only responses keep the outer container; merge variable-switch 2985 // price/availability templates so variation resets can restore prices. 2986 if ( 2987 typeof wcpt_util !== "undefined" && 2988 wcpt_util.merge_variable_switch_templates_from_response 2989 ) { 2990 wcpt_util.merge_variable_switch_templates_from_response( 2991 $new_container, 2992 $container, 2993 ); 2994 } 2995 } 2996 2997 // Header pagination lives inside preserved .wcpt-navigation, so update it 2998 // separately after the footer/table swap on paginate-only responses. 2999 function update_header_pagination($container, $new_container) { 3000 var $newPagination = $new_container 3001 .find( 3002 ".wcpt-add-selected-to-cart-with-pagination .wcpt-pagination, .wcpt-pagination", 3003 ) 3004 .first(); 3005 3006 if (!$newPagination.length) { 3007 return; 3008 } 3009 3010 $container.find(".wcpt-navigation .wcpt-pagination").each(function () { 3011 $(this).replaceWith($newPagination.clone(true, true)); 3012 }); 3013 } 3014 3015 function replace_result_count_placeholders(template, context) { 3016 if (typeof template !== "string") { 3017 return ""; 3018 } 3019 3020 [ 3021 ["displayed_results", context.displayed_results || 0], 3022 ["total_results", context.total_results || 0], 3023 ["first_result", context.first_result || 0], 3024 ["last_result", context.last_result || 0], 3025 ].forEach(function (entry) { 3026 var key = entry[0], 3027 value = String(entry[1]); 3028 3029 template = template 3030 .split("[" + key + "]") 3031 .join(value) 3032 .split("[wcpt_" + key + "]") 3033 .join(value); 3034 }); 3035 3036 return template; 3037 } 3038 3039 function update_result_count($container) { 3040 var contextAttr = $container.attr("data-wcpt-result-count-context"), 3041 $resultCounts = $container.find(".wcpt-result-count"); 3042 3043 if (!contextAttr || !$resultCounts.length) { 3044 return; 3045 } 3046 3047 var context = {}; 3048 try { 3049 context = JSON.parse(contextAttr); 3050 } catch (e) { 3051 return; 3052 } 3053 3054 $resultCounts.each(function () { 3055 var $resultCount = $(this); 3056 3057 $resultCount.removeClass( 3058 "wcpt-single-result wcpt-no-results wcpt-single-page", 3059 ); 3060 3061 if (context.html_class) { 3062 $resultCount.addClass(context.html_class); 3063 } 3064 3065 $resultCount 3066 .find(".wcpt-result-message") 3067 .text( 3068 replace_result_count_placeholders( 3069 $resultCount.attr("data-wcpt-message-template"), 3070 context, 3071 ), 3072 ); 3073 3074 $resultCount 3075 .find(".wcpt-single-page-message") 3076 .text( 3077 replace_result_count_placeholders( 3078 $resultCount.attr("data-wcpt-single-page-template"), 3079 context, 3080 ), 3081 ); 3082 3083 $resultCount 3084 .find(".wcpt-single-result-message") 3085 .text( 3086 replace_result_count_placeholders( 3087 $resultCount.attr("data-wcpt-single-result-template"), 3088 context, 3089 ), 3090 ); 3091 3092 $resultCount 3093 .find(".wcpt-no-results-message") 3094 .text( 3095 replace_result_count_placeholders( 3096 $resultCount.attr("data-wcpt-no-results-template"), 3097 context, 3098 ), 3099 ); 3100 });
3101 } 3102 3103 // ajax successful 3104 function ajax_success(response, $container, purpose) { 3105 $("body").trigger("wcpt_before_ajax_container_replace", { 3106 response: response, 3107 $container: $container, 3108 }); 3109 3110 // replace $container inner html 3111 var $new_container = get_response_container(response); 3112 if (purpose === "paginate" && $container.find(".wcpt-navigation").length) { 3113 replace_paginated_content($container, $new_container); 3114 update_header_pagination($container, $new_container); 3115 } else { 3116 $container.html($new_container.html()); 3117 } 3118 3119 sync_container_attributes($container, $new_container); 3120 update_result_count($container); 3121 $container.data("wcpt_last_ajax_purpose", purpose); 3122 3123 // scroll 3124 // -- get params 3125 var sc_attrs_string = $container.attr("data-wcpt-sc-attrs"), 3126 default_offset = 40, 3127 sc_attrs = 3128 sc_attrs_string && sc_attrs_string !== "{}" 3129 ? JSON.parse(sc_attrs_string) 3130 : {}, 3131 offset = { 3132 laptop: 3133 typeof sc_attrs.laptop_scroll_offset == "undefined" || 3134 sc_attrs.laptop_scroll_offset == "" 3135 ? default_offset 3136 : sc_attrs.laptop_scroll_offset, 3137 tablet: 3138 typeof sc_attrs.tablet_scroll_offset == "undefined" || 3139 sc_attrs.tablet_scroll_offset == "" 3140 ? default_offset 3141 : sc_attrs.tablet_scroll_offset, 3142 phone: 3143 typeof sc_attrs.phone_scroll_offset == "undefined" || 3144 sc_attrs.phone_scroll_offset == "" 3145 ? default_offset 3146 : sc_attrs.phone_scroll_offset, 3147 }; 3148 3149 // -- do scroll 3150 var device = wcpt_get_device_2($container); 3151 if ( 3152 (sc_attrs["auto_scroll"] === "true" || 3153 sc_attrs[device + "_auto_scroll"] === "true") && 3154 ["lazy_load", "device_switch", "refresh_table"].indexOf(purpose) === -1 3155 ) { 3156 var offset = offset[device]; 3157 3158 if (isNaN(offset)) { 3159 if (typeof offset === "string") { 3160 // selector 3161 offset = $(offset).height(); 3162 } else if (typeof offset === "object") { 3163 // jQuery object 3164 offset = offset.height(); 3165 } 3166 } 3167 3168 var containerTop = $container.offset().top - parseFloat(offset); 3169 var windowTop = $(window).scrollTop(); 3170 var windowBottom = windowTop + $(window).height(); 3171 3172 if (containerTop < windowTop || containerTop > windowBottom) { 3173 $("html, body").animate( 3174 { 3175 scrollTop: Math.max(0, containerTop), 3176 }, 3177 200, 3178 ); 3179 } 3180 } 3181 } 3182 3183 var build_ajax_query_string = (window.wcpt_build_ajax_query_string = ( 3184 $container, 3185 new_query, 3186 append, 3187 purpose, 3188 ) => { 3189 var table_id = $container.attr("data-wcpt-table-id"); 3190 3191 if (typeof purpose == "undefined") { 3192 throw "WCPT: Define AJAX purpose"; 3193 } 3194 3195 // combine earlier query 3196 var query = "", 3197 earlier_query = $container.attr("data-wcpt-query-string"); 3198 if (append && earlier_query) { 3199 earlier_query = earlier_query.substring(1); 3200 query = "?"; 3201 3202 $.each( 3203 $.extend( 3204 {}, 3205 wcpt_util.parse_query_string(earlier_query), 3206 wcpt_util.parse_query_string(new_query), 3207 ), 3208 function (key, val) { 3209 if (val !== "undefined") { 3210 query += key + "=" + encodeURIComponent(val) + "&"; 3211 } 3212 }, 3213 ); 3214 query = query.substring(0, query.length - 1); 3215 } else { 3216 query = "?" + new_query; 3217 } 3218 3219 // lazy load 3220 if (purpose == "lazy_load") { 3221 query += "&" + window.location.search.substr(1); 3222 } 3223 3224 // persist params 3225 var parsed_params = wcpt_util.parse_query_string( 3226 window.location.search.substring(1), 3227 ); 3228 var query_obj = wcpt_util.parse_query_string(query.substring(1)); 3229 3230 if (typeof window.wcpt_persist_params !== "undefined") { 3231 $.each(wcpt_persist_params, function (index, i) { 3232 if ( 3233 parsed_params[i] !== "undefined" && 3234 typeof parsed_params[i] !== "undefined" && 3235 typeof query_obj[i] == "undefined" 3236 ) { 3237 query += "&" + i + "=" + parsed_params[i]; 3238 } 3239 }); 3240 } 3241 3242 // device 3243 var device = wcpt_get_device_2($container); 3244 3245 var query_obj = wcpt_util.parse_query_string(query); 3246 if (query_obj[table_id + "_device"] !== device) { 3247 query += "&" + table_id + "_device=" + device; 3248 } 3249 3250 // switching device 3251 if (purpose === "device_switch") { 3252 // remove column sort 3253 // first find out if column sort is present 3254 if (query.indexOf(table_id + "_orderby=column_") !== -1) { 3255 query = query.replace( 3256 new RegExp(table_id + "_orderby=column_\\d+", "g"), 3257 "", 3258 ); 3259 query = query.replace( 3260 new RegExp(table_id + "_order=(ASC|DESC)", "g"), 3261 "", 3262 ); 3263 } 3264 } 3265 3266 // shortcode attributes 3267 var sc_attrs = get_sc_attrs($container); 3268 3269 // search - orderby relevance 3270 var new_query_p = new_query ? wcpt_util.parse_query_string(new_query) : {}, 3271 earlier_query_p = earlier_query 3272 ? wcpt_util.parse_query_string(earlier_query.substring(1)) 3273 : {}, 3274 search_orderby = sc_attrs.search_orderby ? "search_orderby" : "relevance", 3275 search_order = sc_attrs.search_order ? "search_order" : ""; 3276 3277 // detect if a new search is taking place in this query 3278 $.each(new_query_p, function (key, val) { 3279 if ( 3280 key.indexOf("search") !== -1 && 3281 val && 3282 earlier_query_p[key] !== val.replace(/\+/g, " ") 3283 ) { 3284 // reset search orderby and order 3285 query += 3286 "&" + 3287 table_id + 3288 "_orderby=" + 3289 search_orderby + 3290 "&" + 3291 table_id + 3292 "_order=" + 3293 search_order; 3294 return false; 3295 } 3296 });
3297 3298 // flag table has been filtered 3299 if (purpose == "filter") { 3300 query += "&" + table_id + "_filtered=true"; 3301 } 3302 3303 // coming from shop? 3304 if (parsed_params[table_id + "_from_shop"]) { 3305 query += "&" + table_id + "_from_shop=true"; 3306 } 3307 3308 // add shortcode attributes 3309 if (!$.isEmptyObject(sc_attrs)) { 3310 // applying fix % conflict with % -> %25 3311 // query += '&' + table_id + '_sc_attrs=' + encodeURIComponent( _sc_attrs.replaceAll('%', '%25') ); 3312 query += 3313 "&" + 3314 table_id + 3315 "_sc_attrs=" + 3316 encodeURIComponent(JSON.stringify(sc_attrs)); 3317 } 3318 3319 if (purpose === "paginate") { 3320 query += "&wcpt_paginate_only=1"; 3321 } 3322 3323 return query; 3324 }); 3325 3326 function get_sc_attrs($container) { 3327 var _sc_attrs = $container.attr("data-wcpt-sc-attrs"), 3328 sc_attrs = _sc_attrs && _sc_attrs !== "{}" ? JSON.parse(_sc_attrs) : {}; 3329 return sc_attrs; 3330 } 3331 3332 function get_column_layout_source_map($container) { 3333 var raw = $container.attr("data-wcpt-column-layout-source-map"); 3334 if (!raw || raw === "{}") return {}; 3335 try { 3336 return JSON.parse(raw); 3337 } catch (e) { 3338 return {}; 3339 } 3340 } 3341 3342 function get_layout_source_for_device(layoutMap, device) { 3343 if (!layoutMap || typeof layoutMap !== "object") return device; 3344 return layoutMap[device] ? layoutMap[device] : device; 3345 } 3346 3347 // get device based on device column container being displayed 3348 function wcpt_get_device_2($container) { 3349 var device = "laptop", 3350 // Single-device container system: device wrapper should always exist. 3351 // Avoid :visible here to prevent timing/CSS interactions causing 3352 // an incorrect "current device" and triggering unnecessary reloads. 3353 $scroll_outer = $container 3354 .find(".wcpt-table-scroll-wrapper-outer") 3355 .first(); 3356 3357 if ($scroll_outer.length) { 3358 if ($scroll_outer.hasClass("wcpt-device-phone")) { 3359 device = "phone"; 3360 } else if ($scroll_outer.hasClass("wcpt-device-tablet")) { 3361 device = "tablet"; 3362 } 3363 } else if ($("body").hasClass("wcpt-nav-modal-on")) { 3364 device = $(".wcpt-nav-modal").attr("data-wcpt-device"); 3365 } else if ( 3366 $( 3367 ".wcpt-required-but-missing-nav-filter-message, .wcpt-no-results", 3368 $container, 3369 ).length 3370 ) { 3371 device = $( 3372 ".wcpt-required-but-missing-nav-filter-message, .wcpt-no-results", 3373 $container, 3374 ).attr("data-wcpt-device"); 3375 } 3376 3377 return device; 3378 } 3379 3380 // request markup from sever and apply callback 3381 var fetch_markup_and_apply_callback = 3382 (window.wcpt_fetch_markup_and_apply_callback = ( 3383 $container, 3384 query, 3385 purpose, 3386 callback, 3387 ) => { 3388 var table_id = $container.attr("data-wcpt-table-id"), 3389 sc_attrs = wcpt_util.get_sc_attrs($container); 3390 3391 // prep url 3392 var url = 3393 wcpt_params.wc_ajax_url.replace("%%endpoint%%", "wcpt_ajax") + 3394 "&" + 3395 query.slice(1), 3396 data = { 3397 wcpt_encrypted_attrs: wcpt_util.get_encrypted_attrs($container), 3398 id: table_id, // @TODO -- remove this 3399 }; 3400 3401 // reload if ajax is disabled 3402 if (sc_attrs.disable_ajax && sc_attrs.disable_ajax !== "false") { 3403 var wcpt_redirect_params = { 3404 $container: $container, 3405 query: query, 3406 base_url: window.location.href.split("?")[0], 3407 purpose: purpose, 3408 proceed: true, 3409 }; 3410 $("body").trigger("wcpt_before_nav_redirect", wcpt_redirect_params); 3411 if (wcpt_redirect_params.proceed) { 3412 window.location.href = wcpt_redirect_params.base_url + "?" + query; 3413 } 3414 return; 3415 } 3416 3417 // allow table from cache @TODO -- convert to query param 3418 var permit_cache = 3419 purpose !== "refresh_table" && typeof WavePlayer === "undefined"; 3420 3421 if ( 3422 // use cache 3423 permit_cache && 3424 window.wcpt_cache.exist(query) 3425 ) { 3426 callback(window.wcpt_cache.get(query), $container, purpose); 3427 $container.data("wcpt_last_ajax_purpose", purpose); 3428 after_every_load($container); 3429 return; 3430 } else { 3431 // else AJAX 3432 $.ajax({
3433 url: url, 3434 method: "POST", 3435 beforeSend: function () { 3436 $container.addClass("wcpt-loading"); 3437 $container.trigger("wcpt_before_ajax"); 3438 return true; 3439 }, 3440 data: data, 3441 }).done(function (response) { 3442 // success 3443 if (response && response.indexOf("wcpt-table") !== -1) { 3444 window.wcpt_cache.set(query, response); // update cache 3445 callback(window.wcpt_cache.get(query), $container, purpose); 3446 $container.data("wcpt_last_ajax_purpose", purpose); 3447 after_every_load($container); 3448 3449 // fail 3450 } else { 3451 console.log("wcpt notice: query fail"); 3452 } 3453 3454 $container.removeClass("wcpt-loading"); 3455 }); 3456 } 3457 }); 3458 3459 // variable product modal form 3460 //-- close modal 3461 $("body").on("click", ".wcpt-modal, .wcpt-close-modal", function (e) { 3462 var $target = $(e.target), 3463 $modal = $(this).closest(".wcpt-modal"); 3464 if ( 3465 $target.hasClass("wcpt-modal") || 3466 $target.closest(".wcpt-close-modal").length 3467 ) { 3468 $modal.trigger("wcpt_close"); 3469 } 3470 }); 3471 3472 window.wcpt_update_cart_items = function (cart) { 3473 var cart_products = {}, 3474 total = 0; 3475 $.each(cart, function (key, item) { 3476 if (!cart_products[item.product_id]) { 3477 cart_products[item.product_id] = 0; 3478 } 3479 3480 if (item.variation_id && !cart_products[item.variation_id]) { 3481 cart_products[item.variation_id] = 0; 3482 } 3483 3484 cart_products[item.product_id] += item.quantity; 3485 3486 if (item.variation_id) { 3487 cart_products[item.variation_id] += item.quantity; 3488 } 3489 3490 total += item.quantity; 3491 }); 3492 3493 // -- update each product row 3494 $(".wcpt-row").each(function () { 3495 var $this = $(this), 3496 id = $this.attr("data-wcpt-variation-id") 3497 ? $this.attr("data-wcpt-variation-id") 3498 : $this.attr("data-wcpt-product-id"), 3499 qty = cart_products[id] ? cart_products[id] : 0, 3500 $badge = $this.find(".wcpt-cart-badge-number"), 3501 $remove = $this.find(".wcpt-remove"); 3502 3503 $this.attr("data-wcpt-in-cart", qty); 3504 3505 if (qty) { 3506 add_count_badge_to_button(qty, $badge.closest(".wcpt-button")); 3507 } else { 3508 $badge.text(""); 3509 } 3510 }); 3511 }; 3512 3513 // anchor tag 3514 $("body").on("click touchstart", "[data-wcpt-href]", function () { 3515 window.location = $(this).attr("data-wcpt-href"); 3516 }); 3517 3518 // accordion 3519 //-- filters 3520 // $('body').on('click', '.wcpt-left-sidebar .wcpt-filter > .wcpt-filter-heading', function(e){ 3521 // if( $(e.target).closest('.wcpt-tooltip').length ){ 3522 // return; 3523 // } 3524 3525 // var $this = $(this), 3526 // $filter = $this.closest('.wcpt-filter'); 3527 // $filter.toggleClass('wcpt-filter-open wcpt-open'); 3528 // }) 3529 // //-- filter clicked directly outside of heading 3530 // $('body').on('click', '.wcpt-left-sidebar .wcpt-filter:not(.wcpt-filter-open)', function(e){ 3531 // var $this = $(this); 3532 // if( e.target === this ){ 3533 // $this.addClass('wcpt-filter-open'); 3534 // } 3535 // }) 3536 //-- taxonomy parent 3537 $("body").on( 3538 "click", 3539 ".wcpt-filter-hierarchy-accordion__trigger", 3540 function (e) { 3541 var $this = $(this); 3542 $this 3543 .closest(".wcpt-filter-hierarchy-accordion") 3544 .toggleClass("wcpt-filter-hierarchy-accordion--open"); 3545 e.stopPropagation(); 3546 return false; 3547 }, 3548 ); 3549 3550 // nav modal 3551 function nav_modal(e) { 3552 var $button = $(e.target).closest(".wcpt-rn-button"), 3553 modal_type = $button.attr("data-wcpt-modal"), 3554 $wcpt = $button.closest(".wcpt"), 3555 wcpt_id = $wcpt.attr("id"), 3556 $nav_modal = $($wcpt.find(".wcpt-nav-modal-tpl").html()), 3557 // Header + sidebar may both contain the same filters â keep one of each 3558 $filters = dedupe_nav_elements( 3559 $wcpt.find(".wcpt-filter").not('[data-wcpt-filter="sort_by"]'), 3560 ), 3561 $search = dedupe_nav_elements($wcpt.find(".wcpt-search-wrapper")), 3562 $sort = dedupe_nav_elements( 3563 $wcpt.find('[data-wcpt-filter="sort_by"].wcpt-filter'), 3564 ), 3565 radios = {}; 3566 3567 $(".wcpt-nm-sort-placeholder", $nav_modal).replaceWith($sort.clone()); 3568 $(".wcpt-nm-filters-placeholder", $nav_modal).replaceWith( 3569 $search.clone().add($filters.clone()), 3570 ); 3571 3572 $( 3573 '.wcpt-nm-sort-placeholder [data-wcpt-filter="sort_by"]', 3574 $nav_modal, 3575 ).addClass("wcpt-open"); 3576 3577 if (modal_type == "sort") { 3578 $nav_modal.addClass("wcpt-show-sort").removeClass("wcpt-show-filters"); 3579 } else { 3580 // filter 3581 $nav_modal.addClass("wcpt-show-filters").removeClass("wcpt-show-sort"); 3582 } 3583 3584 // record radios 3585 $wcpt.find("input[type=radio]:checked").each(function () { 3586 var $this = $(this); 3587 radios[$this.attr("name")] = $this.val(); 3588 });
3589 $nav_modal.data("wcpt-radios", radios); 3590 3591 // ':' at the end of row labels 3592 $nav_modal 3593 .find( 3594 ".wcpt-filter.wcpt-options-row > .wcpt-filter-heading > .wcpt-options-heading > .wcpt-item-row > .wcpt-text:last-child", 3595 ) 3596 .each(function () { 3597 var $this = $(this), 3598 text = $this.text().trim(); 3599 3600 if (text.substr(-1) === ":") { 3601 $this.text(text.substr(0, text.length - 1)); 3602 } 3603 }); 3604 3605 // duplicate header 3606 // if( ! $nav_modal.find('.wcpt-nm-heading--sticky').length ){ 3607 // var $header = $nav_modal.find('.wcpt-nm-heading'); 3608 // $header.clone().addClass('wcpt-nm-heading--sticky').insertAfter( $header ); 3609 // } 3610 3611 // switch option row to dropdown 3612 const switch_classes = { 3613 "wcpt-options-row": "wcpt-dropdown", 3614 "wcpt-option": "wcpt-dropdown-option", 3615 "wcpt-options-heading": "wcpt-dropdown-label", 3616 "wcpt-options": " wcpt-dropdown-menu", 3617 }; 3618 $.each(switch_classes, function (key, value) { 3619 $nav_modal 3620 .find("." + key) 3621 .removeClass(key) 3622 .addClass(value); 3623 }); 3624 3625 // append 3626 window.wcpt_nav_modal_scroll = window.scrollY; 3627 3628 $("body") 3629 .trigger("wcpt-nav-modal-on") 3630 .addClass("wcpt-nav-modal-on") 3631 .append('<div class="wcpt-nav-modal-backdrop"></div>') 3632 .append($nav_modal); 3633 3634 $(".wcpt-nav-modal-backdrop").one("click", function () { 3635 $nav_modal.trigger("wcpt_close"); 3636 }); 3637 3638 // open filter dropdown if its alone 3639 $other_filters = $nav_modal.find( 3640 '.wcpt-filter:not([data-wcpt-filter="sort_by"])', 3641 ); 3642 3643 if ($other_filters.length === 1) { 3644 $other_filters.addClass("wcpt-filter-open wcpt-open"); 3645 } 3646 3647 // multirange 3648 $(".wcpt-range-slider-wrapper", $nav_modal).each(function () { 3649 var $this = $(this), 3650 $original = $this.children(".original"), 3651 $ghost = $this.children(".ghost"), 3652 $new_slider = $("<input/>").attr({ 3653 type: "range", 3654 class: "wcpt-range-slider", 3655 min: $original.attr("min"), 3656 max: $original.attr("max"), 3657 step: $original.attr("step"), 3658 value: $original.attr("data-wcpt-initial-value"), 3659 }); 3660 3661 $original.add($ghost).remove(); 3662 3663 $this.append($new_slider); 3664 wcpt__multirange($new_slider[0]); 3665 }); 3666 3667 // apply 3668 //-- filter 3669 $nav_modal.find(".wcpt-nm-apply").on("click", function () { 3670 var $nav_clone = $nav_modal.clone(); 3671 3672 nav_clone_operations($nav_clone); 3673 3674 var query = $("<form>").append($nav_clone).serialize(), 3675 $container = $("#" + wcpt_id); 3676 3677 $nav_modal.trigger("wcpt_close"); 3678 3679 attempt_ajax($container, query, false, "filter"); 3680 }); 3681 //-- sort 3682 $nav_modal.filter(".wcpt-show-sort").on("change", function () { 3683 var $nav_clone = $nav_modal.clone(); 3684 3685 nav_clone_operations($nav_clone); 3686 3687 var query = $("<form>").append($nav_clone).serialize(), 3688 $container = $("#" + wcpt_id); 3689 3690 $nav_modal.trigger("wcpt_close"); 3691 3692 attempt_ajax($container, query, false, "filter"); 3693 }); 3694 3695 // clear 3696 $nav_modal.find(".wcpt-nm-reset").on("click", function () { 3697 var query = $("<form>").append($nav_modal.clone()).serialize(), 3698 $container = $("#" + wcpt_id), 3699 query = ""; 3700 3701 $nav_modal.trigger("wcpt_close"); 3702 3703 attempt_ajax($container, query, false, "filter"); 3704 }); 3705 3706 // close 3707 $nav_modal.find(".wcpt-nm-close").on("click", function (e) { 3708 e.preventDefault(); 3709 3710 var $container = $("#" + wcpt_id), 3711 radios = $.extend({}, $nav_modal.data("wcpt-radios")); 3712 3713 $nav_modal.trigger("wcpt_close"); 3714 3715 $.each(radios, function (name, val) { 3716 $wcpt 3717 .find('input[type=radio][name="' + name + '"][value="' + val + '"]') 3718 .each(function () { 3719 $(this).prop("checked", "checked"); 3720 }); 3721 }); 3722 }); 3723 3724 // scroll fix 3725 // var prev_y = false; 3726 // $(".wcpt-nav-modal") 3727 // .on("touchstart", function (e) { 3728 // prev_y = e.originalEvent.touches[0].clientY; 3729 // }) 3730 // .on("touchmove", function (e) { 3731 // if ( 3732 // (e.originalEvent.touches[0].clientY > prev_y && !this.scrollTop) || 3733 // (e.originalEvent.touches[0].clientY < prev_y && 3734 // this.scrollTop === this.scrollHeight - this.offsetHeight) 3735 // ) { 3736 // e.preventDefault(); 3737 // } 3738 // }); 3739 } 3740 3741 $("body").on("wcpt_close", ".wcpt-nav-modal", function () { 3742 var $this = $(this), 3743 table_id = $this.attr("data-wcpt-table-id"), 3744 $container = $("#wcpt-" + table_id); 3745 3746 $("body").trigger("wcpt-nav-modal-off").removeClass("wcpt-nav-modal-on"); 3747 3748 $(".wcpt-nav-modal-backdrop").remove(); 3749 $this.remove(); 3750 window.scroll(null, wcpt_nav_modal_scroll); 3751 }); 3752 3753 // row option change ev 3754 $("body").on("change", ".wcpt-options-row .wcpt-option input", function () { 3755 var $this = $(this), 3756 $label = $this.closest(".wcpt-option "); 3757 3758 if ($this.is(":radio")) { 3759 if (this.checked) { 3760 $label.addClass("wcpt-active").siblings().removeClass("wcpt-active"); 3761 } 3762 } else { 3763 // checkbox 3764 if (this.checked) { 3765 $label.addClass("wcpt-active"); 3766 } else { 3767 $label.removeClass("wcpt-active"); 3768 } 3769 } 3770 }); 3771 3772 // toggle 3773 $("body").on("click", ".wcpt-tg-trigger", function () { 3774 var $this = $(this), 3775 $toggle = $this.closest(".wcpt-toggle"), 3776 $table = $this.closest(".wcpt-table"); 3777 3778 $toggle.toggleClass(" wcpt-tg-on wcpt-tg-off "); 3779 }); 3780 3781 $("body").on("click", ".wcpt-rn-filter, .wcpt-rn-sort", nav_modal); 3782 3783 // photoswipe 3784 3785 // -- open 3786 function init_photoswipe($this, index, append_item) { 3787 // photoswipe gallery 3788 if ( 3789 typeof PhotoSwipe !== "undefined" && 3790 typeof PhotoSwipeUI_Default !== "undefined" 3791 ) { 3792 var items = JSON.parse($this.attr("data-wcpt-photoswipe-items")), 3793 index = typeof index == "undefined" || !index ? 0 : parseInt(index), 3794 color_theme = $this.attr("data-wcpt-lightbox-color-theme") || "black"; 3795 3796 // append items 3797 if (append_item) { 3798 items.push(append_item); 3799 } 3800 3801 var index_src = items[index].src; 3802 3803 // remove duplicates 3804 var unique_src = [], 3805 _items = []; 3806 3807 $.each(items, function (index2, item) { 3808 if (-1 === $.inArray(item.src, unique_src)) { 3809 _items.push(item); 3810 unique_src.push(item.src); 3811 } 3812 }); 3813 items = _items; 3814 3815 var options = JSON.parse($this.attr("data-wcpt-photoswipe-options")), 3816 photoswipe = new PhotoSwipe( 3817 $(".pswp")[0], 3818 PhotoSwipeUI_Default, 3819 items, 3820 options, 3821 ); 3822 3823 photoswipe.init(); 3824 3825 // add wcpt-photoswipe class and trigger selector attr 3826 var color_theme_class = "wcpt-photoswipe--theme-" + color_theme; 3827 $(".pswp").addClass("wcpt-photoswipe " + color_theme_class); 3828 3829 // add wcpt class on photoswipe and trigger selector 3830 3831 // reposition index in case item was deleted 3832 $.each(items, function (index3, item) {
3833 if (item.src === index_src) { 3834 index = index3; 3835 } 3836 }); 3837 3838 photoswipe.goTo(index); 3839 3840 var $body = $("body"); 3841 3842 $body.addClass("wcpt-photoswipe-visible"); 3843 photoswipe.listen("close", function () { 3844 setTimeout(function () { 3845 // fix photoswipe click through bug 3846 $body.removeClass("wcpt-photoswipe-visible"); 3847 }, 10); 3848 }); 3849 3850 // remove wcpt-photoswipe class and trigger selector attr after closing 3851 photoswipe.listen("destroy", function () { 3852 $(".pswp").removeClass("wcpt-photoswipe " + color_theme_class); 3853 }); 3854 3855 $(photoswipe.container).data("wcpt_photoswipe", photoswipe); 3856 3857 return true; 3858 } else { 3859 return false; 3860 } 3861 } 3862 3863 // -- close 3864 $("body").on("click", ".pswp__container", function (e) { 3865 var $this = $(this), 3866 photoswipe = $this.data("wcpt_photoswipe"); 3867 3868 if (window.innerWidth < 720 && photoswipe) { 3869 var $target = $(e.target); 3870 if (!$target.closest(".pswp__button").length) { 3871 photoswipe.close(); 3872 } 3873 } 3874 }); 3875 3876 // gallery strip 3877 3878 // -- image 3879 $("body").on("click", ".wcpt-gallery__item", function () { 3880 var $this = $(this), 3881 index = parseInt($this.attr("data-wcpt-gallery-item")), 3882 $gallery = $this.closest(".wcpt-gallery"); 3883 3884 if (!$gallery.hasClass("wcpt-gallery--include-featured")) { 3885 index += 1; 3886 } 3887 3888 init_photoswipe($gallery, index); 3889 }); 3890 3891 // -- link 3892 $("body").on("click", ".wcpt-gallery a", function (e) { 3893 e.preventDefault(); 3894 3895 var $this = $(this), 3896 $gallery = $this.closest(".wcpt-gallery"); 3897 3898 init_photoswipe($gallery); 3899 }); 3900 3901 // image lightbox 3902 $("body").on("click", ".wcpt-lightbox-enabled", function () { 3903 destroy_offset_zoom_containers(); 3904 3905 var $this = $(this); 3906 if ( 3907 $this 3908 .closest(".wcpt") 3909 .hasClass("wcpt-quick-view-trigger--product-image") && 3910 !$this 3911 .closest(".wcpt-row") 3912 .hasClass("wcpt-quick-view-trigger__disabled-for-product") 3913 ) { 3914 return; 3915 } 3916 3917 var index = 0, 3918 $row = get_product_rows($this), 3919 src = $this.attr("data-wcpt-lightbox"), 3920 pswp_items = JSON.parse($this.attr("data-wcpt-photoswipe-items")), 3921 append_item = false; 3922 3923 if ( 3924 $row.attr("data-wcpt-type") === "variable" && 3925 $row.data("wcpt_variation_selected") 3926 ) { 3927 var variation = $row.data("wcpt_variation"), 3928 src = variation.image.full_src, 3929 found = false; 3930 3931 $.each(pswp_items, function (_index, item) { 3932 if (item.src == src) { 3933 index = _index; 3934 found = true; 3935 return false; 3936 } 3937 }); 3938 3939 if (!found) { 3940 append_item = { 3941 src: variation.image.full_src, 3942 w: variation.image.full_src_w, 3943 h: variation.image.full_src_h, 3944 title: variation.image.title, 3945 }; 3946 3947 index = pswp_items.length; 3948 } 3949 } else { 3950 // get starting index 3951 if ($this.attr("data-wcpt-photoswipe-items")) { 3952 $.each(pswp_items, function (_index, item) { 3953 if (item.src == src) { 3954 index = _index; 3955 return false; 3956 } 3957 }); 3958 } 3959 } 3960 3961 if (!init_photoswipe($this, index, append_item)) { 3962 var $el = $( 3963 '<div class="wcpt-lightbox-screen"><div class="wcpt-lightbox-loader"></div><div class="wcpt-lightbox-close"></div><img class="wcpt-lightbox-image" src="' + 3964 src + 3965 '"></div>', 3966 ); 3967 $("body").append($el); 3968 $el.on("click ", function () { 3969 $el.remove(); 3970 }); 3971 } 3972 }); 3973 3974 // image zoom 3975 //-- image hover 3976 $("body").on( 3977 "mouseenter", 3978 '.wcpt-zoom-enabled[data-wcpt-zoom-trigger="image_hover"]', 3979 function () { 3980 var $this = $(this), 3981 level = $this.attr("data-wcpt-zoom-level"); 3982 if (!level) { 3983 level = "1.5"; 3984 } 3985 3986 if ($this.closest(".wcpt-device-tablet, .wcpt-device-phone").length) { 3987 return; 3988 } 3989 3990 $this.css({ 3991 transform: "scale(" + level + ")", 3992 "z-index": "2", 3993 }); 3994 3995 $this.one("mouseleave", function () { 3996 $this.css({ 3997 transform: "", 3998 "z-index": "", 3999 }); 4000 }); 4001 }, 4002 ); 4003 //-- row hover 4004 $("body").on("mouseenter", ".wcpt-row", function () { 4005 var $row = $(this); 4006 $row 4007 .find('.wcpt-zoom-enabled[data-wcpt-zoom-trigger="row_hover"]') 4008 .each(function () { 4009 var $zoom_me = $(this), 4010 level = $zoom_me.attr("data-wcpt-zoom-level"); 4011 if (!level) { 4012 level = "1.5"; 4013 } 4014 4015 if ( 4016 $zoom_me.closest(".wcpt-device-tablet, .wcpt-device-phone").length 4017 ) { 4018 return; 4019 } 4020 4021 $zoom_me.css({ 4022 transform: "scale(" + level + ")", 4023 "z-index": "2", 4024 }); 4025 4026 $row.one("mouseleave", function () { 4027 $zoom_me.css({ 4028 transform: "", 4029 "z-index": "", 4030 }); 4031 }); 4032 }); 4033 }); 4034 4035 // product image offset zoom 4036 // -- attach hover handler 4037 $("body").on(
4038 "mouseenter.wcpt_offset_zoom", 4039 ".wcpt-product-image-wrapper--offset-zoom-enabled, .wcpt-gallery--offset-zoom-enabled .wcpt-gallery__item-wrapper", 4040 function (e) { 4041 var $this = $(this), 4042 $img = $this.find("img").not(".wcpt-product-image-on-hover").first(), 4043 zoom_src = $this.attr("data-wcpt-offset-zoom-image-src"), 4044 $offset_zoom_img = $("<img>", { 4045 class: "wcpt-offset-zoom-container__image", 4046 src: zoom_src || $img.attr("src"), 4047 }); 4048 4049 var $offset_zoom = $("<div>", { 4050 class: 4051 "wcpt-offset-zoom-container " + 4052 $this.attr("data-wcpt-offset-zoom-image-html-class"), 4053 }).append($offset_zoom_img), 4054 $wcpt = $this.closest(".wcpt"), 4055 original_title = $img.attr("title"); 4056 4057 // Remove the title on mouseenter and store original in data attr 4058 $img.data("wcpt-original-title", original_title); 4059 $img.removeAttr("title"); 4060 4061 if ($this.closest(".frzTbl--grab-and-scroll--grabbing").length) { 4062 return; 4063 } 4064 4065 destroy_offset_zoom_containers(); 4066 4067 $wcpt.append($offset_zoom).on("mousemove.wcpt_offset_zoom", function (e) { 4068 position_offset_zoom_container(e, $offset_zoom, $this); 4069 }); 4070 4071 setTimeout(function () { 4072 $offset_zoom.addClass("wcpt-offset-zoom-container--fade-in"); 4073 }, 50); 4074 4075 $this.on("mouseleave", function () { 4076 // Restore the original title on mouseleave 4077 var $img = $this.find("img"), 4078 original_title = $img.data("wcpt-original-title"); 4079 if (typeof original_title !== "undefined") { 4080 $img.attr("title", original_title); 4081 } 4082 destroy_offset_zoom_containers(); 4083 }); 4084 }, 4085 ); 4086 // -- turn off hover handler on touch screens 4087 $("body").on("touchstart", function () { 4088 $("body").off("mouseenter.wcpt_offset_zoom"); 4089 }); 4090 4091 function position_offset_zoom_container(e, $offset_zoom, $trigger) { 4092 var left = e.originalEvent.clientX + 40, 4093 top = e.originalEvent.clientY, 4094 position = "right"; 4095 4096 $offset_zoom.css({ 4097 left: left, 4098 top: top, 4099 }); 4100 4101 var rect = $offset_zoom.get(0).getBoundingClientRect(), 4102 viewport_width = 4103 window.innerWidth || document.documentElement.clientWidth, 4104 viewport_height = 4105 window.innerHeight || document.documentElement.clientHeight; 4106 4107 if (rect.right > viewport_width) { 4108 position = "left"; 4109 } 4110 4111 if (position == "left") { 4112 left = $trigger.get(0).getBoundingClientRect().left - 40 - rect.width; 4113 } 4114 4115 if (rect.top < 0) { 4116 top = 0 + 0.25 * rect.height; 4117 } else if (rect.bottom > viewport_height) { 4118 top -= rect.bottom - viewport_height; 4119 } 4120 4121 $offset_zoom.css({ 4122 left: left, 4123 top: top, 4124 }); 4125 } 4126 4127 function destroy_offset_zoom_containers() { 4128 $(".wcpt-offset-zoom-container").remove(); 4129 $body.off("mousemove.wcpt_offset_zoom"); 4130 } 4131 4132 // uncheck variation radio 4133 $("body").on("click", ".wcpt-variation-radio", function (e) { 4134 var $this = $(this), 4135 $variation = $this.closest(".wcpt-select-variation"), 4136 $row = $this.closest(".wcpt-row"); 4137 4138 if ( 4139 $variation.hasClass("wcpt-selected") && 4140 window.navigator.userAgent.indexOf("Edge") == -1 4141 ) { 4142 $this.prop("checked", false); 4143 $this.change(); 4144 4145 $row.trigger("select_variation", { 4146 variation_id: false, 4147 complete_match: false, 4148 attributes: false, 4149 variation: false, 4150 variation_found: false, 4151 variation_selected: false, 4152 variation_available: false, 4153 }); 4154 } 4155 }); 4156 4157 // variation selected class toggle 4158 $("body").on("change", ".wcpt-variation-radio", function () { 4159 var $this = $(this), 4160 $others = $( 4161 '.wcpt-variation-radio[name="' + $(this).attr("name") + '"]', 4162 ).not($(this)), 4163 $variation = $this.closest(".wcpt-select-variation"); 4164 4165 if ($this.is(":checked")) { 4166 $variation.addClass("wcpt-selected"); 4167 } else { 4168 $variation.removeClass("wcpt-selected"); 4169 } 4170 4171 $others 4172 .not(":checked") 4173 .closest(".wcpt-select-variation") 4174 .removeClass("wcpt-selected"); 4175 }); 4176 4177 // select variation (main) 4178 //-- sync 4179 $("body").on( 4180 "select_variation", 4181 ".wcpt-product-type-variable", 4182 function (e, data) { 4183 var $row = get_product_rows($(this)); 4184 4185 // update dropdown 4186 var $variation_dropdown = $row.find(".wcpt-select-variation-dropdown"); 4187 $variation_dropdown.val(data.variation_id ? data.variation_id : ""); 4188 4189 // update radio 4190 $row 4191 .find('.wcpt-variation-radio[value="' + data.variation_id + '"]') 4192 .prop("checked", true); 4193 4194 // update form 4195 $row.find(".variations_form").each(function () { 4196 var $this = $(this); 4197 current_variation_id = 4198 $(".variation_id", $this).val() === "0" 4199 ? "" 4200 : $(".variation_id", $this).val(); 4201 4202 if (data.variation_id != current_variation_id) { 4203 window.wcpt_form_reset_flag = true; 4204 $(".reset_variations", $this).trigger("click.wc-variation-form"); 4205 window.wcpt_form_reset_flag = false; 4206 4207 // select variation in form 4208 if (data.variation_id) { 4209 $(".variations select", $this).each(function () { 4210 var $this = $(this), 4211 name = $this.attr("name"); 4212 4213 if (typeof data.attributes[name] !== "undefined") { 4214 $this.val(data.attributes[name]); 4215 } else { 4216 $this.val(""); 4217 } 4218 }); 4219 4220 $this.trigger("check_variations"); 4221 } 4222 } 4223 }); 4224 4225 // update row 4226 $row.data("wcpt_variation", data.variation); 4227 $row.data("wcpt_variation_id", data.variation_id); 4228 $row.data("wcpt_complete_match", data.complete_match); 4229 $row.data("wcpt_attributes", data.attributes); 4230 $row.data("wcpt_variation_found", data.variation_found); 4231 $row.data("wcpt_variation_selected", data.variation_selected); 4232 $row.data("wcpt_variation_available", data.variation_available); 4233 $row.data("wcpt_variation_qty", data.variation_qty); 4234 4235 // update total 4236 update_row_total($row); 4237 update_table_add_selected_to_cart.call($row.get(0)); 4238 4239 // reset 4240 if (!data.variation_selected) { 4241 // -- add to cart buton 4242 var $button = $row.find('[data-wcpt-link-code^="cart"]'); 4243 4244 // -- -- no variation selected 4245 if ( 4246 $row.find(".wcpt-add-to-cart-wrapper").length || // cart form 4247 $variation_dropdown.length || // select variation -- dropdown 4248 $row.find(".wcpt-variation-radio").length // select variation -- radio 4249 ) { 4250 disable_button($button, "wcpt-no-variation-selected"); 4251 } 4252 4253 // -- -- out of stock 4254 if ($row.hasClass("wcpt-all-variations-out-of-stock")) { 4255 disable_button($button, "wcpt-all-variations-out-of-stock"); 4256 } else { 4257 enable_button($button, "wcpt-all-variations-out-of-stock"); 4258 } 4259 4260 // -- checkbox (UI only â do not clear cross-page persisted checkbox state) 4261 $row.first().trigger("wcpt_checkbox_change", false); 4262 4263 // -- qty input 4264 var $qty_wrapper = $row.find(".wcpt-quantity"), 4265 $qty = $qty_wrapper.find("input[type=number].qty"); 4266 4267 // -- -- disabled 4268 $qty_wrapper.addClass("wcpt-disabled"); 4269 $qty.attr("disabled", "disabled"); 4270 4271 if ($qty.length) { 4272 $qty.each(function () { 4273 var $this = $(this), 4274 inital_value = $this.attr("data-wcpt-initial-value"), 4275 value = ""; 4276 4277 if (inital_value === "0") { 4278 value = 0; 4279 } else if (inital_value == "min") { 4280 // No variation selected â max is 0 4281 value = 0; 4282 } else { 4283 value = ""; 4284 } 4285 4286 $this.attr({ 4287 min: "0", 4288 max: "0", 4289 step: "", 4290 value: value, 4291 }); 4292 4293 $this.val(value); 4294 4295 limit_qty_controller($this.closest(".wcpt-quantity")); 4296 }); 4297 } 4298 4299 // -- product image 4300 var $product_image_wrapper = $(".wcpt-product-image-wrapper", $row), 4301 $product_image = $( 4302 ".wcpt-product-image-wrapper > img:not(.wcpt-product-image-on-hover)", 4303 $row, 4304 ), 4305 $original_row = wcpt_get_original_row($row); 4306 4307 if ($product_image_wrapper.length) { 4308 if (!$original_row.data("wcpt_default_image")) { 4309 if ($product_image[0]) { 4310 // lazy load fix 4311 $original_row.data( 4312 "wcpt_default_image", 4313 $product_image[0].outerHTML, 4314 ); 4315 } else { 4316 handle_product_image_lazy_load($product_image_wrapper); 4317 } 4318 } else { 4319 $product_image.replaceWith( 4320 $original_row.data("wcpt_default_image"), 4321 ); 4322 } 4323 4324 if ($product_image_wrapper.hasClass("wcpt-lightbox-enabled")) { 4325 if (!$product_image_wrapper.attr("data-wcpt-lightbox--original")) { 4326 $product_image_wrapper.attr( 4327 "data-wcpt-lightbox--original", 4328 $product_image_wrapper.attr("data-wcpt-lightbox"), 4329 ); 4330 } else { 4331 $product_image_wrapper.attr( 4332 "data-wcpt-lightbox", 4333 $product_image_wrapper.attr("data-wcpt-lightbox--original"), 4334 ); 4335 } 4336 } 4337 4338 if ( 4339 $product_image_wrapper.hasClass( 4340 "wcpt-product-image-wrapper--offset-zoom-enabled", 4341 ) 4342 ) { 4343 if ( 4344 !$product_image_wrapper.attr( 4345 "data-wcpt-offset-zoom-image-src--original", 4346 ) 4347 ) { 4348 $product_image_wrapper.attr( 4349 "data-wcpt-offset-zoom-image-src--original", 4350 $product_image_wrapper.attr("data-wcpt-offset-zoom-image-src"), 4351 ); 4352 } else { 4353 $product_image_wrapper.attr( 4354 "data-wcpt-offset-zoom-image-src", 4355 $product_image_wrapper.attr( 4356 "data-wcpt-offset-zoom-image-src--original", 4357 ), 4358 ); 4359 } 4360 } 4361 } 4362 4363 // -- sku 4364 $row.find(".wcpt-sku").each(function () { 4365 var $sku = $(this), 4366 sku = $sku.attr("data-wcpt-sku"); 4367 var $label = $sku.find(".wcpt-property-label"); 4368 if ($label.length) { 4369 $sku.html($label.prop("outerHTML") + sku); 4370 } else { 4371 $sku.text(sku); 4372 } 4373 }); 4374 4375 // -- gtin 4376 $row.find(".wcpt-gtin").each(function () { 4377 var $gtin = $(this), 4378 gtin = $gtin.attr("data-wcpt-gtin"); 4379 $gtin.text(gtin); // revert to variable product gtin 4380 }); 4381 4382 // -- product id 4383 $row.find(".wcpt-product-id").each(function () { 4384 var $product_id = $(this), 4385 product_id = $product_id.attr("data-wcpt-product-id"); 4386 $product_id.text(product_id); // revert to variable product ID 4387 }); 4388 4389 // -- price 4390 4391 // -- -- wcpt price element 4392 $row 4393 .filter(".wcpt-product-type-variable") 4394 .find(".wcpt-price.wcpt-variable-switch") 4395 .each(function () { 4396 var $this = $(this), 4397 id = $this.attr("data-wcpt-element-id"), 4398 tpl = $this.attr("data-wcpt-variable-template"), 4399 template_html = $( 4400 "[data-wcpt-element-id=" + 4401 id + 4402 "][data-wcpt-price-type=" + 4403 tpl + 4404 "]", 4405 ).html(), 4406 $html, 4407 o = [ 4408 "highest-price", 4409 "lowest-price", 4410 "sale-price", 4411 "regular-price", 4412 ]; 4413 4414 // No template yet (paginate / infinite scroll before merge) â keep 4415 // the server-rendered default price instead of wiping it blank. 4416 if (!template_html) { 4417 return; 4418 } 4419 4420 $html = $(template_html); 4421 4422 $.each(o, function (index, val) { 4423 $(".wcpt-" + val + " .wcpt-amount", $html).text( 4424 $this.attr("data-wcpt-" + val), 4425 ); 4426 }); 4427 4428 $this.html($html); 4429 4430 if (tpl == "sale") { 4431 $this.addClass("wcpt-product-on-sale"); 4432 } else { 4433 $this.removeClass("wcpt-product-on-sale"); 4434 } 4435 }); 4436 4437 // -- -- default woocommerce template 4438 $row 4439 .filter(".wcpt-product-type-variable") 4440 .find(".wcpt-variable-price-default-woocommerce-template") 4441 .each(function () { 4442 var $this = $(this); 4443 $default = $(".wcpt-variable-switch__default", $this); 4444 $default.show().next(".price").remove(); 4445 }); 4446 4447 // -- -- price attribute 4448 $row.attr("data-wcpt-price", ""); 4449 4450 // -- on sale 4451 $row 4452 .filter(".wcpt-product-type-variable") 4453 .find(".wcpt-on-sale.wcpt-variable-switch") 4454 .each(function () { 4455 var $this = $(this); 4456 $this.addClass("wcpt-hide"); 4457 }); 4458 4459 // -- availability 4460 $row 4461 .filter(".wcpt-product-type-variable") 4462 .find(".wcpt-availability.wcpt-variable-switch") 4463 .each(function () { 4464 var $this = $(this), 4465 id = $this.attr("data-wcpt-element-id"), 4466 stock = $this.attr("data-wcpt-stock"), 4467 message_tpl = $this.attr("data-wcpt-default-message_tpl"), 4468 stock_class = $this.attr("data-wcpt-default-stock_class"), 4469 message = $( 4470 "[data-wcpt-element-id=" + 4471 id + 4472 '][data-wcpt-availability-message="' + 4473 message_tpl + 4474 '"]', 4475 ).html(), 4476 $label = $this.find(".wcpt-property-label"), 4477 label_html = $label.length ? $label.prop("outerHTML") : "", 4478 $message = $($.parseHTML("<span>" + message + "</span>")); 4479 4480 $this.html( 4481 label_html + 4482 "<span class='" + 4483 stock_class + 4484 "'>" + 4485 $message 4486 .find(".wcpt-stock-placeholder") 4487 .text(stock) 4488 .end() 4489 .html() + 4490 "</span>", 4491 ); 4492 }); 4493 4494 // -- stock 4495 $row 4496 .filter(".wcpt-product-type-variable") 4497 .find(".wcpt-stock") 4498 .each(function () { 4499 var $this = $(this), 4500 stock = $this.attr("data-wcpt-stock"), 4501 rules = $this.attr("data-wcpt-stock-range-labels"), 4502 parsed_rules = !rules || rules == "{}" ? [] : JSON.parse(rules), 4503 $label = $this.find(".wcpt-property-label"), 4504 label_html = $label.length ? $label.prop("outerHTML") : "", 4505 label = stock; 4506 4507 var found_rule = false; 4508 4509 if (stock && parsed_rules.length) { 4510 $.each(parsed_rules, function (index, rule) { 4511 if (rule[0] <= stock && rule[1] >= stock) { 4512 label = rule[2]; 4513 found_rule = true; 4514 } 4515 }); 4516 } 4517 4518 if (!found_rule && stock < 0) { 4519 stock = ""; 4520 label = ""; 4521 } 4522 4523 $this.html(label_html + (label + "").replace("[stock]", stock)); 4524 }); 4525 4526 // -- dimensions 4527 $row 4528 .filter(".wcpt-product-type-variable") 4529 .find(".wcpt-dimensions") 4530 .each(function () { 4531 var $this = $(this); 4532
4532 $this.html($this.attr("data-wcpt-default-dimensions")); 4533 }); 4534 4535 // -- height 4536 $row 4537 .filter(".wcpt-product-type-variable") 4538 .find(".wcpt-height") 4539 .each(function () { 4540 var $this = $(this), 4541 template = $this.attr("data-wcpt-template"), 4542 empty_template = $this.attr("data-wcpt-empty-template"), 4543 height = $this.attr("data-wcpt-default-height"); 4544 4545 if (!height) { 4546 $this.html(empty_template ? empty_template : ""); 4547 } else { 4548 $this.html( 4549 template ? template.replace("{n}", height) : empty_template, 4550 ); 4551 } 4552 }); 4553 4554 // -- width 4555 $row 4556 .filter(".wcpt-product-type-variable") 4557 .find(".wcpt-width") 4558 .each(function () { 4559 var $this = $(this), 4560 template = $this.attr("data-wcpt-template"), 4561 empty_template = $this.attr("data-wcpt-empty-template"), 4562 width = $this.attr("data-wcpt-default-width"); 4563 4564 if (!width) { 4565 $this.html(empty_template ? empty_template : ""); 4566 } else { 4567 $this.html(template ? template.replace("{n}", width) : width); 4568 } 4569 }); 4570 4571 // -- length 4572 $row 4573 .filter(".wcpt-product-type-variable") 4574 .find(".wcpt-length") 4575 .each(function () { 4576 var $this = $(this), 4577 template = $this.attr("data-wcpt-template"), 4578 empty_template = $this.attr("data-wcpt-empty-template"), 4579 length = $this.attr("data-wcpt-default-length"); 4580 4581 if (!length) { 4582 $this.html(empty_template ? empty_template : ""); 4583 } else { 4584 $this.html(template ? template.replace("{n}", length) : length); 4585 } 4586 }); 4587 4588 // -- weight 4589 $row 4590 .filter(".wcpt-product-type-variable") 4591 .find(".wcpt-weight") 4592 .each(function () { 4593 var $this = $(this), 4594 template = $this.attr("data-wcpt-template"), 4595 empty_template = $this.attr("data-wcpt-empty-template"), 4596 weight = $this.attr("data-wcpt-default-weight"); 4597 4598 if (!weight) { 4599 $this.html(empty_template ? empty_template : ""); 4600 } else { 4601 $this.html(template ? template.replace("{n}", weight) : weight); 4602 } 4603 }); 4604 4605 // -- custom field 4606 $row 4607 .filter(".wcpt-product-type-variable") 4608 .find(".wcpt-custom-field.wcpt-variable-switch") 4609 .each(function () { 4610 var $this = $(this), 4611 element_id = $this.attr("data-wcpt-element-id"), 4612 product_id = $this 4613 .closest(".wcpt-row") 4614 .attr("data-wcpt-product-id"), 4615 table_id = $this.closest(".wcpt").attr("data-wcpt-table-id"), 4616 $label = $this.find(".wcpt-property-label"), 4617 label_html = $label.length ? $label.prop("outerHTML") : ""; 4618 4619 if ( 4620 "undefined" !== 4621 typeof window["wcpt_" + table_id + "_variable_switch_cf"] 4622 ) { 4623 var cf_vals = window["wcpt_" + table_id + "_variable_switch_cf"]; 4624 if ("undefined" !== typeof cf_vals[element_id]) { 4625 $this.html(label_html + cf_vals[element_id][product_id]); 4626 } 4627 } 4628 }); 4629 } 4630 4631 if (!$.isEmptyObject(data.variation)) { 4632 // -- add to cart buton 4633 var $button = $row.find('[data-wcpt-link-code^="cart"]'); 4634 4635 // -- -- no variation selected 4636 enable_button($button, "wcpt-no-variation-selected"); 4637 4638 // -- -- out of stock 4639 if (data.variation.is_in_stock) { 4640 enable_button($button, "wcpt-variation-out-of-stock"); 4641 } else { 4642 disable_button($button, "wcpt-variation-out-of-stock"); 4643 } 4644 4645 // -- qty input 4646 var $qty_wrapper = $row.find(".wcpt-quantity"), 4647 $qty = $qty_wrapper.find("input[type=number].qty"); 4648 4649 // -- -- disabled 4650 if (data.variation.is_in_stock) { 4651 $qty_wrapper.removeClass("wcpt-disabled"); 4652 $qty.removeAttr("disabled"); 4653 } else { 4654 $qty_wrapper.addClass("wcpt-disabled"); 4655 $qty.attr("disabled", "disabled"); 4656 } 4657 4658 if ($qty.length) { 4659 $qty.each(function () { 4660 var $this = $(this), 4661 $qty_wrapper = $this.closest(".wcpt-quantity"), 4662 inital_value = $this.attr("data-wcpt-initial-value"), 4663 min = data.variation.min_qty 4664 ? parseFloat(data.variation.min_qty) 4665 : 1, 4666 max = data.variation.max_qty 4667 ? parseFloat(data.variation.max_qty) 4668 : "", 4669 step = data.variation.step ? parseFloat(data.variation.step) : "", 4670 value = ""; 4671 4672 // validate val 4673 var current_val = parseFloat($this.val()); 4674 if (current_val && current_val !== NaN) { 4675 // neither empty, nor 0 4676 if (current_val < min) { 4677 value = min; 4678 } else if (max && current_val > max) { 4679 value = max; 4680 } else { 4681 value = current_val; 4682 } 4683 } 4684 4685 if ( 4686 inital_value === "min" && 4687 $this.attr("data-wcpt-reset-on-variation-change") 4688 ) { 4689 value = min; 4690 } 4691 4692 var wcpt_min = min; 4693 4694 if (inital_value === "0") { 4695 min = "0"; 4696 } 4697 4698 $this.attr({ 4699 value: value, 4700 min: min, 4701 "data-wcpt-min": wcpt_min, 4702 max: max, 4703 step: step, 4704 }); 4705 4706 $this.val(value); 4707 4708 $.each(["min", "max", "step"], function (index, attr) { 4709 $qty_wrapper 4710 .find(".wcpt-quantity-error-placeholder--" + attr) 4711 .text($this.attr(attr)); 4712 }); 4713 4714 $this.trigger("change"); 4715 }); 4716 } 4717 4718 // -- qty select 4719 var $select = $row.find(".wcpt-quantity > select.wcpt-qty-select"); 4720 if ($select.length) { 4721 // re-create select 4722 var qty_label = $select.attr("data-wcpt-qty-label"), 4723 max_qty = parseInt($select.attr("data-wcpt-max-qty")), 4724 val = data.variation.min_qty, 4725 options = 4726 '<option value="' + 4727 data.variation.min_qty + 4728 '" selected="selected">' + 4729 qty_label + 4730 data.variation.min_qty + 4731 "</option>"; 4732 if (data.variation.max_qty) { 4733 max_qty = data.variation.max_qty; 4734 } 4735 4736 while (val < max_qty) { 4737 val += data.variation.step || 1; 4738 options += "<option>" + val + "</option>"; 4739 } 4740 $select.html(options); 4741 $select.attr("min", data.variation.min_qty); 4742 } 4743 4744 // -- product image 4745 var $product_image_wrapper = $(".wcpt-product-image-wrapper", $row), 4746 $product_image = $( 4747 ".wcpt-product-image-wrapper img:not(.wcpt-product-image-on-hover)", 4748 $row, 4749 ), 4750 $original_row = wcpt_get_original_row($row); 4751 4752 if ($product_image[0]) { 4753 if (!$original_row.data("wcpt_default_image")) { 4754 $original_row.data( 4755 "wcpt_default_image", 4756 $product_image[0].outerHTML, 4757 ); 4758 } 4759 4760 // replace variation image accoring to 'product image' element size settings 4761 if (data.variation.wcpt_image && data.variation.wcpt_image.src) { 4762 data.variation.image.src = data.variation.image.srcset = 4763 data.variation.wcpt_image.src; 4764 } 4765 4766 if ( 4767 $product_image.length && 4768 data.variation.image && 4769 data.variation.image.src 4770 ) { 4771 $product_image.attr({ 4772 src: data.variation.image.src, 4773 srcset: data.variation.image.srcset 4774 ? data.variation.image.srcset 4775 : "", 4776 sizes: data.variation.image.sizes, 4777 }); 4778 4779 if ($product_image_wrapper.hasClass("wcpt-lightbox-enabled")) { 4780 if ( 4781 !$product_image_wrapper.attr("data-wcpt-lightbox--original") 4782 ) { 4783 $product_image_wrapper.attr( 4784 "data-wcpt-lightbox--original", 4785 $product_image_wrapper.attr("data-wcpt-lightbox"), 4786 ); 4787 } 4788 4789 $product_image_wrapper.attr( 4790 "data-wcpt-lightbox", 4791 data.variation.image.full_src, 4792 ); 4793 } 4794 4795 if ( 4796 $product_image_wrapper.hasClass( 4797 "wcpt-product-image-wrapper--offset-zoom-enabled", 4798 ) 4799 ) { 4800 if ( 4801 !$product_image_wrapper.attr( 4802 "data-wcpt-offset-zoom-image-src--original", 4803 ) 4804 ) { 4805 $product_image_wrapper.attr( 4806 "data-wcpt-offset-zoom-image-src--original", 4807 $product_image_wrapper.attr( 4808 "data-wcpt-offset-zoom-image-src", 4809 ), 4810 ); 4811 } 4812 4813 $product_image_wrapper.attr( 4814 "data-wcpt-offset-zoom-image-src", 4815 data.variation.image.full_src, 4816 ); 4817 } 4818 } 4819 } else { 4820 handle_product_image_lazy_load($product_image_wrapper); 4821 } 4822 4823 // -- sku 4824 if (data.variation.sku) { 4825 $row.find(".wcpt-sku").each(function () { 4826 var $this = $(this); 4827 if ($this.hasClass("wcpt-variable-switch")) { 4828 var $label = $this.find(".wcpt-property-label"); 4829 if ($label.length) { 4830 $this.html($label.prop("outerHTML") + data.variation.sku); 4831 } else { 4832 $this.text(data.variation.sku); 4833 } 4834 } 4835 }); 4836 } 4837 4838 // -- gtin 4839 if (data.variation.wcpt_gtin) { 4840 $row.find(".wcpt-gtin").each(function () { 4841 var $this = $(this); 4842 if ($this.hasClass("wcpt-variable-switch")) { 4843 $this.text(data.variation.wcpt_gtin); 4844 } 4845 }); 4846 } 4847 4848 // -- product id 4849 if (data.variation.variation_id) { 4850 $row.find(".wcpt-product-id").each(function () { 4851 var $this = $(this); 4852 if ($this.hasClass("wcpt-variable-switch")) { 4853 $this.text(data.variation.variation_id); 4854 } 4855 }); 4856 } 4857 4858 // -- price 4859 4860 // -- -- wcpt price element 4861 $row 4862 .filter(".wcpt-product-type-variable") 4863 .find(".wcpt-price.wcpt-variable-switch") 4864 .each(function () { 4865 var $this = $(this), 4866 id = $this.attr("data-wcpt-element-id"), 4867 tpl = 4868 parseFloat(data.variation.display_price) < 4869 parseFloat(data.variation.display_regular_price) 4870 ? "sale" 4871 : "regular", 4872 template_html = $( 4873 "[data-wcpt-element-id=" + 4874 id + 4875 "][data-wcpt-price-type=" + 4876 tpl + 4877 "]", 4878 ).html(), 4879 $html; 4880 4881 if (!template_html) { 4882 return; 4883 } 4884 4885 $html = $(template_html); 4886 4887 $html 4888 .find(".wcpt-regular-price .wcpt-amount") 4889 .text(data.variation.display_regular_price) 4890 .end() 4891 .find(".wcpt-sale-price .wcpt-amount") 4892 .text(data.variation.display_price); 4893 $this.html($html); 4894 4895 if (tpl == "sale") { 4896 $this.addClass("wcpt-product-on-sale"); 4897 } else { 4898 $this.removeClass("wcpt-product-on-sale"); 4899 } 4900 }); 4901 4902 // -- -- default woocommerce template 4903 $row 4904 .filter(".wcpt-product-type-variable") 4905 .find(".wcpt-variable-price-default-woocommerce-template") 4906 .each(function () { 4907 var $this = $(this); 4908 $default = $(".wcpt-variable-switch__default", $this); 4909 4910 if (!data.variation.price_html) { 4911 // single variation won't have price html 4912 return; 4913 } 4914 4915 $default 4916 .hide() 4917 .nextAll(".price") 4918 .remove() 4919 .end() 4920 .after(data.variation.price_html); 4921 }); 4922 4923 // -- -- price attribute 4924 $row.attr("data-wcpt-price", data.variation.display_price); 4925 4926 // -- on sale 4927 $row 4928 .filter(".wcpt-product-type-variable") 4929 .find(".wcpt-on-sale.wcpt-variable-switch") 4930 .each(function () { 4931 var $this = $(this), 4932 precision = $this.attr("data-wcpt-precision"), 4933 is_on_sale = 4934 parseFloat(data.variation.display_price) < 4935 parseFloat(data.variation.display_regular_price), 4936 $price_diff = $this.find(".wcpt-on-sale__price-diff"), 4937 $percent_diff = $this.find(".wcpt-on-sale__percent-diff"); 4938
4939 if (is_on_sale) { 4940 $this.removeClass("wcpt-hide"); 4941 4942 var price_diff = 4943 data.variation.display_regular_price - 4944 data.variation.display_price; 4945 var percent_diff = parseFloat( 4946 ( 4947 (price_diff / data.variation.display_regular_price) * 4948 100 4949 ).toFixed(precision), 4950 ); 4951 4952 $price_diff.html(format_price(price_diff)); 4953 $percent_diff.text(percent_diff); 4954 } else { 4955 $this.addClass("wcpt-hide"); 4956 } 4957 }); 4958 4959 // -- availability 4960 $row 4961 .filter(".wcpt-product-type-variable") 4962 .find(".wcpt-availability.wcpt-variable-switch") 4963 .each(function () { 4964 var $this = $(this), 4965 id = $this.attr("data-wcpt-element-id"), 4966 message_tpl = "", 4967 stock_class = "", 4968 out_of_stock_message = $( 4969 "[data-wcpt-element-id=" + 4970 id + 4971 '][data-wcpt-availability-message="out_of_stock_message"]', 4972 ).html(), 4973 low_stock_message = $( 4974 "[data-wcpt-element-id=" + 4975 id + 4976 '][data-wcpt-availability-message="low_stock_message"]', 4977 ).html(), 4978 single_stock_message = $( 4979 "[data-wcpt-element-id=" + 4980 id + 4981 '][data-wcpt-availability-message="single_stock_message"]', 4982 ).html(), 4983 in_stock_message = $( 4984 "[data-wcpt-element-id=" + 4985 id + 4986 '][data-wcpt-availability-message="in_stock_message"]', 4987 ).html(), 4988 in_stock_managed_message = $( 4989 "[data-wcpt-element-id=" + 4990 id + 4991 '][data-wcpt-availability-message="in_stock_managed_message"]', 4992 ).html(), 4993 on_backorder_message = $( 4994 "[data-wcpt-element-id=" + 4995 id + 4996 '][data-wcpt-availability-message="on_backorder_message"]', 4997 ).html(), 4998 on_backorder_managed_message = $( 4999 "[data-wcpt-element-id=" + 5000 id + 5001 '][data-wcpt-availability-message="on_backorder_managed_message"]', 5002 ).html(), 5003 low_stock_threshold = $this.attr("data-wcpt-low_stock_threshold"); 5004 5005 $this.show(); 5006 5007 if (!data.variation.is_in_stock) { 5008 message_tpl = "out_of_stock_message"; 5009 stock_class = "wcpt-out-of-stock"; 5010 } else if ( 5011 ((data.variation.managing_stock && 5012 data.variation.is_on_backorder && 5013 data.variation.backorders_require_notification) || 5014 (!data.variation.managing_stock && 5015 data.variation.is_on_backorder)) && 5016 on_backorder_message 5017 ) { 5018 message_tpl = "on_backorder_message"; 5019 stock_class = "wcpt-on-backorder"; 5020 } else if (data.variation.managing_stock) { 5021 // in stock, managed 5022 message_tpl = "in_stock_managed_message"; 5023 stock_class = "wcpt-in-stock"; 5024 5025 if (!data.variation.backorders_allowed) { 5026 if ( 5027 // low stock 5028 data.variation.stock == 1 && 5029 single_stock_message 5030 ) { 5031 message_tpl = "single_stock_message"; 5032 stock_class = "wcpt-low-stock"; 5033 } else if ( 5034 // single stock 5035 low_stock_message && 5036 low_stock_threshold && 5037 data.variation.stock <= low_stock_threshold 5038 ) { 5039 message_tpl = "low_stock_message"; 5040 stock_class = "wcpt-low-stock"; 5041 } 5042 } else if ( 5043 data.variation.backorders_require_notification && 5044 on_backorder_managed_message 5045 ) { 5046 // backorder allowed, managed stock, greater than 0 5047 message_tpl = "on_backorder_managed_message"; 5048 stock_class = "wcpt-on-backorder"; 5049 } else if (data.variation.stock <= 0) { 5050 // backorder allowed, managed stock, 0 or less 5051 message_tpl = "on_backorder_message"; 5052 stock_class = ""; 5053 $this.hide(); 5054 } else { 5055 // backorder allowed, managed stock, greater than 0 5056 message_tpl = "in_stock_message"; 5057 stock_class = "wcpt-in-stock"; 5058 } 5059 } else { 5060 // in stock, not managed 5061 message_tpl = ""; 5062 stock_class = ""; 5063 5064 if (in_stock_message) { 5065 message_tpl = "in_stock_message"; 5066 stock_class = "wcpt-in-stock"; 5067 } 5068 } 5069 5070 const message_tpl_selector = `[data-wcpt-element-id="${id}"][data-wcpt-availability-message="${message_tpl}"]`; 5071 const $message = $( 5072 $.parseHTML( 5073 "<span>" + $(message_tpl_selector).html() + "</span>", 5074 ), 5075 ); 5076 const $label = $this.find(".wcpt-property-label"); 5077 const label_html = $label.length ? $label.prop("outerHTML") : ""; 5078 5079 $this.html( 5080 label_html + 5081 "<span class='" + 5082 stock_class + 5083 "'>" + 5084 $message 5085 .find(".wcpt-stock-placeholder") 5086 .text(data.variation.stock ? data.variation.stock : "") 5087 .end() 5088 .html() + 5089 "</span>", 5090 ); 5091 }); 5092 5093 // -- stock 5094 $row 5095 .filter(".wcpt-product-type-variable") 5096 .find(".wcpt-stock.wcpt-variable-switch") 5097 .each(function () { 5098 var $this = $(this), 5099 stock = data.variation.stock, 5100 rules = $this.attr("data-wcpt-stock-range-labels"), 5101 parsed_rules = !rules || rules == "{}" ? [] : JSON.parse(rules), 5102 $label = $this.find(".wcpt-property-label"), 5103 label_html = $label.length ? $label.prop("outerHTML") : "", 5104 label = stock; 5105 5106 if ( 5107 typeof label === "undefined" || 5108 label === null || 5109 label == NaN 5110 ) { 5111 $this.hide(); 5112 return; 5113 } else { 5114 $this.show(); 5115 } 5116 5117 var found_rule = false; 5118 if (stock && parsed_rules.length) { 5119 $.each(parsed_rules, function (index, rule) { 5120 if (rule[0] <= stock && rule[1] >= stock) { 5121 label = rule[2]; 5122 found_rule = true; 5123 } 5124 }); 5125 } 5126 5127 if (!found_rule && stock < 0) { 5128 stock = ""; 5129 label = ""; 5130 } 5131 5132 $this.html(label_html + (label + "").replace("[stock]", stock)); 5133 }); 5134 5135 // -- dimensions 5136 $row 5137 .filter(".wcpt-product-type-variable") 5138 .find(".wcpt-dimensions.wcpt-variable-switch") 5139 .each(function () { 5140 var $this = $(this); 5141 $this.html(data.variation.dimensions_html); 5142 }); 5143 5144 // -- height 5145 $row 5146 .filter(".wcpt-product-type-variable") 5147 .find(".wcpt-height.wcpt-variable-switch") 5148 .each(function () { 5149 var $this = $(this), 5150 template = $this.attr("data-wcpt-template"), 5151 empty_template = $this.attr("data-wcpt-empty-template"), 5152 height = data.variation.dimensions.height; 5153 5154 if (!height) { 5155 $this.html(empty_template ? empty_template : ""); 5156 } else { 5157 $this.html( 5158 template ? template.replace("{n}", height) : empty_template, 5159 ); 5160 } 5161 }); 5162
5163 // -- width 5164 $row 5165 .filter(".wcpt-product-type-variable") 5166 .find(".wcpt-width.wcpt-variable-switch") 5167 .each(function () { 5168 var $this = $(this), 5169 template = $this.attr("data-wcpt-template"), 5170 empty_template = $this.attr("data-wcpt-empty-template"), 5171 width = data.variation.dimensions.width; 5172 5173 if (!width) { 5174 $this.html(empty_template ? empty_template : ""); 5175 } else { 5176 $this.html(template ? template.replace("{n}", width) : width); 5177 } 5178 }); 5179 5180 // -- length 5181 $row 5182 .filter(".wcpt-product-type-variable") 5183 .find(".wcpt-length.wcpt-variable-switch") 5184 .each(function () { 5185 var $this = $(this), 5186 template = $this.attr("data-wcpt-template"), 5187 empty_template = $this.attr("data-wcpt-empty-template"), 5188 length = data.variation.dimensions.length; 5189 5190 if (!length) { 5191 $this.html(empty_template ? empty_template : ""); 5192 } else { 5193 $this.html(template ? template.replace("{n}", length) : length); 5194 } 5195 }); 5196 5197 // -- weight 5198 $row 5199 .filter(".wcpt-product-type-variable") 5200 .find(".wcpt-weight.wcpt-variable-switch") 5201 .each(function () { 5202 var $this = $(this), 5203 template = $this.attr("data-wcpt-template"), 5204 empty_template = $this.attr("data-wcpt-empty-template"), 5205 weight = data.variation.weight; 5206 5207 if (!weight) { 5208 $this.html(empty_template ? empty_template : ""); 5209 } else { 5210 $this.html(template ? template.replace("{n}", weight) : weight); 5211 } 5212 }); 5213 5214 // -- custom field 5215 $row 5216 .filter(".wcpt-product-type-variable") 5217 .find(".wcpt-custom-field.wcpt-variable-switch") 5218 .each(function () { 5219 var $this = $(this), 5220 element_id = $this.attr("data-wcpt-element-id"), 5221 table_id = $this.closest(".wcpt").attr("data-wcpt-table-id"); 5222 (($label = $this.find(".wcpt-property-label")), 5223 (label_html = $label.length ? $label.prop("outerHTML") : "")); 5224 5225 if ( 5226 "undefined" !== 5227 typeof window["wcpt_" + table_id + "_variable_switch_cf"] 5228 ) { 5229 var cf_vals = window["wcpt_" + table_id + "_variable_switch_cf"]; 5230 if ("undefined" !== typeof cf_vals[element_id]) { 5231 $this.html( 5232 label_html + cf_vals[element_id][data.variation.variation_id], 5233 ); 5234 } 5235 } 5236 }); 5237 } 5238 }, 5239 ); 5240 5241 //-- -- update from dropdown 5242 $("body").on("change", ".wcpt-select-variation-dropdown", function (e) { 5243 var $this = $(this), 5244 $selected = $this.find("option:selected"), 5245 $row = $this.closest(".wcpt-row"); 5246 5247 $row.trigger("select_variation", { 5248 variation_id: $this.val(), 5249 complete_match: $selected.hasClass("wcpt-complete_match"), 5250 attributes: $selected.attr("data-wcpt-attributes") 5251 ? JSON.parse($selected.attr("data-wcpt-attributes")) 5252 : "", 5253 variation: $selected.attr("data-wcpt-variation") 5254 ? JSON.parse($selected.attr("data-wcpt-variation")) 5255 : "", 5256 variation_found: !!$selected.attr("value"), 5257 variation_selected: !!$selected.attr("value"), 5258 variation_available: 5259 !$selected.is(":disabled") && !!$selected.attr("value"), 5260 }); 5261 }); 5262 5263 //-- -- update from radio 5264 $("body").on("change", ".wcpt-variation-radio", function (e) { 5265 var $this = $(this), 5266 $wrapper = $this.closest(".wcpt-select-variation"), 5267 $row = $this.closest(".wcpt-row"); 5268 5269 if ($this.is(":checked")) { 5270 $row.trigger("select_variation", { 5271 variation_id: $this.val(), 5272 complete_match: $wrapper.hasClass("wcpt-complete_match"), 5273 attributes: JSON.parse($wrapper.attr("data-wcpt-attributes")), 5274 variation: JSON.parse($wrapper.attr("data-wcpt-variation")), 5275 variation_found: true, 5276 variation_selected: true, 5277 variation_available: !$this.is(":disabled"), 5278 }); 5279 } 5280 }); 5281 5282 //-- -- update from form 5283 $("body").on( 5284 "woocommerce_variation_has_changed", 5285 ".wcpt-row .variations_form", 5286 function (e) { 5287 get_select_variation_from_cart_form($(this)); 5288 }, 5289 ); 5290 5291 function get_select_variation_from_cart_form($form) { 5292 if (window.wcpt_form_reset_flag) { 5293 // avoid infinite loop 5294 return; 5295 } 5296 5297 var variations = JSON.parse($form.attr("data-product_variations")), 5298 $row = $form.closest(".wcpt-row"), 5299 $variation_id = $(".variation_id", $form), 5300 variation = {}, 5301 attributes = {}, 5302 selected_variation = $variation_id.val(); 5303 5304 $.each(variations, function (index, value) { 5305 if (parseInt(value["variation_id"]) == selected_variation) { 5306 variation = value; 5307 return false; 5308 } 5309 });
5310 5311 var variation_selected = true; 5312 $(".variations select", $form).each(function () { 5313 var $this = $(this); 5314 attributes[$this.attr("name")] = $this.val(); 5315 5316 if (!$this.val()) { 5317 variation_selected = false; 5318 } 5319 }); 5320 5321 var variation_available = false; 5322 if ( 5323 variation && 5324 !$.isEmptyObject(variation) && 5325 variation.is_purchasable && 5326 variation.is_in_stock && 5327 variation.variation_is_visible 5328 ) { 5329 variation_available = true; 5330 } 5331 5332 $row.trigger("select_variation", { 5333 variation: variation, 5334 variation_id: selected_variation, 5335 complete_match: true, 5336 attributes: attributes, 5337 variation_found: !!selected_variation, 5338 variation_selected: variation_selected, 5339 variation_available: variation_available, 5340 }); 5341 } 5342 5343 // prepare variation options 5344 function prep_variation_options($new_rows) { 5345 function mark_row_prep_variation_complete($row) { 5346 $row.data("wcpt_prep_variation_complete", true); 5347 $row.trigger("wcpt_row_prep_variation_complete"); 5348 } 5349 5350 function shouldSkipPrepVariationReset($row) { 5351 var productId = $row.attr("data-wcpt-product-id"), 5352 table_id = 5353 typeof wcpt_util !== "undefined" 5354 ? wcpt_util.get_table_id($row.closest(".wcpt")) 5355 : ""; 5356 5357 return !!( 5358 productId && 5359 table_id && 5360 typeof window.wcpt_cart_checkbox_persist !== "undefined" && 5361 typeof window.wcpt_cart_checkbox_persist.hasPersistedVariableProduct === 5362 "function" && 5363 window.wcpt_cart_checkbox_persist.hasPersistedVariableProduct( 5364 table_id, 5365 productId, 5366 ) 5367 ); 5368 } 5369 5370 $($new_rows) 5371 .filter(".wcpt-product-type-variable") 5372 .each(function () { 5373 var $row = $(this), 5374 $dropdown = $(".wcpt-select-variation-dropdown", $row), 5375 $radio = $(".wcpt-variation-radio", $row), 5376 $form = $(".variations_form", $row), 5377 $options = $dropdown.add($radio).add($form); 5378 5379 $row.data("wcpt_prep_variation_complete", false); 5380 5381 // flag availability of options in row 5382 if ($options.length) { 5383 $row.data("wcpt_variation_ops", true); 5384 } 5385 5386 // trigger 'select_variation' 5387 5388 // cart form element 5389 if ($form.length) { 5390 $form.each(function () { 5391 var $form = $(this), 5392 skipPrepReset = shouldSkipPrepVariationReset($row); 5393 5394 setTimeout(function () { 5395 // need to match setTimeout in add-to-cart-variation.js that delays form init 5396 // this init syncs the WC form with WCPT native controls including add-to-cart button 5397 if (!skipPrepReset) { 5398 $form.find("select").first().change(); 5399 } 5400 mark_row_prep_variation_complete($row); 5401 }, 200); 5402 }); 5403 5404 // select variation element 5405 5406 // -- dropdown 5407 } else if ($dropdown.length) { 5408 if (!shouldSkipPrepVariationReset($row)) { 5409 $dropdown.trigger("change"); 5410 } 5411 mark_row_prep_variation_complete($row); 5412 5413 // -- radio 5414 } else if ($radio.length) { 5415 if (!shouldSkipPrepVariationReset($row)) { 5416 var $checked = $radio.filter(":checked"); 5417 if ($checked.length) { 5418 $checked.trigger("change"); 5419 } else { 5420 $row.trigger("select_variation", { 5421 variation_id: false, 5422 complete_match: false, 5423 attributes: false, 5424 variation: false, 5425 variation_found: false, 5426 variation_selected: false, 5427 variation_available: false, 5428 }); 5429 } 5430 } 5431 mark_row_prep_variation_complete($row); 5432 } else { 5433 mark_row_prep_variation_complete($row); 5434 } 5435 }); 5436 } 5437 5438 // lazy loaded product image 5439 function handle_product_image_lazy_load($product_image_wrapper) { 5440 if ( 5441 $product_image_wrapper.length && 5442 !$product_image_wrapper.hasClass("wcpt-awaiting-image-lazy-load") 5443 ) { 5444 $product_image_wrapper.addClass("wcpt-awaiting-image-lazy-load"); 5445 5446 $product_image_wrapper[0].addEventListener( 5447 "load", 5448 function (event) { 5449 if ( 5450 event.target.tagName === "IMG" && 5451 $product_image_wrapper.hasClass("wcpt-awaiting-image-lazy-load") 5452 ) { 5453 $product_image_wrapper.removeClass("wcpt-awaiting-image-lazy-load"); 5454 5455 var $row = wcpt_get_original_row( 5456 $product_image_wrapper.closest(".wcpt-row"), 5457 ), 5458 data = $row.data(), 5459 $product_image = $product_image_wrapper.find(".wp-post-image"); 5460 5461 $row.data("wcpt_default_image", $product_image[0].outerHTML); 5462 5463 if ( 5464 !$.isEmptyObject(data.wcpt_variation) && 5465 $product_image.length && 5466 data.wcpt_variation.image && 5467 data.wcpt_variation.image.src 5468 ) { 5469 $product_image.attr({ 5470 src: data.wcpt_variation.image.src, 5471 srcset: data.wcpt_variation.image.srcset 5472 ? data.wcpt_variation.image.srcset 5473 : "", 5474 }); 5475 5476 if ($product_image_wrapper.hasClass("wcpt-lightbox-enabled")) { 5477 $product_image_wrapper.attr( 5478 "data-wcpt-lightbox", 5479 data.wcpt_variation.image.full_src, 5480 ); 5481 } 5482 5483 if ( 5484 $product_image_wrapper.hasClass( 5485 "wcpt-product-image-wrapper--offset-zoom-enabled", 5486 ) 5487 ) { 5488 $product_image_wrapper.attr( 5489 "data-wcpt-offset-zoom-image-src", 5490 data.variation.image.full_src, 5491 ); 5492 } 5493 } 5494 } 5495 }, 5496 true, 5497 ); 5498 } 5499 } 5500 5501 if (window.innerWidth < 1200) { 5502 $("body").on("contextmenu", ".wcpt-noselect", function () { 5503 return false; 5504 });
5505 } 5506 5507 /** 5508 * Device interaction type detection 5509 * Sets appropriate mouse event types based on device capabilities 5510 */ 5511 if ("ontouchstart" in document.documentElement) { 5512 var mousedown = "touchstart", 5513 mouseup = "touchend"; 5514 } else { 5515 var mousedown = "mousedown", 5516 mouseup = "mouseup"; 5517 } 5518 5519 /** 5520 * Quantity Controller Module 5521 * 5522 * This module handles quantity input functionality for WooCommerce product tables, 5523 * including touch screen support, increment/decrement controls, validation, and 5524 * cross-table synchronization. 5525 */ 5526 5527 /** 5528 * Touch Screen Handler 5529 * Automatically moves cursor to end of input field on touch devices 5530 * for better user experience when editing quantities 5531 */ 5532 $("body").on("touchend", ".wcpt-quantity .qty", function () { 5533 var $this = $(this), 5534 _ = this; 5535 if (!$this.is(":focus")) { 5536 $this.one("focus", function () { 5537 var val = _.value; 5538 _.value = ""; 5539 setTimeout(function () { 5540 _.value = val; 5541 }, 1); 5542 }); 5543 } 5544 }); 5545 5546 /** 5547 * Quantity Controller Click Handler 5548 * Manages increment/decrement button interactions including: 5549 * - Initial value changes 5550 * - Continuous increment/decrement on hold 5551 * - Min/max boundary enforcement 5552 * - Touch device support 5553 */ 5554 $("body").on("click", ".wcpt-qty-controller", handleQuantityChange); 5555 $("body").on("wcptPressing", ".wcpt-qty-controller", handleQuantityChange); 5556 $("body").on("dblclick", ".wcpt-qty-controller", function (e) { 5557 e.preventDefault(); 5558 }); 5559 5560 function handleQuantityChange(e) { 5561 var $this = $(this), 5562 $wrapper = $this.closest(".wcpt-quantity"), 5563 $qty = $wrapper.find(".qty"), 5564 visualMin = $qty.attr("min") ? $qty.attr("min") : 0, 5565 realMin = $qty.attr("data-wcpt-min") 5566 ? $qty.attr("data-wcpt-min") 5567 : visualMin, 5568 max = $qty.attr("max") ? $qty.attr("max") : false, 5569 step = $qty.attr("step") ? $qty.attr("step") : 1, 5570 val = $qty.val() > -1 ? $qty.val() : visualMin; 5571 5572 if ($wrapper.hasClass("wcpt-disabled")) { 5573 return; 5574 } 5575 5576 if ($this.hasClass("wcpt-disabled")) { 5577 return; 5578 } 5579 5580 if (step % 1 === 0) { 5581 visualMin = parseInt(visualMin); 5582 realMin = parseInt(realMin); 5583 max = parseInt(max); 5584 step = parseInt(step); 5585 val = parseInt(val); 5586 } else { 5587 visualMin = parseFloat(visualMin); 5588 realMin = parseFloat(realMin); 5589 max = parseFloat(max); 5590 step = parseFloat(step); 5591 val = parseFloat(val); 5592 } 5593 5594 if (isNaN(val)) { 5595 val = 0; 5596 } 5597 5598 // update quantity value 5599 if ($this.hasClass("wcpt-plus")) { 5600 var next = (val * 1e12 + step * 1e12) / 1e12; 5601 // If increasing from below realMin, jump to realMin 5602 if (val < realMin && next >= visualMin) { 5603 next = realMin; 5604 } 5605 } else { 5606 // wcpt-minus 5607 var next = (val * 1e12 - step * 1e12) / 1e12; 5608 // If decreasing from above or equal to realMin, drop straight to visualMin or 0 5609 if (val >= realMin && next < realMin) { 5610 next = visualMin > 0 ? 0 : visualMin; 5611 } else if (next < visualMin) { 5612 next = 0; 5613 } 5614 } 5615 $qty.val(next).change(); 5616 5617 limit_qty_controller($wrapper); 5618 } 5619 5620 function startContinuousQuantityChange(e) { 5621 $("body").addClass("wcpt-noselect--qty-increment"); 5622 } 5623 5624 function stopContinuousQuantityChange(e) { 5625 $("body").removeClass("wcpt-noselect--qty-increment"); 5626 } 5627 5628 /** 5629 * Quantity Input Validator 5630 * Ensures quantity values respect: 5631 * - Minimum and maximum limits 5632 * - Step increments 5633 * - Initial value rules 5634 * @param {Event} e - Change event 5635 */ 5636 $("body").on( 5637 "change", 5638 ".wcpt-quantity .qty", 5639 function qty_controller_validate(e) { 5640 var $this = $(this), 5641 min = $this.attr("min") ? parseFloat($this.attr("min")) : 0, 5642 max = 5643 $this.attr("max") !== undefined && $this.attr("max") !== "" 5644 ? parseFloat($this.attr("max")) 5645 : 1e12, 5646 initial = $this.attr("data-wcpt-initial-value"), 5647 step = $this.attr("step") ? parseFloat($this.attr("step")) : 1, 5648 raw = $this.val(), 5649 val = parseFloat(raw); 5650 5651 // Empty field only â do not treat 0 as empty (OOS uses 0 with max 0). 5652 if (raw === "" || isNaN(val)) { 5653 if (initial === "min") { 5654 $this.val(!isNaN(max) && max < min ? Math.max(0, max) : min); 5655 } else if (initial === "empty") { 5656 $this.val(""); 5657 } else if (initial === "0") { 5658 $this.val("0"); 5659 } 5660 val = parseFloat($this.val()); 5661 } 5662 5663 // enforce min 5664 if (val && val < min) { 5665 $this.val(min); 5666 val = min; 5667 } 5668 5669 // enforce max 5670 if (!isNaN(val) && val > max) { 5671 $this.val(max); 5672 val = max; 5673 } 5674 5675 // enforce step 5676 if (val && (val * 1e12) % (step * 1e12)) { 5677 var _val = val * 1e12, 5678 _step = step * 1e12, 5679 new_val = (_val + _step - (val % step) * 1e12) / 1e12; 5680 5681 $this.val(new_val); 5682 } 5683 5684 limit_qty_controller($(this).parent()); 5685 }, 5686 ); 5687 5688 /** 5689 * Enter Key Cart Addition Handler 5690 * Allows users to press Enter while focused on quantity input 5691 * to trigger the add-to-cart action for that product 5692 * 5693 * @param {KeyboardEvent} e - Keyboard event object 5694 */ 5695 $("body").on("keypress", ".wcpt-quantity .qty", function (e) { 5696 if (e.keyCode == 13) { 5697 var $rows = get_product_rows($(this)); 5698 $rows.find('.wcpt-button[data-wcpt-link-code^="cart"]').eq(0).click(); 5699 } 5700 }); 5701 5702 /** 5703 * Quantity Controller State Manager 5704 * Updates the enabled/disabled state of increment/decrement buttons based on: 5705 * - Current quantity value 5706 * - Minimum and maximum limits 5707 * - Step size 5708 * @param {jQuery} $qty_wrapper - The quantity input wrapper element 5709 */ 5710 function limit_qty_controller($qty_wrapper) { 5711 $qty_wrapper.each(function () { 5712 var $this = $(this),
5713 $minus = $this.children(".wcpt-minus"), 5714 $plus = $this.children(".wcpt-plus"), 5715 $qty = $this.find(".qty"); 5716 5717 if ($this.hasClass("wcpt-disabled")) { 5718 $minus.addClass("wcpt-disabled"); 5719 $plus.addClass("wcpt-disabled"); 5720 return; 5721 } 5722 5723 var initial = $qty.attr("data-wcpt-initial-value"), 5724 min = $qty.attr("min") ? parseFloat($qty.attr("min")) : 1, 5725 max = $qty.attr("max") ? parseFloat($qty.attr("max")) : false, 5726 step = $qty.attr("step") ? parseFloat($qty.attr("step")) : 1, 5727 val = parseFloat($qty.val()); 5728 5729 if (!val || isNaN(val)) { 5730 val = 0; 5731 } 5732 5733 if (-1 !== $.inArray(initial, ["empty", "0"])) { 5734 min = 0; 5735 } 5736 5737 $minus.removeClass("wcpt-disabled"); 5738 if (val - step < min) { 5739 $minus.addClass("wcpt-disabled"); 5740 } 5741 5742 $plus.removeClass("wcpt-disabled"); 5743 if (false !== max && val + step > max) { 5744 $plus.addClass("wcpt-disabled"); 5745 } 5746 }); 5747 } 5748 5749 /** 5750 * Initial Quantity Value Handler 5751 * Sets up initial quantity values after table load based on configuration: 5752 * - Can set to minimum value 5753 * - Can set to empty 5754 * - Can set to zero 5755 * - Triggers appropriate events for other components 5756 */ 5757 $("body").on( 5758 "wcpt_after_every_load.wcpt_initial_qty_update", 5759 ".wcpt", 5760 function () { 5761 var $this__container = $(this), 5762 $target_qty_elms = $( 5763 '.wcpt-quantity input[type="number"].qty', 5764 wcpt_util.get_uninit_rows($this__container), 5765 ); 5766 5767 $target_qty_elms.each(function () { 5768 var $this__input = $(this), 5769 initial_value = $this__input.attr("data-wcpt-initial-value"); 5770 5771 if (initial_value === "0") { 5772 $this__input.val(0); 5773 } else if (initial_value === "empty") { 5774 $this__input.val(""); 5775 } else if (initial_value === "min") { 5776 var min = $this__input.attr("min") 5777 ? parseFloat($this__input.attr("min")) 5778 : 1, 5779 maxAttr = $this__input.attr("max"), 5780 max = 5781 maxAttr !== undefined && maxAttr !== "" 5782 ? parseFloat(maxAttr) 5783 : NaN; 5784 // OOS: max is 0 â don't leave purchase min in the field (WC page-load 5785 // script will also re-bump value if min stays above max). 5786 if (!isNaN(max) && max < min) { 5787 $this__input.attr("min", Math.max(0, max)); 5788 $this__input.val(Math.max(0, max)); 5789 } else { 5790 $this__input.val(min); 5791 } 5792 } 5793 5794 $this__input 5795 .trigger("wcpt_updating_initial_quantity") 5796 .trigger("change") 5797 .trigger("wcpt_initial_quantity_updated"); 5798 }); 5799 }, 5800 ); 5801 5802 /** 5803 * Quantity Error Handler 5804 * Manages error states and visual feedback for: 5805 * - Values below minimum 5806 * - Values above maximum 5807 * - Invalid step increments 5808 * Updates associated cart buttons accordingly 5809 */ 5810 $("body").on("keyup change", ".wcpt-quantity .qty", function (e) { 5811 var $this = $(this), 5812 max = $this.attr("max") ? parseFloat($this.attr("max")) : 1e9, 5813 min = $this.attr("min") ? parseFloat($this.attr("min")) : 1, 5814 step = $this.attr("step") ? parseFloat($this.attr("step")) : 1, 5815 val = isNaN(parseFloat($this.val())) ? 0 : parseFloat($this.val()), 5816 $wrapper = $this.closest(".wcpt-quantity"); 5817 5818 $wrapper.removeClass( 5819 "wcpt-quantity-error wcpt-quantity-error--min wcpt-quantity-error--max wcpt-quantity-error--step", 5820 ); 5821 5822 if (!val) { 5823 return; 5824 } 5825 5826 if (val < min) { 5827 $wrapper.addClass("wcpt-quantity-error wcpt-quantity-error--min"); 5828 } else if (val > max) { 5829 $wrapper.addClass("wcpt-quantity-error wcpt-quantity-error--max"); 5830 } else if ((val * 1e12) % (step * 1e12)) { 5831 $wrapper.addClass("wcpt-quantity-error wcpt-quantity-error--step"); 5832 } 5833 5834 var $row = $this.closest(".wcpt-row"), 5835 $button = $row.find('.wcpt-button[data-wcpt-link-code^="cart_"]'); 5836 5837 if ($wrapper.hasClass("wcpt-quantity-error")) { 5838 disable_button($button, "wcpt-quantity-input-error"); 5839 } else { 5840 enable_button($button, "wcpt-quantity-input-error"); 5841 } 5842 5843 limit_qty_controller($wrapper); 5844 }); 5845 5846 /** 5847 * Cross-Table Quantity Synchronization Handler 5848 * Ensures quantity values remain synchronized across: 5849 * - Multiple instances of the same product in different tables 5850 * - WooCommerce default cart buttons 5851 * - Custom quantity select dropdowns 5852 * 5853 * Updates: 5854 * - All quantity inputs for the same product 5855 * - WooCommerce add-to-cart button data attributes 5856 * 5857 * @param {Event} e - Change event object 5858 */ 5859 $("body").on( 5860 "change", 5861 ".wcpt-quantity input.qty, select.wcpt-qty-select", 5862 function (e) { 5863 var $input = $(this), 5864 $product_rows = get_product_rows($input), 5865 $siblings = $product_rows 5866 .find("input.qty, select.wcpt-qty-select") 5867 .not(this), 5868 val = $input.val(); 5869 5870 $siblings.val(val); 5871 5872 if ($input.closest(".wcpt-add-to-cart-wrapper").length) { 5873 return; 5874 } 5875 5876 var $wc_default_button = $(".add_to_cart_button", $product_rows); 5877 $wc_default_button.data("quantity", val); 5878 $wc_default_button.attr("data-quantity", val); 5879 }, 5880 ); 5881 5882 /** 5883 * Quantity Input Keyboard Navigation Handler 5884 * Manages up/down arrow key interactions for quantity adjustments:
5885 * - ArrowUp: Increments value by step amount 5886 * - ArrowDown: Decrements value by step amount 5887 * - Respects min/max boundaries 5888 * - Handles special cases for real vs visual minimum values 5889 * 5890 * @param {KeyboardEvent} e - The keyboard event object 5891 */ 5892 $("body").on("keydown", ".wcpt-quantity .qty", function (e) { 5893 if (e.key !== "ArrowUp" && e.key !== "ArrowDown") return; 5894 5895 var $qty = $(this); 5896 var visualMin = $qty.attr("min") ? parseFloat($qty.attr("min")) : 0; 5897 var realMin = $qty.attr("data-wcpt-min") 5898 ? parseFloat($qty.attr("data-wcpt-min")) 5899 : visualMin; 5900 var step = $qty.attr("step") ? parseFloat($qty.attr("step")) : 1; 5901 var currentVal = parseFloat($qty.val()) || 0; 5902 5903 // Calculate next value 5904 var next = 5905 e.key === "ArrowUp" 5906 ? (currentVal * 1e12 + step * 1e12) / 1e12 5907 : (currentVal * 1e12 - step * 1e12) / 1e12; 5908 5909 // Apply same logic as plus/minus buttons 5910 if (e.key === "ArrowUp") { 5911 if (currentVal < realMin && next >= visualMin) { 5912 next = realMin; 5913 } 5914 } else { 5915 // ArrowDown 5916 if (currentVal >= realMin && next < realMin) { 5917 next = visualMin > 0 ? 0 : visualMin; 5918 } else if (next < visualMin) { 5919 next = 0; 5920 } 5921 } 5922 5923 $qty.val(next).change(); 5924 e.preventDefault(); 5925 }); 5926 5927 /** 5928 * Handles clicks on disabled cart-related buttons and quantity controls. 5929 * Shows appropriate error messages when users try to interact with: 5930 * 5931 * For variable products: 5932 * - Products missing variation selection 5933 * - Products with no matching variations 5934 * - Products with unavailable variations 5935 * 5936 * For simple products: 5937 * - Out of stock products 5938 * 5939 * @param {Event} event - The click event object 5940 */ 5941 $("body").on( 5942 "click", 5943 ".wcpt-plus.wcpt-qty-controller.wcpt-disabled, .wcpt-button-cart_ajax.wcpt-disabled, .wcpt-button-cart_redirect.wcpt-disabled, .wcpt-button-cart_refresh.wcpt-disabled, .wcpt-button-cart_checkout.wcpt-disabled", 5944 function () { 5945 var $product_row = $(this).closest(".wcpt-row"), 5946 product_type = $product_row.attr("data-wcpt-type"); 5947 5948 if (product_type === "variable") { 5949 var product_data = $product_row.data(); 5950 5951 if (!product_data.wcpt_variation_selected) { 5952 alert(wcpt_i18n.i18n_make_a_selection_text); 5953 return; 5954 } 5955 5956 if (!product_data.wcpt_variation_found) { 5957 alert(wcpt_i18n.i18n_no_matching_variations_text); 5958 return; 5959 } 5960 5961 if ( 5962 !product_data.wcpt_variation || 5963 !product_data.wcpt_variation.is_in_stock 5964 ) { 5965 alert(wcpt_i18n.i18n_unavailable_variation_text); 5966 return; 5967 } 5968 } else if ($product_row.hasClass("wcpt-out-of-stock")) { 5969 alert(wcpt_i18n.i18n_out_of_stock_text); 5970 } 5971 }, 5972 ); 5973 5974 // total 5975 $("body").on("keyup mouseup change", ".wcpt-quantity .qty", function () { 5976 update_row_total($(this).closest(".wcpt-row")); 5977 }); 5978 5979 // -- name your price: ensure qty if name your price is used 5980 $("body").on( 5981 "keyup change", 5982 ".wcpt .wcpt-name-your-price--input", 5983 function (e) { 5984 var $nyp = $(this), 5985 nyp_val = $nyp.val(), 5986 $rows = wcpt_get_sibling_rows($nyp.closest(".wcpt-row")), 5987 $qty = $(".wcpt-quantity input.qty", $rows), 5988 qty_val = $qty.val(), 5989 $cb = $(".wcpt-cart-checkbox ", $rows); 5990 5991 nyp_validate($nyp); 5992 5993 // added val - add qty 5994 if (nyp_val && !qty_val) { 5995 $qty.val($qty.attr("min")); 5996 5997 // removed val - remove qty 5998 } else if (!nyp_val && qty_val) { 5999 var inital_val_type = $qty.attr("data-wcpt-nyp-initial-value"), 6000 val = $qty.attr("min"); 6001 6002 if (inital_val_type === "empty") { 6003 val = ""; 6004 } else if (inital_val_type === "0") { 6005 val = "0"; 6006 } 6007 6008 $qty.val(val); 6009 } 6010 6011 update_row_total($(this).closest(".wcpt-row")); 6012 6013 if ($qty.length) { 6014 $qty.change(); 6015 } else if ($cb.length) { 6016 $rows.trigger("wcpt_checkbox_change", !!$nyp.val()); 6017 } 6018 }, 6019 ); 6020 6021 function update_row_total($row, force_qty) { 6022 var $rows = $row, 6023 $qty = $rows.find(".wcpt-quantity .qty").eq(0), 6024 qty = $qty.length ? parseFloat($qty.val() ? $qty.val() : 0) : 1, 6025 $total = $(".wcpt-total", $rows), 6026 $cb = $(".wcpt-cart-checkbox ", $rows), 6027 prev_total = parseFloat($total.attr("data-wcpt-in-cart-total") || 0), 6028 price = $rows.attr("data-wcpt-price") 6029 ? parseFloat($rows.attr("data-wcpt-price")) 6030 : 0, 6031 total = 0; 6032 6033 if (force_qty) { 6034 qty = force_qty; 6035 } 6036 6037 // variable 6038 if ($rows.hasClass("wcpt-product-type-variable")) { 6039 if ( 6040 $rows.data("wcpt_variation_found") && 6041 $rows.data("wcpt_variation") && 6042 $rows.data("wcpt_variation").display_price 6043 ) { 6044 price = unformat_price_figure( 6045 $rows.data("wcpt_variation").display_price, 6046 ); 6047 } else { 6048 price = 0; 6049 } 6050 } 6051 6052 // name your price 6053 if ($row.hasClass("wcpt-product-has-name-your-price")) { 6054 var $nyp = $(".wcpt-name-your-price--input", $rows); 6055 if ($nyp.filter(":visible").length) { 6056 // variable product compatibility 6057 price = $nyp.val() ? parseFloat($nyp.val()) : 0; 6058 } 6059 } 6060 6061 total = (qty * price).toFixed(2); 6062 6063 // if checkbox is unchecked and qty is not min then no total 6064 if ( 6065 $cb.length && 6066 !$rows.data("wcpt_checked") && 6067 $qty.attr("data-wcpt-initial-value") !== "min" 6068 ) { 6069 total = 0; 6070 } 6071 6072 $total.each(function () { 6073 var $this = $(this), 6074 _total = total; 6075 6076 if ($this.hasClass("wcpt-total--include-total-in-cart")) { 6077 _total = parseFloat(_total) + parseFloat(prev_total); 6078 } 6079 6080 if (parseFloat(_total)) { 6081 $this 6082 .removeClass("wcpt-total--empty") 6083 .find(".wcpt-amount") 6084 .text(format_price_figure(_total)); 6085 } else { 6086 $this.addClass("wcpt-total--empty"); 6087 } 6088 });
6089 6090 $row.data("wcpt-total", total); 6091 6092 $row.trigger("wcpt_total_updated"); 6093 } 6094 6095 // audio player 6096 $("body").on("click", ".wcpt-player__button", function () { 6097 var $button = $(this), 6098 $container = $button.closest(".wcpt-player"), 6099 src = $container.attr("data-wcpt-src"), 6100 loop = $container.attr("data-wcpt-loop"), 6101 $el = $container.data("wcpt-media-el"); 6102 6103 if (!$el) { 6104 $el = $('<audio class="wcpt-audio-elm" src="' + src + '"></audio>'); 6105 $container.append($el); 6106 $container.data("wcpt-media-el", $el); 6107 6108 if (loop) { 6109 $el.prop("loop", true); 6110 } else { 6111 $el.on("ended", function () { 6112 $container.toggleClass("wcpt-player--playing"); 6113 }); 6114 } 6115 } 6116 6117 if ($button.hasClass("wcpt-player__play-button")) { 6118 $el[0].play(); 6119 6120 if (!$container.hasClass("wcpt-media-loaded")) { 6121 $el.on("canplay", function () { 6122 $container.addClass("wcpt-media-loaded"); 6123 }); 6124 } 6125 6126 $("audio.wcpt-audio-elm") 6127 .not($el) 6128 .each(function () { 6129 this.currentTime = 0; 6130 this.pause(); 6131 }); 6132 6133 // pause others 6134 var $other_players = $(".wcpt-player.wcpt-player--playing").not( 6135 $container, 6136 ); 6137 $other_players.find(".wcpt-player__playing-button").click(); 6138 } else if ($button.hasClass("wcpt-player__pause-button")) { 6139 $el[0].pause(); 6140 } else if ($button.hasClass("wcpt-player__stop-button")) { 6141 $el[0].pause(); 6142 $el[0].currentTime = 0; 6143 } 6144 6145 $container.toggleClass("wcpt-player--playing"); 6146 }); 6147 6148 // term click 6149 // -- trigger filter 6150 $("body").on("click", ".wcpt-trigger_filter > [data-wcpt-slug]", function () { 6151 var $this = $(this), 6152 slug = $this.attr("data-wcpt-slug"), 6153 taxonomy = $this.parent().attr("data-wcpt-taxonomy"), 6154 $container = $this.closest(".wcpt"), 6155 $nav = $container.find(".wcpt-navigation"), 6156 $option = $nav.find( 6157 '[data-wcpt-taxonomy="' + 6158 taxonomy + 6159 '"] [data-wcpt-slug="' + 6160 slug + 6161 '"]', 6162 ), 6163 $input = $("input", $option); 6164 6165 if (!$option.length) { 6166 return; 6167 } 6168 6169 // $nav.addClass("wcpt-force-hide-dropdown-menus"); 6170 $input.prop("checked", !$input.prop("checked")); 6171 $nav.first().trigger("change"); 6172 }); 6173 // -- archive redirect 6174 $("body").on( 6175 "click", 6176 ".wcpt-archive_redirect > [data-wcpt-slug]", 6177 function () { 6178 var $this = $(this), 6179 url = $this.attr("data-wcpt-archive-url"); 6180 if (!$this.is("a")) { 6181 window.location = url; 6182 } 6183 }, 6184 ); 6185 6186 // remove 6187 $("body").on( 6188 "click", 6189 ".wcpt-row:not(.wcpt-removing-product) .wcpt-remove:not(.wcpt-disabled)", 6190 function () { 6191 var $this = $(this), 6192 $row = ($product_row = get_product_rows($this)), 6193 product_id = $row.attr("data-wcpt-product-id"), 6194 variation_id = $row.attr("data-wcpt-variation-id"), 6195 params = { 6196 payload: { 6197 products: {}, 6198 variations: {}, 6199 overwrite_cart_qty: true, 6200 }, 6201 }; 6202 6203 if ($this.hasClass("wcpt-refresh-enabled")) { 6204 params.redirect = window.location.href; 6205 } 6206 6207 params.payload.products[product_id] = 0; 6208 6209 // variation 6210 if (variation_id) { 6211 params.payload.variations[product_id] = {}; 6212 params.payload.variations[product_id][variation_id] = 0; 6213 } 6214 6215 // variable product 6216 if ($row.hasClass("wcpt-product-type-variable")) { 6217 params.payload.variations[product_id] = $.extend( 6218 {}, 6219 wcpt_cart_result_cache.in_cart[product_id], 6220 ); 6221 $.each( 6222 params.payload.variations[product_id], 6223 function (variation_id, qty) { 6224 params.payload.variations[product_id][variation_id] = 0; 6225 }, 6226 ); 6227 } 6228 6229 wcpt_cart(params); 6230 }, 6231 ); 6232 6233 // toggle content (show more / less) 6234 $("body").on("click", ".wcpt-toggle-trigger", function (e) { 6235 e.preventDefault(); 6236 var $this = $(this); 6237 $this.closest(".wcpt-toggle-enabled").toggleClass("wcpt-toggle"); 6238 }); 6239 6240 // freeze table selectors for originals 6241 function wcpt_get_container_original_table($container) { 6242 return $container.find(".wcpt-table:visible").not(".frzTbl-clone-table"); 6243 } 6244 6245 // get sibling tables under freeze table 6246 function wcpt_get_sibling_tables($table) { 6247 var $freeze_table = $table.closest(".frzTbl"); 6248 if (!$freeze_table.length) { 6249 return $table; 6250 } 6251 6252 return $(".wcpt-table", $freeze_table); 6253 } 6254 6255 // get sibling rows under freeze table 6256 window.wcpt_get_sibling_rows = function ($row) { 6257 var $freeze_table = $row.closest(".frzTbl"); 6258 if (!$freeze_table.length) { 6259 return $row; 6260 } 6261 6262 var product_id = $row.attr("data-wcpt-product-id"), 6263 variation_id = $row.attr("data-wcpt-variation-id"), 6264 row_selector; 6265 6266 if (variation_id) { 6267 row_selector = 6268 '[data-wcpt-variation-id="' + 6269 variation_id + 6270 '"].wcpt-row.wcpt-product-type-variation'; 6271 } else { 6272 row_selector = 6273 '[data-wcpt-product-id="' + 6274 product_id + 6275 '"].wcpt-row:not(.wcpt-product-type-variation)'; 6276 } 6277 6278 return $(row_selector, $freeze_table); 6279 }; 6280 6281 // get original row under freeze table 6282 function wcpt_get_original_row($row) { 6283 var $sibling_rows = wcpt_get_sibling_rows($row); 6284 6285 $sibling_rows.each(function () { 6286 var $row = $(this); 6287 if (!$row.closest("table").hasClass("frzTbl-clone-table")) { 6288 $original = $row; 6289 return false; 6290 } 6291 });
6292 6293 return $original; 6294 } 6295 6296 // unique key per row (variation first, then product) 6297 function wcpt_get_row_key($row) { 6298 var variationId = $row.data("wcpt_variation_id"); 6299 if (variationId) { 6300 return "v:" + String(variationId); 6301 } 6302 var variationIdAttr = $row.attr("data-wcpt-variation-id"); 6303 if (variationIdAttr) { 6304 return "v:" + String(variationIdAttr); 6305 } 6306 var productId = $row.attr("data-wcpt-product-id"); 6307 if (productId) { 6308 return "p:" + String(productId); 6309 } 6310 return ""; 6311 } 6312 6313 window.wcpt_get_original_row = wcpt_get_original_row; 6314 window.wcpt_get_row_key = wcpt_get_row_key; 6315 6316 // checkbox 6317 6318 // -- $cb 'change' handler -> triggers '_wcpt_checkbox_change' on $row 6319 $("body").on("change", ".wcpt-cart-checkbox", function (e) { 6320 var $this = $(this), 6321 $row = $this.closest(".wcpt-row"); 6322 6323 $row.trigger("_wcpt_checkbox_change", $this.prop("checked")); 6324 }); 6325 6326 // -- $row '_wcpt_checkbox_change' handler -> triggers 'wcpt_checkbox_change' on original $row, sets same state on sibling $cb 6327 $("body").on("_wcpt_checkbox_change", ".wcpt-row", function (e, state) { 6328 var $this = $(this), 6329 $original_row = wcpt_get_original_row($this), 6330 $table = $original_row.closest("table"), 6331 $sibling_rows = wcpt_get_sibling_rows($this), 6332 $sibling_cbs = $(".wcpt-cart-checkbox", $sibling_rows); 6333 6334 // unnecessary trigger 6335 if (!$sibling_cbs.length || !$sibling_cbs.not(":disabled").length) { 6336 return; 6337 } 6338 6339 // $row .data()-> state 6340 $original_row.data("wcpt_checked", state); 6341 6342 // $table .data()-> checked_rows 6343 var $table_checked_rows = $table.data("wcpt_checked_rows") 6344 ? $table.data("wcpt_checked_rows") 6345 : $(); 6346 6347 if (state) { 6348 $table_checked_rows = $table_checked_rows.add($original_row); 6349 } else { 6350 $table_checked_rows = $table_checked_rows.not($original_row); 6351 } 6352 6353 $table.data("wcpt_checked_rows", $table_checked_rows); 6354 6355 // publish event 6356 $original_row.trigger("wcpt_checkbox_change", state); 6357 }); 6358 6359 // -- 'wcpt_checkbox_change' handler -> set the same state on siblings rows' $cb and data on sibling rows 6360 $("body").on("wcpt_checkbox_change", ".wcpt-row", function (e, state) { 6361 var $this = $(this), 6362 $sibling_rows = wcpt_get_sibling_rows($this), 6363 $sibling_cbs = $(".wcpt-cart-checkbox", $sibling_rows); 6364 6365 $sibling_rows.data("wcpt_checked", state); 6366 $sibling_cbs.prop("checked", state); 6367 }); 6368 6369 // -- row html class 6370 $("body").on("wcpt_checkbox_change", ".wcpt-row", function (e, state) { 6371 var $this = $(this), 6372 $original_row = wcpt_get_original_row($this), 6373 $sibling_rows = wcpt_get_sibling_rows($this), 6374 html_class = "wcpt-row--checked"; 6375 6376 if ($original_row.data("wcpt_checked")) { 6377 $sibling_rows.addClass(html_class); 6378 } else { 6379 $sibling_rows.removeClass(html_class); 6380 } 6381 }); 6382 6383 // -- select variation 6384 $("body").on("select_variation", ".wcpt-row", function (e) { 6385 var $this = $(this), 6386 selected_variation = $this.data("wcpt_variation"), 6387 $cb = $this.find(".wcpt-cart-checkbox"); 6388 6389 // reset 6390 $cb.removeAttr("disabled"); 6391 $cb.removeClass("wcpt-disabled"); 6392 6393 if ( 6394 !selected_variation || 6395 $.isEmptyObject(selected_variation) || 6396 (!selected_variation.is_in_stock && !selected_variation.is_on_backorder) 6397 ) { 6398 $this.trigger("wcpt_checkbox_change", false); 6399 $cb.attr("disabled", true); 6400 $cb.addClass("wcpt-disabled"); 6401 } 6402 }); 6403 6404 // -- qty 6405 // -- -- '.qty' change handler -> trigger '_wcpt_checkbox_change' on $row 6406 setTimeout(function () { 6407 // too many 3rd party modules trigger $qty at page load 6408 $("body").on( 6409 "change", 6410 ".wcpt input.qty, .wcpt select.wcpt-qty-select", 6411 function (e) { 6412 var $this = $(this), 6413 $row = $this.closest(".wcpt-row"), 6414 $original_row = wcpt_get_original_row($row); 6415 6416 if ( 6417 $this.closest("form.cart").length || 6418 // don't permit auto-check when initial-val is min 6419 ($this.attr("data-wcpt-initial-value") === "min" && 6420 !$original_row.data("wcpt_checked")) 6421 ) { 6422 return; 6423 } 6424 6425 $original_row.trigger( 6426 "_wcpt_checkbox_change", 6427 !!parseFloat($this.val()), 6428 ); 6429 }, 6430 ); 6431 }, 1); 6432 6433 // -- -- raise qty to min or reduce to initial qty upon 'wcpt_checkbox_change' 6434 $("body").on("wcpt_checkbox_change", ".wcpt-row", function (e, state) { 6435 var $this = $(this), 6436 $sibling_rows = wcpt_get_sibling_rows($this), 6437 $sibling_qty = $("input.qty, select.wcpt-qty-select", $sibling_rows), 6438 $sibling_qty_wrappers = $(".wcpt-quantity", $sibling_rows); 6439 6440 $sibling_qty.each(function () { 6441 var $this = $(this), 6442 val = $this.val(), 6443 min = $this.attr("data-wcpt-min") 6444 ? parseFloat($this.attr("data-wcpt-min")) 6445 : $this.attr("min"), 6446 initial_qty = $this.attr("data-wcpt-initial-value"); 6447 6448 if (state) { 6449 if (!val || val === "0" || isNaN(val)) { 6450 val = min; 6451 } 6452 } else { 6453 if (initial_qty == "empty") { 6454 val = ""; 6455 } else if (initial_qty == "0") { 6456 val = 0; 6457 } else { 6458 val = min; 6459 } 6460 } 6461 6462 $this.val(val); 6463 });
6464 6465 limit_qty_controller($sibling_qty_wrappers); 6466 }); 6467 6468 // -- -- update total upon 'wcpt_checkbox_change' 6469 $("body").on("wcpt_checkbox_change", ".wcpt-row", function (e, state) { 6470 var $this = $(this), 6471 $sibling_rows = wcpt_get_sibling_rows($this); 6472 6473 update_row_total($sibling_rows); 6474 }); 6475 6476 // -- 'wcpt_checkbox_change' handler -> create / update / hide '.wcpt-cart-checkbox-trigger' button 6477 $("body").on("wcpt_checkbox_change", ".wcpt-row", wcpt_checkbox_trigger_init); 6478 6479 // Also refresh the global trigger after each layout (e.g. AJAX pagination) 6480 $("body").on("wcpt_layout", ".wcpt", function () { 6481 wcpt_checkbox_trigger_init(); 6482 }); 6483 6484 function wcpt_checkbox_trigger_init() { 6485 // global floating checkbox trigger (across all tables on the page) 6486 // setup / show trigger 6487 var $checkbox_trigger = $(".wcpt-cart-checkbox-trigger"); 6488 6489 if (!$checkbox_trigger.length) { 6490 var html = $("#tmpl-wcpt-cart-checkbox-trigger").html(); 6491 $checkbox_trigger = $(html).appendTo("body"); 6492 } else { 6493 $checkbox_trigger.removeClass("wcpt-hide"); 6494 } 6495 6496 // aggregate selection across ALL tables on the page, 6497 // using the per-container cross-page state on each $wcpt 6498 var totalQty = 0, 6499 totalCost = 0; 6500 6501 $(".wcpt").each(function () { 6502 var $container = $(this), 6503 table_id = 6504 typeof wcpt_util !== "undefined" 6505 ? wcpt_util.get_table_id($container) 6506 : "", 6507 tableState = $container.data("wcpt_all_checked") || {}; 6508 6509 if ( 6510 table_id && 6511 window.wcpt_cart_checked_state && 6512 window.wcpt_cart_checked_state[table_id] 6513 ) { 6514 tableState = window.wcpt_cart_checked_state[table_id]; 6515 } 6516 6517 if (!tableState || typeof tableState !== "object") { 6518 return; 6519 } 6520 6521 Object.keys(tableState).forEach(function (key) { 6522 var entry = tableState[key]; 6523 if (!entry) { 6524 return; 6525 } 6526 6527 var q = !isNaN(parseFloat(entry.qty)) ? parseFloat(entry.qty) : 1, 6528 t = !isNaN(parseFloat(entry.total)) ? parseFloat(entry.total) : 0; 6529 6530 totalQty = (totalQty * 1e12 + q * 1e12) / 1e12; 6531 totalCost = (totalCost * 1e12 + t * 1e12) / 1e12; 6532 }); 6533 }); 6534 6535 if (!totalQty) { 6536 $checkbox_trigger.hide(); 6537 return; 6538 } 6539 6540 $checkbox_trigger 6541 .data({ 6542 wcpt_qty: totalQty, 6543 wcpt_cost: totalCost, 6544 }) 6545 .find(".wcpt-total-selected") 6546 .text(totalQty) 6547 .end() 6548 .find(".wcpt-total-selected-cost") 6549 .text(format_price_figure(totalCost)); 6550 6551 $checkbox_trigger 6552 .trigger("wcpt_checkbox_trigger_updating") 6553 .show() 6554 .trigger("wcpt_checkbox_trigger_updated"); 6555 } 6556 6557 window.wcpt_checkbox_trigger_init = wcpt_checkbox_trigger_init; 6558 6559 // -- heading 6560 // -- -- check all $cb in table via $heading_cb '.wcpt-cart-checkbox-heading' 6561 $("body").on("click", ".wcpt-cart-checkbox-heading", function () { 6562 var $this = $(this), 6563 state = $this.prop("checked"), 6564 $container = $this.closest(".wcpt"),
6565 $table = wcpt_get_container_original_table($container), 6566 $rows = $(".wcpt-row", $table); 6567 6568 $rows.trigger("_wcpt_checkbox_change", state); 6569 $("wcpt-cart-checkbox--last-clicked", $table).removeClass( 6570 "wcpt-cart-checkbox--last-clicked", 6571 ); 6572 }); 6573 6574 // -- -- auto toggle heading checkbox 6575 $("body").on("wcpt_checkbox_change", ".wcpt-row", function (e, state) { 6576 // Debounce multiple calls 6577 clearTimeout(window.wcpt_cb_heading); 6578 window.wcpt_cb_heading = setTimeout(function () { 6579 $(".wcpt").each(function () { 6580 var $container = $(this), 6581 $heading_cb = $container.find(".wcpt-cart-checkbox-heading:visible"), 6582 $checkboxes = $container.find(".wcpt-cart-checkbox"), 6583 $available_cbs = $checkboxes.filter(":visible").not(":disabled"), 6584 $checked_cbs = $available_cbs.filter(":checked"), 6585 all_checked = 6586 $available_cbs.length && 6587 $checked_cbs.length === $available_cbs.length; 6588 6589 // Always update heading checkbox state 6590 $heading_cb.prop("checked", all_checked); 6591 }); 6592 }, 100); 6593 }); 6594 6595 // -- shift + click 6596 // -- -- record shift key 6597 $("body").on("keydown", function (e) { 6598 if (e.shiftKey) { 6599 wcpt_shiftKey = true; 6600 $("body").one("keyup", function (e) { 6601 wcpt_shiftKey = false; 6602 }); 6603 } 6604 }); 6605 6606 // -- -- act upon $cb shift key selected by user 6607 $("body").on("change", ".wcpt-cart-checkbox", function (e, wcpt_sync) { 6608 if (wcpt_sync) { 6609 return false; 6610 } 6611 6612 var $this = $(this), 6613 $table = $this.closest(".wcpt-table"), 6614 $cb = $(".wcpt-cart-checkbox", $table); 6615 (($last_clicked = $cb.filter(".wcpt-cart-checkbox--last-clicked")), 6616 (checked = $this.prop("checked"))); 6617 6618 $last_clicked.removeClass("wcpt-cart-checkbox--last-clicked"); 6619 $this.addClass("wcpt-cart-checkbox--last-clicked"); 6620 6621 if ( 6622 $last_clicked.length && 6623 typeof wcpt_shiftKey !== "undefined" && 6624 wcpt_shiftKey 6625 ) { 6626 var min = Math.min($cb.index($this), $cb.index($last_clicked)), 6627 max = Math.max($cb.index($this), $cb.index($last_clicked)); 6628 6629 $cb 6630 .filter(function () { 6631 var $this = $(this), 6632 index = $cb.index($this); 6633 6634 if ($this.prop("disabled")) { 6635 return false; 6636 } 6637 6638 if (index >= min && index <= max) { 6639 return true; 6640 } else { 6641 return false; 6642 } 6643 }) 6644 .prop("checked", checked) 6645 .trigger("change", true); 6646 } 6647 }); 6648 6649 // -- select / clear all 6650 $("body").on( 6651 "click", 6652 ".wcpt-add-selected__select-all, .wcpt-add-selected__clear-all", 6653 function (e) { 6654 var $this = $(this), 6655 isSelectAll = $this.hasClass("wcpt-add-selected__select-all"), 6656 $container = $this.closest(".wcpt"),
6657 $table = wcpt_get_container_original_table($container), 6658 $rows = $(".wcpt-row", $table); 6659 6660 $rows.trigger("_wcpt_checkbox_change", isSelectAll); 6661 6662 // If this is clear-all, also clear cross-page selection state for this table 6663 if (!isSelectAll && typeof wcpt_util !== "undefined") { 6664 var table_id = wcpt_util.get_table_id($container); 6665 if (table_id) { 6666 // delegate actual clearing to the persistence module 6667 $(document).trigger("wcpt_clear_all_checked_for_table", [ 6668 table_id, 6669 $container, 6670 ]); 6671 6672 // ensure the per-container mirror used by the global trigger is cleared 6673 $container.data("wcpt_all_checked", {}); 6674 6675 // immediately recompute / hide the global floating trigger if needed 6676 wcpt_checkbox_trigger_init(); 6677 } 6678 } 6679 $("wcpt-cart-checkbox--last-clicked", $table).removeClass( 6680 "wcpt-cart-checkbox--last-clicked", 6681 ); 6682 }, 6683 ); 6684 6685 // -- -- toggle the buttons 6686 $("body").on( 6687 "wcpt_checkbox_change", 6688 ".wcpt-row", 6689 update_table_add_selected_to_cart, 6690 ); 6691 6692 function update_table_add_selected_to_cart() { 6693 var _ = this; 6694 setTimeout(function () { 6695 var $this = $(_), 6696 $container = $this.closest(".wcpt"),
6697 $table = wcpt_get_container_original_table($container), 6698 // cross-page selection state for this table (from persistence module) 6699 tableState = $container.data("wcpt_all_checked") || {}; 6700 $add_checked = $(".wcpt-add-selected:visible", $container); 6701 6702 $add_checked.removeClass( 6703 "wcpt-add-selected--unselected wcpt-add-selected--single-item-selected", 6704 ); 6705 6706 var keys = Object.keys(tableState), 6707 totalQty = 0, 6708 totalCost = 0; 6709 6710 if (keys.length) { 6711 // derive total quantity and total cost across all pages from persisted state 6712 keys.forEach(function (k) { 6713 var entry = tableState[k]; 6714 if (!entry) { 6715 return; 6716 } 6717 var q = !isNaN(parseFloat(entry.qty)) ? parseFloat(entry.qty) : 1, 6718 t = !isNaN(parseFloat(entry.total)) ? parseFloat(entry.total) : 0; 6719 6720 totalQty = (totalQty * 1e12 + q * 1e12) / 1e12; 6721 totalCost = (totalCost * 1e12 + t * 1e12) / 1e12; 6722 }); 6723 6724 $(".wcpt-total-selected", $add_checked).text(totalQty); 6725 $(".wcpt-total-selected-cost .wcpt-amount", $add_checked).text( 6726 format_price_figure(totalCost), 6727 ); 6728 6729 if (totalQty == 1) { 6730 $add_checked.addClass("wcpt-add-selected--single-item-selected"); 6731 } 6732 } else { 6733 // nothing selected across any page 6734 $add_checked.addClass("wcpt-add-selected--unselected"); 6735 $(".wcpt-total-selected", $add_checked).text(0); 6736 $(".wcpt-total-selected-cost .wcpt-amount", $add_checked).text( 6737 format_price_figure(0), 6738 ); 6739 } 6740 }, 100); 6741 } 6742 6743 // price decimal 6744 function format_price_figure(price) { 6745 price = parseFloat(price); 6746 6747 // remove decimal if it is unnecessary ie .00 6748 if (price !== parseInt(price)) { 6749 price = parseFloat(price).toFixed(wcpt_params.price_decimals); 6750 } else { 6751 price = parseInt(price); 6752 } 6753 6754 // decimal separator 6755 price = (price + "").replace(".", wcpt_params.price_decimal_separator); 6756 6757 // thousands separator 6758 var decimal_split = price.split(wcpt_params.price_decimal_separator); 6759 ((non_decimal = decimal_split[0].toString()), (decimal = decimal_split[1])); 6760 6761 if (non_decimal.length > 3) { 6762 formatted_non_decimal = 6763 non_decimal.slice(0, -3) + 6764 wcpt_params.price_thousand_separator + 6765 non_decimal.slice(-3); 6766 price = 6767 formatted_non_decimal + 6768 (decimal ? wcpt_params.price_decimal_separator + decimal : ""); 6769 } 6770 6771 return price; 6772 } 6773 6774 function unformat_price_figure(price) { 6775 price = price.toString(); 6776 6777 // remove thousands separator 6778 price = price.replace(wcpt_params.price_thousand_separator, ""); 6779 6780 // replace decimal separator 6781 price = price.replace(wcpt_params.price_decimal_separator, "."); 6782 6783 return parseFloat(price); 6784 } 6785 6786 // follow wc settings for price display 6787 function format_price(num) { 6788 if (!num && num !== "0" && num !== 0) { 6789 return ""; 6790 } 6791 6792 return wcpt_params.price_format 6793 .replace("%1$s", wcpt_params.currency_symbol) 6794 .replace("%2$s", format_price_figure(num)); 6795 } 6796 6797 // -- add to cart 6798 $("body").on( 6799 "click", 6800 ".wcpt-cart-checkbox-trigger, .wcpt-cart-checkbox-trigger--local", 6801 wcpt_cart_checkbox, 6802 ); 6803 function wcpt_cart_checkbox() { 6804 var $this = $(this), 6805 products = {}, 6806 variations = {}, 6807 attributes = {}, 6808 addons = {}, 6809 measurement = {}, 6810 nyp = {}, // name your price 6811 $checked_rows = $(), 6812 $table = $(), 6813 tableContainersById = {}; 6814 6815 if ($this.hasClass("wcpt-cart-checkbox-trigger")) { 6816 // global 6817 var $container = $(".wcpt"); 6818 6819 $container.each(function () { 6820 var $c = $(this), 6821 table_id = 6822 typeof wcpt_util !== "undefined" 6823 ? wcpt_util.get_table_id($c) 6824 : false, 6825 $orig_table = wcpt_get_container_original_table($c); 6826 6827 if (table_id) { 6828 tableContainersById[table_id] = $c; 6829 } 6830 6831 $table = $table.add($orig_table); 6832 }); 6833 6834 $this.addClass("wcpt-hide"); 6835 } else { 6836 // local 6837 var $container = $this.closest(".wcpt"), 6838 table_id = 6839 typeof wcpt_util !== "undefined" 6840 ? wcpt_util.get_table_id($container) 6841 : false; 6842 6843 if (table_id) { 6844 tableContainersById[table_id] = $container; 6845 } 6846
6847 $table = wcpt_get_container_original_table($container); 6848 } 6849 6850 $table.each(function () { 6851 var $this = $(this); 6852 $table_checked_rows = $this.data("wcpt_checked_rows") 6853 ? $this.data("wcpt_checked_rows") 6854 : $(); 6855 6856 $checked_rows = $checked_rows.add($table_checked_rows); 6857 }); 6858 6859 $checked_rows.each(function () { 6860 var $row = $(this), 6861 product_id = $row.attr("data-wcpt-product-id"), 6862 variation_id = false, 6863 variation_attributes = false, 6864 $qty = $(".qty, .wcpt-qty-select", $row).first(), 6865 val = parseFloat($qty.length ? $qty.val() : 1); 6866 if (isNaN(val)) { 6867 val = 0; 6868 } 6869 6870 if (typeof products[product_id] === "undefined") { 6871 products[product_id] = val; 6872 } else { 6873 products[product_id] += val; // variation 6874 } 6875 6876 // variable 6877 if ($row.hasClass("wcpt-product-type-variable")) { 6878 var data = $row.data(); 6879 6880 if ( 6881 data.wcpt_variation_selected && 6882 data.wcpt_variation_available && 6883 data.wcpt_complete_match && 6884 data.wcpt_variation_id 6885 ) { 6886 variation_id = data.wcpt_variation_id; 6887 6888 if (data.wcpt_attributes) { 6889 variation_attributes = data.wcpt_attributes; 6890 } 6891 } 6892 6893 // variation 6894 } else if ($row.hasClass("wcpt-product-type-variation")) { 6895 variation_id = $row.attr("data-wcpt-variation-id"); 6896 variation_attributes = JSON.parse( 6897 $row.attr("data-wcpt-variation-attributes"), 6898 ); 6899 } 6900 6901 if (variation_id) { 6902 if (!variations[product_id]) { 6903 variations[product_id] = {}; 6904 } 6905 6906 if (!variations[product_id][variation_id]) { 6907 variations[product_id][variation_id] = val; 6908 } 6909 6910 if (variation_attributes) { 6911 attributes[variation_id] = variation_attributes; 6912 } 6913 } 6914 6915 // addons 6916 if ( 6917 $row.hasClass("wcpt-product-has-addons") && 6918 wcpt_row_has_inline_addons_form($row) 6919 ) { 6920 addons[product_id] = wcpt_get_addons($row); 6921 } 6922 6923 // measurement 6924 if ($row.hasClass("wcpt-product-has-measurement")) { 6925 measurement[product_id] = wcpt_get_measurement($row); 6926 } 6927 6928 // name your price 6929 if ($row.hasClass("wcpt-product-has-name-your-price")) { 6930 nyp[product_id] = wcpt_get_nyp($row); 6931 } 6932 }); 6933 6934 // Off-page selections from the checkbox persistence module 6935 if (window.wcpt_cart_checkbox_persist) { 6936 window.wcpt_cart_checkbox_persist.aggregateOffPageIntoPayload( 6937 tableContainersById, 6938 { 6939 products: products, 6940 variations: variations, 6941 attributes: attributes, 6942 }, 6943 ); 6944 window.wcpt_cart_checkbox_persist.clearTables(tableContainersById); 6945 } 6946 6947 // Uncheck all currently checked rows on the page before wcpt_cart, 6948 // otherwise totals / visual state may be inconsistent. 6949 $checked_rows.trigger("_wcpt_checkbox_change", false); 6950 6951 // When every selection was off-page, $checked_rows is empty so the 6952 // checkbox-change â update_table_add_selected_to_cart path never runs. 6953 // Refresh the local "Add selected" button(s) from cleared state. 6954 $.each(tableContainersById, function (id, $c) { 6955 if ($c && $c.length) { 6956 update_table_add_selected_to_cart.call($c.get(0)); 6957 } 6958 }); 6959 6960 var payload = { 6961 products: products, 6962 addons: addons, 6963 measurement: measurement, 6964 variations: variations, 6965 attributes: attributes, 6966 nyp: nyp, 6967 }; 6968 6969 // mark this cart request as originating from a checkbox-trigger bulk add 6970 var checkboxScope = $this.hasClass("wcpt-cart-checkbox-trigger") 6971 ? "global" 6972 : "local"; 6973 6974 wcpt_cart({ 6975 payload: payload, 6976 redirect: $this.attr("data-wcpt-redirect-url"), 6977 external_payload: { 6978 wcpt_checkbox_bulk: checkboxScope, 6979 }, 6980 }); 6981 } 6982 6983 // addons 6984 // True when addon fields are printed and visible in the row's cart form. 6985 // Select variation "attribute dropdowns" embeds a cart_form with only attributes 6986 // visible â hidden addon markup there must not block the product form modal. 6987 window.wcpt_row_has_inline_addons_form = function ($row) { 6988 return !!$( 6989 [ 6990 ".wcpt-add-to-cart-wrapper .prad-addons-wrapper", 6991 ".wcpt-add-to-cart-wrapper .wc-pao-addon", 6992 ".wcpt-add-to-cart-wrapper .wc-pao-addons-container", 6993 ".wcpt-add-to-cart-wrapper #product-addons-total", 6994 ".wcpt-add-to-cart-wrapper .wcpa_form_outer", 6995 ].join(", "), 6996 wcpt_get_sibling_rows($row), 6997 ).filter(":visible").length; 6998 }; 6999 7000 window.wcpt_get_addons = function ($row) { 7001 var $form = $( 7002 ".wcpt-add-to-cart-wrapper form", 7003 wcpt_get_sibling_rows($row), 7004 ), 7005 addons = {}; 7006 7007 // WooCommerce Custom Product Addons 7008 var $wcpa_fields = $form.find(".wcpa_form_outer"); 7009 if ($wcpa_fields.length) { 7010 var $fields = $wcpa_fields, 7011 $_form = $("<form>"); 7012 7013 $_form 7014 .append($fields.clone()) 7015 .find("select") 7016 .each(function () { 7017 // cloned select needs correct value 7018 var $this = $(this), 7019 name = $this.attr("name"), 7020 value = $fields.find('[name="' + name + '"]select').val(); 7021 $this.val(value); 7022 }); 7023 7024 $.each($_form.serializeArray(), function (i, field) { 7025 var field_name = field.name, 7026 suffix_index = field_name.indexOf("--wcpt"); 7027 7028 if (-1 !== suffix_index) { 7029 // must be checkbox or radio 7030 field_name = field_name.substring(0, suffix_index); 7031 7032 if (field.name.slice(-1) == "]") { 7033 // checkbox 7034 if (!addons[field_name]) { 7035 addons[field_name] = []; 7036 } 7037 addons[field_name].push(field.value); 7038 return; 7039 } else { 7040 // radio 7041 addons[field_name] = field.value; 7042 } 7043 } else { 7044 // other field types 7045 addons[field_name] = field.value; 7046 } 7047 }); 7048 7049 // official WooCommerce Product Addons 7050 } else { 7051 $.each($form.serializeArray(), function (i, field) { 7052 var field_name = field.name; 7053 7054 if (field_name.slice(-2) == "[]") { 7055 field_name = field_name.substring(0, field_name.length - 2); 7056 } 7057 7058 if (typeof addons[field_name] === "undefined") { 7059 addons[field_name] = field.value; // add prop 7060 } else { 7061 if (typeof addons[field_name] !== "object") { 7062 // make it an array 7063 addons[field_name] = [addons[field_name]]; 7064 } 7065 addons[field_name].push(field.value); // update prop 7066 } 7067 }); 7068 } 7069 7070 return addons; 7071 }; 7072 7073 // measurement 7074 window.wcpt_get_measurement = function ($row) { 7075 var $price_calculator = $( 7076 ".wcpt-add-to-cart-wrapper form #price_calculator", 7077 wcpt_get_sibling_rows($row), 7078 ), 7079 measurement = {}; 7080 7081 $("input", $price_calculator).each(function () { 7082 var $this = $(this); 7083 measurement[$this.attr("name")] = $this.val(); 7084 });
7085 7086 return measurement; 7087 }; 7088 7089 // name your price 7090 window.wcpt_get_nyp = function ($row) { 7091 var $nyp = get_nyp_input_element($row), 7092 val = 0; 7093 7094 if ($nyp.length) { 7095 val = $nyp.val(); 7096 } 7097 7098 return val; 7099 }; 7100 7101 function get_nyp_input_element($row) { 7102 return $(".wcpt-name-your-price--input", wcpt_get_sibling_rows($row)); 7103 } 7104 7105 $("body").on("keyup input", ".wcpt-search-filter-options", function (e) { 7106 var $this = $(this), 7107 val = $this.val().toLowerCase().trim(), 7108 $filter = $this.closest(".wcpt-filter"), 7109 $option = $(".wcpt-dropdown-option", $filter); 7110 7111 e.preventDefault(); 7112 7113 if (!val) { 7114 $option.show(); 7115 7116 $(".wcpt-filter-hierarchy-accordion--open", $filter).removeClass( 7117 "wcpt-filter-hierarchy-accordion--open", 7118 ); 7119 7120 $("input[type=radio], input[type=checkbox]", $option).each(function () { 7121 var $this = $(this); 7122 if ($this.is(":checked")) { 7123 $this 7124 .closest(".wcpt-dropdown-option.wcpt-filter-hierarchy-accordion") 7125 .addClass("wcpt-filter-hierarchy-accordion--open"); 7126 } 7127 }); 7128 return; 7129 } 7130 7131 $option.each(function () { 7132 var $this = $(this), 7133 label = $this.text().toLowerCase().trim(); 7134 if (label.indexOf(val) == -1) { 7135 $this.hide(); 7136 } else { 7137 $this.show(); 7138 $this 7139 .closest(".wcpt-dropdown-option.wcpt-filter-hierarchy-accordion") 7140 .addClass("wcpt-filter-hierarchy-accordion--open"); 7141 } 7142 }); 7143 }); 7144 7145 // global product search shortcode 7146 $("body").on( 7147 "change keydown keyup", 7148 ".wcpt-global-search__keyword-input", 7149 function () { 7150 var $this = $(this), 7151 $search = $this.closest(".wcpt-global-search"); 7152 7153 if ($this.val()) { 7154 $search.removeClass("wcpt-global-search--empty"); 7155 } else { 7156 $search.addClass("wcpt-global-search--empty"); 7157 } 7158 }, 7159 ); 7160 7161 $(".wcpt-global-search__keyword-input").trigger("change"); 7162 7163 // -- facade 7164 $("body").on("change", ".wcpt-global-search__category-selector", function () { 7165 var $this = $(this), 7166 value = $this.val(), 7167 $option = $('option[value="' + value + '"]', $this), 7168 text = $option.text().trim(), 7169 $facade = $this.siblings(".wcpt-global-search__category-selector-facade"); 7170 7171 $(".wcpt-global-search__category-selector-facade__text", $facade).text( 7172 text, 7173 ); 7174 }); 7175 7176 // -- redirect empty 7177 $("body").on("submit", ".wcpt-global-search__form", function (e) { 7178 var $this = $(this), 7179 keyword = $this.find(".wcpt-global-search__keyword-input").val(), 7180 $select = $this.find(".wcpt-global-search__category-selector"), 7181 clear_redirect_url = $this.attr("data-wcpt-clear-redirect-url"), 7182 clear_redirect = $this.attr("data-wcpt-clear-redirect"), 7183 redirect = $this.attr("data-wcpt-redirect"), 7184 category = $(".wcpt-global-search__category-selector", $this).val(), 7185 action = $this.attr("action"); 7186 7187 // select 'All' category 7188 if (!keyword) { 7189 if (clear_redirect !== "category") { 7190 $select.val("").change(); 7191 } 7192 7193 // redirect 7194 if (clear_redirect_url) { 7195 e.preventDefault(); 7196 window.location = clear_redirect_url; 7197 } 7198 } else if (redirect == "category" && category) { 7199 e.preventDefault(); 7200 window.location = wcpt_product_category_links[category] + "?s=" + keyword; 7201 } else if (redirect == "shop") { 7202 e.preventDefault(); 7203 var url = action + "?s=" + keyword; 7204 if (category) { 7205 url += "&wcpt_search_category=" + category; 7206 } 7207 window.location = url; 7208 } else if (redirect == "search") { 7209 e.preventDefault(); 7210 var url = action + "?s=" + keyword + "&post_type=product"; 7211 if (category) { 7212 url += "&wcpt_search_category=" + category; 7213 } 7214 window.location = url; 7215 } 7216 }); 7217 7218 // -- clear 7219 $("body").on("click", ".wcpt-global-search__clear", function () { 7220 var $this = $(this), 7221 $input = $this.siblings(".wcpt-global-search__keyword-input"), 7222 $form = $this.closest(".wcpt-global-search__form"); 7223 $input.val(""); 7224 $form.submit(); 7225 }); 7226 7227 // -- focus / blur 7228 $("body") 7229 .on("focus", ".wcpt-global-search__keyword-input", function () { 7230 var $this = $(this), 7231 $wrapper = $this.parent(); 7232 $wrapper.addClass("wcpt-global-search__keyword-input-wrapper--focus"); 7233 }) 7234 .on("blur", ".wcpt-global-search__keyword-input", function () { 7235 var $this = $(this), 7236 $wrapper = $this.parent(); 7237 $wrapper.removeClass("wcpt-global-search__keyword-input-wrapper--focus"); 7238 });
7239 7240 // cart 7241 7242 window.wcpt_cart = function (params) { 7243 // maybe add to pending requests 7244 if ( 7245 // there has been at least one cart call earlier 7246 window.wcpt_cart_call_id && 7247 // no results cache ie, no call is resolved yet 7248 (!window.wcpt_cart_result_cache || 7249 // the call id in result cache (ie last resolved call) is uneqal to current call id 7250 window.wcpt_cart_call_id != 7251 window.wcpt_cart_result_cache.payload.wcpt_cart_call_id) 7252 ) { 7253 !window.wcpt_cart_pending_req_params && 7254 (window.wcpt_cart_pending_req_params = {}); 7255 7256 // -- add this req to pending as well and return 7257 $.extend(true, window.wcpt_cart_pending_req_params, params); 7258 return; 7259 } 7260 7261 // begin / increment the cart call counter 7262 window.wcpt_cart_call_id = window.wcpt_cart_call_id 7263 ? window.wcpt_cart_call_id + 1 7264 : 1; 7265 7266 var _params = { 7267 payload: { 7268 wcpt_cart_call_id: window.wcpt_cart_call_id, 7269 }, 7270 before: false, 7271 always: false, 7272 redirect: false, 7273 external_payload: {}, 7274 }; 7275 params = $.extend({}, _params, params ? params : {}); 7276 7277 params.payload.wcpt_cart_call_id = window.wcpt_cart_call_id; 7278 params.payload.cart_widget_permitted = cart_widget_permitted(); 7279 7280 // view 7281 7282 $(".wcpt-cart-widget").addClass("wcpt-cart-widget--loading"); // cart widget 7283 7284 // product req data used by view 7285 var product_request = { 7286 products: params.payload.products 7287 ? $.extend({}, params.payload.products) 7288 : {}, 7289 variations: params.payload.variations 7290 ? $.extend({}, params.payload.variations) 7291 : {}, 7292 }; 7293 7294 if ( 7295 // add in variation modal request data so it may be used by the view 7296 typeof params.payload !== "undefined" && 7297 params.payload.variation_form && 7298 typeof params.external_payload !== "undefined" && 7299 params.external_payload["add-to-cart"] 7300 ) { 7301 var product_id = params.external_payload["add-to-cart"], 7302 variation_id = params.external_payload["variation_id"] 7303 ? params.external_payload["variation_id"] 7304 : "", 7305 quantity = parseFloat(params.external_payload["quantity"]); 7306 7307 product_request.products[product_id] = quantity; 7308 7309 if (variation_id) { 7310 product_request.variations[product_id] = {}; 7311 product_request.variations[product_id][variation_id] = quantity; 7312 } 7313 } 7314 7315 $(".wcpt-row:visible").each(function () { 7316 var $row = $(this), 7317 $sibling_rows = wcpt_get_sibling_rows($row), 7318 product_id = $row.attr("data-wcpt-product-id"), 7319 variation_id = $row.attr("data-wcpt-variation-id"), 7320 $button = $(cart_button_selector, $row), 7321 in_cart = parseFloat($row.attr("data-wcpt-in-cart")); 7322 7323 // before request, update view like - add loading badge, animate Remove el 7324 if (product_request.products) { 7325 $.each(product_request.products, function (id, qty) { 7326 if (id == product_id) { 7327 if ( 7328 // product is a variation, and not part of the cart request 7329 variation_id && 7330 product_request.variations && 7331 product_request.variations[product_id] && 7332 typeof product_request.variations[product_id][variation_id] == 7333 "undefined" 7334 ) { 7335 return; 7336 } 7337 7338 loading_badge_on_button($button); 7339 7340 if (0 === qty && in_cart) { 7341 // removing 7342 $row.addClass("wcpt-removing-product"); 7343 } else { 7344 // adding 7345 $row.addClass("wcpt-adding-product"); 7346 7347 var $qty = $("input.qty", $row), 7348 $wcpt_qty = $qty.not(".cart .qty"), 7349 initial = $wcpt_qty.attr("data-wcpt-initial-value"), 7350 min = parseFloat($wcpt_qty.attr("data-wcpt-min")), 7351 return_to_initial = $wcpt_qty.attr( 7352 "data-wcpt-return-to-initial", 7353 ); 7354 7355 if (return_to_initial) { 7356 $sibling_rows.trigger("_wcpt_checkbox_change", false); 7357 7358 if (initial == "min") { 7359 $qty.val(min); 7360 } else if (initial === "0") { 7361 $qty.val(0); 7362 } else if (initial === "empty") { 7363 $qty.val(""); 7364 } 7365 } 7366 7367 var $wrapper = $qty.closest(".wcpt-quantity"); 7368 if ($wrapper.length) { 7369 limit_qty_controller($wrapper); 7370 } 7371 7372 // dropdown 7373 var $qty = $("select.wcpt-qty-select", $row), 7374 $first = $qty.children("option:first-child"); 7375 7376 $qty.val($first.attr("value")); 7377 } 7378 7379 // total 7380 var $total = $(".wcpt-total", $sibling_rows), 7381 force_total_qty = qty; 7382 7383 if ( 7384 variation_id && 7385 product_request.variations && 7386 product_request.variations[product_id] && 7387 product_request.variations[product_id][variation_id] 7388 ) { 7389 force_total_qty = 7390 product_request.variations[product_id][variation_id]; 7391 } 7392 7393 // don't allow it to be reset by qty going to 0 7394 if ($total.length) { 7395 update_row_total($sibling_rows, force_total_qty); 7396 } 7397 } 7398 }); 7399 } 7400 });
7401 7402 var data = $.extend( 7403 {}, 7404 { 7405 wcpt_payload: params.payload, 7406 lang: wcpt_i18n.lang, 7407 }, 7408 params.external_payload ? params.external_payload : {}, 7409 ); 7410 7411 $("body").trigger("wcpt_cart_request", data); 7412 7413 // use cache 7414 if (params.payload.use_cache && window.wcpt_cart_result_cache) { 7415 window.wcpt_cart_result_cache.payload.wcpt_cart_call_id = 7416 window.wcpt_cart_call_id; 7417 $("body").trigger("wcpt_cart", window.wcpt_cart_result_cache); 7418 7419 // fetch fresh 7420 } else { 7421 $.post( 7422 wcpt_params.wc_ajax_url.replace("%%endpoint%%", "wcpt_cart"), 7423 data, 7424 function (result) { 7425 window.wcpt_cart_result_cache = $.extend({}, result, { 7426 success: true, 7427 notice: "", 7428 is_cache: true, 7429 }); 7430 $("body").trigger("wcpt_cart", result); 7431 }, 7432 ).always(function (result) { 7433 if (params.always) { 7434 params.always(result); 7435 } 7436 7437 // resolve any pending requests 7438 if (window.wcpt_cart_pending_req_params) { 7439 wcpt_cart(window.wcpt_cart_pending_req_params); 7440 window.wcpt_cart_pending_req_params = false; 7441 } 7442 7443 if (params.redirect) { 7444 if ( 7445 // redirect after user reads error 7446 !result.success && 7447 result.notice 7448 ) { 7449 $("body").on( 7450 "click touchstart", 7451 ".wcpt-notice-wrapper", 7452 function () { 7453 window.location = params.redirect; 7454 }, 7455 ); 7456 } else { 7457 // redirect immediately 7458 window.location = params.redirect; 7459 } 7460 } 7461 }); 7462 } 7463 }; 7464 7465 $("body").on("wcpt_cart", function (e, result) { 7466 // do not resolve if there are pending cart calls 7467 if (window.wcpt_cart_pending_req_params) { 7468 return; 7469 } 7470 7471 // cart widget 7472 if (result.payload.wcpt_cart_call_id === window.wcpt_cart_call_id) { 7473 // latest call resolved 7474 $(".wcpt-cart-widget ").removeClass("wcpt-cart-widget--loading"); 7475 } 7476 7477 // error 7478 if (!result.success && result.notice) { 7479 var $body = $("body"), 7480 $notice = $( 7481 '<div class="wcpt-notice-wrapper">' + result.notice + "</div>", 7482 ); 7483 $body.append($notice); 7484 $body.one("click", function () { 7485 $notice.remove(); 7486 }); 7487 } 7488 7489 if (result.cart_widget && cart_widget_permitted()) { 7490 var $body = $("body"), 7491 $old = $(".wcpt-cart-widget").not(".wcpt-cart-checkbox-trigger"), 7492 $new = $(result.cart_widget); 7493 7494 if ($new.hasClass("wcpt-hide")) { 7495 $body.removeClass("wcpt-cart-widget-visible"); 7496 } else { 7497 $body.addClass("wcpt-cart-widget-visible"); 7498 } 7499 7500 $body.append($new); 7501 $old.remove(); 7502 } 7503 7504 // If this cart response came from a checkbox-trigger bulk add-to-cart, 7505 // clear all selection state across tables (including persisted state) 7506 // so nothing remains checked on any page. 7507 if ( 7508 result && 7509 result.external_payload && 7510 result.external_payload.wcpt_checkbox_bulk 7511 ) { 7512 if (typeof wcpt_util !== "undefined") { 7513 $(".wcpt").each(function () { 7514 var $container = $(this), 7515 table_id = wcpt_util.get_table_id($container); 7516 7517 if (!table_id) { 7518 return; 7519 } 7520 7521 // Use the built-in API to clear persisted cross-page state 7522 $(document).trigger("wcpt_clear_all_checked_for_table", [ 7523 table_id, 7524 $container, 7525 ]); 7526 7527 // Uncheck any rows currently in the DOM for this table 7528 $container.find(".wcpt-row").each(function () { 7529 $(this).trigger("_wcpt_checkbox_change", false); 7530 }); 7531 }); 7532 } 7533 7534 // Hide the global floating trigger (if present) 7535 $(".wcpt-cart-checkbox-trigger").addClass("wcpt-hide").hide(); 7536 } 7537 7538 // added / removed 7539 var added = (removed = false); 7540 7541 if (result.payload.products && result.in_cart) { 7542 $.each(result.payload.products, function (product_id, product_qty) { 7543 var variation_id = (variation_qty = false); 7544 7545 if ( 7546 result.payload.variations && 7547 typeof result.payload.variations[product_id] !== "undefined" 7548 ) { 7549 variation_id = Object.keys(result.payload.variations[product_id])[0]; 7550 variation_qty = Object.values( 7551 result.payload.variations[product_id], 7552 )[0]; 7553 } 7554 7555 if (product_qty === "0") { 7556 // req. type: "remove" 7557 if ( 7558 $.isEmptyObject(result.in_cart) || 7559 !result.in_cart.length || 7560 !result.in_cart[product_id] || 7561 (variation_id && !result.in_cart[product_id][variation_id]) 7562 ) { 7563 removed = true; 7564 } 7565 } else { 7566 // req. type: "add" 7567 if ( 7568 !$.isEmptyObject(result.in_cart) && 7569 result.in_cart[product_id] && 7570 (!variation_id || result.in_cart[product_id][variation_id]) 7571 ) { 7572 added = true; 7573 } 7574 } 7575 }); 7576 } 7577 7578 if (result.payload.variation_form) { 7579 added = true; 7580 } 7581 7582 var in_cart_products = []; // variations and parent variable products at same level 7583 7584 if (result.in_cart) { 7585 $.each(result.in_cart, function (key, val) { 7586 // variable product 7587 if (typeof val === "object") { 7588 // related to parent variable product 7589 var qty = 0, 7590 total = 0; 7591 7592 // iterate over variations 7593 $.each(val, function (key2, val2) { 7594 var _total = result.in_cart_total[key][key2]; 7595
7596 // insert variations as separate entries 7597 in_cart_products.push({ 7598 id: key2, 7599 type: "variation", 7600 quantity: val2, 7601 total: _total, 7602 }); 7603 7604 // sum up variation quantities and totals (decimal fix) 7605 qty = (qty * 1000 + parseFloat(val2) * 1000) / 1000; 7606 total = (total * 1000 + parseFloat(_total) * 1000) / 1000; 7607 }); 7608 7609 // insert parent variable product 7610 in_cart_products.push({ 7611 id: key, 7612 type: "variable", 7613 quantity: qty, 7614 total: total, 7615 }); 7616 } else { 7617 // simple 7618 in_cart_products.push({ 7619 id: key, 7620 type: "simple", 7621 quantity: val, 7622 total: result.in_cart_total[key], 7623 }); 7624 } 7625 }); 7626 } 7627 7628 // view 7629 7630 var $rows = $(".wcpt-row"); 7631 7632 $rows.each(function () { 7633 var $row = $(this), 7634 type = $row.attr("data-wcpt-type"), 7635 product_id = $row.attr("data-wcpt-product-id"), 7636 variation_id = $row.attr("data-wcpt-variation-id"), 7637 id = type == "variation" ? variation_id : product_id, 7638 cart_item = false, 7639 min_call_id = $row.data("wcpt-min-cart-call-id"); 7640 7641 if (min_call_id > result.payload.wcpt_cart_call_id) { 7642 return; 7643 } 7644 7645 $.each(in_cart_products, function (key, item) { 7646 if ( 7647 // variation 7648 (variation_id && variation_id == item.id) || 7649 // regular product 7650 (!variation_id && product_id == item.id) 7651 ) { 7652 cart_item = item; 7653 return false; 7654 } 7655 }); 7656 7657 // wcpt initiated adding / removing process is complete 7658 $row.removeClass("wcpt-adding-product wcpt-removing-product"); 7659 7660 // update 'in cart' qty 7661 var cart_qty = cart_item ? cart_item.quantity : 0; 7662 $row.attr("data-wcpt-in-cart", cart_qty); 7663 $in_cart = $(".wcpt-in-cart", $row).text(cart_qty); 7664 $in_cart.each(function () { 7665 var $this = $(this), 7666 template = $this.attr("data-wcpt-template"), 7667 empty_template = $this.attr("data-wcpt-empty-template"); 7668 7669 if (cart_qty) { 7670 $this.text(template.replace("{n}", cart_qty)); 7671 } else { 7672 $this.text(empty_template); 7673 } 7674 }); 7675 7676 // update 'in cart' total 7677 var cart_total = cart_item ? cart_item.total : 0; 7678 $(".wcpt-total", $row).attr("data-wcpt-in-cart-total", cart_total); 7679 update_row_total($row); 7680 7681 // badge 7682 7683 if (!$row.hasClass("wcpt-adding-product")) { 7684 $button = $(cart_button_selector, $row); 7685 add_count_badge_to_button(cart_qty, $button); 7686 7687 if (!$button.hasClass("wcpt-out-of-stock")) { 7688 enable_button($button); 7689 } 7690 } 7691 7692 // remove 7693 7694 if (!$row.hasClass("wcpt-removing-product")) { 7695 var $remove = $(".wcpt-remove", $row); 7696 7697 // -- enable 7698 if (cart_item) { 7699 $remove.removeClass("wcpt-disabled"); 7700 7701 // -- disable 7702 } else { 7703 // $remove.addClass('wcpt-disabled').removeClass('wcpt-removing'); 7704 $remove.addClass("wcpt-disabled"); 7705 7706 // wc button 7707 $(".add_to_cart_button", $row) 7708 .removeClass("added") 7709 .next(".added_to_cart") 7710 .remove(); 7711 } 7712 } 7713 }); 7714 7715 if ( 7716 result.fragments && 7717 !result.payload.skip_cart_triggers && 7718 !result.is_cache 7719 ) { 7720 var trigger_lock = true, 7721 $body = $("body"), 7722 $button = false; 7723 7724 // fake button for single add / remove 7725 if ( 7726 result.payload && 7727 result.payload.products && 7728 Object.keys(result.payload.products).length === 1 7729 ) { 7730 $.each(result.payload.products, function (product_id, qty) { 7731 if (qty) { 7732 // added 7733 $button = $('<button data-product_id="' + product_id + '">'); 7734 } else { 7735 // removed 7736 $button = $('<a href="" data-product_id="' + product_id + '">'); 7737 } 7738 }); 7739 } 7740 7741 if (added || result.payload.force_refresh_cart) { 7742 $body.trigger("added_to_cart", [ 7743 result.fragments, 7744 result.cart_hash, 7745 $button, 7746 trigger_lock, 7747 ]); 7748 } 7749 7750 if (removed) { 7751 $body.trigger("removed_from_cart", [ 7752 result.fragments, 7753 result.cart_hash, 7754 $button, 7755 trigger_lock, 7756 ]); 7757 } 7758 } 7759 }); 7760 7761 $("body").on( 7762 "added_to_cart removed_from_cart", 7763 function (e, fragment, cart_hash, $button, trigger_lock) { 7764 if (!trigger_lock) { 7765 wcpt_cart({ 7766 payload: { skip_cart_triggers: true }, 7767 }); 7768 } 7769 }, 7770 ); 7771 7772 // PRO 7773 7774 $("body").on("click", ".wcpt-nav-redirect-option", function (e) { 7775 var $this = $(this), 7776 $link = $this.find("a.wcpt-nav-redirect-link"), 7777 url = $link.attr("href"); 7778 7779 if ( 7780 $(e.target).closest(".wcpt-filter-hierarchy-accordion__trigger").length 7781 ) { 7782 return; 7783 } 7784 7785 window.location.href = url; 7786 }); 7787 7788 // table heading offset based on nav header 7789 $("body").on("wcpt_layout", ".wcpt", function (e, data) { 7790 var $this = $(this), 7791 $freeze_nav_sidebar = $( 7792 ".wcpt-navigation.wcpt-left-sidebar.wcpt-sticky:visible", 7793 $this, 7794 ), 7795 $freeze_nav_header = $( 7796 ".wcpt-navigation.wcpt-header.wcpt-sticky:visible", 7797 $this, 7798 ), 7799 $freeze_table_heading = $( 7800 ".frzTbl-fixed-heading-wrapper-outer:visible", 7801 $this, 7802 ), 7803 sc_attrs = $this.data("wcpt_sc_attrs"), 7804 device = get_device(), 7805 offset = parseInt( 7806 sc_attrs[device + "_freeze_heading_offset"] 7807 ? sc_attrs[device + "_freeze_heading_offset"] 7808 : sc_attrs[device + "_scroll_offset"] 7809 ? sc_attrs[device + "_scroll_offset"] 7810 : 0, 7811 ); 7812 7813 $freeze_nav_sidebar.css("top", offset); 7814 7815 if (device == "laptop") { 7816 $freeze_nav_header.css("top", offset); 7817 7818 offset += parseInt($freeze_nav_header.outerHeight()); 7819 7820 $freeze_table_heading.css({ top: offset }); 7821 } else { 7822 offset += parseInt($freeze_nav_sidebar.outerHeight()); 7823 $freeze_nav_header.css({ top: offset }); 7824 7825 offset += $freeze_nav_header.outerHeight(); 7826 $freeze_table_heading.css({ top: offset }); 7827 } 7828 }); 7829 7830 // cart widget permission 7831 function cart_widget_permitted() { 7832 var url = window.location.href.split("?")[0], 7833 include_match = false, 7834 exclude_match = false, 7835 include_urls = false, 7836 exclude_urls = false, 7837 site_url = wcpt_params.site_url + "/"; 7838 7839 if (!wcpt_params.cart_widget_enabled_site_wide && !$(".wcpt").length) { 7840 return false; 7841 } 7842 7843 if (wcpt_params.cart_widget_include_urls.trim()) { 7844 include_urls = wcpt_params.cart_widget_include_urls 7845 .trim() 7846 .replace(/\r\n/g, "\n") 7847 .split("\n"); 7848 } 7849 7850 if (wcpt_params.cart_widget_exclude_urls.trim()) { 7851 exclude_urls = wcpt_params.cart_widget_exclude_urls 7852 .trim() 7853 .replace(/\r\n/g, "\n") 7854 .split("\n"); 7855 } 7856 7857 if (include_urls.length) { 7858 include_match = false; 7859 $.each(include_urls, function (i, path) { 7860 path = path.trim(); 7861 7862 if (path.indexOf(site_url) === 0) { 7863 path = path.substring(site_url.length, path.length); 7864 } 7865 7866 if (path.trim() == "/") { 7867 if (url === site_url) { 7868 include_match = true; 7869 } 7870 return; 7871 } 7872 7873 if (path.trim() == "/*") { 7874 include_match = true; 7875 return; 7876 } 7877 7878 path = path.replace(/(^\s*\/)|(\/\s*$)/g, ""); // ensure no slash at start or end 7879 if ("*" === path.substr(-1)) { 7880 var remove = 1; 7881 if ("/*" === path.substr(-2)) { 7882 remove = 2; 7883 } 7884 if ( 7885 -1 !== 7886 url.indexOf( 7887 site_url + path.substring(0, path.length - remove) + "/", 7888 ) 7889 ) { 7890 include_match = true; 7891 } 7892 } else { 7893 path += "/"; 7894 if (url == site_url + path) { 7895 include_match = true; 7896 } 7897 } 7898 }); 7899 7900 if (!include_match) { 7901 return false; 7902 } 7903 } 7904 7905 if (exclude_urls.length) { 7906 exclude_match = false; 7907 7908 $.each(exclude_urls, function (i, path) { 7909 path = path.trim(); 7910 7911 if (path.indexOf(site_url) === 0) { 7912 path = path.substring(site_url.length, path.length); 7913 } 7914 7915 if (path.trim() == "/") { 7916 if (url === site_url) { 7917 exclude_match = true; 7918 } 7919 return; 7920 } 7921 7922 if (path.trim() == "/*") { 7923 exclude_match = true; 7924 return; 7925 } 7926 7927 path = path.replace(/(^\s*\/)|(\/\s*$)/g, ""); // ensure no slash at start or end 7928 if ("*" === path.substr(-1)) { 7929 var remove = 1; 7930 if ("/*" === path.substr(-2)) { 7931 remove = 2; 7932 } 7933 7934 if ( 7935 -1 !== 7936 url.indexOf( 7937 site_url + path.substring(0, path.length - remove) + "/", 7938 ) 7939 ) { 7940 exclude_match = true; 7941 } 7942 } else { 7943 path += "/"; 7944 if (url == site_url + path) { 7945 exclude_match = true; 7946 } 7947 } 7948 }); 7949 7950 if (exclude_match) { 7951 return false; 7952 } 7953 } 7954 7955 return true; 7956 } 7957 7958 // cart widget close x 7959 $("body").on("click", ".wcpt-cart-checkbox-trigger__close", function (e) { 7960 e.stopPropagation(); 7961 var $cart_widget = $(this).closest(".wcpt-cart-checkbox-trigger"); 7962 7963 if (typeof wcpt_util !== "undefined") { 7964 $(".wcpt").each(function () { 7965 var $container = $(this), 7966 table_id = wcpt_util.get_table_id($container); 7967 7968 if (!table_id) { 7969 return; 7970 } 7971 7972 $(document).trigger("wcpt_clear_all_checked_for_table", [ 7973 table_id, 7974 $container, 7975 ]); 7976 7977 $container.find(".wcpt-row").each(function () { 7978 $(this).trigger("_wcpt_checkbox_change", false); 7979 }); 7980 }); 7981 } 7982 7983 wcpt_checkbox_trigger_init(); 7984 $cart_widget.addClass("wcpt-hide").hide(); 7985 }); 7986 7987 // multirange 7988 7989 // -- input[type=number] change 7990 $("body").on( 7991 "input change", 7992 ".wcpt-range-input-min, .wcpt-range-input-max", 7993 function () { 7994 var $this = $(this), 7995 $container = $this.closest(".wcpt-range-options-wrapper"), 7996 $min = $container.find(".wcpt-range-input-min"), 7997 min_val = parseFloat($min.val()), 7998 $max = $container.find(".wcpt-range-input-max"), 7999 max_val = parseFloat($max.val()); 8000 8001 $range = $container.find(".wcpt-range-slider.original"); 8002 8003 if (!$range.length) { 8004 return; 8005 } 8006 8007 // max step fix 8008 var actual_max = parseFloat($this.attr("data-wcpt-actual-max")), 8009 val = parseFloat($this.val()); 8010 8011 if (!isNaN(actual_max) && !isNaN(val) && val > actual_max) { 8012 $this.val(actual_max); 8013 } 8014 8015 // handle cleared min/max inputs 8016 if (isNaN(min_val)) { 8017 min_val = parseFloat($range.attr("min")); 8018 $min.val(min_val); 8019 } 8020 8021 if (isNaN(max_val)) { 8022 max_val = parseFloat($range.attr("max")); 8023 $max.val(max_val); 8024 } 8025 8026 $range.val(min_val + "," + max_val); 8027 }, 8028 ); 8029 8030 // -- input[type=range] change 8031 $("body").on("input change", ".wcpt-range-slider", function () { 8032 var $this = $(this), 8033 $container = $this.closest(".wcpt-range-options-wrapper"), 8034 $min = $(".wcpt-range-input-min", $container), 8035 $max = $(".wcpt-range-input-max", $container), 8036 $range_original = $container.find(".wcpt-range-slider.original"), 8037 $range_ghost = $container.find(".wcpt-range-slider.ghost"), 8038 min = parseFloat($range_original.prop("valueLow")), 8039 max = parseFloat($range_original.prop("valueHigh")), 8040 permitted_max = parseFloat($this.attr("max")), 8041 step = parseFloat($this.attr("step")); 8042 8043 // range input max fix 8044 // if( max + step > permitted_max ){ 8045 // max = permitted_max; // sending this to input[type="number"] 8046 // } 8047 8048 $min.val(min); 8049 $max.val(max); 8050 }); 8051 8052 // switch elements based on selected variation 8053 $("body").on( 8054 "select_variation", 8055 ".wcpt-product-type-variable", 8056 function (e, data) { 8057 var $row = get_product_rows($(this)), 8058 $items = $( 8059 ".wcpt-variation-description__item, .wcpt-variation-content", 8060 $row, 8061 ); 8062 8063 $items.hide(); 8064 8065 if ($row.data("wcpt_variation_selected")) { 8066 var variation_id = $row.data("wcpt_variation_id"); 8067 $items.filter("[data-wcpt-variation-id=" + variation_id + "]").show(); 8068 } 8069 }, 8070 ); 8071 8072 // name your price 8073 $("body").on( 8074 "select_variation", 8075 ".wcpt-product-type-variable", 8076 function (e, data) { 8077 var $row = get_product_rows($(this)), 8078 $input = $(".wcpt-name-your-price--input", $row), 8079 initial_value_field = $input.attr("data-wcpt-nyp-initial-value-field"), 8080 input = false, 8081 min = false, 8082 max = false, 8083 suggested = false; 8084 8085 // if price has no variable switch, always stays "$min - $max" then just replace it completely with "from $_min" 8086 // if it has variable switch 8087 // then hide it whenever selected variation has nyp 8088 8089 $row.removeClass("wcpt-product-has-name-your-price"); 8090 8091 if ($row.data("wcpt_variation_selected")) { 8092 // variation selected 8093 var variation = $row.data("wcpt_variation"); 8094 8095 if (variation.is_nyp) { 8096 input = true; 8097 min = variation.minimum_price; 8098 max = variation.maximum_price; 8099 suggested = variation.suggested_price; 8100 8101 $row.addClass("wcpt-product-has-name-your-price"); 8102 } 8103 } else { 8104 // variation not selected 8105 input = false; 8106 } 8107 8108 // no nyp input field 8109 if (!input) { 8110 $input.parent().addClass("wcpt-hide"); 8111 $row 8112 .find("wcpt-price.wcpt-variable-switch") 8113 .removeClass("wcpt-hide--name-your-price"); 8114 8115 // found nyp input field 8116 } else { 8117 $input.parent().removeClass("wcpt-hide"); 8118 8119 if (min) { 8120 $input.attr({ 8121 min: min, 8122 "data-wcpt-nyp-minimum-price": min, 8123 }); 8124 } else { 8125 $input.attr({ 8126 min: 0, 8127 "data-wcpt-nyp-minimum-price": 0, 8128 }); 8129 } 8130 8131 if (max) { 8132 $input.attr("max", max); 8133 8134 $input.attr({ 8135 max: max, 8136 "data-wcpt-nyp-maximum-price": max, 8137 }); 8138 } else { 8139 $input.attr({ 8140 max: "", 8141 "data-wcpt-nyp-maximum-price": "", 8142 }); 8143 } 8144 8145 if (initial_value_field && !$input.val()) { 8146 var value = variation[initial_value_field + "_price"] 8147 ? variation[initial_value_field + "_price"] 8148 : ""; 8149 $input.val(value); 8150 } 8151 8152 $row 8153 .find("wcpt-price.wcpt-variable-switch") 8154 .addClass("wcpt-hide--name-your-price"); 8155 } 8156 8157 if (!min) { 8158 $row.find(".wcpt-name-your-price--minimum").addClass("wcpt-hide"); 8159 } else { 8160 $row.find(".wcpt-name-your-price--minimum").removeClass("wcpt-hide");
8161 $row.find(".wcpt-name-your-price--minimum .wcpt-amount").text(min); 8162 } 8163 8164 if (!max) { 8165 $row.find(".wcpt-name-your-price--maximum").addClass("wcpt-hide"); 8166 } else { 8167 $row.find(".wcpt-name-your-price--maximum").removeClass("wcpt-hide"); 8168 $row.find(".wcpt-name-your-price--maximum .wcpt-amount").text(max); 8169 } 8170 8171 if (!suggested) { 8172 $row.find(".wcpt-name-your-price--suggested").addClass("wcpt-hide"); 8173 } else { 8174 $row.find(".wcpt-name-your-price--suggested").removeClass("wcpt-hide"); 8175 $row 8176 .find(".wcpt-name-your-price--suggested .wcpt-amount") 8177 .text(suggested); 8178 } 8179 8180 $input.change(); 8181 }, 8182 ); 8183 8184 // -- checkbox 8185 $("body").on("wcpt_checkbox_change", ".wcpt-row", function (e, checked) { 8186 var $this = $(this), 8187 $rows = wcpt_get_sibling_rows($this.closest(".wcpt-row")), 8188 $nyp = get_nyp_input_element($rows); 8189 8190 if (checked) { 8191 nyp_validate($nyp); 8192 } else if (!$nyp.val()) { 8193 nyp_hide_error($nyp); 8194 } 8195 }); 8196 8197 // -- validate 8198 function nyp_validate($nyp) { 8199 var $nyp_wrapper = $nyp.parent(), 8200 nyp_val = $nyp.val(), 8201 nyp_min = $nyp.attr("data-wcpt-nyp-minimum-price"), 8202 nyp_max = $nyp.attr("data-wcpt-nyp-maximum-price"), 8203 message_template = "", 8204 $error = "", 8205 $row = $nyp.closest(".wcpt-row"), 8206 checked = $row.data("wcpt_checked"); 8207 8208 if ( 8209 (nyp_val && nyp_min && parseFloat(nyp_val) < parseFloat(nyp_min)) || 8210 (checked && !nyp_val && nyp_min) 8211 ) { 8212 $nyp_wrapper.addClass( 8213 "wcpt-name-your-price-wrapper--input-error wcpt-name-your-price-wrapper--input-error--min-price", 8214 ); 8215 8216 $error = $( 8217 ".wcpt-name-your-price-input-error-message--min-price", 8218 $nyp_wrapper, 8219 ); 8220 message_template = $error.attr("data-wcpt-error-message-template"); 8221 $error.text( 8222 message_template.replace("[min]", format_price_figure(nyp_min)), 8223 ); 8224 } else if ( 8225 nyp_val && 8226 nyp_max && 8227 parseFloat(nyp_val) > parseFloat(nyp_max) 8228 ) { 8229 $nyp_wrapper.addClass( 8230 "wcpt-name-your-price-wrapper--input-error wcpt-name-your-price-wrapper--input-error--max-price", 8231 ); 8232 8233 $error = $( 8234 ".wcpt-name-your-price-input-error-message--max-price", 8235 $nyp_wrapper, 8236 ); 8237 message_template = $error.attr("data-wcpt-error-message-template"); 8238 $error.text( 8239 message_template.replace("[max]", format_price_figure(nyp_max)), 8240 ); 8241 } else { 8242 $nyp_wrapper.removeClass( 8243 "wcpt-name-your-price-wrapper--input-error wcpt-name-your-price-wrapper--input-error--min-price wcpt-name-your-price-wrapper--input-error--max-price", 8244 ); 8245 } 8246 } 8247 8248 // -- hide validation errors 8249 function nyp_hide_error($nyp) { 8250 $nyp 8251 .parent() 8252 .removeClass( 8253 "wcpt-name-your-price-wrapper--input-error wcpt-name-your-price-wrapper--input-error--min-price wcpt-name-your-price-wrapper--input-error--max-price", 8254 ); 8255 } 8256 8257 // trigger nav feedback from range slider and min max 8258 $("body").on("change", ".wcpt-range-options-wrapper input", function () { 8259 var $this = $(this), 8260 $filter = $this.closest(".wcpt-filter"); 8261 8262 $filter.find("input[type=radio]").prop("checked", false); 8263 nav_filter_feedback($this.closest(".wcpt-navigation")); 8264 }); 8265 8266 // refresh / block table 8267 // -- lock 8268 $("body").on("wcpt_cart_request", function (e, data) { 8269 if ( 8270 data.wcpt_payload.skip_cart_triggers || 8271 data.wcpt_payload.use_cache || 8272 data.is_cache 8273 ) { 8274 return; 8275 } 8276 8277 $(".wcpt").each(function () { 8278 var $container = $(this), 8279 sc_attrs = JSON.parse($container.attr("data-wcpt-sc-attrs")); 8280 8281 if (sc_attrs.refresh_table) { 8282 $container.addClass("wcpt-loading wcpt-refreshing"); 8283 } else if (sc_attrs.block_table) { 8284 $container.addClass("wcpt-loading"); 8285 } 8286 }); 8287 }); 8288 // -- refresh 8289 $("body").on("wcpt_cart", function (e, result) { 8290 if ( 8291 result.payload.skip_cart_triggers || 8292 result.payload.use_cache || 8293 result.is_cache 8294 ) { 8295 return; 8296 } 8297 8298 $(".wcpt").each(function () { 8299 var $container = $(this), 8300 sc_attrs = JSON.parse($container.attr("data-wcpt-sc-attrs")), 8301 table_id = $container.attr("data-wcpt-table-id"); 8302 8303 if (sc_attrs.refresh_table) { 8304 $.each(window.wcpt_cache.data, function (key, val) { 8305 if ( 8306 key.indexOf("?" + table_id + "_") !== -1 || 8307 key.indexOf("&" + table_id + "_") !== -1 8308 ) { 8309 delete window.wcpt_cache.data[key]; 8310 } 8311 }); 8312 8313 attempt_ajax($container, "", true, "refresh_table"); 8314 } else if (sc_attrs.block_table) { 8315 $container.removeClass("wcpt-loading"); 8316 } 8317 });
8318 }); 8319 8320 // switching variation should update Button element's product link 8321 $("body").on( 8322 "select_variation", 8323 ".wcpt-product-type-variable", 8324 function (e, data) { 8325 var $row = get_product_rows($(this)), 8326 $link_button = $( 8327 ".wcpt-button[data-wcpt-link-code='product_link']", 8328 $row, 8329 ); 8330 8331 if ($link_button.length) { 8332 var original_link = $link_button.attr("data-wcpt-product-link"), 8333 new_link = ""; 8334 8335 if (!original_link) { 8336 original_link = $link_button.attr("href"); 8337 $link_button.attr("data-wcpt-product-link", original_link); 8338 } 8339 8340 if (data.variation_id) { 8341 new_link = original_link + "?"; 8342 $.each(data.attributes, function (key, value) { 8343 new_link = new_link + key + "=" + value + "&"; 8344 }); 8345 new_link = new_link + "variation_id=" + data.variation_id; 8346 $link_button.attr("href", new_link); 8347 } else { 8348 $link_button.attr("href", original_link); 8349 } 8350 } 8351 }, 8352 ); 8353 8354 // berocket 8355 $(document).on("berocket_ajax_filtering_end", function (e) { 8356 $(".wcpt").each(function () { 8357 after_every_load($(this)); 8358 }); 8359 }); 8360 8361 // waveplayer 8362 8363 // -- holds the detached waveplayer instances 8364 window.$wcpt_waveplayer_preserve = $(); 8365 8366 //-- avoid losing functionality on cloned players during freeze table 8367 $("body").on("wcpt_layout", ".wcpt", function () { 8368 var $this = $(this); 8369 8370 if (typeof WavePlayer !== "undefined") { 8371 WavePlayer.loadInstances(); 8372 8373 if (!$wcpt_waveplayer_preserve.length) { 8374 return; 8375 } 8376 8377 var $replace = get_matching_waveplayer_elm($this); 8378 8379 if ($replace.length) { 8380 $replace.replaceWith($wcpt_waveplayer_preserve); 8381 $wcpt_waveplayer_preserve 8382 .find(".wvpl-waveform canvas") 8383 .each(function () { 8384 var $this = $(this); 8385 $this.width($this.attr("data-wcpt-last-width")); 8386 $this.height($this.attr("data-wcpt-last-height")); 8387 }); 8388 8389 waveplayer_active_ui_feedback.call($wcpt_waveplayer_preserve.get(0)); 8390 8391 $wcpt_waveplayer_preserve = $(); 8392 8393 WavePlayer.redrawAllInstances(); 8394 } 8395 } 8396 }); 8397 8398 // -- check for and return waveplayer element in container that needs to be replaced 8399 function get_matching_waveplayer_elm($container) { 8400 if (WavePlayer.persistentTrack && WavePlayer.instances.length) { 8401 var instance = false, 8402 track_index = false; 8403 8404 $.each(WavePlayer.instances, function (_instance_index, _instance) { 8405 var $_instances = $(_instance.node); 8406 if ( 8407 $_instances.is(":visible") && 8408 $_instances.parent().hasClass("wcpt-waveplayer-container") 8409 ) { 8410 var $row = $_instances.closest(".wcpt-row"); 8411 8412 if ($row.length) { 8413 $.each(_instance.tracks, function (_track_index, _track) { 8414 if ( 8415 _track.file === WavePlayer.persistentTrack.file && 8416 _track.product_id === WavePlayer.persistentTrack.product_id 8417 ) { 8418 instance = _instance; 8419 track_index = _track_index; 8420 } 8421 }); 8422 } 8423 } 8424 }); 8425 8426 if (instance) { 8427 return $(instance.node).parent(".wcpt-waveplayer-container"); 8428 } 8429 } 8430 8431 return $(); 8432 } 8433 8434 // -- add to cart 8435 $("body").on("added_to_cart", function (e, fragment, cart_hash, $button) { 8436 if (typeof WavePlayer === "undefined" || !$button) { 8437 return; 8438 } 8439 8440 var product_id = $button.attr("data-product_id"); 8441 if (typeof WavePlayer.updateTrackCartStatus !== "undefined") { 8442 WavePlayer.updateTrackCartStatus(product_id, "add"); 8443 } 8444 8445 $(".wvpl-cart[data-product_id=" + product_id + "]") 8446 .attr("title", WavePlayer.__("Add to cart", "waveplayer")) 8447 .attr("data-event", "goToCart") 8448 .attr("data-callback", "goToCart") 8449 .removeClass("wvpl-add_to_cart") 8450 .addClass("wvpl-in_cart") 8451 .removeClass("wvpl-spin"); 8452 }); 8453 8454 // -- row color 8455 $("body").on( 8456 "click", 8457 ".wcpt-row .wcpt-waveplayer-container", 8458 waveplayer_active_ui_feedback, 8459 ); 8460 8461 // -- active feedback 8462 function waveplayer_active_ui_feedback() { 8463 var $this = $(this), 8464 active_row_background_color = $this.attr( 8465 "data-wcpt-waveplayer-active-row-background-color", 8466 ), 8467 active_row_outline_color = $this.attr( 8468 "data-wcpt-waveplayer-active-row-outline-color", 8469 ) 8470 ? $this.attr("data-wcpt-waveplayer-active-row-outline-color") 8471 : "#4198de", 8472 active_row_outline_width = $this.attr( 8473 "data-wcpt-waveplayer-active-row-outline-width", 8474 ) 8475 ? parseFloat( 8476 $this.attr("data-wcpt-waveplayer-active-row-outline-width"), 8477 ) + "px" 8478 : "1px", 8479 $rows = wcpt_get_sibling_rows($this.closest(".wcpt-row")); 8480 8481 $rows.addClass("wcpt-waveplayer-active").css({ 8482 background: active_row_background_color 8483 ? active_row_background_color 8484 : "", 8485 outline: active_row_outline_width + " solid " + active_row_outline_color, 8486 });
8487 8488 $(".wcpt-row").not($rows).removeClass("wcpt-waveplayer-active").css({ 8489 background: "", 8490 outline: "", 8491 }); 8492 } 8493 8494 // 3rd party tab and accordion compatibility 8495 8496 // -- elementor accordion, tab & toggle 8497 $("body").on("click", ".elementor-tab-title", function () { 8498 var $this = $(this), 8499 $container = $this.closest(".elementor-widget-container"), 8500 $wcpt = $container.find(".wcpt"); 8501 8502 $wcpt.trigger("wcpt_layout", { source: "elementor__tab" }); 8503 }); 8504 8505 // -- divi 8506 // -- -- tab 8507 $("ul.et_pb_tabs_controls > li").on("click", function () { 8508 var $this = $(this), 8509 index = $this.index(), 8510 $controls = $this.closest(".et_pb_tabs_controls"), 8511 $tabs = $controls.siblings(".et_pb_all_tabs"), 8512 $container = $tabs.children().eq(index), 8513 $wcpt = $container.find(".wcpt"); 8514 8515 setTimeout(function () { 8516 $wcpt.trigger("wcpt_layout", { source: "divi__tab" }); 8517 }, 700); 8518 }); 8519 // -- -- accordion 8520 $(".et_pb_toggle_title").on("click", function () { 8521 var $this = $(this), 8522 $content = $this.next(".et_pb_toggle_content"), 8523 $wcpt = $content.find(".wcpt"); 8524 8525 setTimeout(function () { 8526 $wcpt.trigger("wcpt_layout", { source: "divi__tab" }); 8527 }, 700); 8528 }); 8529 8530 // -- beaver builder 8531 $("body").on("click", ".fl-accordion-button, .fl-tabs-label", function () { 8532 var $this = $(this), 8533 $container = $this.closest(".fl-accordion-item, .fl-tabs-panel"), 8534 $wcpt = $container.find(".wcpt"); 8535 8536 $wcpt.trigger("wcpt_layout", { source: "beaver_builder__tab" }); 8537 }); 8538 8539 // -- shortcode ultimate 8540 // -- -- tab 8541 $("body").on("click", ".su-tabs-nav span", function () { 8542 var $this = $(this), 8543 index = $this.index(), 8544 $nav = $this.closest(".su-tabs-nav"), 8545 $panes = $nav.siblings(".su-tabs-panes"), 8546 $container = $panes.children().eq(index), 8547 $wcpt = $container.find(".wcpt"); 8548 8549 $wcpt.trigger("wcpt_layout", { source: "shortcode_ultimate__tab" }); 8550 }); 8551 // -- -- accordion 8552 $("body").on("click", ".su-spoiler-title", function () { 8553 var $this = $(this), 8554 $container = $this.closest(".su-spoiler"), 8555 $wcpt = $container.find(".wcpt"); 8556 8557 $wcpt.trigger("wcpt_layout", { 8558 source: "shortcode_ultimate__accordion", 8559 }); 8560 }); 8561 8562 // -- wp bakery visual composer 8563 // -- -- tab 8564 $("body").on("click", ".vce-classic-tabs-tab", function () { 8565 var $this = $(this), 8566 index = $this.index(), 8567 $nav = $this.closest(".vce-classic-tabs-container"), 8568 $panes = $nav.siblings(".vce-classic-tabs-panels-container"), 8569 $container = $panes 8570 .children(".vce-classic-tabs-panels") 8571 .children() 8572 .eq(index), 8573 $wcpt = $container.find(".wcpt"); 8574 8575 setTimeout(function () { 8576 $wcpt.trigger("wcpt_layout", { source: "visual_composer__tab" }); 8577 }, 1); 8578 }); 8579 // -- -- accordion (1) 8580 $("body").on("click", ".vce-classic-accordion-panel-heading", function () { 8581 var $this = $(this), 8582 $container = $this.closest(".vce-classic-accordion-panel"), 8583 $wcpt = $container.find(".wcpt"); 8584 setTimeout(function () { 8585 $wcpt.trigger("wcpt_layout", { 8586 source: "visual_composer__accordion", 8587 }); 8588 }, 1); 8589 }); 8590 // -- -- accordion (2) 8591 $("body").on("click", ".vc_tta-panel-heading", function () { 8592 var $this = $(this), 8593 $container = $this.closest(".vc_tta-panel"), 8594 $wcpt = $container.find(".wcpt"); 8595 setTimeout(function () { 8596 $wcpt.trigger("wcpt_layout", { 8597 source: "visual_composer__accordion", 8598 }); 8599 }, 1); 8600 }); 8601 8602 // -- responsive accordion and collapse 8603 $("body").on("click", ".wpsm_panel-heading", function () { 8604 var $this = $(this), 8605 $container = $this.closest(".wpsm_panel"), 8606 $content = $container.find(".wpsm_panel-collapse"); 8607 8608 $(".wcpt", $content).trigger("wcpt_layout", { 8609 source: "rac__accordion", 8610 }); 8611 }); 8612 8613 // -- king composer 8614 $("body").on("click", ".kc_accordion_header", function () { 8615 var $this = $(this), 8616 $container = $this.closest(".kc_accordion_section"), 8617 $content = $container.find(".kc_accordion_content"); 8618 8619 $(".wcpt", $content).trigger("wcpt_layout", { 8620 source: "king_composer__accordion", 8621 });
8622 }); 8623 8624 // -- helpie faq 8625 $("body").on("mouseup touchend", ".accordion__header", function () { 8626 var $this = $(this), 8627 $container = $this.closest(".accordion__item"), 8628 $content = $container.find(".accordion__body"); 8629 8630 clearTimeout(window.wcpt_helpie_timeout); 8631 window.wcpt_helpie_timeout = setTimeout(function () { 8632 $(".wcpt:visible", $content).trigger("wcpt_layout", { 8633 source: "helpie_faq__accordion", 8634 }); 8635 }, 200); 8636 }); 8637 8638 // -- tabs (wpshopmart) 8639 $("body").on("click", ".wpsm_nav li", function () { 8640 var $this = $(this), 8641 index = $this.index(), 8642 $nav = $this.closest(".wpsm_nav"), 8643 $panes = $nav.siblings(".tab-content"), 8644 $container = $panes.children().eq(index), 8645 $wcpt = $container.find(".wcpt"); 8646 8647 $wcpt.trigger("wcpt_layout", { source: "tabs_wpshopmart__tab" }); 8648 }); 8649 8650 // -- tabby responsive tabs 8651 $("body").on("click", ".tabtitle", function () { 8652 var $this = $(this), 8653 $panel = $this.siblings(".tabcontent"); 8654 8655 $(".wcpt", $panel).trigger("wcpt_layout", { source: "tabby_tab" }); 8656 }); 8657 8658 $("body").on("click", ".responsive-tabs__list__item", function () { 8659 var $this = $(this), 8660 index = $this.index(), 8661 $container = $this.closest(".responsive-tabs"), 8662 $panel = $(".tabcontent", $container).eq(index); 8663 8664 $(".wcpt", $panel).trigger("wcpt_layout", { source: "tabby_tab" }); 8665 }); 8666 8667 // -- kadence blocks 8668 $("body").on("click", ".kt-title-item", function () { 8669 var $this = $(this), 8670 $tab_container = $this.closest(".kt-tabs-wrap"); 8671 8672 setTimeout(function () { 8673 $tab_container.find(".wcpt:visible").trigger("wcpt_layout"); 8674 }, 100); 8675 }); 8676 8677 // sonaar 8678 // -- audio player play / pause 8679 $("body").on("click", ".wcpt-player--sonaar", function () { 8680 var $this = $(this), 8681 playlist_id = $this.attr("data-wcpt-sonaar-playlist-id"); 8682 8683 if (typeof IRON == "undefined" || !playlist_id) { 8684 return; 8685 } 8686 8687 $(".wcpt-player--sonaar").not($this).removeClass("wcpt-player--playing"); 8688 8689 // pause 8690 if ( 8691 IRON.sonaar.player.isPlaying && 8692 IRON.sonaar.player.playlistID == playlist_id 8693 ) { 8694 IRON.sonaar.player.pause(); 8695 $this.removeClass("wcpt-player--playing wcpt-media-loaded"); 8696 } else { 8697 $this.addClass("wcpt-player--playing wcpt-media-loaded"); 8698 8699 IRON.sonaar.player.setPlayer({ 8700 id: playlist_id, 8701 autoplay: true, 8702 soundwave: true, 8703 }); 8704 } 8705 }); 8706 8707 // -- auto set play / pause status after every load 8708 function sonaar_player_auto_status() { 8709 if (typeof IRON == "undefined") { 8710 return; 8711 } 8712 8713 var playlist_id = IRON.sonaar.player.playlistID; 8714 8715 if (playlist_id) { 8716 $( 8717 '.wcpt-player--sonaar[data-wcpt-sonaar-playlist-id="' + 8718 playlist_id + 8719 '"]', 8720 ).addClass("wcpt-player--playing wcpt-media-loaded"); 8721 } 8722 } 8723 8724 // TI Wishlist integration 8725 8726 if (typeof wcpt_ti_wishlist_ids !== "undefined") { 8727 // update counter widget 8728 setTimeout(function () { 8729 $(".wishlist_products_counter_number").text(wcpt_ti_wishlist_ids.length); 8730 if (wcpt_ti_wishlist_ids.length) { 8731 $(".wishlist_products_counter").addClass( 8732 "wishlist-counter-with-products", 8733 ); 8734 } else { 8735 $(".wishlist_products_counter").removeClass( 8736 "wishlist-counter-with-products", 8737 ); 8738 } 8739 }, 100); 8740 8741 // wishlist button 8742 $("body").on("click", ".wcpt-wishlist", function (e) { 8743 var $this = $(this), 8744 $row = $this.closest(".wcpt-row"), 8745 $wcpt = $this.closest(".wcpt"), 8746 product_type = $row.attr("data-wcpt-type"), 8747 product_id = $row.attr("data-wcpt-product-id"), 8748 variation_id = $row.attr("data-wcpt-variation-id") 8749 ? $row.attr("data-wcpt-variation-id") 8750 : $row.data("wcpt_variation_id") 8751 ? $row.data("wcpt_variation_id") 8752 : false, 8753 variable_permitted = $this.attr("data-wcpt-variable-permitted"); 8754 8755 // variable product must have variation selected 8756 if (product_type == "variable" && !variable_permitted && !variation_id) { 8757 alert("Please select some options first"); 8758 return; 8759 } 8760 8761 var list_id = variation_id ? variation_id : product_id; 8762 8763 $this.addClass("wcpt-loading"); 8764 8765 var data = { 8766 tinv_wishlist_id: false, 8767 tinv_wishlist_name: false, 8768 product_type: $row.attr("data-wcpt-type"), 8769 product_id: $row.attr("data-wcpt-product-id"), 8770 product_variation: variation_id, 8771 redirect: false, 8772 }; 8773 8774 if (variation_id) { 8775 var attributes = 8776 data.product_type === "variable" 8777 ? $row.data("wcpt_attributes") 8778 : JSON.parse($row.attr("data-wcpt-variation-attributes")); 8779 8780 $.extend(data, { 8781 form: $.extend( 8782 { 8783 quantity: 1, 8784 product_id: product_id, 8785 variation_id: variation_id, 8786 }, 8787 attributes, 8788 ), 8789 }); 8790 } 8791 8792 // add 8793 if (-1 === wcpt_ti_wishlist_ids.indexOf(list_id)) { 8794 $.ajax({
8795 url: wcpt_params.ajax_url, 8796 method: "POST", 8797 beforeSend: function () { 8798 // variation added from variable product, refresh page to show it 8799 if ( 8800 product_type === "variable" && 8801 $wcpt.data("wcpt_sc_attrs").ti_wishlist 8802 ) { 8803 $wcpt.addClass("wcpt-loading"); 8804 } 8805 }, 8806 data: $.extend({}, { product_action: "addto" }, data), 8807 }).done(function (response) { 8808 // variation added from variable product, refresh page to show it 8809 if ( 8810 product_type === "variable" && 8811 $wcpt.data("wcpt_sc_attrs").ti_wishlist 8812 ) { 8813 window.location.reload(); 8814 } 8815 8816 wcpt_ti_wishlist_update_row_view($row); 8817 8818 wcpt_ti_wishlist_growler({ 8819 name: $this.attr("data-wcpt-product-name"), 8820 view_wishlist_label: $this.attr("data-wcpt-view-wishlist-label"), 8821 item_added_label: $this.attr("data-wcpt-item-added-label"), 8822 icon: $this.attr("data-wcpt-icon"), 8823 url: $this.attr("data-wcpt-custom-url"), 8824 duration_seconds: $this.attr("data-wcpt-duration-seconds"), 8825 }); 8826 8827 // update counter widget 8828 if (response && typeof response.wishlists_data !== "undefined") { 8829 $(".wishlist_products_counter_number").text( 8830 response.wishlists_data.counter, 8831 ); 8832 if (response.wishlists_data.counter) { 8833 $(".wishlist_products_counter").addClass( 8834 "wishlist-counter-with-products", 8835 ); 8836 } else { 8837 $(".wishlist_products_counter").removeClass( 8838 "wishlist-counter-with-products", 8839 ); 8840 } 8841 } 8842 }); 8843 8844 // add to maintained list as well 8845 wcpt_ti_wishlist_ids.push(list_id); 8846 8847 // update UI 8848 $this.addClass("wcpt-active"); 8849 8850 // remove 8851 } else { 8852 $.ajax({
8853 url: wcpt_params.ajax_url, 8854 method: "POST", 8855 data: $.extend({}, { product_action: "remove" }, data), 8856 }).done(function (response) { 8857 // update counter widget 8858 if (response && typeof response.wishlists_data !== "undefined") { 8859 $(".wishlist_products_counter_number").text( 8860 response.wishlists_data.counter, 8861 ); 8862 if (response.wishlists_data.counter) { 8863 $(".wishlist_products_counter").addClass( 8864 "wishlist-counter-with-products", 8865 ); 8866 } else { 8867 $(".wishlist_products_counter").removeClass( 8868 "wishlist-counter-with-products", 8869 ); 8870 } 8871 } 8872 }); 8873 8874 // remove from maintained list as well 8875 var index = wcpt_ti_wishlist_ids.indexOf(list_id); 8876 if (index > -1) { 8877 wcpt_ti_wishlist_ids.splice(index, 1); 8878 } 8879 8880 wcpt_ti_wishlist_update_row_view($row); 8881 8882 // update UI 8883 // $this.removeClass('wcpt-active'); 8884 8885 // remove product row from wishlist table 8886 if ($wcpt.data("wcpt_sc_attrs").ti_wishlist) { 8887 var $remove_row = $row; 8888 8889 // don't remove variable product if it was being used to remove a variation only 8890 if (variation_id) { 8891 $remove_row = $wcpt.find( 8892 '.wcpt-product-type-variation[data-wcpt-variation-id="' + 8893 variation_id + 8894 '"]', 8895 ); 8896 } 8897 8898 $remove_row.addClass("wcpt-wishlist-removing-row"); 8899 setTimeout(function () { 8900 $remove_row.remove(); 8901 }, 500); 8902 } 8903 } 8904 }); 8905 8906 function wcpt_ti_wishlist_growler(data) { 8907 var template = $("#wcpt-ti-wishlist-growler-template").html(), 8908 $growler = $(template.replace("{n}", '"' + data.name + '"')), 8909 reveal_class = "wcpt-ti-wishlist-growler--revealed", 8910 duration_ms = data.duration_seconds * 1000; 8911 8912 $growler.attr("data-wcpt-icon", data.icon); 8913 $(".wcpt-ti-wishlist-growler").remove(); // remove previous 8914 8915 if (data.item_added_label) {
8916 $(".wcpt-ti-wishlist-growler__label--item-added", $growler).text( 8917 data.item_added_label.replace("{n}", '"' + data.name + '"'), 8918 ); 8919 } 8920 8921 if (data.view_wishlist_label) { 8922 $(".wcpt-ti-wishlist-growler__label--view-wishlist", $growler).text( 8923 data.view_wishlist_label.replace("{n}", '"' + data.name + '"'), 8924 ); 8925 } 8926 8927 if (data.url) { 8928 $growler.attr("href", data.url); 8929 } else if (wcpt_ti_wishlist_url) { 8930 $growler.attr("href", wcpt_ti_wishlist_url); 8931 } 8932 8933 $("body").append($growler); 8934 8935 setTimeout(function () { 8936 $growler.addClass(reveal_class); 8937 }, 100); 8938 8939 setTimeout(function () { 8940 $growler.removeClass(reveal_class); 8941 }, duration_ms); 8942 8943 setTimeout(function () { 8944 $growler.remove(); 8945 }, duration_ms + 500); 8946 } 8947 8948 $("body").on("wcpt_after_every_load", ".wcpt", function () { 8949 var $this__container = $(this), 8950 $visible_rows = wcpt_util 8951 .get_uninit_rows($this__container) 8952 .filter(":visible"); 8953 8954 // variable products in wishlist were definitely added without variation selected 8955 if (wcpt_util.get_sc_attrs($this__container).ti_wishlist) { 8956 $visible_rows 8957 .filter(".wcpt-product-type-variable") 8958 .trigger("select_variation", { 8959 variation_id: false, 8960 complete_match: false, 8961 attributes: false, 8962 variation: false, 8963 variation_found: false, 8964 variation_selected: false, 8965 variation_available: false, 8966 }); 8967 } 8968 8969 $visible_rows.each(function () { 8970 var $this__container = $(this); 8971 wcpt_ti_wishlist_update_row_view($this__container); 8972 }); 8973 }); 8974 8975 $("body").on("select_variation", ".wcpt-row", function () { 8976 var $this = $(this); 8977 wcpt_ti_wishlist_update_row_view($this); 8978 }); 8979 8980 function wcpt_ti_wishlist_update_row_view($row) { 8981 var product_type = $row.attr("data-wcpt-type"), 8982 product_id = $row.attr("data-wcpt-product-id"), 8983 variation_id = $row.data("wcpt_variation_id") 8984 ? $row.data("wcpt_variation_id") 8985 : $row.attr("data-wcpt-variation-id") 8986 ? $row.attr("data-wcpt-variation-id") 8987 : false, 8988 $buttons = $(".wcpt-wishlist", wcpt_get_sibling_rows($row)), 8989 variable_permitted = $buttons.attr("data-wcpt-variable-permitted"); 8990 8991 if (product_type === "variable" && !variable_permitted && !variation_id) { 8992 $buttons.removeClass("wcpt-active").addClass("wcpt-disabled"); 8993 8994 return; 8995 } 8996 8997 $buttons.removeClass("wcpt-disabled wcpt-loading"); 8998 8999 var list_id = variation_id ? variation_id : product_id; 9000 9001 if (-1 === wcpt_ti_wishlist_ids.indexOf(list_id + "")) { 9002 $buttons.removeClass("wcpt-active"); 9003 } else { 9004 $buttons.addClass("wcpt-active"); 9005 } 9006 } 9007 } 9008 9009 // WooCommerce Wholesale Prices 9010 9011 // -- select_variation handler to switch vals in wcpt_wholesale 9012 $("body").on("select_variation", ".wcpt-row", function () { 9013 var $this = $(this), 9014 variation = $this.data("wcpt_variation"), 9015 $wholesale_shortcode = $(".wcpt-wholesale", $this); 9016 9017 $wholesale_shortcode.each(function () { 9018 var $this = $(this); 9019 9020 // set the view: default or variation 9021 $this.removeClass( 9022 "wcpt-wholesale--variation-view-enabled wcpt-wholesale--default-view-enabled", 9023 ); 9024 9025 if (variation) { 9026 $this.addClass("wcpt-wholesale--variation-view-enabled"); 9027 } else { 9028 $this.addClass("wcpt-wholesale--default-view-enabled"); 9029 } 9030 9031 // wholesale table 9032 if ($this.hasClass("wcpt-wholesale--wholesale-table")) { 9033 var variation_html = "-"; 9034 9035 if (variation && variation.wholesale_price) { 9036 var table_html_match = 9037 variation.price_html.match(/(<table.+table)>/s); 9038 if (table_html_match) { 9039 variation_html = table_html_match[0]; 9040 } 9041 } 9042 9043 $this.html(variation_html); 9044 } 9045 9046 // wholesale price 9047 if ($this.hasClass("wcpt-wholesale--wholesale-price")) { 9048 var variation_text = "-"; 9049 9050 if (variation) { 9051 var wholesale_price = ""; 9052 if (variation.wholesale_price) { 9053 wholesale_price = variation.wholesale_price; 9054 } 9055 variation_text = format_price(wholesale_price); 9056 } 9057 9058 $(".wcpt-wholesale__variation-view", $this).text(variation_text); 9059 } 9060 9061 // original price 9062 if ($this.hasClass("wcpt-wholesale--original-price")) { 9063 var variation_text = "-"; 9064 9065 if (variation) { 9066 var original_price = ""; 9067 if (variation.wholesale_price) { 9068 original_price = variation.original_display_price; // cannot use display_price here because it is set to wholesale_price by WCPT 9069 } else { 9070 original_price = variation.display_price; 9071 } 9072 variation_text = format_price(original_price); 9073 } 9074 9075 $(".wcpt-wholesale__variation-view", $this).text(variation_text); 9076 } 9077 9078 // wholesale label 9079 if ($this.hasClass("wcpt-wholesale--wholesale-label")) { 9080 $this.removeClass( 9081 "wcpt-wholesale--variation-is-on-wholesale-view-enabled wcpt-wholesale--variation-is-not-on-wholesale-view-enabled", 9082 ); 9083 if (variation) { 9084 if (variation.wholesale_price) { 9085 $this.addClass( 9086 "wcpt-wholesale--variation-is-on-wholesale-view-enabled", 9087 ); 9088 } else { 9089 $this.addClass( 9090 "wcpt-wholesale--variation-is-not-on-wholesale-view-enabled", 9091 ); 9092 } 9093 } 9094 } 9095 });
9096 }); 9097 9098 // Variation Swatches 9099 $("body").on( 9100 "change", 9101 ".wcpt-row .variations_form .variation_id", 9102 function () { 9103 var $this = $(this), 9104 $form = $this.closest(".variations_form"); 9105 9106 if ($form.hasClass("wvs-loaded")) { 9107 get_select_variation_from_cart_form($form); 9108 } 9109 }, 9110 ); 9111 9112 // init tables 9113 var $wcpt = $(".wcpt"); 9114 if ($wcpt.length) { 9115 $wcpt.each(function () { 9116 after_every_load($(this)); 9117 }); 9118 } 9119 9120 // lazy load 9121 lazy_load_start(); 9122 9123 // init cart widget 9124 var cart_init_required = false; 9125 9126 if ( 9127 document.cookie.indexOf("woocommerce_items_in_cart") !== -1 || 9128 (typeof wcpt_cart_result_cache !== "undefined" && 9129 wcpt_cart_result_cache.cart_quantity) 9130 ) { 9131 cart_init_required = true; 9132 } 9133 9134 if (cart_init_required) { 9135 wcpt_cart({ 9136 payload: { skip_cart_triggers: true }, 9137 }); 9138 } 9139 9140 $(window).on("pageshow", function (e) { 9141 // trigger cart events 9142 if (e.originalEvent.persisted) { 9143 wcpt_cart({ 9144 payload: { skip_cart_triggers: true }, 9145 }); 9146 } 9147 9148 // reset select variation to default 9149 $(".wcpt-select-variation-dropdown").each(function () { 9150 var $this = $(this), 9151 $row = $this.closest(".wcpt-row"), 9152 variation_id = $row.data("wcpt_variation_id"); 9153 9154 if (variation_id) { 9155 $this.val(variation_id); 9156 } else { 9157 $this.val(""); 9158 } 9159 }); 9160 }); 9161}); 9162 9163// module: child row 9164(function ($) { 9165 /** 9166 * Accordion mode variable. Set to true to allow only one child row open at a time. 9167 */ 9168 var accordion_mode = true; // set this variable as needed 9169 var child_row_instance_counter = 0; 9170 9171 function getChildRowContainerSelector($container) { 9172 var instanceId = $container.attr("data-wcpt-child-row-instance-id"); 9173 9174 if (!instanceId) { 9175 child_row_instance_counter += 1; 9176 instanceId = "wcpt-child-row-" + child_row_instance_counter; 9177 $container.attr("data-wcpt-child-row-instance-id", instanceId); 9178 } 9179 9180 return '.wcpt[data-wcpt-child-row-instance-id="' + instanceId + '"]'; 9181 } 9182 9183 /** 9184 * Register event handlers for child row logic 9185 */ 9186 $("body") 9187 // Re-evaluate disabled toggles when viewport/device changes. 9188 .on("wcpt_layout", ".wcpt", function () { 9189 updateChildRowToggleDisabledState($(this)); 9190 }) 9191 // Initialize all child row DOM logic after table load 9192 .on("wcpt_after_every_load", ".wcpt", initChildRows) 9193 // Each child row: register parent/child data binding and set background 9194 .on("wcpt_init_child_row", ".wcpt-child-row", registerParentChild) 9195 .on("wcpt_init_child_row", ".wcpt-child-row", setChildRowBg) 9196 // Click: toggle child rows by explicit control 9197 .on("click", ".wcpt-child-row-toggle", toggleChildRow) 9198 // Click: anywhere in designated rows, except on interactive elements 9199 .on("click", ".wcpt-has-child-row--click-anywhere", toggleChildRowAnywhere) 9200 // Hover state handlers (parent/child highlighting) 9201 .on("mouseenter", ".wcpt-has-child-row", parentRowMouseenter) 9202 .on("mouseleave", ".wcpt-has-child-row", parentRowMouseleave) 9203 .on("mouseenter", ".wcpt-child-row", childRowMouseenter) 9204 .on("mouseleave", ".wcpt-child-row", childRowMouseleave); 9205 9206 // In pages with multiple tables, some instances can be rendered before this 9207 // module attaches wcpt_after_every_load listeners. Initialize existing tables. 9208 $(function () { 9209 $(".wcpt").each(function () { 9210 initChildRows.call(this); 9211 }); 9212 }); 9213 9214 /** 9215 * Init all child rows in the current .wcpt table, 9216 * triggers event to register proper properties 9217 */ 9218 function initChildRows() { 9219 var $container = $(this); 9220 9221 ensureParentChildBindings($container); 9222 9223 $(".wcpt-child-row:not(.wcpt-child-row--init)", $container).each( 9224 function () { 9225 var $row = $(this); 9226 $row.trigger("wcpt_init_child_row").addClass("wcpt-child-row--init"); 9227 }, 9228 ); 9229 9230 updateChildRowToggleDisabledState($container); 9231 } 9232 9233 function getChildRowViewportDevice($container) { 9234 var device = "laptop"; 9235 var $scroll_outer = $container 9236 .find(".wcpt-table-scroll-wrapper-outer") 9237 .first(); 9238 9239 if ($scroll_outer.length) { 9240 if ($scroll_outer.hasClass("wcpt-device-phone")) { 9241 device = "phone"; 9242 } else if ($scroll_outer.hasClass("wcpt-device-tablet")) { 9243 device = "tablet"; 9244 } 9245 } else if (window.wcpt_params && wcpt_params.breakpoints) { 9246 var width = $(window).width(); 9247 9248 if (width <= wcpt_params.breakpoints.phone) { 9249 device = "phone"; 9250 } else if (width <= wcpt_params.breakpoints.tablet) { 9251 device = "tablet"; 9252 } 9253 } 9254 9255 return device; 9256 } 9257 9258 function getChildRowOriginalTable($container) { 9259 return $container.find(".wcpt-table:visible").not(".frzTbl-clone-table"); 9260 } 9261 9262 function childRowElementIsConfiguredForDevice($element, device) { 9263 var style = $element.attr("style") || ""; 9264 9265 return style.indexOf("--wcpt-child-row-column-order-" + device) !== -1; 9266 } 9267 9268 function childRowElementIsHiddenByEmptyColumns($element, $table) { 9269 if ($element.hasClass("wcpt-hide")) { 9270 return true; 9271 } 9272 9273 var colIndex = $element.attr("data-wcpt-column-index"); 9274 9275 if (typeof colIndex === "undefined") { 9276 var match = ($element.attr("class") || "").match( 9277 /wcpt-child-row__element--column-(\d+)/, 9278 ); 9279 colIndex = match ? match[1] : null; 9280 } 9281 9282 if (colIndex === null) { 9283 return false; 9284 } 9285 9286 var $cells = $table.find( 9287 '.wcpt-cell[data-wcpt-column-index="' + colIndex + '"]', 9288 ); 9289 9290 return ( 9291 $cells.length > 0 && 9292 $cells.filter(".wcpt-hide").length === $cells.length 9293 ); 9294 } 9295 9296 function updateChildRowToggleDisabledState($container) { 9297 var sc_attrs = 9298 typeof wcpt_util !== "undefined" 9299 ? wcpt_util.get_sc_attrs($container) 9300 : {}; 9301 9302 if (!sc_attrs.hide_empty_columns) { 9303 $container 9304 .find("td.wcpt-child-row-toggle") 9305 .removeClass("wcpt-child-row-toggle--disabled"); 9306 return; 9307 } 9308 9309 var device = getChildRowViewportDevice($container); 9310 var $table = getChildRowOriginalTable($container); 9311 9312 $container.find("td.wcpt-child-row-toggle").each(function () { 9313 var $toggle = $(this); 9314 var $row = $toggle.closest(".wcpt-row"); 9315 var $childRow = $row.data("wcpt_child_row"); 9316 var shouldDisable = false; 9317 var hasConfiguredForDevice = false; 9318 9319 if ( 9320 (!$childRow || !$childRow.length) && 9321 $row.next().hasClass("wcpt-child-row") 9322 ) { 9323 $childRow = $row.next(); 9324 $row.data("wcpt_child_row", $childRow); 9325 $childRow.data("wcpt_parent_row", $row); 9326 } 9327 9328 if ($childRow && $childRow.length) { 9329 shouldDisable = true; 9330 var $elements = $childRow.find(".wcpt-child-row__element"); 9331 9332 $elements.each(function () { 9333 if (!childRowElementIsConfiguredForDevice($(this), device)) { 9334 return; 9335 } 9336 9337 hasConfiguredForDevice = true; 9338 9339 if ( 9340 !childRowElementIsHiddenByEmptyColumns($(this), $table) 9341 ) { 9342 shouldDisable = false; 9343 return false; 9344 } 9345 }); 9346 9347 if (!hasConfiguredForDevice) { 9348 shouldDisable = false; 9349 } 9350 } 9351 9352 if (shouldDisable) { 9353 $toggle.addClass("wcpt-child-row-toggle--disabled"); 9354 9355 if (!$toggle.hasClass("wcpt-child-row-toggle--closed")) { 9356 $toggle.addClass("wcpt-child-row-toggle--closed"); 9357 $row.removeClass("wcpt-has-child-row--visible"); 9358 9359 if ($childRow && $childRow.length) { 9360 $childRow.find(".wcpt-child-row__wrapper").hide(); 9361 } 9362 } 9363 } else { 9364 $toggle.removeClass("wcpt-child-row-toggle--disabled"); 9365 } 9366 });
9367 } 9368 9369 function ensureParentChildBindings($container) { 9370 $container.find(".wcpt-child-row").each(function () { 9371 var $child = $(this); 9372 var $parent = $child.prev(".wcpt-row"); 9373 9374 if (!$parent.length) return; 9375 9376 if (!$child.data("wcpt_parent_row")) { 9377 $child.data("wcpt_parent_row", $parent); 9378 } 9379 9380 if (!$parent.data("wcpt_child_row")) { 9381 $parent.data("wcpt_child_row", $child); 9382 } 9383 }); 9384 } 9385 9386 /** 9387 * Binds parent <-> child row references for easier later access 9388 */ 9389 function registerParentChild() { 9390 var $child = $(this), 9391 $parent = $child.prev(); 9392 9393 $child.data("wcpt_parent_row", $parent); 9394 $parent.data("wcpt_child_row", $child); 9395 } 9396 9397 /** 9398 * Sets background color of a child row to match its parent's first cell (or whole row) 9399 */ 9400 function setChildRowBg() { 9401 var $child = $(this), 9402 $parent = $child.data("wcpt_parent_row"); 9403 9404 // fallback safely in case parent not found 9405 if (!$parent || !$parent.length) return; 9406 9407 var parentBg = $parent.css("background-color") || "", 9408 firstCellBg = $("> td:first-child", $parent).css("background-color"), 9409 chosenBg = 9410 firstCellBg && firstCellBg !== "rgba(0, 0, 0, 0)" 9411 ? firstCellBg 9412 : parentBg; 9413 9414 $("> td", $child).css("background-color", chosenBg); 9415 } 9416 9417 /** 9418 * Ensures all cells in table have stable width when child row opened (for visual stability) 9419 */ 9420 function applyColumnWidthStyles($container) { 9421 var $firstOpenRow = $container 9422 .find( 9423 ".wcpt-row:has(td.wcpt-child-row-toggle:not(.wcpt-child-row-toggle--closed))", 9424 ) 9425 .first(); 9426 var styleHtml = '<style class="wcpt-child-row-width">'; 9427 var containerSelector = getChildRowContainerSelector($container); 9428 if ($firstOpenRow.length) { 9429 $firstOpenRow.children(".wcpt-cell").each(function (index) { 9430 var width = $(this).outerWidth(); 9431 // Scope styles to a unique table instance on the page. 9432 styleHtml += 9433 containerSelector + 9434 " .wcpt-row > .wcpt-cell:nth-child(" + 9435 (index + 1) + 9436 ") { width: " + 9437 width + 9438 "px !important; }"; 9439 }); 9440 styleHtml += "</style>"; 9441 $container.find("style.wcpt-child-row-width").remove(); 9442 $container.append(styleHtml); 9443 } 9444 } 9445 9446 /** 9447 * Main click handler: toggles child row open/closed for a given row/cell or header cell. 9448 * Supports accordion_mode: if true, only one child row stays open at a time (unless opening all). 9449 */ 9450 function toggleChildRow(e) { 9451 var $clicked = $(this), 9452 $container = $clicked.closest(".wcpt"); 9453 9454 if ($clicked.hasClass("wcpt-child-row-toggle--disabled")) { 9455 return; 9456 } 9457 9458 // Reconcile bindings if this table was rendered before child-row init hooks. 9459 ensureParentChildBindings($container); 9460 9461 // Detect if this is a table heading (bulk open/close) 9462 var isHeader = !$clicked.is("td"); 9463 9464 if (isHeader) { 9465 // Header toggle always continues as normal: opens/closes all 9466 toggleAllChildRows( 9467 $container, 9468 !$clicked.hasClass("wcpt-child-row-toggle--closed"), 9469 ); 9470 updateRowVisibilityMarks($container); 9471 handleColumnWidths( 9472 $container, 9473 !$clicked.hasClass("wcpt-child-row-toggle--closed"), 9474 ); 9475 $clicked.toggleClass("wcpt-child-row-toggle--closed"); 9476 return; 9477 } 9478 9479 var wasClosed = $clicked.hasClass("wcpt-child-row-toggle--closed"); 9480 9481 // If accordion mode is on, and we're opening a row (not closing), close any other open rows first (excluding this one) 9482 // if (accordion_mode && wasClosed) { 9483 if (wasClosed) { 9484 // Find all open toggles except the one being clicked 9485 $container 9486 .find("td.wcpt-child-row-toggle:not(.wcpt-child-row-toggle--closed)") 9487 .each(function () { 9488 var $otherTd = $(this); 9489 if (!$otherTd.is($clicked)) { 9490 // Only close if not this one 9491 var $otherRow = $otherTd.closest(".wcpt-row"); 9492 var $otherChildRow = $otherRow.data("wcpt_child_row"); 9493 $otherTd.addClass("wcpt-child-row-toggle--closed"); 9494 if ($otherChildRow && $otherChildRow.length) { 9495 var $otherWrapper = $otherChildRow.find( 9496 ".wcpt-child-row__wrapper", 9497 ); 9498 $otherWrapper.slideUp(); 9499 } 9500 } 9501 }); 9502 } 9503 9504 // Now toggle clicked row as usual 9505 $clicked.toggleClass("wcpt-child-row-toggle--closed"); 9506 var isClosedNow = $clicked.hasClass("wcpt-child-row-toggle--closed"); 9507 9508 // Handle column width stabilization 9509 handleColumnWidths($container, !isClosedNow); 9510 9511 // Single row toggle logic 9512 toggleSingleChildRow($clicked, $container, isClosedNow); 9513 9514 updateRowVisibilityMarks($container); 9515 } 9516 9517 /** 9518 * Ensures all columns keep their current width (optional: only when opening). 9519 */ 9520 function handleColumnWidths($container, openingOne) { 9521 var $openRows = $container.find( 9522 ".wcpt-row:has(td.wcpt-child-row-toggle:not(.wcpt-child-row-toggle--closed))", 9523 ); 9524 var $style = $container.find("style.wcpt-child-row-width"); 9525 var containerSelector = getChildRowContainerSelector($container); 9526 9527 if (openingOne && $openRows.length) { 9528 // Lock cell widths according to the first row with open toggle 9529 var $row = $openRows.first(); 9530 var styleHtml = '<style class="wcpt-child-row-width">'; 9531 $row.children(".wcpt-cell").each(function (i) { 9532 styleHtml += 9533 containerSelector + 9534 " .wcpt-row > .wcpt-cell:nth-child(" + 9535 (i + 1) + 9536 ") { width: " + 9537 $(this).outerWidth() + 9538 "px !important; }"; 9539 }); 9540 styleHtml += "</style>"; 9541 $style.remove(); 9542 $container.append(styleHtml); 9543 } else { 9544 // Only remove width style if ALL now closed 9545 var allClosed = 9546 $container.find("td.wcpt-child-row-toggle").length === 9547 $container.find( 9548 "td.wcpt-child-row-toggle.wcpt-child-row-toggle--closed", 9549 ).length; 9550 if (allClosed) { 9551 $style.remove(); 9552 } else { 9553 applyColumnWidthStyles($container); // fallback to routine (already defined) 9554 } 9555 } 9556 } 9557 9558 /** 9559 * Toggle a single row's child row visibility and header icon state. 9560 */ 9561 function toggleSingleChildRow($clicked, $container, isClosedNow) { 9562 var $row = $clicked.closest(".wcpt-row"); 9563 var $childRow = $row.data("wcpt_child_row"); 9564 9565 if ( 9566 (!$childRow || !$childRow.length) && 9567 $row.next().hasClass("wcpt-child-row") 9568 ) { 9569 $childRow = $row.next(); 9570 $row.data("wcpt_child_row", $childRow); 9571 $childRow.data("wcpt_parent_row", $row); 9572 } 9573 9574 if ($childRow && $childRow.length) { 9575 var $wrapper = $childRow.find(".wcpt-child-row__wrapper"); 9576 if ($row.hasClass("wcpt-has-child-row--visible")) { 9577 $wrapper.slideUp(); 9578 } else { 9579 $wrapper.slideDown(); 9580 } 9581 } 9582 9583 // Sync header toggle icon: open header icon if ANY row is open, else close header icon 9584 var anyOpen = 9585 $container.find( 9586 "td.wcpt-child-row-toggle:not(.wcpt-child-row-toggle--closed)", 9587 ).length > 0;
9588 var $headerToggles = $container.find("th.wcpt-child-row-toggle"); 9589 if (anyOpen) { 9590 $headerToggles.removeClass("wcpt-child-row-toggle--closed"); 9591 } else { 9592 $headerToggles.addClass("wcpt-child-row-toggle--closed"); 9593 } 9594 } 9595 9596 /** 9597 * Toggle all child rows via the header toggle cell. 9598 * Support "close all" even if accordion_mode is enabled. 9599 */ 9600 function toggleAllChildRows($container, closing) { 9601 var $tdToggles = $container.find("td.wcpt-child-row-toggle"); 9602 $tdToggles.each(function () { 9603 var $td = $(this); 9604 9605 if ($td.hasClass("wcpt-child-row-toggle--disabled")) { 9606 return; 9607 } 9608 9609 var $row = $td.closest(".wcpt-row"); 9610 var $childRow = $row.data("wcpt_child_row"); 9611 var $wrapper = $childRow 9612 ? $childRow.find(".wcpt-child-row__wrapper") 9613 : $(); 9614 if (closing) { 9615 if (!$td.hasClass("wcpt-child-row-toggle--closed")) { 9616 $td.addClass("wcpt-child-row-toggle--closed"); 9617 if ($childRow && $wrapper.length) $wrapper.slideUp(); 9618 } 9619 } else { 9620 if ($td.hasClass("wcpt-child-row-toggle--closed")) { 9621 $td.removeClass("wcpt-child-row-toggle--closed"); 9622 if ($childRow && $wrapper.length) $wrapper.slideDown(); 9623 } 9624 } 9625 }); 9626 // If opening bulk, re-apply width stabilization 9627 if (!closing) { 9628 applyColumnWidthStyles($container); 9629 } else { 9630 // Always remove width stabilization on close all 9631 var $style = $container.find("style.wcpt-child-row-width"); 9632 $style.remove(); 9633 } 9634 } 9635 9636 /** 9637 * Add or remove .wcpt-has-child-row--visible on rows according to open toggle state (for CSS effects). 9638 */ 9639 function updateRowVisibilityMarks($container) { 9640 $container.find("td.wcpt-child-row-toggle").each(function () { 9641 var $td = $(this); 9642 var $row = $td.closest(".wcpt-row"); 9643 // Use a short delay to let animations run and class state stabilize 9644 setTimeout(function () { 9645 if ($td.hasClass("wcpt-child-row-toggle--closed")) { 9646 $row.removeClass("wcpt-has-child-row--visible"); 9647 } else { 9648 $row.addClass("wcpt-has-child-row--visible"); 9649 } 9650 }, 100); 9651 }); 9652 } 9653 9654 /** 9655 * Handler: clicking anywhere in "click-anywhere" parent row will toggle child, 9656 * but not if clicking on interactive/child row controls or links. 9657 */ 9658 function toggleChildRowAnywhere(e) { 9659 var $row = $(this).closest(".wcpt-row"); 9660 var $toggle = $(".wcpt-child-row-toggle", $row); 9661 9662 if ($toggle.hasClass("wcpt-child-row-toggle--disabled")) { 9663 return; 9664 } 9665 9666 var ignoreSelectors = [ 9667 ".wcpt-child-row-toggle", 9668 "a", 9669 ".wcpt-tooltip", 9670 "input", 9671 "button", 9672 ".wcpt-button", 9673 ".wcpt-link", 9674 ".wcpt-quantity", 9675 ].join(", "); 9676 if (!$(e.target).closest(ignoreSelectors).length) { 9677 $(".wcpt-child-row-toggle", $row).click(); 9678 } 9679 } 9680 9681 /** Highlighting logic for hover grouping (parent and child row highlight effects) **/ 9682 function parentRowMouseenter() { 9683 var $parent = $(this), 9684 $child = $parent.data("wcpt_child_row"); 9685 if ($child && $child.length) { 9686 $child.addClass("wcpt-parent-row-hovered"); 9687 } 9688 } 9689 9690 function parentRowMouseleave() { 9691 var $parent = $(this), 9692 $child = $parent.data("wcpt_child_row"); 9693 if ($child && $child.length) { 9694 $child.removeClass("wcpt-parent-row-hovered"); 9695 } 9696 } 9697 9698 function childRowMouseenter() { 9699 var $child = $(this), 9700 $parent = $child.data("wcpt_parent_row"); 9701 if ($parent && $parent.length) { 9702 $parent.addClass("wcpt-child-row-hovered"); 9703 } 9704 } 9705 9706 function childRowMouseleave() { 9707 var $child = $(this), 9708 $parent = $child.data("wcpt_parent_row"); 9709 if ($parent && $parent.length) { 9710 $parent.removeClass("wcpt-child-row-hovered"); 9711 } 9712 } 9713})(jQuery); 9714 9715// module: instant search 9716(function ($) { 9717 $("body").on("wcpt_after_every_load", ".wcpt", init_instant_search); 9718 9719 // -- add instant search html class on $search and stop 'enter' key handler 9720 function init_instant_search() { 9721 var $container = $(this), 9722 $search = $(".wcpt-search-wrapper", $container); 9723 9724 if ( 9725 wcpt_util.get_sc_attrs($container).instant_search && 9726 !$search.hasClass("wcpt-instant-search") 9727 ) { 9728 $search.addClass("wcpt-instant-search"); 9729 $(".wcpt-instant-search .wcpt-search-input", $container).on( 9730 "keydown", 9731 function (e) { 9732 if (e.keyCode === 13 || e.which === 13) { 9733 e.stopPropagation(); 9734 } 9735 }, 9736 ); 9737 } else { 9738 $search.removeClass("wcpt-instant-search"); 9739 } 9740 } 9741 9742 function get_instant_search_words(val) {
9743 return val.split(/\s+/).filter(Boolean); 9744 } 9745 9746 // Per-column text cache; each search word must match at least one column. 9747 function get_instant_search_cell_texts($row) { 9748 var texts = $row.data("wcpt_instant_search_cell_texts"); 9749 if (!texts) { 9750 texts = []; 9751 $row.children(".wcpt-cell").each(function () { 9752 texts.push($(this).text().toLowerCase().trim()); 9753 }); 9754 if (!texts.length) { 9755 texts = [$row.text().toLowerCase().trim()]; 9756 } 9757 $row.data("wcpt_instant_search_cell_texts", texts); 9758 } 9759 return texts; 9760 } 9761 9762 function row_matches_instant_search($row, val) { 9763 var words = get_instant_search_words(val), 9764 cellTexts = get_instant_search_cell_texts($row), 9765 w, 9766 c, 9767 wordMatch; 9768 9769 for (w = 0; w < words.length; w++) { 9770 wordMatch = false; 9771 for (c = 0; c < cellTexts.length; c++) { 9772 if (cellTexts[c].indexOf(words[w]) !== -1) { 9773 wordMatch = true; 9774 break; 9775 } 9776 } 9777 if (!wordMatch) { 9778 return false; 9779 } 9780 } 9781 return true; 9782 } 9783 9784 // -- search logic 9785 $("body").on( 9786 "keyup input", 9787 ".wcpt-instant-search .wcpt-search-input", 9788 function (e) { 9789 var $this = $(this), 9790 val = $this.val().toLowerCase().trim(), 9791 $wcpt = $this.closest(".wcpt"), 9792 $table = $(".wcpt-table:visible:not(.frzTbl-clone-table)", $wcpt), 9793 $rows = $(".wcpt-row", $table); 9794 9795 if (!val) { 9796 $rows.removeClass("wcpt-row--instant-search-hidden"); 9797 } else { 9798 $rows.each(function () { 9799 var $this = $(this), 9800 match = row_matches_instant_search($this, val); 9801 9802 if (match) { 9803 $this.removeClass("wcpt-row--instant-search-hidden"); 9804 } else { 9805 $this.addClass("wcpt-row--instant-search-hidden"); 9806 } 9807 9808 // child row -- reveal both if either has match 9809 if ($this.hasClass("wcpt-child-row")) { 9810 $parent_row = $this.data("wcpt_parent_row"); 9811 9812 if ( 9813 !$this.hasClass("wcpt-row--instant-search-hidden") || 9814 !$parent_row.hasClass("wcpt-row--instant-search-hidden") 9815 ) { 9816 $this 9817 .add($parent_row) 9818 .removeClass("wcpt-row--instant-search-hidden"); 9819 } 9820 } 9821 }); 9822 9823 // show category group headings if it has search results 9824 $rows.filter(".wcpt-row--category-heading").each(function () { 9825 var $this = $(this), 9826 cat_slug = $this.attr("data-wcpt-group-by-category-slug"); 9827 9828 if ( 9829 $rows.filter( 9830 '[data-wcpt-group-by-category-slug="' + cat_slug + '"]:visible', 9831 ).length 9832 ) { 9833 $this.removeClass("wcpt-row--instant-search-hidden"); 9834 } 9835 }); 9836 } 9837 9838 wcpt_util.assign_even_odd_row_classes($table); 9839 }, 9840 ); 9841})(jQuery); 9842 9843// module: instant sort 9844(function ($) { 9845 function get_instant_sort_params_for_heading( 9846 $sorting_icons, 9847 all_sort_params, 9848 ) { 9849 var current_sort_params = {}; 9850 9851 if (!$sorting_icons || !$sorting_icons.length || !all_sort_params) { 9852 return current_sort_params; 9853 } 9854 9855 // Preferred: match by element id class (wcpt-{id}) 9856 $.each(all_sort_params.column_heading || {}, function (id, params) { 9857 if ($sorting_icons.hasClass("wcpt-" + id)) { 9858 current_sort_params = $.extend({}, params); 9859 return false; 9860 } 9861 }); 9862 9863 if (!$.isEmptyObject(current_sort_params) && current_sort_params.orderby) { 9864 return current_sort_params; 9865 } 9866 9867 // Fallback: match by data attributes on the sorting icons. 9868 // Needed when heading.sorting ids were regenerated and no longer match 9869 // the keys stored in data-wcpt-instant-sort-params. 9870 var orderby = $sorting_icons.attr("data-wcpt-orderby") || "", 9871 meta_key = ( 9872 $sorting_icons.attr("data-wcpt-meta-key") || "" 9873 ).toLowerCase(), 9874 orderby_attribute = ( 9875 $sorting_icons.attr("data-wcpt-orderby-attribute") || "" 9876 ).toLowerCase(), 9877 orderby_taxonomy = ( 9878 $sorting_icons.attr("data-wcpt-orderby-taxonomy") || "" 9879 ).toLowerCase(); 9880 9881 if (!orderby) { 9882 return {}; 9883 } 9884 9885 $.each(all_sort_params.column_heading || {}, function (id, params) { 9886 if (!params || params.orderby !== orderby) { 9887 return; 9888 } 9889 9890 if ( 9891 -1 !== $.inArray(orderby, ["meta_value", "meta_value_num"]) && 9892 String(params.meta_key || "") 9893 .toLowerCase() 9894 .trim() !== meta_key 9895 ) { 9896 return; 9897 } 9898 9899 if ( 9900 -1 !== $.inArray(orderby, ["attribute", "attribute_num"]) && 9901 String(params.orderby_attribute || "") 9902 .toLowerCase() 9903 .trim() !== orderby_attribute 9904 ) { 9905 return; 9906 } 9907 9908 if ( 9909 orderby === "taxonomy" && 9910 String(params.orderby_taxonomy || "") 9911 .toLowerCase() 9912 .trim() !== orderby_taxonomy 9913 ) { 9914 return; 9915 } 9916 9917 current_sort_params = $.extend({}, params); 9918 return false; 9919 });
9920 9921 return current_sort_params; 9922 } 9923 9924 function get_instant_sort_heading_icons($wcpt, id, option_sort_params) { 9925 var $sorting_icons = $(".wcpt-" + id, $wcpt); 9926 9927 if ($sorting_icons.length) { 9928 return $sorting_icons; 9929 } 9930 9931 // Fallback when icon class id doesn't match the params key 9932 return $(".wcpt-sorting-icons", $wcpt).filter(function () { 9933 var $icons = $(this), 9934 orderby = $icons.attr("data-wcpt-orderby") || "", 9935 meta_key = ($icons.attr("data-wcpt-meta-key") || "").toLowerCase(), 9936 orderby_attribute = ( 9937 $icons.attr("data-wcpt-orderby-attribute") || "" 9938 ).toLowerCase(), 9939 orderby_taxonomy = ( 9940 $icons.attr("data-wcpt-orderby-taxonomy") || "" 9941 ).toLowerCase(); 9942 9943 if (!option_sort_params || orderby !== option_sort_params.orderby) { 9944 return false; 9945 } 9946 9947 if ( 9948 -1 !== $.inArray(orderby, ["meta_value", "meta_value_num"]) && 9949 String(option_sort_params.meta_key || "") 9950 .toLowerCase() 9951 .trim() !== meta_key 9952 ) { 9953 return false; 9954 } 9955 9956 if ( 9957 -1 !== $.inArray(orderby, ["attribute", "attribute_num"]) && 9958 String(option_sort_params.orderby_attribute || "") 9959 .toLowerCase() 9960 .trim() !== orderby_attribute 9961 ) { 9962 return false; 9963 } 9964 9965 if ( 9966 orderby === "taxonomy" && 9967 String(option_sort_params.orderby_taxonomy || "") 9968 .toLowerCase() 9969 .trim() !== orderby_taxonomy 9970 ) { 9971 return false; 9972 } 9973 9974 return true; 9975 }); 9976 } 9977 9978 // -- init 9979 $("body").on("wcpt_after_every_load", ".wcpt", init_instant_sort); 9980 9981 function init_instant_sort() { 9982 var $this = $(this), 9983 sc_attrs = $this.data("wcpt_sc_attrs"); 9984 9985 if (sc_attrs.instant_sort) { 9986 // attach data 9987 var sort_data = []; 9988 $( 9989 ".wcpt-row:not(.wcpt-child-row):not(.wcpt-row--category-heading)", 9990 $this, 9991 ).each(function () { 9992 var $this = $(this), 9993 id = $this.attr("data-wcpt-product-id"), 9994 variation_id = $this.attr("data-wcpt-variation-id"), 9995 product_sort_data = $.extend( 9996 {}, 9997 { 9998 id: id, 9999 variation_id: variation_id, 10000 }, 10001 JSON.parse($this.attr("data-wcpt-instant-sort-props")), 10002 ); 10003 sort_data.push(product_sort_data); 10004 }); 10005 10006 $this.data("wcpt_sort_data", sort_data); 10007 10008 // replace handlers 10009 wcpt_util.run_once_on_container( 10010 $this, 10011 "wcpt_instant_sort_init", 10012 function ($container) { 10013 // -- column heading sort icons handler 10014 10015 // -- -- remove previous handlers and the init html class on container 10016 $container.off( 10017 "click.wcpt_sort_by_column_headings", 10018 ".wcpt-heading.wcpt-sortable", 10019 ); 10020 10021 // -- -- attach new handlers 10022 $container.on( 10023 "click.wcpt_instant_sort", 10024 ".wcpt-heading.wcpt-sortable", 10025 function (e) { 10026 var $this = $(this), 10027 $sorting_icons = $(".wcpt-sorting-icons", $this), 10028 new_order = $sorting_icons.hasClass("wcpt-sorting-asc") 10029 ? "desc" 10030 : "asc", 10031 $wcpt = $this.closest(".wcpt"), 10032 all_sort_params = JSON.parse( 10033 $wcpt.attr("data-wcpt-instant-sort-params"), 10034 ), 10035 current_sort_params = get_instant_sort_params_for_heading( 10036 $sorting_icons, 10037 all_sort_params, 10038 ); 10039 10040 // If heading icons aren't linked to instant-sort params (id mismatch), 10041 // fall back to the regular AJAX column-heading sort. 10042 if ( 10043 $.isEmptyObject(current_sort_params) || 10044 !current_sort_params.orderby 10045 ) { 10046 return window.wcpt_column_heading_sort_handler.call(this, e); 10047 } 10048 10049 $.extend(current_sort_params, { order: new_order }); 10050 10051 wcpt_instant_sort(current_sort_params, $wcpt); 10052 10053 instant_sort_ui_feedback(current_sort_params, $wcpt); 10054 }, 10055 ); 10056 10057 // -- 'Sort By' dropdown handler 10058 $container.on( 10059 "change.wcpt_instant_sort", 10060 '[data-wcpt-filter="sort_by"] input[type="radio"]', 10061 function (e) { 10062 e.stopPropagation(); // block access to wcpt regular sort handler 10063 10064 var $this = $(this), 10065 $option = $this.closest(".wcpt-option, .wcpt-dropdown-option"), // @TODO include dropdown option 10066 $wcpt = $this.closest(".wcpt"), 10067 all_sort_params = JSON.parse( 10068 $wcpt.attr("data-wcpt-instant-sort-params"), 10069 ), 10070 index = $option.index(), 10071 current_sort_params = all_sort_params.dropdown[index]; 10072 10073 wcpt_instant_sort(current_sort_params, $wcpt); 10074 10075 instant_sort_ui_feedback(current_sort_params, $wcpt); 10076 }, 10077 ); 10078 }, 10079 ); 10080 } 10081 } 10082 10083 function instant_sort_ui_feedback(current_sort_params, $wcpt) { 10084 var all_sort_params = JSON.parse( 10085 $wcpt.attr("data-wcpt-instant-sort-params"), 10086 ); 10087 10088 if ( 10089 -1 !== $.inArray(current_sort_params.orderby, ["rating", "price-desc"]) 10090 ) { 10091 current_sort_params.order = "desc"; 10092 } 10093 10094 if (current_sort_params.orderby == "price-desc") { 10095 current_sort_params.orderby = "price"; 10096 } 10097 10098 // 'Sort By' dropdown (or row) 10099 $.each( 10100 all_sort_params["dropdown"], 10101 function (dropdown_option_index, option_sort_params) { 10102 if ( 10103 -1 !== $.inArray(option_sort_params.orderby, ["rating", "price-desc"]) 10104 ) { 10105 option_sort_params.order = "desc"; 10106 } 10107 10108 if (option_sort_params.orderby == "price-desc") { 10109 option_sort_params.orderby = "price"; 10110 } 10111 10112 if ( 10113 current_sort_params.orderby !== option_sort_params.orderby || 10114 (current_sort_params.order && 10115 current_sort_params.order.toLowerCase() !== 10116 option_sort_params.order.toLowerCase()) || 10117 (current_sort_params.orderby == "meta_key" && 10118 current_sort_params.meta_key && 10119 current_sort_params.meta_key.toLowerCase() !== 10120 option_sort_params.meta_key.toLowerCase()) || 10121 (-1 !== 10122 $.inArray(current_sort_params.orderby, [ 10123 "attribute", 10124 "attribute_num", 10125 ]) && 10126 current_sort_params.orderby_attribute && 10127 current_sort_params.orderby_attribute.toLowerCase() !== 10128 option_sort_params.orderby_attribute.toLowerCase()) || 10129 (current_sort_params.orderby == "taxonomy" && 10130 current_sort_params.orderby_taxonomy && 10131 current_sort_params.orderby_taxonomy.toLowerCase() !== 10132 option_sort_params.orderby_taxonomy.toLowerCase()) 10133 ) { 10134 return; 10135 } 10136 10137 var $dropdown = $('[data-wcpt-filter="sort_by"]', $wcpt), // might be row 10138 $selected_input = $dropdown.find("input").eq(dropdown_option_index), 10139 $selected_option = $selected_input.closest( 10140 ".wcpt-dropdown-option, .wcpt-option", 10141 ), 10142 $heading_label = $(".wcpt-dropdown-label", $dropdown); 10143 10144 $heading_label.text($("span", $selected_option).text()); 10145 $selected_input.prop("checked", true); 10146 $selected_option 10147 .addClass("wcpt-active") 10148 .siblings() 10149 .removeClass("wcpt-active"); 10150 10151 $dropdown.removeClass("wcpt-open"); 10152 }, 10153 ); 10154 10155 // 'Sorting' column heading icons 10156 $.each( 10157 all_sort_params["column_heading"], 10158 function (id, option_sort_params) { 10159 if ( 10160 current_sort_params.orderby !== option_sort_params.orderby || 10161 ($.inArray(current_sort_params.orderby, [ 10162 "meta_value", 10163 "meta_value_num", 10164 ]) && 10165 current_sort_params.meta_key && 10166 current_sort_params.meta_key.toLowerCase() !== 10167 option_sort_params.meta_key.toLowerCase()) || 10168 (-1 !== 10169 $.inArray(current_sort_params.orderby, [ 10170 "attribute", 10171 "attribute_num", 10172 ]) && 10173 current_sort_params.orderby_attribute && 10174 current_sort_params.orderby_attribute.toLowerCase() !== 10175 option_sort_params.orderby_attribute.toLowerCase()) || 10176 (current_sort_params.orderby == "taxonomy" && 10177 current_sort_params.orderby_taxonomy && 10178 current_sort_params.orderby_taxonomy.toLowerCase() !== 10179 option_sort_params.orderby_taxonomy.toLowerCase()) 10180 ) { 10181 return; 10182 } 10183 10184 var $sorting_icons = get_instant_sort_heading_icons( 10185 $wcpt, 10186 id, 10187 option_sort_params, 10188 ), 10189 new_order = current_sort_params.order.toLowerCase(); 10190 10191 if (!$sorting_icons.length) { 10192 return; 10193 } 10194 10195 // UI feedback 10196 $(".wcpt-sorting-icons", $wcpt).removeClass( 10197 "wcpt-sorting-asc wcpt-sorting-desc", 10198 ); 10199 $(".wcpt-sorting-icon", $wcpt).removeClass("wcpt-active wcpt-inactive"); 10200 10201 if (new_order == "asc") { 10202 $sorting_icons 10203 .addClass("wcpt-sorting-asc") 10204 .removeClass("wcpt-sorting-desc"); 10205 } else { 10206 $sorting_icons 10207 .addClass("wcpt-sorting-desc") 10208 .removeClass("wcpt-sorting-asc"); 10209 } 10210 10211 $(".wcpt-sorting-" + new_order + "-icon", $sorting_icons).addClass( 10212 "wcpt-active", 10213 ); 10214 }, 10215 ); 10216 } 10217 10218 window.wcpt_instant_sort = function (params, $wcpt) { 10219 var sort_data = $wcpt.data("wcpt_sort_data"); 10220 10221 if (!params.order) { 10222 params.order = "asc"; 10223 } 10224 10225 params.order = params.order.toLowerCase(); 10226 10227 if (-1 !== $.inArray(params.orderby, ["price-desc", "rating"])) { 10228 params.order = "desc"; 10229 } 10230 10231 switch (params.orderby) { 10232 case "title": 10233 sort_data.sort(function (a, b) { 10234 return params.order == "asc" 10235 ? a.title.localeCompare(b.title) 10236 : b.title.localeCompare(a.title); 10237 }); 10238 10239 break; 10240 10241 case "sku": // as text 10242 sort_data.sort(function (a, b) { 10243 return params.order == "asc" 10244 ? a.sku.localeCompare(b.sku) 10245 : b.sku.localeCompare(a.sku); 10246 }); 10247 10248 break; 10249 10250 case "sku_num": 10251 sort_data.sort(function (a, b) { 10252 var a_sku_num = isNaN(parseFloat(a.sku)) ? 0 : parseFloat(a.sku), 10253 b_sku_num = isNaN(parseFloat(b.sku)) ? 0 : parseFloat(b.sku); 10254 10255 return params.order == "asc" 10256 ? a_sku_num - b_sku_num 10257 : b_sku_num - a_sku_num; 10258 }); 10259 10260 break; 10261 10262 case "menu_order": 10263 sort_data.sort(function (a, b) { 10264 return params.order == "asc" 10265 ? a.menu_order - b.menu_order 10266 : b.menu_order - a.menu_order; 10267 }); 10268 10269 break; 10270 10271 case "price": 10272 case "price-desc": 10273 sort_data.sort(function (a, b) { 10274 var a_price = a.price, 10275 b_price = b.price; 10276 10277 if (params.order == "asc" && a.min_price) { 10278 a_price = a.min_price; 10279 } 10280 10281 if (params.order == "desc" && a.max_price) { 10282 a_price = a.max_price; 10283 } 10284 10285 if (params.order == "asc" && b.min_price) { 10286 b_price = b.min_price; 10287 } 10288 10289 if (params.order == "desc" && b.max_price) { 10290 b_price = b.max_price; 10291 } 10292 10293 return params.order == "asc" ? a_price - b_price : b_price - a_price; 10294 }); 10295 10296 break; 10297 10298 case "meta_value": // as text 10299 sort_data.sort(function (a, b) { 10300 var key = "meta_value__" + params.meta_key, 10301 a_meta = a[key] == null ? "" : String(a[key]),
10302 b_meta = b[key] == null ? "" : String(b[key]); 10303 10304 return params.order == "asc" 10305 ? a_meta.localeCompare(b_meta) 10306 : b_meta.localeCompare(a_meta); 10307 }); 10308 10309 break; 10310 10311 case "meta_value_num": 10312 sort_data.sort(function (a, b) { 10313 var key = "meta_value__" + params.meta_key, 10314 a_meta = a[key], 10315 b_meta = b[key]; 10316 10317 var a_meta_num = isNaN(parseFloat(a_meta)) ? 0 : parseFloat(a_meta), 10318 b_meta_num = isNaN(parseFloat(b_meta)) ? 0 : parseFloat(b_meta); 10319 10320 return params.order == "asc" 10321 ? a_meta_num - b_meta_num 10322 : b_meta_num - a_meta_num; 10323 }); 10324 10325 break; 10326 10327 case "attribute": // as text 10328 sort_data.sort(function (a, b) { 10329 var key = "attribute__" + params.orderby_attribute, 10330 a_val = a[key] == null ? "" : String(a[key]), 10331 b_val = b[key] == null ? "" : String(b[key]); 10332 10333 return params.order == "asc" 10334 ? a_val.localeCompare(b_val) 10335 : b_val.localeCompare(a_val); 10336 }); 10337 10338 break; 10339 10340 case "attribute_num": // as number 10341 sort_data.sort(function (a, b) { 10342 var key = "attribute__" + params.orderby_attribute, 10343 a_val = a[key], 10344 b_val = b[key]; 10345 10346 var a_val_num = isNaN(parseFloat(a_val)) ? 0 : parseFloat(a_val), 10347 b_val_num = isNaN(parseFloat(b_val)) ? 0 : parseFloat(b_val); 10348 10349 return params.order == "asc" 10350 ? a_val_num - b_val_num 10351 : b_val_num - a_val_num; 10352 }); 10353 10354 break; 10355 10356 case "taxonomy": // as text 10357 sort_data.sort(function (a, b) { 10358 var key = "taxonomy__" + params.orderby_taxonomy, 10359 a_val = a[key] == null ? "" : String(a[key]), 10360 b_val = b[key] == null ? "" : String(b[key]); 10361 10362 return params.order == "asc" 10363 ? a_val.localeCompare(b_val) 10364 : b_val.localeCompare(a_val); 10365 }); 10366 10367 break; 10368 10369 case "brand": // product_brand taxonomy (same prop key as taxonomy sort) 10370 sort_data.sort(function (a, b) { 10371 var key = "taxonomy__product_brand", 10372 a_val = a[key] || "", 10373 b_val = b[key] || ""; 10374 10375 return params.order == "asc" 10376 ? a_val.localeCompare(b_val) 10377 : b_val.localeCompare(a_val); 10378 }); 10379 10380 break; 10381 10382 case "taxonomy_num": // as number 10383 sort_data.sort(function (a, b) { 10384 var a_val = a["taxonomy__" + params.orderby_taxonomy], 10385 b_val = b["taxonomy__" + params.orderby_taxonomy]; 10386 10387 var a_val_num = isNaN(parseFloat(a_val)) ? 0 : parseFloat(a_val), 10388 b_val_num = isNaN(parseFloat(b_val)) ? 0 : parseFloat(b_val); 10389 10390 return params.order == "asc" 10391 ? a_val_num - b_val_num 10392 : b_val_num - a_val_num; 10393 }); 10394 10395 break; 10396 10397 case "category": 10398 sort_data.sort(function (a, b) { 10399 return params.order == "asc" 10400 ? a.category.localeCompare(b.category) 10401 : b.category.localeCompare(a.category); 10402 }); 10403 10404 case "date": 10405 case "popularity": 10406 case "id": 10407 case "rating": 10408 sort_data.sort(function (a, b) { 10409 return params.order == "asc" 10410 ? a[params.orderby] - b[params.orderby] 10411 : b[params.orderby] - a[params.orderby]; 10412 }); 10413 10414 break; 10415 10416 default: 10417 break; 10418 } 10419 10420 // render 10421 $.each(sort_data, function (index, product_sort_data) { 10422 var $row = false; 10423 if (product_sort_data.variation_id) { 10424 // variation 10425 $row = $wcpt.find( 10426 '.wcpt-row[data-wcpt-variation-id="' + 10427 product_sort_data.variation_id + 10428 '"]', 10429 ); 10430 } else { 10431 // other 10432 $row = $wcpt.find( 10433 '.wcpt-row[data-wcpt-product-id="' + product_sort_data.id + '"]', 10434 ); 10435 } 10436 10437 $row.each(function () { 10438 var $this = $(this), 10439 $tbody = $this.closest("tbody"); 10440 $this.detach().appendTo($tbody); 10441 }); 10442 });
10443 10444 // -- group by category 10445 if ( 10446 wcpt_util.get_sc_attrs($wcpt)[ 10447 wcpt_util.get_device() + "_group_by_category" 10448 ] 10449 ) { 10450 $( 10451 $(".wcpt-row:not(.wcpt-row--category-heading)", $wcpt).get().reverse(), 10452 ).each(function () { 10453 var $this = $(this), 10454 cat_slug = $this.attr("data-wcpt-group-by-category-slug"), 10455 $category_heading = $this.siblings( 10456 '.wcpt-row--category-heading[data-wcpt-group-by-category-slug="' + 10457 cat_slug + 10458 '"]', 10459 ); 10460 $this.detach().insertAfter($category_heading); 10461 }); 10462 } 10463 10464 wcpt_util.assign_even_odd_row_classes($(".wcpt-table", $wcpt)); 10465 }; 10466})(jQuery); 10467 10468// module: download csv 10469(function ($) { 10470 $("body").on("click", ".wcpt-csv-download", function () { 10471 var $this = $(this), 10472 file_name = $this.attr("data-wcpt-file-name"), 10473 include_all_products = $this.attr("data-wcpt-csv-include-all-products"), 10474 $container = $this.closest(".wcpt"), 10475 encrypted_query_vars = $container.attr("data-wcpt-encrypted-query-vars"), 10476 encrypted_user_filters = $container.attr( 10477 "data-wcpt-encrypted-user-filters", 10478 ), 10479 encrypted_columns = $this.attr("data-wcpt-csv-encrypted-columns"), 10480 table_id = $this.closest(".wcpt").attr("data-wcpt-table-id"); 10481 10482 if ($this.hasClass("wcpt-disabled")) { 10483 return; 10484 } 10485 10486 $.ajax({
10487 url: wcpt_params.wc_ajax_url.replace("%%endpoint%%", "wcpt_get_csv"), 10488 method: "POST", // @todo: change to GET 10489 beforeSend: function () { 10490 $this.addClass("wcpt-disabled wcpt-loading"); 10491 }, 10492 data: { 10493 wcpt_csv_include_all_products: include_all_products, 10494 wcpt_csv_encrypted_query_vars: encrypted_query_vars, 10495 wcpt_csv_encrypted_user_filters: encrypted_user_filters, 10496 wcpt_csv_encrypted_columns: encrypted_columns, 10497 wcpt_csv_table_id: table_id, 10498 }, 10499 }).done(function (json_data) { 10500 $this.removeClass("wcpt-disabled wcpt-loading"); 10501 build_csv_and_download(json_data, file_name); 10502 }); 10503 }); 10504 10505 function build_csv_and_download(json_data, file_name) { 10506 var csv = ""; 10507 $.each(json_data, function (_, row) { 10508 $.each(row, function (_, val) { 10509 csv += val + ","; 10510 }); 10511 csv = csv.slice(0, -1) + "\n"; 10512 }); 10513 10514 var $pseudo_link = $("<a>", { 10515 href: "data:Application/octet-stream," + encodeURIComponent(csv), 10516 download: file_name + ".csv", 10517 }); 10518 10519 $pseudo_link.appendTo("body").get(0).click(); 10520 $pseudo_link.remove(); 10521 } 10522})(jQuery); 10523 10524// module: reveal sidebar 10525(function ($) { 10526 function get_reveal_sidebar_parts($from) { 10527 var $container = $from.closest(".wcpt"), 10528 $wrapper = $container.find(".wcpt-navigation-wrapper").first(), 10529 $button = $wrapper.find(".wcpt-header .wcpt-reveal-sidebar"), 10530 $sidebar = $wrapper.children(".wcpt-left-sidebar"); 10531 10532 return { 10533 $container: $container, 10534 $wrapper: $wrapper, 10535 $button: $button, 10536 $sidebar: $sidebar, 10537 }; 10538 } 10539 10540 function get_sidebar_filter_counts($sidebar) { 10541 var used = 0, 10542 total = 0; 10543 10544 if (!$sidebar || !$sidebar.length) { 10545 return { used: 0, total: 0 }; 10546 } 10547 10548 $sidebar.find(".wcpt-filter").each(function () { 10549 var $filter = $(this), 10550 filter = $filter.attr("data-wcpt-filter"), 10551 $checked = $filter.find("input[type=checkbox]:checked"), 10552 $selected = $filter.find("input[type=radio]:checked"), 10553 $min = $filter.find(".wcpt-range-input-min"), 10554 $max = $filter.find(".wcpt-range-input-max"), 10555 is_used = false; 10556 10557 if (filter === "sort_by" || filter === "results_per_page") { 10558 return; 10559 } 10560 10561 total += 1; 10562 10563 if ($checked.length) { 10564 is_used = true; 10565 } else if ($selected.length && $selected.attr("value")) { 10566 is_used = true; 10567 } else if ( 10568 $min.length && 10569 ($min.val() != $min.attr("min") || $max.val() != $max.attr("max")) 10570 ) { 10571 is_used = true; 10572 } 10573 10574 if (is_used) { 10575 used += 1; 10576 } 10577 }); 10578 10579 $sidebar.find(".wcpt-search").each(function () { 10580 var $search = $(this), 10581 keyword = ($search.find(".wcpt-search-input").val() || "").trim(); 10582 10583 total += 1; 10584 10585 if (keyword || $search.hasClass("wcpt-active")) { 10586 used += 1; 10587 } 10588 }); 10589 10590 return { used: used, total: total }; 10591 } 10592 10593 function update_reveal_sidebar_count($from) { 10594 var parts = get_reveal_sidebar_parts($from), 10595 $button = parts.$button, 10596 $sidebar = parts.$sidebar, 10597 $count = $button.find(".wcpt-active-count--reveal-sidebar"), 10598 counts; 10599 10600 if (!$button.length || !$count.length) { 10601 return; 10602 } 10603 10604 counts = get_sidebar_filter_counts($sidebar); 10605 $count 10606 .text(counts.total) 10607 .toggleClass("wcpt-active-count--zero", !counts.used); 10608 } 10609 10610 function save_reveal_sidebar_scroll($from) { 10611 var parts = get_reveal_sidebar_parts($from), 10612 $container = parts.$container, 10613 $sidebar = parts.$sidebar, 10614 sidebar, 10615 scrollTop, 10616 anchorEl, 10617 $filter, 10618 active; 10619 10620 if (!$container.length || !$sidebar.length || !$sidebar[0]) { 10621 return; 10622 } 10623 10624 sidebar = $sidebar[0]; 10625 scrollTop = sidebar.scrollTop; 10626 $container.data("wcpt-reveal-sidebar-scroll", scrollTop); 10627 10628 // Prefer the focused control; otherwise the first filter intersecting 10629 // the current viewport â used to re-align after content height shifts. 10630 active = document.activeElement; 10631 if (active && sidebar.contains(active)) { 10632 anchorEl = active.closest(".wcpt-filter, .wcpt-search"); 10633 } 10634 10635 if (!anchorEl) { 10636 $sidebar.find(".wcpt-filter, .wcpt-search").each(function () { 10637 if (this.offsetTop + this.offsetHeight > scrollTop) { 10638 anchorEl = this; 10639 return false; 10640 } 10641 }); 10642 } 10643 10644 if (!anchorEl) { 10645 $container.removeData("wcpt-reveal-sidebar-scroll-anchor"); 10646 return; 10647 } 10648 10649 $filter = $(anchorEl).closest(".wcpt-filter"); 10650 $container.data("wcpt-reveal-sidebar-scroll-anchor", { 10651 filter: $filter.attr("data-wcpt-filter") || "", 10652 taxonomy: $filter.attr("data-wcpt-taxonomy") || "", 10653 metaKey: 10654 $filter.attr("data-wcpt-meta-key") || 10655 $filter.attr("data-wcpt-meta_key") || 10656 "", 10657 isSearch: $(anchorEl).closest(".wcpt-search").length > 0, 10658 viewOffset: anchorEl.offsetTop - scrollTop, 10659 });
10660 } 10661 10662 function find_reveal_sidebar_scroll_anchor($sidebar, anchor) { 10663 var $match = $(); 10664 10665 if (!anchor) { 10666 return $match; 10667 } 10668 10669 if (anchor.isSearch) { 10670 return $sidebar.find(".wcpt-search").first(); 10671 } 10672 10673 if (!anchor.filter) { 10674 return $match; 10675 } 10676 10677 $match = $sidebar.find( 10678 '.wcpt-filter[data-wcpt-filter="' + anchor.filter + '"]', 10679 ); 10680 10681 if (anchor.taxonomy) { 10682 $match = $match.filter('[data-wcpt-taxonomy="' + anchor.taxonomy + '"]'); 10683 } 10684 10685 if (anchor.metaKey) { 10686 $match = $match.filter(function () { 10687 var $this = $(this); 10688 return ( 10689 $this.attr("data-wcpt-meta-key") === anchor.metaKey || 10690 $this.attr("data-wcpt-meta_key") === anchor.metaKey 10691 ); 10692 }); 10693 } 10694 10695 return $match.first(); 10696 } 10697 10698 function restore_reveal_sidebar_scroll($from) { 10699 var parts = get_reveal_sidebar_parts($from), 10700 $container = parts.$container, 10701 $sidebar = parts.$sidebar, 10702 scroll = $container.data("wcpt-reveal-sidebar-scroll"), 10703 anchor = $container.data("wcpt-reveal-sidebar-scroll-anchor"), 10704 $anchor, 10705 sidebar; 10706 10707 if (!$sidebar.length || !$sidebar[0] || typeof scroll === "undefined") { 10708 return; 10709 } 10710 10711 sidebar = $sidebar[0]; 10712 $anchor = find_reveal_sidebar_scroll_anchor($sidebar, anchor); 10713 10714 if ($anchor.length) { 10715 sidebar.scrollTop = Math.max(0, $anchor[0].offsetTop - anchor.viewOffset); 10716 } else { 10717 sidebar.scrollTop = scroll; 10718 } 10719 } 10720 10721 // Re-apply after layout settles â early restore can get clamped while the 10722 // panel's scrollHeight is still shrinking/growing, which drifts scroll up. 10723 function schedule_reveal_sidebar_scroll_restore($from) { 10724 var $container = $from.closest(".wcpt"); 10725 10726 $container.data("wcpt-reveal-sidebar-scroll-pending", true); 10727 restore_reveal_sidebar_scroll($container); 10728 10729 requestAnimationFrame(function () { 10730 restore_reveal_sidebar_scroll($container); 10731 requestAnimationFrame(function () { 10732 restore_reveal_sidebar_scroll($container); 10733 $container.removeData("wcpt-reveal-sidebar-scroll-pending"); 10734 }); 10735 }); 10736 } 10737 10738 function close_reveal_sidebar($from) { 10739 var parts = $from && $from.length ? get_reveal_sidebar_parts($from) : null, 10740 $wrappers = parts 10741 ? parts.$wrapper 10742 : $(".wcpt-navigation-wrapper--sidebar-open"); 10743 10744 $wrappers.each(function () { 10745 var $wrapper = $(this), 10746 $container = $wrapper.closest(".wcpt"); 10747 10748 $wrapper 10749 .removeClass("wcpt-navigation-wrapper--sidebar-open") 10750 .find("> .wcpt-left-sidebar") 10751 .attr("aria-hidden", "true"); 10752 $wrapper 10753 .find(".wcpt-header .wcpt-reveal-sidebar") 10754 .attr("aria-expanded", "false"); 10755 $container.data("wcpt-reveal-sidebar-open", false); 10756 }); 10757 10758 $(".wcpt-reveal-sidebar-backdrop").remove(); 10759 if (!$(".wcpt-navigation-wrapper--sidebar-open").length) { 10760 $("body").removeClass("wcpt-reveal-sidebar-on"); 10761 } 10762 } 10763 10764 function open_reveal_sidebar($from, skip_animation) { 10765 var parts = get_reveal_sidebar_parts($from), 10766 $container = parts.$container, 10767 $wrapper = parts.$wrapper, 10768 $button = parts.$button, 10769 $sidebar = parts.$sidebar; 10770 10771 if ( 10772 !$sidebar.length || 10773 !$button.length || 10774 $button.hasClass("wcpt-reveal-sidebar--unavailable") 10775 ) { 10776 return; 10777 } 10778 10779 // When restoring after a table refresh, open instantly (no slide-in) 10780 if (skip_animation) { 10781 $sidebar.addClass("wcpt-reveal-sidebar--no-anim"); 10782 } 10783 10784 $wrapper.addClass("wcpt-navigation-wrapper--sidebar-open"); 10785 $button.attr("aria-expanded", "true"); 10786 $sidebar.attr("aria-hidden", "false"); 10787 $container.data("wcpt-reveal-sidebar-open", true); 10788 10789 $("body").addClass("wcpt-reveal-sidebar-on"); 10790 if (!$(".wcpt-reveal-sidebar-backdrop").length) { 10791 $("body").append('<div class="wcpt-reveal-sidebar-backdrop"></div>'); 10792 } 10793 10794 if (skip_animation) { 10795 // force reflow so the "open" state applies without transition, then 10796 // restore transitions for subsequent interactions 10797 $sidebar[0].offsetHeight; 10798 $sidebar.removeClass("wcpt-reveal-sidebar--no-anim"); 10799 // HTML replace resets scroll; put the panel back where the user was 10800 schedule_reveal_sidebar_scroll_restore($container); 10801 } 10802 } 10803 10804 function init_reveal_sidebar() { 10805 var $container = $(this), 10806 parts = get_reveal_sidebar_parts($container), 10807 $wrapper = parts.$wrapper, 10808 $button = parts.$button, 10809 $sidebar = parts.$sidebar; 10810 10811 if (!$button.length) { 10812 $wrapper.removeClass( 10813 "wcpt-navigation-wrapper--reveal-sidebar wcpt-navigation-wrapper--sidebar-open", 10814 ); 10815 return; 10816 } 10817 10818 if (!$sidebar.length) { 10819 $button.addClass("wcpt-reveal-sidebar--unavailable"); 10820 return; 10821 } 10822 10823 var table_id = $container.attr("data-wcpt-table-id"), 10824 sidebar_id = "wcpt-reveal-sidebar-" + table_id; 10825 10826 $wrapper.addClass("wcpt-navigation-wrapper--reveal-sidebar"); 10827 $button.removeClass("wcpt-reveal-sidebar--unavailable").attr({ 10828 "aria-controls": sidebar_id, 10829 "aria-expanded": "false", 10830 });
10831 $sidebar.attr({ 10832 id: sidebar_id, 10833 "aria-hidden": "true", 10834 }); 10835 10836 update_reveal_sidebar_count($container); 10837 10838 // Keep sidebar open across filter refreshes (without re-animating) 10839 if ($container.data("wcpt-reveal-sidebar-open")) { 10840 open_reveal_sidebar($container, true); 10841 } 10842 } 10843 10844 $("body") 10845 .on("wcpt_before_ajax_container_replace", function (e, data) { 10846 if (data && data.$container && data.$container.length) { 10847 save_reveal_sidebar_scroll(data.$container); 10848 } 10849 }) 10850 .on("wcpt_layout", ".wcpt", function () { 10851 var $container = $(this); 10852 if ( 10853 $container.data("wcpt-reveal-sidebar-open") && 10854 $container.data("wcpt-reveal-sidebar-scroll-pending") 10855 ) { 10856 restore_reveal_sidebar_scroll($container); 10857 } 10858 }) 10859 .on("wcpt_after_every_load", ".wcpt", init_reveal_sidebar) 10860 .on("click", ".wcpt-header .wcpt-reveal-sidebar", function (e) { 10861 e.preventDefault(); 10862 open_reveal_sidebar($(this)); 10863 }) 10864 .on("click", ".wcpt-reveal-sidebar-backdrop", function () { 10865 close_reveal_sidebar($(".wcpt-navigation-wrapper--sidebar-open").first()); 10866 }) 10867 .on( 10868 "change input", 10869 ".wcpt-left-sidebar .wcpt-filter, .wcpt-left-sidebar .wcpt-search", 10870 function () { 10871 update_reveal_sidebar_count($(this)); 10872 }, 10873 ) 10874 .on("keydown", function (e) { 10875 if (e.key === "Escape") { 10876 close_reveal_sidebar(); 10877 } 10878 }); 10879 10880 $(function () { 10881 $(".wcpt").each(function () { 10882 init_reveal_sidebar.call(this); 10883 }); 10884 }); 10885})(jQuery); 10886 10887// module: infinite scroll 10888(function ($) { 10889 // controller 10890 // -- after_every_load handler - attach observer once 10891 $("body").on("wcpt_after_every_load", ".wcpt", function () { 10892 var $this__container = $(this), 10893 sc_attrs = wcpt_util.get_sc_attrs($this__container), 10894 $infinite_scroll_dots = $(".wcpt-infinite-scroll-dots", $this__container); 10895 10896 if (!$infinite_scroll_dots.length) { 10897 return; 10898 } 10899 10900 // intersection observer for load trigger 10901 wcpt_util.run_once_on_container( 10902 $this__container, 10903 "wcpt_infinite_scroll_init", 10904 function ($container) { 10905 const intersectionObserver = new IntersectionObserver( 10906 (entries) => { 10907 if (entries[0].intersectionRatio <= 0) return; 10908 10909 if ( 10910 !$container.hasClass( 10911 "wcpt-loading wcpt-infinite-scroll-loading-results", 10912 ) 10913 ) { 10914 $container.trigger("wcpt_infinite_scroll"); 10915 } 10916 }, 10917 { rootMargin: "100px" }, 10918 ); 10919 // start observing 10920 intersectionObserver.observe($infinite_scroll_dots[0]); 10921 $this__container.data( 10922 "wcpt-infinite-scroll-intersection-observer", 10923 intersectionObserver, 10924 ); 10925 }, 10926 ); 10927 10928 // fresh dots 10929 var intersection_observer = $this__container.data( 10930 "wcpt-infinite-scroll-intersection-observer", 10931 ); 10932 intersection_observer.observe($infinite_scroll_dots[0]); 10933 }); 10934 10935 // -- a separate trigger 'wcpt_infinite_scroll' that inits infinite scroll on $wcpt 10936 $("body").on("wcpt_infinite_scroll", ".wcpt", function () { 10937 var $this__container = $(this); 10938 append_next_page($this__container); 10939 }); 10940 10941 // -- helper: handler 10942 append_next_page = ($container) => { 10943 var table_id = wcpt_util.get_table_id($container), 10944 page_number = wcpt_util.get_current_page_number($container), 10945 new_query = `${table_id}_paged=${page_number + 1}`, 10946 append_new_query = true, 10947 purpose = "infinite_scroll", 10948 query = window.wcpt_build_ajax_query_string( 10949 $container, 10950 new_query, 10951 append_new_query, 10952 purpose, 10953 ); 10954 10955 window.wcpt_fetch_markup_and_apply_callback( 10956 $container, 10957 query, 10958 purpose, 10959 ajax_success__infinite_scroll, 10960 ); 10961 $container.removeClass("wcpt-loading"); 10962 $container.addClass("wcpt-infinite-scroll-loading-results"); 10963 }; 10964 10965 // ajax success handler 10966 function ajax_success__infinite_scroll(response, $container) { 10967 var $response = $(response), 10968 $new_container = $response.filter(".wcpt").first(); 10969 10970 if (!$new_container.length) { 10971 $new_container = $response.find(".wcpt").first(); 10972 } 10973 if (!$new_container.length) { 10974 $new_container = $response; 10975 } 10976 10977 var $new_rows = $(".wcpt-row", $new_container); 10978 $(".wcpt-table > tbody", $container).append($new_rows); 10979 10980 if ( 10981 typeof wcpt_util !== "undefined" && 10982 wcpt_util.merge_variable_switch_cf_from_response 10983 ) { 10984 wcpt_util.merge_variable_switch_cf_from_response( 10985 response, 10986 wcpt_util.get_table_id($container), 10987 ); 10988 } 10989 10990 if ( 10991 typeof wcpt_util !== "undefined" && 10992 wcpt_util.merge_variable_switch_templates_from_response 10993 ) { 10994 wcpt_util.merge_variable_switch_templates_from_response( 10995 $new_container, 10996 $container, 10997 ); 10998 } 10999 11000 $container.attr( 11001 "data-wcpt-query-string", 11002 $new_container.attr("data-wcpt-query-string"), 11003 ); 11004 $container.removeClass("wcpt-infinite-scroll-loading-results"); 11005 11006 // remove infinit scroll dots if results exhausted 11007 if (!$new_container.find(".wcpt-infinite-scroll-dots").length) { 11008 $container.find(".wcpt-infinite-scroll-dots").hide(); 11009 } 11010 11011 $("body").trigger("wcpt_infinite_scroll_after_load", { 11012 $container: $container, 11013 });
11014 } 11015})(jQuery); 11016 11017// module: freeze table 11018(function ($) { 11019 // @TODO - add init command here as well 11020 11021 // destroy previous instance before replacing the table 11022 $("body").on("wcpt_before_ajax_container_replace", (ev, data) => { 11023 var $freeze_table = wcpt_util.get_freeze_table(data.$container); 11024 if ($freeze_table) { 11025 $freeze_table.freezeTable("destroy"); 11026 } 11027 }); 11028 11029 $("body") 11030 .on("wcpt-nav-modal-on", function () { 11031 $(".frzTbl-table").freezeTable("pause"); 11032 }) 11033 .on("wcpt-nav-modal-off", function () { 11034 $(".frzTbl-table").freezeTable("unpause"); 11035 }); 11036})(jQuery); 11037 11038// module: form mode 11039(function ($) { 11040 // redirect to shop with correct table id in url params 11041 $("body").on("wcpt_before_ajax_query", function (e, data) { 11042 var sc_attrs = wcpt_util.get_sc_attrs(data.$wcpt), 11043 purpose = data.purpose, 11044 table_id = wcpt_util.get_table_id(data.$wcpt); 11045 11046 if (purpose == "filter" && sc_attrs.form_mode) { 11047 if ( 11048 sc_attrs.dynamic_filters_lazy_load || 11049 sc_attrs.dynamic_hide_filters || 11050 sc_attrs.dynamic_recount 11051 ) { 11052 var $apply = data.$wcpt.find(".wcpt-apply"); 11053 if ($apply.length && !$apply.hasClass("wcpt-apply--clicked")) { 11054 return; 11055 } 11056 } 11057 11058 // replace table id in query string 11059 data.query = data.query 11060 .split(table_id + "_") 11061 .join(wcpt_params.shop_table_id + "_"); 11062 11063 // don't let lang param repeat, lang=fr,fr error 11064 // var i = wcpt_params.shop_url.indexOf("?lang="); 11065 // if (i !== -1 && query.indexOf("lang=") !== -1) { 11066 // wcpt_params.shop_url = wcpt_params.shop_url.substring(0, i); 11067 // } 11068 11069 // add query string to shop url 11070 data.query = 11071 wcpt_params.shop_url + 11072 (wcpt_params.shop_url.indexOf("?") == -1 11073 ? data.query 11074 : "&" + data.query.slice(1)); 11075 11076 // hide form on submit 11077 if (sc_attrs.hide_form_on_submit) { 11078 data.query += "&hide_form=" + table_id; 11079 } 11080 11081 // disable device requirement 11082 data.query += "&" + wcpt_params.shop_table_id + "_device="; 11083 11084 // redirect to shop with correct table id in url params 11085 window.location = data.query; 11086 11087 // disable proceeding with ajax query 11088 data.proceed = false; 11089 } 11090 }); 11091})(jQuery); 11092 11093// module permission 11094// -- check if disabled 11095wcpt_is_module_disabled = function (module, $container) { 11096 var disabled_modules = $container.data("wcpt-disabled-modules"); 11097 if (!disabled_modules) { 11098 return false; 11099 } 11100 11101 var disabled = false; 11102 jQuery.each(disabled_modules, function (key, val) { 11103 if (val.module == module) { 11104 disabled = true; 11105 } 11106 }); 11107 11108 return disabled; 11109}; 11110 11111// -- disable 11112wcpt_disable_module = function (module, $container, source) { 11113 var disabled_modules = $container.data("wcpt-disabled-modules"); 11114 if (!disabled_modules) { 11115 disabled_modules = []; 11116 } 11117 11118 disabled_modules.push({ 11119 module: module, 11120 source: source, 11121 }); 11122 11123 $container.data("wcpt-disabled-modules", disabled_modules); 11124}; 11125 11126// -- permit 11127wcpt_permit_module = function (module, $container, source) { 11128 var disabled_modules = $container.data("wcpt-disabled-modules"); 11129 if (!disabled_modules) { 11130 disabled_modules = []; 11131 } 11132 11133 var i = disabled_modules.length; 11134 while (i) { 11135 --i; 11136 if ( 11137 disabled_modules[i].module == module && 11138 disabled_modules[i].source == source 11139 ) { 11140 disabled_modules.splice(i, 1); 11141 } 11142 } 11143 11144 $container.data("wcpt-disabled-modules", disabled_modules); 11145}; 11146 11147// util functions 11148(function ($) { 11149 window.wcpt_util = { 11150 get_table_id: ($wcpt) => { 11151 return $wcpt.attr("data-wcpt-table-id"); 11152 }, 11153 11154 get_current_page_number: ($wcpt) => { 11155 var params = wcpt_util.get_table_parsed_query($wcpt), 11156 table_id = wcpt_util.get_table_id($wcpt), 11157 sc_attrs = wcpt_util.get_sc_attrs($wcpt), 11158 current_page = params[`${table_id}_paged`] 11159 ? params[`${table_id}_paged`] 11160 : 1; 11161 current_page = parseInt(current_page, 10); 11162 11163 // Archive tables: fall back to the URL when PHP missed the page query var 11164 // (common on the WooCommerce shop page, which paginates via `page` not `paged`). 11165 if (current_page <= 1 && sc_attrs._archive) { 11166 if (wcpt_params.permalink_structure) { 11167 var regex = new RegExp( 11168 "\\/" + wcpt_params.pagination_slug + "\\/(\\d+)", 11169 ); 11170 var match = window.location.pathname.match(regex); 11171 if (match && match[1]) { 11172 current_page = parseInt(match[1], 10); 11173 } 11174 } else { 11175 var url_params = new URLSearchParams(window.location.search); 11176 if (url_params.has(wcpt_params.pagination_slug)) { 11177 current_page = parseInt( 11178 url_params.get(wcpt_params.pagination_slug), 11179 10, 11180 ); 11181 } 11182 } 11183 } 11184 11185 return current_page; 11186 }, 11187 11188 get_table_parsed_query: ($wcpt) => { 11189 var query_string = $wcpt.attr("data-wcpt-query-string"), 11190 _index = query_string.indexOf("?"), 11191 params = 11192 _index == -1 11193 ? {}
11194 : wcpt_util.parse_query_string(query_string.slice(_index + 1)); 11195 11196 return params; 11197 }, 11198 11199 parse_query_string: (query) => { 11200 // if first character is ? then remove it 11201 if (query.charAt(0) === "?") { 11202 query = query.slice(1); 11203 } 11204 11205 var vars = query.split("&"); 11206 var query_string = {}; 11207 for (var i = 0; i < vars.length; i++) { 11208 var pair = vars[i].split("="), 11209 key = decodeURIComponent(pair[0]), 11210 value = decodeURIComponent(pair[1]); 11211 11212 // If first entry with this name 11213 if (typeof query_string[key] === "undefined") { 11214 query_string[key] = value; 11215 11216 // If second entry with this name 11217 } else if (typeof query_string[key] === "string") { 11218 var arr = [query_string[key], value]; 11219 query_string[key] = arr; 11220 11221 // If third or later entry with this name 11222 } else { 11223 query_string[key].push(value); 11224 } 11225 } 11226 return query_string; 11227 }, 11228 11229 get_sc_attrs: ($container, refresh_cache) => { 11230 if (!refresh_cache) { 11231 if ($container.data("wcpt_sc_attrs")) { 11232 return $container.data("wcpt_sc_attrs"); 11233 } else { 11234 return {}; 11235 } 11236 } 11237 11238 var sc_attrs = {}; 11239 var sc_attrs_string = $container.attr("data-wcpt-sc-attrs"); 11240 if (sc_attrs_string && -1 == $.inArray(sc_attrs_string, ["[]", "{}"])) { 11241 sc_attrs = JSON.parse(sc_attrs_string); 11242 } 11243 $container.data("wcpt_sc_attrs", sc_attrs); // access sc attrs from here 11244 11245 return sc_attrs; 11246 }, 11247 11248 get_device: () => { 11249 // device 11250 var device = "laptop"; // default 11251 11252 if ($(window).width() <= wcpt_params.breakpoints.phone) { 11253 device = "phone"; 11254 } else if ($(window).width() <= wcpt_params.breakpoints.tablet) { 11255 device = "tablet"; 11256 } 11257 11258 return device; 11259 }, 11260 11261 get_uninit_rows: ($container, refresh_cache) => { 11262 if (!refresh_cache) { 11263 return $container.data("wcpt_new_rows"); 11264 } 11265 11266 var $new_rows = $container.find(".wcpt-row:not(.wcpt-row--init)"); 11267 $container.data("wcpt_new_rows", $new_rows); // access sc attrs from here 11268 11269 return $new_rows; 11270 }, 11271 11272 run_once_on_container: ($container, init_data_flag, callback) => { 11273 if (!$container.data(init_data_flag)) { 11274 callback($container); 11275 $container.data(init_data_flag, true); 11276 } 11277 }, 11278 11279 // legacy 11280 do_once_on_container: ($container, init_data_flag, callback) => { 11281 wcpt_util.run_once_on_container($container, init_data_flag, callback); 11282 }, 11283 11284 get_freeze_table: ($container) => { 11285 var $freeze_table = false; 11286 $container.find(".frzTbl-table").each(function () { 11287 var $this = $(this); 11288 if ($this.data("freezeTable")) { 11289 $freeze_table = $this; 11290 } 11291 }); 11292 return $freeze_table; 11293 }, 11294 11295 assign_even_odd_row_classes: ($table) => { 11296 var $rows = $( 11297 ".wcpt-row:not(.wcpt-child-row, .wcpt-row--category-heading):visible", 11298 $table, 11299 ), 11300 $child_rows = $(".wcpt-child-row", $table); 11301 11302 $rows.each(function () { 11303 var $this = $(this); 11304 11305 if ($rows.index($this) % 2) { 11306 $this.addClass("wcpt-even").removeClass("wcpt-odd"); 11307 } else { 11308 $this.addClass("wcpt-odd").removeClass("wcpt-even"); 11309 } 11310 }); 11311 11312 $child_rows.each(function () { 11313 var $this = $(this), 11314 $parent_row = $this.data("wcpt_parent_row"); 11315 11316 if ($parent_row.hasClass("wcpt-even")) { 11317 $this.removeClass("wcpt-odd").addClass("wcpt-even"); 11318 } else { 11319 // odd 11320 $this.removeClass("wcpt-even").addClass("wcpt-odd"); 11321 } 11322 }); 11323 }, 11324 11325 update_url: (query, $container) => { 11326 if (typeof window.history === "undefined") { 11327 return; 11328 } 11329 11330 // Get current URL and params 11331 const current_url = new URL(window.location.href); 11332 const new_params = new URLSearchParams(query); 11333 11334 // Start fresh with current non-table params 11335 const final_params = new URLSearchParams(); 11336 11337 // Copy over non-table related params from current URL
11338 current_url.searchParams.forEach((val, key) => { 11339 // Skip table-specific params (those with numeric prefix) 11340 if (!/^\d+_\w+/.test(key)) { 11341 // Only add if not in new_params 11342 if (!new_params.has(key)) { 11343 final_params.append(key, val); 11344 } 11345 } 11346 }); 11347 11348 // Add new params, filtering out unnecessary ones 11349 new_params.forEach((val, key) => { 11350 // Skip device params 11351 if (key.slice(-7) === "_device") { 11352 return; 11353 } 11354 11355 // Skip paged=1 params 11356 if (key.slice(-6) === "_paged" && val === "1") { 11357 return; 11358 } 11359 11360 // Add param (will override any existing) 11361 final_params.append(key, val); 11362 }); 11363 11364 // update pagination on archive pages 11365 const table_id = wcpt_util.get_table_id($container); 11366 const sc_attrs = wcpt_util.get_sc_attrs($container); 11367 11368 if (sc_attrs._archive) { 11369 // Archive page pagination (uses WP core pagination) 11370 const current_page = wcpt_util.get_current_page_number($container); 11371 11372 // if permalinks are like ?paged=2 then do that 11373 if (!wcpt_params.permalink_structure) { 11374 if (current_page > 1) { 11375 final_params.set("paged", current_page); 11376 } else { 11377 final_params.delete("paged"); 11378 } 11379 } else { 11380 if (current_page <= 1) { 11381 // Remove pagination from URL 11382 const regex = new RegExp( 11383 "\\/(?:page|" + wcpt_params.pagination_slug + ")\\/\\d+", 11384 ); 11385 current_url.pathname = current_url.pathname.replace(regex, ""); 11386 } else { 11387 // Check if URL already has /{pagination_slug}/ in it 11388 if ( 11389 current_url.pathname.includes( 11390 "/" + wcpt_params.pagination_slug + "/", 11391 ) 11392 ) { 11393 // Replace existing page number 11394 const regex = new RegExp( 11395 "\\/" + wcpt_params.pagination_slug + "\\/\\d+", 11396 ); 11397 current_url.pathname = current_url.pathname.replace( 11398 regex, 11399 "/" + wcpt_params.pagination_slug + "/" + current_page, 11400 ); 11401 } else { 11402 // Add /{pagination_slug}/N to end of URL before query params 11403 current_url.pathname = 11404 current_url.pathname.replace(/\/$/, "") + 11405 "/" + 11406 wcpt_params.pagination_slug + 11407 "/" + 11408 current_page; 11409 } 11410 } 11411 } 11412 11413 final_params.delete(`${table_id}_paged`); 11414 } 11415 11416 // Update URL 11417 current_url.search = final_params.toString(); 11418 11419 var data = { 11420 url: current_url.href, 11421 $container: $container, 11422 proceed: true, 11423 }; 11424 11425 $("body").trigger("wcpt_update_url", data); 11426 11427 if (data.proceed) { 11428 window.history.replaceState({}, "", data.url); 11429 } 11430 }, 11431 11432 remove_param_from_url: (param) => { 11433 if ( 11434 param && 11435 typeof window.history !== "undefined" && 11436 window.location.search 11437 ) { 11438 var url_object = new URL(window.location); 11439 url_object.searchParams.delete(param); 11440 history.replaceState({}, "", url_object.href); 11441 } 11442 }, 11443 11444 get_encrypted_attrs: ($container) => { 11445 const encrypted_attrs = {}; 11446 $container.each(function () { 11447 $.each(this.attributes, function () { 11448 if (this.specified && this.name.startsWith("data-wcpt-encrypted-")) { 11449 encrypted_attrs[this.name] = this.value; 11450 } 11451 }); 11452 }); 11453 return encrypted_attrs; 11454 }, 11455 11456 infinite_scroll_enabled: ($container) => { 11457 var sc_attrs = wcpt_util.get_sc_attrs($container); 11458 var device = wcpt_util.get_device($container); 11459 return sc_attrs[device + "_infinite_scroll"]; 11460 }, 11461 11462 merge_variable_switch_cf_from_response: (response, table_id) => { 11463 if (!response || !table_id) { 11464 return; 11465 } 11466 11467 var varName = "wcpt_" + table_id + "_variable_switch_cf", 11468 prefix = "var " + varName + " = ", 11469 text = typeof response === "string" ? response : "", 11470 idx = text.indexOf(prefix), 11471 jsonStr, 11472 newData; 11473 11474 if (idx === -1) { 11475 return; 11476 } 11477 11478 jsonStr = text.substring(idx + prefix.length).trim(); 11479 11480 if (jsonStr.charAt(jsonStr.length - 1) === ";") { 11481 jsonStr = jsonStr.slice(0, -1).trim(); 11482 } 11483 11484 idx = jsonStr.indexOf("</script>"); 11485 if (idx !== -1) { 11486 jsonStr = jsonStr.substring(0, idx).trim(); 11487 if (jsonStr.charAt(jsonStr.length - 1) === ";") { 11488 jsonStr = jsonStr.slice(0, -1).trim(); 11489 } 11490 } 11491
11492 try { 11493 newData = JSON.parse(jsonStr); 11494 } catch (e) { 11495 return; 11496 } 11497 11498 if (!newData || typeof newData !== "object") { 11499 return; 11500 } 11501 11502 if (typeof window[varName] === "undefined") { 11503 window[varName] = newData; 11504 return; 11505 } 11506 11507 $.each(newData, function (element_id, product_vals) { 11508 if ( 11509 typeof window[varName][element_id] === "undefined" || 11510 window[varName][element_id] === null 11511 ) { 11512 window[varName][element_id] = product_vals; 11513 } else if (product_vals && typeof product_vals === "object") { 11514 $.extend(window[varName][element_id], product_vals); 11515 } 11516 }); 11517 }, 11518 11519 // Merge price / availability <script type="text/template"> nodes from an 11520 // AJAX response into the live table. Needed for paginate-only swaps and 11521 // infinite scroll, which only append rows and would otherwise leave 11522 // variable-switch price resets without a template (blank prices). 11523 merge_variable_switch_templates_from_response: ($source, $container) => { 11524 if (!$source || !$source.length || !$container || !$container.length) { 11525 return; 11526 } 11527 11528 var $target_scope = $container.find(".wcpt-nav-main").first(); 11529 if (!$target_scope.length) { 11530 $target_scope = $container; 11531 } 11532 11533 $source 11534 .find( 11535 "script[data-wcpt-price-type], script[data-wcpt-availability-message]", 11536 ) 11537 .addBack( 11538 "script[data-wcpt-price-type], script[data-wcpt-availability-message]", 11539 ) 11540 .each(function () { 11541 var $tpl = $(this), 11542 element_id = $tpl.attr("data-wcpt-element-id"), 11543 price_type = $tpl.attr("data-wcpt-price-type"), 11544 availability_message = $tpl.attr("data-wcpt-availability-message"), 11545 selector; 11546 11547 if (!element_id) { 11548 return; 11549 } 11550 11551 if (price_type) { 11552 selector = 11553 'script[data-wcpt-element-id="' + 11554 element_id + 11555 '"][data-wcpt-price-type="' + 11556 price_type + 11557 '"]'; 11558 } else if (availability_message) { 11559 selector = 11560 'script[data-wcpt-element-id="' + 11561 element_id + 11562 '"][data-wcpt-availability-message="' + 11563 availability_message + 11564 '"]'; 11565 } else { 11566 return; 11567 } 11568 11569 if (!$container.find(selector).length) { 11570 $target_scope.append($tpl.clone()); 11571 } 11572 }); 11573 }, 11574 11575 format_price_figure: (price) => { 11576 price = parseFloat(price); 11577 11578 // remove decimal if it is unnecessary ie .00 11579 if (price !== parseInt(price)) { 11580 price = parseFloat(price).toFixed(wcpt_params.price_decimals); 11581 } else { 11582 price = parseInt(price); 11583 } 11584 11585 // decimal separator 11586 price = (price + "").replace(".", wcpt_params.price_decimal_separator); 11587 11588 // thousands separator 11589 var decimal_split = price.split(wcpt_params.price_decimal_separator); 11590 ((non_decimal = decimal_split[0].toString()), 11591 (decimal = decimal_split[1])); 11592 11593 if (non_decimal.length > 3) { 11594 formatted_non_decimal = 11595 non_decimal.slice(0, -3) + 11596 wcpt_params.price_thousand_separator + 11597 non_decimal.slice(-3); 11598 price = 11599 formatted_non_decimal + 11600 (decimal ? wcpt_params.price_decimal_separator + decimal : ""); 11601 } 11602 11603 return price; 11604 }, 11605 11606 reload_table: ($container) => { 11607 var $wcpt = $container.hasClass("wcpt") 11608 ? $container 11609 : $container.closest(".wcpt"); 11610 if (!$wcpt.length) return; 11611 var table_id = $wcpt.attr("data-wcpt-table-id"); 11612 // Clear cache for this table so reload fetches fresh data 11613 if (window.wcpt_cache && window.wcpt_cache.data) { 11614 $.each(window.wcpt_cache.data, function (key) { 11615 if ( 11616 key.indexOf("?" + table_id + "_") !== -1 || 11617 key.indexOf("&" + table_id + "_") !== -1 11618 ) { 11619 delete window.wcpt_cache.data[key]; 11620 } 11621 }); 11622 } 11623 // Reload with all current parameters (same as refresh_table flow) 11624 window.wcpt_attempt_ajax($wcpt, "", true, "refresh_table"); 11625 }, 11626 }; 11627})(jQuery); 11628 11629// Create a new event called wcptPressing 11630(function ($) { 11631 const holdDelay = 300; 11632 const moveThreshold = 10; 11633 const holdingInterval = 100;
11634 const activeHolds = new Map(); 11635 let currentMouseTarget = null; // Track the element where mousedown started 11636 11637 // Helper function to handle hold start 11638 function startHold(element, holdData) { 11639 const holdTimer = setTimeout(() => { 11640 const hold = activeHolds.get(holdData.target); 11641 if (hold) { 11642 element.trigger("wcptTouchHoldStart"); 11643 hold.hasStarted = true; 11644 11645 const holdingTimer = setInterval(() => { 11646 if (hold.hasStarted) { 11647 element.trigger("wcptPressing"); 11648 } 11649 }, holdingInterval); 11650 hold.holdingTimer = holdingTimer; 11651 } 11652 }, holdDelay); 11653 11654 activeHolds.set(holdData.target, { 11655 element, 11656 holdTimer, 11657 holdingTimer: null, 11658 startY: holdData.startY, 11659 hasStarted: false, 11660 }); 11661 } 11662 11663 // Helper function to handle hold end 11664 function endHold(target) { 11665 const hold = activeHolds.get(target); 11666 if (hold) { 11667 clearTimeout(hold.holdTimer); 11668 clearInterval(hold.holdingTimer); 11669 if (hold.hasStarted) { 11670 hold.element.trigger("wcptTouchHoldStop"); 11671 } 11672 activeHolds.delete(target); 11673 } 11674 } 11675 11676 // Touch Events 11677 $(document).on("touchstart", function (e) { 11678 const element = $(e.target); 11679 startHold(element, { 11680 target: e.target, 11681 startY: e.originalEvent.touches[0].clientY, 11682 }); 11683 }); 11684 11685 $(document).on("touchmove", function (e) { 11686 const touch = e.originalEvent.touches[0]; 11687 const hold = activeHolds.get(e.target); 11688 11689 if (hold) { 11690 const moveY = Math.abs(touch.clientY - hold.startY); 11691 if (moveY > moveThreshold) { 11692 endHold(e.target); 11693 } 11694 } 11695 }); 11696 11697 $(document).on("touchend touchcancel", function (e) { 11698 endHold(e.target); 11699 }); 11700 11701 // Mouse Events 11702 $(document).on("mousedown", function (e) { 11703 currentMouseTarget = e.target; // Store the initial target 11704 const element = $(currentMouseTarget); 11705 startHold(element, { 11706 target: currentMouseTarget, 11707 startY: e.clientY, 11708 }); 11709 }); 11710 11711 $(document).on("mousemove", function (e) { 11712 if (currentMouseTarget) { 11713 // Only check if we have an active mousedown 11714 const hold = activeHolds.get(currentMouseTarget); 11715 if (hold) { 11716 const moveY = Math.abs(e.clientY - hold.startY); 11717 if (moveY > moveThreshold) { 11718 endHold(currentMouseTarget); 11719 currentMouseTarget = null; 11720 } 11721 } 11722 } 11723 }); 11724 11725 $(document).on("mouseup", function (e) { 11726 if (currentMouseTarget) { 11727 endHold(currentMouseTarget); 11728 currentMouseTarget = null; 11729 } 11730 }); 11731 11732 // Handle cases where mouse leaves the document 11733 $(document).on("mouseleave", function (e) { 11734 if (currentMouseTarget) { 11735 endHold(currentMouseTarget); 11736 currentMouseTarget = null; 11737 } 11738 }); 11739})(jQuery); 11740 11741// woocommerce mini-cart block integration with MutationObserver 11742(function ($) { 11743 // Set up MutationObserver to watch for changes to data-cart-totals 11744 const miniCartElement = document.querySelector(".wc-block-mini-cart"); 11745 11746 if (miniCartElement) { 11747 // Keep track of the previous cart totals to detect actual changes 11748 let previousCartTotals = miniCartElement.getAttribute("data-cart-totals"); 11749 11750 // Create a new observer 11751 const observer = new MutationObserver(function (mutations) { 11752 mutations.forEach(function (mutation) { 11753 if ( 11754 mutation.type === "attributes" && 11755 mutation.attributeName === "data-cart-totals" 11756 ) { 11757 const newCartTotals = 11758 miniCartElement.getAttribute("data-cart-totals"); 11759 11760 // Only run wcpt_cart if the data-cart-totals value has changed 11761 if (newCartTotals !== previousCartTotals) { 11762 previousCartTotals = newCartTotals; 11763 if (typeof wcpt_cart === "function") { 11764 wcpt_cart(); 11765 } 11766 } 11767 } 11768 }); 11769 }); 11770 11771 // Start observing the mini cart element for attribute changes 11772 observer.observe(miniCartElement, { 11773 attributes: true, 11774 attributeFilter: ["data-cart-totals"], // Only watch the data-cart-totals attribute 11775 });
11776 } 11777 11778 function refreshCartStore() { 11779 if (window.wp && window.wp.data) { 11780 // Option 1: Invalidate the cart store's resolution cache and force a refresh 11781 const cartStore = window.wp.data.dispatch("wc/store/cart"); 11782 if (cartStore) { 11783 // First invalidate any cached data 11784 if (cartStore.invalidateResolutionForStore) { 11785 cartStore.invalidateResolutionForStore(); 11786 } 11787 11788 // Force a refresh by requesting cart data again 11789 window.wp.data.select("wc/store/cart").getCartData(true); // true forces bypass of cache 11790 11791 return true; 11792 } 11793 11794 // Option 2: Alternative approach using resolveSelect if the above doesn't work 11795 const resolver = 11796 window.wp.data && 11797 window.wp.data.controls && 11798 window.wp.data.controls.resolveSelect; 11799 if (resolver) { 11800 resolver("wc/store/cart", "getCartData", [true]); // Force refresh 11801 return true; 11802 } 11803 } 11804 } 11805 11806 $(document).on("wcpt_cart", function (e, data) { 11807 const wp = window.wp; 11808 if (!wp || !wp.data) return; 11809 11810 const cartStore = wp.data.select("wc/store/cart"); 11811 if (!cartStore || !cartStore.getCartData) return; 11812 11813 const cartData = cartStore.getCartData(); 11814 if (!cartData || !cartData.items) return; 11815 11816 const totalQuantity = cartData.items.reduce((total, item) => { 11817 return total + (item.quantity || 0); 11818 }, 0); 11819 11820 if (data.cart_quantity != totalQuantity) { 11821 refreshCartStore(); 11822 } 11823 }); 11824 11825 // window.wcptRefreshMiniCart = refreshMiniCart; 11826})(jQuery); 11827 11828// WP MediaElementJS audio player playing HTML class for WCPT tables 11829(function ($) { 11830 // Handle audio player initialization after WCPT content loads 11831 $("body").on("wcpt_after_every_load", ".wcpt", function () { 11832 var $new_rows = wcpt_util.get_uninit_rows($(this), true); 11833 11834 // Find all audio players in the newly loaded rows 11835 var $mejs_players = $new_rows.find(".wcpt-audio-player .mejs-container"); 11836 // Initialize each audio player 11837 initMejsPlayers($mejs_players); 11838 }); 11839 11840 // Helper function to initialize audio players 11841 function initMejsPlayers($mejs_players) { 11842 $mejs_players.each(function () { 11843 var $player = $(this); 11844 var playingClass = "wcpt-mejs-playing"; 11845 11846 // Get the native media element 11847 var mediaElement = $player.find("audio")[0]; 11848 11849 // For existing MediaElement.js instances 11850 if (mediaElement.player) { 11851 attachMediaEvents(mediaElement, $player, playingClass); 11852 } 11853 // For players that haven't been initialized yet 11854 else { 11855 // Wait for MediaElement to initialize 11856 $(document).on("MEJSready", function (e, mejsObj) { 11857 // Check if this is our audio element 11858 if (mejsObj && mejsObj.media === mediaElement) { 11859 attachMediaEvents(mediaElement, $player, playingClass); 11860 } 11861 }); 11862 11863 // Fallback to native media events 11864 attachMediaEvents(mediaElement, $player, playingClass); 11865 } 11866 }); 11867 } 11868 11869 // Helper function to attach media event handlers 11870 function attachMediaEvents(mediaElement, $player, playingClass) { 11871 if (!mediaElement || !$player.length) return; 11872 11873 // Add event listeners 11874 mediaElement.addEventListener("play", handlePlay); 11875 mediaElement.addEventListener("pause", handlePause); 11876 mediaElement.addEventListener("ended", handleEnded); 11877 11878 // Named event handlers so we can remove them if needed 11879 function handlePlay() { 11880 $player.addClass(playingClass); 11881 } 11882 11883 function handlePause() { 11884 $player.removeClass(playingClass); 11885 } 11886 11887 function handleEnded() { 11888 $player.removeClass(playingClass); 11889 } 11890 } 11891})(jQuery); 11892 11893// module: apply / reset navigation buttons 11894(function ($) { 11895 // apply button 11896 // -- trigger change 11897 $("body").on("click", ".wcpt-apply", function () { 11898 var $this = $(this); 11899 $this.addClass("wcpt-apply--clicked"); 11900 $(this).trigger("change"); 11901 }); 11902 // -- stop navigation if change came from anything else 11903 $("body").on("wcpt_after_navigation_change", ".wcpt", function (e, data) { 11904 var $container = $(this), 11905 sc_attrs = wcpt_util.get_sc_attrs($container), 11906 $apply = $(".wcpt-apply", $container); 11907 11908 if (!$apply.length) { 11909 return; 11910 } 11911 11912 // don't interrup: 11913 // -- form mode with adaptive filters 11914 if ( 11915 sc_attrs.form_mode && 11916 (sc_attrs.dynamic_filters_lazy_load || 11917 sc_attrs.dynamic_hide_filters || 11918 sc_attrs.dynamic_recount) 11919 ) { 11920 return; 11921 } 11922 // -- inputs marked for immediate filter 11923 if ( 11924 $(data.event.target).closest('[data-wcpt-nav-apply-immediately="true"]') 11925 .length 11926 ) { 11927 return; 11928 } 11929
11930 if (!$container.find(".wcpt-apply--clicked").length) { 11931 data.proceed = false; 11932 } 11933 }); 11934})(jQuery); 11935 11936// module: dynamic filters 11937(function ($) { 11938 // filters lazy load 11939 $("body").on("wcpt_after_every_load", ".wcpt", function () { 11940 var $container = $(this), 11941 sc_attrs = wcpt_util.get_sc_attrs($container), 11942 last_ajax_purpose = $container.data("wcpt_last_ajax_purpose"); 11943 11944 // check for shortcode attrs 11945 if ( 11946 last_ajax_purpose === "paginate" || 11947 !sc_attrs.dynamic_filters_lazy_load || 11948 (!sc_attrs.dynamic_recount && !sc_attrs.dynamic_hide_filters) 11949 ) { 11950 return; 11951 } 11952 11953 var encrypted_cache = $container.attr( 11954 "data-wcpt-encrypted-dynamic-filters-lazy-load-cache", 11955 ); 11956 11957 $.ajax({ 11958 url: wcpt_params.wc_ajax_url.replace( 11959 "%%endpoint%%", 11960 "wcpt__dynamic_filter__lazy_load", 11961 ), 11962 method: "POST", 11963 data: { 11964 wcpt_dynamic_filter_encrypted_cache: encrypted_cache, 11965 }, 11966 success: function (result) { 11967 var result = JSON.parse(result); 11968 11969 // append style and script 11970 $(result.style + result.script).appendTo($container); 11971 11972 var $filters = $(".wcpt-filter", $container); 11973 11974 var target_filters = [ 11975 "category", 11976 "attribute", 11977 "availability", 11978 "on_sale", 11979 "taxonomy", 11980 ]; 11981 11982 if ($container.data("wcpt_sc_attrs").dynamic_recount) { 11983 $filters.each(function () { 11984 var $filter = $(this), 11985 filter_type = $filter.attr("data-wcpt-filter"); 11986 if (!target_filters.includes(filter_type)) return true; 11987 11988 var taxonomy = false; 11989 if (["taxonomy", "attribute"].includes(filter_type)) { 11990 taxonomy = $filter.attr("data-wcpt-taxonomy"); 11991 } 11992 11993 result.options.forEach(function (option) { 11994 if (option.filter !== filter_type) return; 11995 if (taxonomy && option.taxonomy !== taxonomy) return; 11996 11997 var $option = $( 11998 '[data-wcpt-value="' + option.value + '"]', 11999 $filter, 12000 ); 12001 if (!$option.length) return; 12002 12003 var count = 12004 '<span class="wcpt-count">(' + option.count + ")</span>", 12005 $label = $( 12006 'label[data-wcpt-value="' + option.value + '"]', 12007 $filter, 12008 ), 12009 $icon = $label.children(".wcpt-icon"), 12010 $prev_count = $(".wcpt-count", $label); 12011 12012 $prev_count.remove(); // in case duplicate taxonomy filter is printed 12013 12014 if ($icon.length) { 12015 $icon.before(count); 12016 } else { 12017 $label.append(count); 12018 } 12019 }); 12020 }); 12021 } 12022 12023 $filters.removeClass("wcpt--dynamic-filters--loading-filter"); 12024 }, 12025 }); 12026 }); 12027})(jQuery); 12028 12029// module: connect with archive search 12030(function ($) { 12031 $("body").on("wcpt_before_ajax_query", function (e, data) { 12032 var $archive_search = data.$wcpt.find( 12033 ".wcpt-search--connect-with-archive input[type='search']", 12034 ); 12035 12036 if ($archive_search.length) { 12037 var current_keyword = $archive_search.val().trim(); 12038 var previous_keyword = wcpt_util.get_sc_attrs(data.$wcpt)._search; 12039 12040 if (current_keyword !== previous_keyword) { 12041 // redirect to archive search 12042 window.location.href = 12043 wcpt_params.shop_url + "?s=" + current_keyword + "&post_type=product"; 12044 data.proceed = false; 12045 } else { 12046 var search_param_name = $archive_search.attr("name"); 12047 var regex = new RegExp(search_param_name + "=[^&]*"); 12048 data.query = data.query.replace(regex, ""); 12049 } 12050 } 12051 }); 12052})(jQuery); 12053 12054// module: column stacking 12055(function ($) { 12056 // Remove all stacking markers and stacked values before reapplying 12057 function reset_column_stacking($container) { 12058 $container.find(".wcpt-cell-val--stacked").remove(); 12059 $container 12060 .find(".wcpt-cell--stacking,.wcpt-stack--hide") 12061 .removeClass("wcpt-cell--stacking wcpt-stack--hide"); 12062 } 12063 12064 function get_stacking_params($container) { 12065 var device = wcpt_util.get_device(); 12066 var last_device = $container.data("wcpt-stacking-last-device"); 12067 12068 if (last_device === device) { 12069 return false; 12070 } 12071 $container.data("wcpt-stacking-last-device", device); 12072 12073 var sc_attrs = wcpt_util.get_sc_attrs($container); 12074 var stacking_params = sc_attrs[device + "_stack_columns"]; 12075 return stacking_params 12076 ? stacking_params.split("|").map(function (name) { 12077 return name.trim(); 12078 }) 12079 : []; 12080 } 12081 12082 function stack_columns($container, stacking_params) { 12083 reset_column_stacking($container); 12084 if (!stacking_params.length) return; 12085 12086 // Get the name of the first column, so we can skip hiding its heading 12087 var $headingRow = $container.find(".wcpt-heading-row"); 12088 var $headingThs = $headingRow.children("th.wcpt-heading"); 12089 var firstColName = ( 12090 $headingThs.eq(0).attr("data-wcpt-column-name") || "" 12091 ).trim(); 12092 12093 // Hide the heading if it's a stacking column, except for the first column 12094 $headingThs.each(function (i) { 12095 var $th = $(this); 12096 var colName = ($th.attr("data-wcpt-column-name") || "").trim();
12097 // Do not hide heading if it's the first column, even if stacking includes it 12098 if (colName === firstColName) { 12099 return; // skip hiding the first heading 12100 } 12101 if (stacking_params.indexOf(colName) !== -1) { 12102 $th.addClass("wcpt-heading--stacking wcpt-stack--hide"); 12103 } 12104 }); 12105 12106 // For each row, merge stacked columns into previous column 12107 $container.find(".wcpt-row").each(function () { 12108 var $row = $(this); 12109 var $cells = $row.children("td.wcpt-cell"); 12110 // If stacking on the first column, just skip: don't stack or hide it 12111 for (var idx = $cells.length - 1; idx > 0; idx--) { 12112 var $cell = $($cells[idx]); 12113 var $prevCell = $($cells[idx - 1]); 12114 var colName = ($cell.attr("data-wcpt-column-name") || "").trim(); 12115 12116 // Do not apply stacking if this is the first column (idx==0 is never reached by this loop) 12117 if (colName === firstColName) { 12118 continue; // skip stacking and hiding for first column 12119 } 12120 12121 if (stacking_params.indexOf(colName) !== -1) { 12122 $cell.addClass("wcpt-cell--stacking wcpt-stack--hide"); 12123 // Clone contents to move 12124 var $val = $cell.children(".wcpt-cell-val").first(); 12125 if ($val.length && $prevCell.length) { 12126 // Create new stacked container and append the content (prevent duplicate IDs, avoid events) 12127 var $stackedDiv = $( 12128 "<div class='wcpt-cell-val wcpt-cell-val--stacked'></div>", 12129 ); 12130 $stackedDiv.append($val.contents().clone(true, true)); 12131 // Append to previous cell's .wcpt-cell-val 12132 var $prevVal = $prevCell.children(".wcpt-cell-val").first(); 12133 if ($prevVal.length) { 12134 $prevVal.append($stackedDiv); 12135 } 12136 } 12137 } 12138 } 12139 }); 12140 } 12141 12142 // On layout called, apply stacking 12143 $(document).on("wcpt_layout", ".wcpt", function (e, data) { 12144 var $wcpt = $(this); 12145 var stacking_params = get_stacking_params($wcpt); 12146 if (!stacking_params) return; 12147 stack_columns($wcpt, stacking_params); 12148 }); 12149})(jQuery); 12150 12151//module: list layout 12152(function ($) { 12153 $(document).on("wcpt_layout", ".wcpt", function () { 12154 var $container = $(this); 12155 12156 if (!$container.hasClass("wcpt-list-view")) { 12157 return; 12158 } 12159 12160 var table_id = wcpt_util.get_table_id($container), 12161 $table = $container.find(".wcpt-table"), 12162 $first_row = $table.find( 12163 "tbody tr:not(.wcpt-row--category-heading):first", 12164 ), 12165 css = ""; 12166 12167 if (!$table.length) { 12168 return; 12169 } 12170 12171 // remove existing css first 12172 $container.find("style.wcpt-list-view-column-widths").remove(); 12173 12174 // revert list to tabel mode to get dimensions 12175 $container.addClass("wcpt-list-view--calc"); 12176 12177 // get widths and vertical/horizontal alignment from first row cells 12178 var total_width = $first_row.outerWidth(); 12179 $first_row.find("td").each(function (index) { 12180 var width = ($(this).outerWidth() / total_width) * 100; 12181 var verticalAlign = $(this).css("vertical-align") || "middle"; 12182 var textAlign = $(this).css("text-align"); 12183 12184 var alignCSS = ""; 12185 // vertical alignment 12186 if (verticalAlign === "top") { 12187 alignCSS = "justify-content: flex-start;"; 12188 } else if (verticalAlign === "bottom") { 12189 alignCSS = "justify-content: flex-end;"; 12190 } else { 12191 alignCSS = "justify-content: center;"; 12192 } 12193 12194 // // horizontal alignment 12195 // if (textAlign === "left") { 12196 // alignCSS += " justify-content: flex-start;"; 12197 // } else if (textAlign === "right") { 12198 // alignCSS += " justify-content: flex-end;"; 12199 // } else if (textAlign === "center") { 12200 // alignCSS += " justify-content: center;"; 12201 // } 12202 12203 css += 12204 "#wcpt-" + 12205 table_id + 12206 ".wcpt-list-view :is(.wcpt-row, .wcpt-heading-row):not(.wcpt-row--category-heading) :is(.wcpt-cell, .wcpt-heading):nth-child(" + 12207 (index + 1) + 12208 ") { width: " + 12209 width.toFixed(2) + 12210 "% !important; " + 12211 alignCSS + 12212 " }\n"; 12213 });
12214 12215 // get border spacing between rows 12216 var border_spacing = parseInt($table.css("border-spacing").split(" ")[1]); 12217 css += 12218 "#wcpt-" + 12219 table_id + 12220 ".wcpt-list-view :is(.wcpt-row:not(:last-child), .wcpt-heading-row) { margin-bottom: " + 12221 border_spacing + 12222 "px; }\n"; 12223 12224 // add new css 12225 var $style = $('<style class="wcpt-list-view-column-widths"></style>'); 12226 $container.append($style); 12227 $style.html(css); 12228 12229 $container.removeClass("wcpt-list-view--calc"); 12230 }); 12231})(jQuery); 12232 12233// module: input height & width match with add to cart button 12234(function ($) { 12235 // range submit button height match 12236 $("body").on("wcpt_after_every_load", ".wcpt", function () { 12237 const $container = $(this), 12238 $range_submit_button = $container.find( 12239 ".wcpt-range-submit-button, .wcpt-date-picker-submit-button", 12240 ); 12241 12242 $range_submit_button.each(function () { 12243 const $button = $(this); 12244 const height = $button.siblings(".wcpt-range-input-min").outerHeight(); 12245 $button.outerHeight(height); 12246 }); 12247 }); 12248 12249 // quantity element size match with cart button 12250 $("body").on("wcpt_layout", ".wcpt", function () { 12251 const $container = $(this), 12252 $row = $container.find(".wcpt-row"); 12253 12254 $row.each(function () { 12255 const $row = $(this), 12256 $quantity_elements = $row.find(".wcpt-quantity"), 12257 $cart_button_elements = $row.find( 12258 ".wcpt-button[data-wcpt-link-code*='cart']", 12259 ); 12260 12261 if ($quantity_elements.length && $cart_button_elements.length) { 12262 $quantity_elements.each(function () { 12263 const $quantity = $(this); 12264 if ($quantity.hasClass("wcpt-cart-button-height-match")) { 12265 const height = $cart_button_elements.outerHeight(); 12266 $quantity.outerHeight(height); 12267 $quantity.find("select").outerHeight(height); 12268 } 12269 if ($quantity.hasClass("wcpt-cart-button-width-match")) { 12270 const width = $cart_button_elements.outerWidth(); 12271 $quantity 12272 .outerWidth(width) 12273 .css("box-sizing", "border-box !important"); 12274 $quantity.find("select").outerWidth(width); 12275 } 12276 }); 12277 } 12278 }); 12279 }); 12280})(jQuery); 12281 12282// module: search option above filter menu 12283(function ($) { 12284 $("body").on("wcpt_layout", ".wcpt", function () { 12285 var $this = $(this), 12286 $nav_dropdown = $(".wcpt-dropdown.wcpt-filter", $this); 12287 12288 $nav_dropdown.each(function () { 12289 var $this = $(this), 12290 $menu = $(".wcpt-dropdown-menu", $this); 12291 12292 if ( 12293 $this.hasClass("wcpt-filter--search-filter-options-enabled") && 12294 !$(".wcpt-search-filter-options", $this).length 12295 ) { 12296 var placeholder = $this.attr( 12297 "data-wcpt-search-filter-options-placeholder", 12298 ); 12299 12300 $menu.prepend( 12301 '<input type="search" class="wcpt-search-filter-options" placeholder="' + 12302 placeholder + 12303 '" />', 12304 ); 12305 12306 $menu.css("max-height", "none"); 12307 12308 $(".wcpt-search-filter-options", $menu) 12309 .nextAll() 12310 .wrapAll( 12311 $( 12312 '<div class="wcpt-search-filter-option-set" style="max-height: ' + 12313 $menu.css("max-height") + 12314 ';"></div>', 12315 ), 12316 ); 12317 } 12318 12319 // Always set max-height for option set 12320 var $optionSet = $(".wcpt-search-filter-option-set", $menu); 12321 if ($optionSet.length) { 12322 $menu.css("max-height", ""); 12323 $optionSet.css("max-height", $menu.css("max-height")); 12324 $menu.css("max-height", "none"); 12325 } 12326 }); 12327 }); 12328})(jQuery); 12329 12330// module: auto select products - add to cart checkbox 12331(function ($) { 12332 $("body").on("wcpt_after_every_load", ".wcpt", function () { 12333 const $container = $(this), 12334 sc_attrs = wcpt_util.get_sc_attrs($container); 12335 12336 if (!sc_attrs.auto_select_products) { 12337 return; 12338 } 12339 12340 const $checkbox = $container.find(".wcpt-cart-checkbox"); 12341 12342 $checkbox.prop("checked", true).trigger("change"); 12343 }); 12344})(jQuery); 12345 12346// module: pagination 12347(function ($) { 12348 // infinite scroll remove pagination archive slug & table param 12349 $("body").on("wcpt_update_url", function (e, data) { 12350 var $container = data.$container, 12351 table_id = wcpt_util.get_table_id($container), 12352 sc_attrs = wcpt_util.get_sc_attrs($container), 12353 page_param_key = table_id + "_paged", 12354 pagination_slug = wcpt_params.pagination_slug; 12355 12356 if (!wcpt_util.infinite_scroll_enabled($container)) { 12357 return; 12358 } 12359 12360 // Get current URL 12361 var current_url = new URL(data.url); 12362 var pathname = current_url.pathname; 12363 var search = current_url.search; 12364 12365 // Remove any pagination slug from URL 12366 if (sc_attrs && sc_attrs._archive) { 12367 // Remove WP core pagination slug from pathname 12368 pathname = pathname.replace( 12369 new RegExp("/" + pagination_slug + "/\\d+/?"), 12370 "/", 12371 ); 12372 12373 // Remove WP core pagination param from search 12374 search = search.replace( 12375 new RegExp("[?&]" + pagination_slug + "=\\d+"), 12376 "", 12377 ); 12378 } 12379 12380 // Remove custom pagination from search 12381 search = search.replace(new RegExp("[?&]" + page_param_key + "=\\d+"), ""); 12382 12383 data.url = pathname + search; 12384 }); 12385 12386 // reset pagination when ajax is disabled 12387 $("body").on("wcpt_before_nav_redirect", function (e, data) { 12388 if (data.purpose !== "paginate") { 12389 // If not paginating, need to reset to page 1 12390 var $container = data.$container, 12391 table_id = wcpt_util.get_table_id($container), 12392 sc_attrs = wcpt_util.get_sc_attrs($container), 12393 page_param_key = table_id + "_paged", 12394 pagination_slug = wcpt_params.pagination_slug; 12395 12396 // Remove any pagination slug from base_url 12397 if (sc_attrs._archive) { 12398 // Remove WP core pagination slug 12399 data.base_url = data.base_url.replace( 12400 new RegExp("/" + pagination_slug + "/\\d+/?"), 12401 "/", 12402 ); 12403 // Remove WP core pagination param 12404 data.query = data.query.replace( 12405 new RegExp("[?&]" + pagination_slug + "=\\d+"), 12406 "", 12407 ); 12408 } 12409 12410 // Remove custom pagination 12411 data.query = data.query.replace( 12412 new RegExp("[?&]" + page_param_key + "=\\d+"), 12413 "", 12414 ); 12415 } 12416 }); 12417 12418 // apply pagination - ajax disabled / enabled / archive 12419 $("body").on( 12420 "click",
12421 ".wcpt-pagination .page-numbers:not(.dots):not(.current)", 12422 function (e) { 12423 e.preventDefault(); 12424 var $this = $(this), 12425 $container = $this.closest(".wcpt"), 12426 table_id = wcpt_util.get_table_id($container), 12427 page_param_key = table_id + "_paged", 12428 sc_attrs = wcpt_util.get_sc_attrs($container), 12429 url = $this.attr("href"), 12430 pagination_slug = wcpt_params.pagination_slug, // 'page' or 'paged' 12431 page = 1, // Default to page 1 12432 query = "", 12433 append = true; 12434 12435 if (sc_attrs._archive) { 12436 if (sc_attrs.disable_ajax) { 12437 window.location.href = url; 12438 return; 12439 } 12440 12441 // Archive page pagination (uses WP core pagination) unless table delivered over AJAX 12442 if (wcpt_params.permalink_structure) { 12443 // Pretty permalinks: /shop/page/2/ 12444 var regex = new RegExp("/" + pagination_slug + "/(\\d+)"); 12445 var match = url.match(regex); 12446 if (match && match[1]) { 12447 page = parseInt(match[1], 10); 12448 } 12449 } else { 12450 // Plain permalinks: /?paged=2 12451 var index = url.indexOf("?"); 12452 var params = 12453 index === -1 12454 ? false 12455 : wcpt_util.parse_query_string(url.slice(index + 1)); 12456 if (params && params[pagination_slug]) { 12457 page = parseInt(params[pagination_slug], 10); 12458 } 12459 } 12460 } 12461 12462 if (!sc_attrs._archive || page == 1) { 12463 // Standard table pagination - uses custom query var like /?123_paged=2 12464 var index = url.indexOf("?"); 12465 var params = 12466 index === -1 12467 ? false 12468 : wcpt_util.parse_query_string(url.slice(index + 1)); 12469 12470 if (params && params[page_param_key]) { 12471 page = parseInt(params[page_param_key], 10); 12472 } 12473 } 12474 query = page_param_key + "=" + page; 12475 12476 window.wcpt_attempt_ajax($container, query, append, "paginate"); 12477 }, 12478 ); 12479})(jQuery); 12480 12481// module: persistent add-to-cart checkbox state across pages 12482(function ($) { 12483 // --------------------------------------------------------------------------- 12484 // state 12485 // --------------------------------------------------------------------------- 12486 12487 var memoryState = {}; 12488 var pendingRestoreState = {}; 12489 var suppressVariationPersistMutations = false; 12490 var isRestoring = false; 12491 12492 function getState() { 12493 return memoryState; 12494 } 12495 12496 function publishState() { 12497 window.wcpt_cart_checked_state = memoryState; 12498 } 12499 12500 // --------------------------------------------------------------------------- 12501 // helpers 12502 // --------------------------------------------------------------------------- 12503 12504 var QTY_SELECTOR = "input.qty, select.wcpt-qty-select"; 12505 12506 function getPersistedQtyInput($rowElements) { 12507 var $wcptQty = $rowElements 12508 .find(".wcpt-quantity input.qty, .wcpt-quantity select.wcpt-qty-select") 12509 .first(); 12510 12511 if ($wcptQty.length) { 12512 return $wcptQty; 12513 } 12514 12515 var $formQty = $rowElements.find(".variations_form input.qty").first(); 12516 if ($formQty.length) { 12517 return $formQty; 12518 } 12519 12520 return $rowElements.find(QTY_SELECTOR).first(); 12521 } 12522 12523 function rowKey($row) { 12524 return typeof window.wcpt_get_row_key === "function" 12525 ? window.wcpt_get_row_key($row) || "" 12526 : ""; 12527 } 12528 12529 function getRowElements($row) { 12530 if (typeof window.wcpt_get_sibling_rows === "function") { 12531 var $siblings = window.wcpt_get_sibling_rows($row); 12532 if ($siblings && $siblings.length) { 12533 return $siblings; 12534 } 12535 } 12536 return $row; 12537 } 12538 12539 function getPrimaryRow($row) { 12540 if (typeof window.wcpt_get_original_row === "function") { 12541 var $original = window.wcpt_get_original_row($row); 12542 if ($original && $original.length) { 12543 return $original; 12544 } 12545 } 12546 if ($row.closest("table").hasClass("frzTbl-clone-table")) { 12547 return $(); 12548 } 12549 return $row; 12550 } 12551 12552 function resolvePrimaryRow($row) { 12553 var $primary = getPrimaryRow($row); 12554 return $primary.length ? $primary : $row; 12555 } 12556 12557 function isVariableRow($row) { 12558 return ( 12559 $row.hasClass("wcpt-product-type-variable") || 12560 $row.attr("data-wcpt-type") == "variable" 12561 ); 12562 } 12563 12564 function getTableContext($row) { 12565 var $primaryRow = resolvePrimaryRow($row), 12566 $container = $primaryRow.closest(".wcpt"); 12567 12568 if (!$container.length || typeof wcpt_util === "undefined") { 12569 return null; 12570 } 12571 12572 var table_id = wcpt_util.get_table_id($container); 12573 if (!table_id) { 12574 return null; 12575 } 12576 12577 return { $row: $primaryRow, $container: $container, table_id: table_id }; 12578 } 12579 12580 function variationIdsMatch(a, b) { 12581 return a && b && String(a) === String(b); 12582 } 12583 12584 function releaseRestoreSuppress() { 12585 queueMicrotask(function () { 12586 suppressVariationPersistMutations = false; 12587 });
12588 } 12589 12590 function getVariationRowTarget($row) { 12591 var $target = isVariableRow($row) 12592 ? $row 12593 : $row.find(".wcpt-product-type-variable").first(); 12594 12595 return $target.length ? $target : $row; 12596 } 12597 12598 function findVariationInForm($form, variationId) { 12599 var variations = parseJsonAttr($form, "data-product_variations"), 12600 found = null; 12601 12602 $.each(variations || [], function (index, value) { 12603 if (variationIdsMatch(value.variation_id, variationId)) { 12604 found = value; 12605 return false; 12606 } 12607 }); 12608 12609 return found; 12610 } 12611 12612 function readFormAttributes($form) { 12613 var attributes = {}; 12614 12615 $form.find(".variations select").each(function () { 12616 var $select = $(this), 12617 name = $select.attr("name"); 12618 12619 if (name) { 12620 attributes[name] = $select.val(); 12621 } 12622 }); 12623 12624 return attributes; 12625 } 12626 12627 function isVariationPurchasable(variation) { 12628 return !!( 12629 variation && 12630 variation.is_purchasable && 12631 variation.is_in_stock && 12632 variation.variation_is_visible 12633 ); 12634 } 12635 12636 function eachVariationAttributeSelect($rows, fn) { 12637 $rows.find(".wcpt-select-variation-attribute-term").each(function () { 12638 var $select = $(this), 12639 name = $select.attr("data-wcpt-attribute"); 12640 12641 if (name) { 12642 fn($select, name); 12643 } 12644 }); 12645 12646 $rows.find(".variations_form .variations select").each(function () { 12647 var $select = $(this), 12648 name = $select.attr("name"); 12649 12650 if (name) { 12651 fn($select, name); 12652 } 12653 }); 12654 } 12655 12656 function buildSelectVariationMeta(meta, $form, variationId) { 12657 var variation = findVariationInForm($form, variationId); 12658 12659 if (variation) { 12660 meta.variation = variation; 12661 } 12662 12663 meta.attributes = readFormAttributes($form); 12664 meta.variation_id = String(variationId); 12665 meta.variation_found = true; 12666 meta.variation_selected = true; 12667 meta.variation_available = isVariationPurchasable(meta.variation); 12668 12669 return meta; 12670 } 12671 12672 function triggerSelectVariation($target, meta) { 12673 $target.trigger("select_variation", meta); 12674 } 12675 12676 function whenCartFormVariationReady( 12677 $primaryRow, 12678 $form, 12679 variationId, 12680 onReady, 12681 ) { 12682 var finished = false; 12683 12684 function finish() { 12685 if (finished) { 12686 return; 12687 } 12688 finished = true; 12689 $primaryRow.off(".wcptPersistRestore"); 12690 $form.off(".wcptPersistRestore"); 12691 onReady(); 12692 } 12693 12694 function isResolved() { 12695 return variationIdsMatch( 12696 $form.find("input.variation_id").val(), 12697 variationId, 12698 ); 12699 } 12700 12701 $primaryRow.on("select_variation.wcptPersistRestore", function (e, data) { 12702 if (variationIdsMatch(data && data.variation_id, variationId)) { 12703 finish(); 12704 } 12705 }); 12706 12707 $form.on( 12708 "woocommerce_variation_has_changed.wcptPersistRestore", 12709 function () { 12710 if (isResolved()) { 12711 finish(); 12712 } 12713 }, 12714 ); 12715 12716 $form.trigger("check_variations"); 12717 12718 if (isResolved()) { 12719 finish(); 12720 } 12721 } 12722 12723 function parseJsonAttr($el, attr) { 12724 var raw = $el.attr(attr); 12725 if (!raw) { 12726 return null; 12727 } 12728 try { 12729 return JSON.parse(raw); 12730 } catch (e) { 12731 return null; 12732 } 12733 } 12734 12735 function getRowVariationAttributes($row) { 12736 if ($row.hasClass("wcpt-product-type-variation")) { 12737 return parseJsonAttr($row, "data-wcpt-variation-attributes"); 12738 } 12739 12740 var attributes = $row.data("wcpt_attributes"); 12741 attributes = 12742 attributes && !$.isEmptyObject(attributes) 12743 ? $.extend({}, attributes) 12744 : {}; 12745 12746 eachVariationAttributeSelect($row, function ($select, name) { 12747 var val = $select.val(); 12748 if (val) { 12749 attributes[name] = val; 12750 } 12751 }); 12752 12753 return $.isEmptyObject(attributes) ? null : attributes; 12754 } 12755 12756 function findPersistedEntry( 12757 tableState, 12758 productId, 12759 variationId, 12760 variableProduct, 12761 ) { 12762 if (!tableState || !productId) { 12763 return null; 12764 } 12765 12766 if (variationId) { 12767 var variationKey = "v:" + String(variationId); 12768 if (tableState[variationKey]) { 12769 return { key: variationKey, entry: tableState[variationKey] }; 12770 } 12771 } 12772 12773 var foundKey = null; 12774 var foundEntry = null; 12775 12776 $.each(tableState, function (stateKey, entry) { 12777 if ( 12778 foundEntry || 12779 !entry || 12780 String(entry.product_id) !== String(productId) 12781 ) { 12782 return; 12783 } 12784 12785 if (variationId) { 12786 if (variationIdsMatch(entry.variation_id, variationId)) { 12787 foundKey = stateKey; 12788 foundEntry = entry; 12789 } 12790 } else if (variableProduct) { 12791 if (entry.variation_id) { 12792 foundKey = stateKey; 12793 foundEntry = entry; 12794 } 12795 } else if (!entry.variation_id) { 12796 foundKey = stateKey; 12797 foundEntry = entry; 12798 } 12799 });
12800 12801 return foundEntry ? { key: foundKey, entry: foundEntry } : null; 12802 } 12803 12804 function resolveRestoreKey(match, productId) { 12805 if (match.key) { 12806 return match.key; 12807 } 12808 if (match.entry.variation_id) { 12809 return "v:" + String(match.entry.variation_id); 12810 } 12811 if (productId) { 12812 return "p:" + String(productId); 12813 } 12814 return ""; 12815 } 12816 12817 function findRestoreMatch(tableState, $row) { 12818 var productId = $row.attr("data-wcpt-product-id"), 12819 variationId = 12820 $row.attr("data-wcpt-variation-id") || $row.data("wcpt_variation_id"); 12821 12822 if (isVariableRow($row) && productId) { 12823 return findPersistedEntry(tableState, productId, false, true); 12824 } 12825 12826 var match = findPersistedEntry(tableState, productId, variationId); 12827 if (!match) { 12828 var key = rowKey($row); 12829 if (key && tableState[key]) { 12830 match = { key: key, entry: tableState[key] }; 12831 } 12832 } 12833 return match; 12834 } 12835 12836 // --------------------------------------------------------------------------- 12837 // entry build / apply 12838 // --------------------------------------------------------------------------- 12839 12840 function buildPersistedEntry($row) { 12841 var $rowElements = getRowElements($row), 12842 $qty = getPersistedQtyInput($rowElements), 12843 qtyVal = $qty.length ? parseFloat($qty.val()) : 1, 12844 unitPrice = parseFloat($row.attr("data-wcpt-price")); 12845 12846 if (isNaN(qtyVal) || qtyVal <= 0) { 12847 qtyVal = 1; 12848 } 12849 12850 if (isVariableRow($row)) { 12851 var variationData = $row.data("wcpt_variation"); 12852 if (variationData && !isNaN(parseFloat(variationData.display_price))) { 12853 unitPrice = parseFloat(variationData.display_price); 12854 } 12855 } 12856 12857 if (isNaN(unitPrice)) { 12858 unitPrice = 0; 12859 } 12860 12861 return { 12862 product_id: $row.attr("data-wcpt-product-id"), 12863 variation_id: String( 12864 $row.attr("data-wcpt-variation-id") || 12865 $row.data("wcpt_variation_id") || 12866 "", 12867 ), 12868 attributes: getRowVariationAttributes($row), 12869 qty: qtyVal, 12870 total: (unitPrice * qtyVal * 1e12) / 1e12, 12871 }; 12872 } 12873 12874 function applyPersistedQty($rowElements, qtyVal) { 12875 if (isNaN(qtyVal) || qtyVal <= 0) { 12876 return; 12877 } 12878 12879 var $targets = $rowElements 12880 .find(".wcpt-quantity input.qty, .wcpt-quantity select.wcpt-qty-select") 12881 .add($rowElements.find(".variations_form input.qty")); 12882 12883 if (!$targets.length) { 12884 $targets = $rowElements.find(QTY_SELECTOR); 12885 } 12886 12887 $targets.each(function () { 12888 var $qtyInput = $(this); 12889 $qtyInput.val(qtyVal); 12890 if (!$qtyInput.closest("form.cart").length) { 12891 $qtyInput.trigger("change"); 12892 } 12893 }); 12894 } 12895 12896 function applyPersistedQtyWhenReady($rowElements, qtyVal, onDone) { 12897 var finished = false; 12898 12899 function complete() { 12900 if (finished) { 12901 return; 12902 } 12903 finished = true; 12904 if (onDone) { 12905 onDone(); 12906 } 12907 } 12908 12909 function setQty() { 12910 applyPersistedQty($rowElements, qtyVal); 12911 } 12912 12913 if (isNaN(qtyVal) || qtyVal <= 0) { 12914 complete(); 12915 return; 12916 } 12917 12918 var $form = $rowElements.find(".variations_form").first(); 12919 if (!$form.length) { 12920 setQty(); 12921 complete(); 12922 return; 12923 } 12924 12925 // WC cart form re-renders qty on show_variation â apply after that settles 12926 $form.one( 12927 "show_variation.wcptPersistQty found_variation.wcptPersistQty", 12928 function () { 12929 setQty(); 12930 complete(); 12931 }, 12932 ); 12933 12934 queueMicrotask(setQty); 12935 12936 requestAnimationFrame(function () { 12937 setQty(); 12938 complete(); 12939 }); 12940 } 12941 12942 function applyPersistedCheckboxAndQty($row, entry) { 12943 if (!$row || !entry) { 12944 return; 12945 } 12946 12947 var $primaryRow = resolvePrimaryRow($row), 12948 $rowElements = getRowElements($primaryRow), 12949 qtyVal = parseFloat(entry.qty); 12950 12951 function checkAndApply() { 12952 var $enabledCb = $rowElements 12953 .find(".wcpt-cart-checkbox") 12954 .not(":disabled") 12955 .first(); 12956 12957 if (!$enabledCb.length) { 12958 return false; 12959 } 12960 12961 isRestoring = true; 12962 $primaryRow.trigger("_wcpt_checkbox_change", true); 12963 12964 if (!isNaN(qtyVal) && qtyVal > 0) { 12965 applyPersistedQtyWhenReady($rowElements, qtyVal, function () { 12966 isRestoring = false; 12967 }); 12968 } else { 12969 isRestoring = false; 12970 } 12971 12972 return true; 12973 } 12974
12975 if (checkAndApply()) { 12976 return; 12977 } 12978 12979 $primaryRow.one("select_variation.wcptPersistCheckbox", function (e, data) { 12980 if (data && data.variation_selected && data.variation_id) { 12981 checkAndApply(); 12982 } 12983 }); 12984 } 12985 12986 // --------------------------------------------------------------------------- 12987 // state mutations 12988 // --------------------------------------------------------------------------- 12989 12990 function syncContainerMirror($container, table_id) { 12991 $container.data( 12992 "wcpt_all_checked", 12993 memoryState[table_id] ? memoryState[table_id] : {}, 12994 ); 12995 } 12996 12997 function refreshFloatingTrigger() { 12998 if (typeof window.wcpt_checkbox_trigger_init === "function") { 12999 window.wcpt_checkbox_trigger_init(); 13000 } 13001 } 13002 13003 function commitTableState(table_id, $container) { 13004 if ( 13005 memoryState[table_id] && 13006 Object.keys(memoryState[table_id]).length === 0 13007 ) { 13008 delete memoryState[table_id]; 13009 } 13010 if ($container && $container.length) { 13011 syncContainerMirror($container, table_id); 13012 } 13013 publishState(); 13014 refreshFloatingTrigger(); 13015 } 13016 13017 function flushVisibleRows($container) { 13018 if (!$container || !$container.length || typeof wcpt_util === "undefined") { 13019 return; 13020 } 13021 13022 var flushedVariableProducts = {}; 13023 13024 $container.find(".wcpt-row").each(function () { 13025 var $primaryRow = getPrimaryRow($(this)); 13026 13027 if (!$primaryRow.length || !$primaryRow.data("wcpt_checked")) { 13028 return; 13029 } 13030 13031 if ( 13032 isVariableRow($primaryRow) && 13033 !$primaryRow.data("wcpt_variation_id") 13034 ) { 13035 return; 13036 } 13037 13038 var productId = $primaryRow.attr("data-wcpt-product-id"); 13039 if (productId && isVariableRow($primaryRow)) { 13040 if (flushedVariableProducts[productId]) { 13041 return; 13042 } 13043 flushedVariableProducts[productId] = true; 13044 } 13045 13046 saveRow($primaryRow); 13047 }); 13048 } 13049 13050 function pruneUncheckedVisibleEntries($container) { 13051 if (!$container || !$container.length || typeof wcpt_util === "undefined") { 13052 return; 13053 } 13054 13055 var table_id = wcpt_util.get_table_id($container); 13056 if (!table_id || !memoryState[table_id]) { 13057 return; 13058 } 13059 13060 var visibleProductIds = {}, 13061 checkedProductIds = {}; 13062 13063 $container.find(".wcpt-row").each(function () { 13064 var $primaryRow = getPrimaryRow($(this)); 13065 if (!$primaryRow.length) { 13066 return; 13067 } 13068 13069 var productId = $primaryRow.attr("data-wcpt-product-id"); 13070 if (!productId) { 13071 return; 13072 } 13073 13074 visibleProductIds[String(productId)] = true; 13075 13076 if ($primaryRow.data("wcpt_checked")) { 13077 checkedProductIds[String(productId)] = true; 13078 } 13079 }); 13080 13081 var tableState = memoryState[table_id], 13082 removed = false; 13083 13084 $.each(tableState, function (key, entry) { 13085 if (!entry || !entry.product_id) { 13086 return; 13087 } 13088 13089 var productId = String(entry.product_id); 13090 if (!visibleProductIds[productId] || checkedProductIds[productId]) { 13091 return; 13092 } 13093 13094 delete tableState[key]; 13095 removed = true; 13096 }); 13097 13098 if (removed) { 13099 commitTableState(table_id, $container); 13100 } 13101 } 13102 13103 function finalizeTablePage($container) { 13104 queueMicrotask(function () { 13105 if ($container && $container.length) { 13106 // pruneUncheckedVisibleEntries($container); 13107 13108 if (typeof wcpt_util !== "undefined") { 13109 var table_id = wcpt_util.get_table_id($container); 13110 if (table_id) { 13111 syncContainerMirror($container, table_id); 13112 } 13113 } 13114 } 13115 13116 releaseRestoreSuppress(); 13117 refreshFloatingTrigger(); 13118 }); 13119 } 13120 13121 function saveRow($row) { 13122 var ctx = getTableContext($row); 13123 if (!ctx) { 13124 return; 13125 } 13126 13127 $row = ctx.$row; 13128 13129 if (!$row.data("wcpt_checked")) { 13130 return; 13131 } 13132 13133 var key = rowKey($row); 13134 if (!key) { 13135 return; 13136 } 13137 13138 if (isVariableRow($row) && !$row.data("wcpt_variation_id")) { 13139 return; 13140 } 13141 13142 if (!memoryState[ctx.table_id]) { 13143 memoryState[ctx.table_id] = {}; 13144 } 13145 13146 if (isVariableRow($row)) { 13147 removePersistedEntriesForProduct( 13148 ctx.table_id, 13149 $row.attr("data-wcpt-product-id"), 13150 $row.data("wcpt_variation_id"), 13151 ctx.$container, 13152 true, 13153 ); 13154 } 13155 13156 memoryState[ctx.table_id][key] = buildPersistedEntry($row); 13157 commitTableState(ctx.table_id, ctx.$container); 13158 } 13159 13160 function removePersistedEntriesForProduct( 13161 table_id, 13162 productId, 13163 exceptVariationId, 13164 $container, 13165 skipPublish, 13166 ) { 13167 if (!table_id || !productId || !memoryState[table_id]) { 13168 return false; 13169 } 13170 13171 var tableState = memoryState[table_id], 13172 removed = false; 13173 13174 $.each(tableState, function (stateKey, entry) { 13175 if (!entry || String(entry.product_id) !== String(productId)) { 13176 return; 13177 } 13178 13179 if ( 13180 exceptVariationId && 13181 entry.variation_id && 13182 String(entry.variation_id) === String(exceptVariationId) 13183 ) { 13184 return; 13185 } 13186 13187 delete tableState[stateKey]; 13188 removed = true; 13189 });
13190 13191 if (!removed) { 13192 return false; 13193 } 13194 13195 if (!skipPublish) { 13196 commitTableState(table_id, $container); 13197 } 13198 return true; 13199 } 13200 13201 function removeRow($row) { 13202 if (isRestoring) { 13203 return; 13204 } 13205 13206 var ctx = getTableContext($row); 13207 if (!ctx || !memoryState[ctx.table_id]) { 13208 return; 13209 } 13210 13211 var key = rowKey(ctx.$row); 13212 if (!key || !memoryState[ctx.table_id][key]) { 13213 return; 13214 } 13215 13216 delete memoryState[ctx.table_id][key]; 13217 commitTableState(ctx.table_id, ctx.$container); 13218 } 13219 13220 function hasPersistedVariableProduct(table_id, productId) { 13221 if (!table_id || !productId) { 13222 return false; 13223 } 13224 13225 var state = pendingRestoreState[table_id] || memoryState[table_id] || null; 13226 13227 return !!findPersistedEntry(state, productId, false, true); 13228 } 13229 13230 function clearTable(table_id, $container) { 13231 if (memoryState[table_id]) { 13232 delete memoryState[table_id]; 13233 publishState(); 13234 } 13235 13236 if ($container && $container.length) { 13237 $container.data("wcpt_all_checked", {}); 13238 } 13239 refreshFloatingTrigger(); 13240 } 13241 13242 // --------------------------------------------------------------------------- 13243 // restore 13244 // --------------------------------------------------------------------------- 13245 13246 function getVariationMetaFromEntry($row, entry) { 13247 var $rows = getRowElements($row), 13248 variationId = String(entry.variation_id), 13249 meta = { 13250 variation_id: variationId, 13251 complete_match: true, 13252 attributes: entry.attributes ? $.extend({}, entry.attributes) : {}, 13253 variation: false, 13254 variation_found: true, 13255 variation_selected: true, 13256 variation_available: true, 13257 }, 13258 $option = $rows 13259 .find( 13260 '.wcpt-select-variation-dropdown option[value="' + variationId + '"]', 13261 ) 13262 .first(), 13263 $radioWrap = $option.length 13264 ? $() 13265 : $rows 13266 .find('.wcpt-variation-radio[value="' + variationId + '"]') 13267 .first() 13268 .closest(".wcpt-select-variation"), 13269 $source = $option.length ? $option : $radioWrap, 13270 $form = $rows.find(".variations_form").first(); 13271 13272 if ($source.length) { 13273 var attrs = parseJsonAttr($source, "data-wcpt-attributes"), 13274 variation = parseJsonAttr($source, "data-wcpt-variation"); 13275 13276 if (attrs) { 13277 meta.attributes = attrs; 13278 } 13279 if (variation) { 13280 meta.variation = variation; 13281 } 13282 meta.complete_match = $source.hasClass("wcpt-complete_match"); 13283 } 13284 13285 if ($form.length && ($.isEmptyObject(meta.attributes) || !meta.variation)) { 13286 var variation = findVariationInForm($form, variationId); 13287 13288 if (variation) { 13289 if ($.isEmptyObject(meta.attributes) && variation.attributes) { 13290 meta.attributes = $.extend({}, variation.attributes); 13291 } 13292 if (!meta.variation) { 13293 meta.variation = variation; 13294 } 13295 } 13296 } 13297 13298 return meta; 13299 } 13300 13301 function applyAttributeSelects($row, attributes) { 13302 if (!attributes) { 13303 return; 13304 } 13305 13306 eachVariationAttributeSelect( 13307 getRowElements($row), 13308 function ($select, name) { 13309 if (typeof attributes[name] !== "undefined") { 13310 $select.val(attributes[name]); 13311 } 13312 }, 13313 ); 13314 } 13315 13316 function rowNeedsPrepWait($row) { 13317 if (!isVariableRow($row)) { 13318 return false; 13319 } 13320 13321 return !!getRowElements($row).find( 13322 ".variations_form, .wcpt-select-variation-dropdown, .wcpt-variation-radio", 13323 ).length; 13324 } 13325 13326 function whenRowPrepComplete($row, callback) { 13327 var $primaryRow = resolvePrimaryRow($row); 13328 13329 if ($primaryRow.data("wcpt_prep_variation_complete")) { 13330 callback(); 13331 return; 13332 } 13333 13334 $primaryRow.one("wcpt_row_prep_variation_complete", callback); 13335 } 13336 13337 function selectPersistedVariation($row, entry, callback) { 13338 if (!entry || !entry.variation_id) { 13339 if (callback) { 13340 callback(); 13341 } 13342 return; 13343 } 13344 13345 var $primaryRow = resolvePrimaryRow($row), 13346 $rows = getRowElements($primaryRow), 13347 variationId = String(entry.variation_id), 13348 meta = getVariationMetaFromEntry($primaryRow, entry), 13349 $form = $rows.find(".variations_form").first(), 13350 $dropdown = $rows.find(".wcpt-select-variation-dropdown"), 13351 $radio = $rows.find('.wcpt-variation-radio[value="' + variationId + '"]'), 13352 $attrSelects = $rows.find(".wcpt-select-variation-attribute-term"), 13353 $target = getVariationRowTarget($primaryRow); 13354 13355 isRestoring = true; 13356 applyAttributeSelects($primaryRow, meta.attributes); 13357 13358 function finishVariationRestore() { 13359 isRestoring = false; 13360 if (callback) { 13361 callback(); 13362 } 13363 } 13364 13365 function triggerControl($control, setup) { 13366 setup(); 13367 $control.trigger("change"); 13368 finishVariationRestore(); 13369 } 13370 13371 if ($dropdown.length) { 13372 triggerControl($dropdown, function () { 13373 $dropdown.val(variationId); 13374 }); 13375 return; 13376 } 13377 13378 if ($radio.length) { 13379 triggerControl($radio, function () { 13380 $radio.prop("checked", true); 13381 }); 13382 return; 13383 } 13384 13385 if ($form.length) { 13386 whenCartFormVariationReady($primaryRow, $form, variationId, function () { 13387 triggerSelectVariation( 13388 $target, 13389 buildSelectVariationMeta(meta, $form, variationId), 13390 ); 13391 finishVariationRestore(); 13392 }); 13393 return; 13394 } 13395 13396 if ($attrSelects.length) { 13397 $target.one( 13398 "select_variation.wcptPersistRestore", 13399 finishVariationRestore, 13400 ); 13401 $attrSelects.trigger("change"); 13402 return; 13403 } 13404 13405 triggerSelectVariation($target, meta); 13406 finishVariationRestore(); 13407 } 13408 13409 function restorePersistedRow($row, entry, onComplete) { 13410 function runRestore() { 13411 if (isVariableRow($row) && entry.variation_id) { 13412 selectPersistedVariation($row, entry, onComplete); 13413 return; 13414 } 13415 13416 applyPersistedCheckboxAndQty($row, entry); 13417 if (onComplete) { 13418 onComplete(); 13419 } 13420 } 13421 13422 if (rowNeedsPrepWait($row)) { 13423 whenRowPrepComplete($row, runRestore); 13424 return; 13425 } 13426 13427 runRestore(); 13428 } 13429 13430 function restoreTablePage($container) { 13431 if (typeof wcpt_util === "undefined") { 13432 releaseRestoreSuppress(); 13433 return; 13434 } 13435 13436 var table_id = wcpt_util.get_table_id($container); 13437 if (!table_id) { 13438 releaseRestoreSuppress(); 13439 return; 13440 } 13441 13442 var tableState = 13443 pendingRestoreState[table_id] || 13444 (memoryState[table_id] 13445 ? $.extend(true, {}, memoryState[table_id]) 13446 : null); 13447
13448 delete pendingRestoreState[table_id]; 13449 13450 if (!tableState || $.isEmptyObject(tableState)) { 13451 finalizeTablePage($container); 13452 return; 13453 } 13454 13455 if (!memoryState[table_id]) { 13456 memoryState[table_id] = $.extend(true, {}, tableState); 13457 } 13458 13459 publishState(); 13460 13461 var restoredKeys = {}, 13462 restoredVariableProducts = {}, 13463 pendingRestores = 0; 13464 13465 function finishRestoreCycle() { 13466 pendingRestores--; 13467 if (pendingRestores <= 0) { 13468 finalizeTablePage($container); 13469 } 13470 } 13471 13472 $container.find(".wcpt-row").each(function () { 13473 var $primaryRow = getPrimaryRow($(this)); 13474 if (!$primaryRow.length) { 13475 return; 13476 } 13477 13478 var productId = $primaryRow.attr("data-wcpt-product-id"), 13479 match, 13480 restoreKey; 13481 13482 if (isVariableRow($primaryRow) && productId) { 13483 if (restoredVariableProducts[productId]) { 13484 return; 13485 } 13486 match = findPersistedEntry(tableState, productId, false, true); 13487 if (!match) { 13488 return; 13489 } 13490 restoredVariableProducts[productId] = true; 13491 restoreKey = "p:" + String(productId); 13492 } else { 13493 match = findRestoreMatch(tableState, $primaryRow); 13494 if (!match) { 13495 return; 13496 } 13497 restoreKey = resolveRestoreKey(match, productId); 13498 } 13499 13500 if (!restoreKey || restoredKeys[restoreKey]) { 13501 return; 13502 } 13503 13504 restoredKeys[restoreKey] = true; 13505 pendingRestores++; 13506 restorePersistedRow($primaryRow, match.entry, finishRestoreCycle); 13507 }); 13508 13509 if (pendingRestores <= 0) { 13510 finalizeTablePage($container); 13511 } 13512 } 13513 13514 function onVariationSelected($row, data) { 13515 var ctx = getTableContext($row); 13516 if (!ctx) { 13517 return; 13518 } 13519 13520 var productId = ctx.$row.attr("data-wcpt-product-id"); 13521 if (!productId) { 13522 return; 13523 } 13524 13525 var variationId = 13526 data && data.variation_id ? String(data.variation_id) : "", 13527 isSelected = !!(data && data.variation_selected && variationId); 13528 13529 if (isSelected) { 13530 var match = memoryState[ctx.table_id] 13531 ? findPersistedEntry(memoryState[ctx.table_id], productId, variationId) 13532 : null; 13533 13534 if (match) { 13535 applyPersistedCheckboxAndQty(ctx.$row, match.entry); 13536 return; 13537 } 13538 } 13539 13540 if (isRestoring || suppressVariationPersistMutations) { 13541 return; 13542 } 13543 13544 if (!isSelected) { 13545 removePersistedEntriesForProduct( 13546 ctx.table_id, 13547 productId, 13548 false, 13549 ctx.$container, 13550 ); 13551 return; 13552 } 13553 13554 removePersistedEntriesForProduct( 13555 ctx.table_id, 13556 productId, 13557 variationId, 13558 ctx.$container, 13559 ); 13560 13561 if (ctx.$row.data("wcpt_checked")) { 13562 saveRow(ctx.$row); 13563 } 13564 } 13565 13566 // --------------------------------------------------------------------------- 13567 // cart payload integration 13568 // --------------------------------------------------------------------------- 13569 13570 function aggregateOffPageIntoPayload(tableContainersById, payload) { 13571 if (typeof wcpt_util === "undefined" || !tableContainersById || !payload) { 13572 return; 13573 } 13574 13575 var products = payload.products || {}, 13576 variations = payload.variations || {}, 13577 attributes = payload.attributes || {}; 13578 13579 try { 13580 $.each(tableContainersById, function (table_id, $container) { 13581 if (!memoryState[table_id]) { 13582 return; 13583 } 13584 13585 $.each(memoryState[table_id], function (key, entry) { 13586 if (!entry || !entry.product_id) { 13587 return; 13588 } 13589 13590 var product_id = String(entry.product_id), 13591 variation_id = entry.variation_id 13592 ? String(entry.variation_id) 13593 : false, 13594 qty = parseFloat(entry.qty); 13595 13596 if (isNaN(qty) || qty <= 0) { 13597 qty = 1; 13598 } 13599 13600 var $candidate_rows = $container.find( 13601 '.wcpt-row[data-wcpt-product-id="' + product_id + '"]', 13602 ); 13603 13604 if (variation_id) { 13605 $candidate_rows = $candidate_rows.filter(function () { 13606 var $r = $(this); 13607 return ( 13608 $r.attr("data-wcpt-variation-id") === variation_id || 13609 String($r.data("wcpt_variation_id")) === variation_id 13610 ); 13611 }); 13612 } 13613 13614 var has_local_checked = $candidate_rows.filter(function () { 13615 return !!$(this).data("wcpt_checked"); 13616 }).length; 13617 13618 if (has_local_checked) { 13619 return; 13620 } 13621 13622 if (typeof products[product_id] === "undefined") { 13623 products[product_id] = qty; 13624 } else { 13625 products[product_id] += qty; 13626 } 13627 13628 if (variation_id) { 13629 if (!variations[product_id]) { 13630 variations[product_id] = {}; 13631 } 13632 if (!variations[product_id][variation_id]) { 13633 variations[product_id][variation_id] = qty; 13634 } else { 13635 variations[product_id][variation_id] += qty; 13636 } 13637 13638 if (entry.attributes) { 13639 attributes[variation_id] = entry.attributes; 13640 } 13641 } 13642 }); 13643 }); 13644 } catch (e) { 13645 // ignore aggregation failures 13646 } 13647 13648 payload.products = products; 13649 payload.variations = variations; 13650 payload.attributes = attributes; 13651 } 13652 13653 function clearTables(tableContainersById) { 13654 if (typeof wcpt_util === "undefined" || !tableContainersById) { 13655 return; 13656 } 13657 13658 $.each(tableContainersById, function (table_id, $container) { 13659 if (!table_id || !$container || !$container.length) { 13660 return; 13661 } 13662 clearTable(table_id, $container); 13663 });
13664 } 13665 13666 // --------------------------------------------------------------------------- 13667 // event bindings 13668 // --------------------------------------------------------------------------- 13669 13670 // save on check 13671 $("body").on("wcpt_checkbox_change", ".wcpt-row", function (e, state) { 13672 if (isRestoring || suppressVariationPersistMutations || !state) { 13673 return; 13674 } 13675 saveRow($(this)); 13676 }); 13677 13678 $("body").on("change", ".wcpt-cart-checkbox", function () { 13679 if (isRestoring || suppressVariationPersistMutations) { 13680 return; 13681 } 13682 var $cb = $(this); 13683 if (!$cb.prop("checked")) { 13684 removeRow($cb.closest(".wcpt-row")); 13685 } 13686 }); 13687 13688 // qty -> 0 removes persisted row (not wcpt_checkbox_change false â prep resets fire that too) 13689 $("body").on( 13690 "change", 13691 ".wcpt input.qty, .wcpt select.wcpt-qty-select", 13692 function () { 13693 if (isRestoring || suppressVariationPersistMutations) { 13694 return; 13695 } 13696 13697 var $this = $(this); 13698 if ($this.closest("form.cart").length) { 13699 return; 13700 } 13701 13702 var val = parseFloat($this.val()); 13703 if (isNaN(val) || val <= 0) { 13704 var $row = $this.closest(".wcpt-row"); 13705 if (typeof window.wcpt_get_original_row === "function") { 13706 $row = window.wcpt_get_original_row($row); 13707 } 13708 removeRow($row); 13709 return; 13710 } 13711 13712 var $row = $this.closest(".wcpt-row"); 13713 if (typeof window.wcpt_get_original_row === "function") { 13714 $row = window.wcpt_get_original_row($row); 13715 } 13716 13717 if (isVariableRow($row) && !$row.data("wcpt_variation_id")) { 13718 return; 13719 } 13720 13721 if ($row.data("wcpt_checked")) { 13722 saveRow($row); 13723 } 13724 }, 13725 ); 13726 13727 // flush checked rows before DOM is replaced (fast pagination) 13728 $("body").on("wcpt_before_ajax_container_replace", function (e, data) { 13729 if (!data || !data.$container) { 13730 return; 13731 } 13732 flushVisibleRows(data.$container); 13733 }); 13734 13735 // snapshot before prep_variation_options can clear persisted rows via qty reset 13736 $("body").on("wcpt_before_every_load", ".wcpt", function () { 13737 if (typeof wcpt_util === "undefined") { 13738 return; 13739 } 13740 13741 var table_id = wcpt_util.get_table_id($(this)); 13742 if (table_id && memoryState[table_id]) { 13743 pendingRestoreState[table_id] = $.extend(true, {}, memoryState[table_id]); 13744 } 13745 13746 suppressVariationPersistMutations = true; 13747 }); 13748 13749 $("body").on( 13750 "select_variation", 13751 ".wcpt-row.wcpt-product-type-variable", 13752 function (e, data) { 13753 var $row = $(this); 13754 queueMicrotask(function () { 13755 onVariationSelected($row, data); 13756 }); 13757 }, 13758 ); 13759 13760 // run after freeze-table layout so sibling rows exist for restore 13761 $("body").on("wcpt_layout", ".wcpt", function (e, data) { 13762 if (!data || data.source !== "after_every_load") { 13763 return; 13764 } 13765 13766 restoreTablePage($(this)); 13767 }); 13768 13769 $(document).on( 13770 "wcpt_clear_all_checked_for_table", 13771 function (e, table_id, $container) { 13772 clearTable(table_id, $container); 13773 }, 13774 ); 13775 13776 // --------------------------------------------------------------------------- 13777 // public API 13778 // --------------------------------------------------------------------------- 13779 13780 window.wcpt_cart_checkbox_persist = { 13781 getState: getState, 13782 hasPersistedVariableProduct: hasPersistedVariableProduct, 13783 aggregateOffPageIntoPayload: aggregateOffPageIntoPayload, 13784 clearTables: clearTables, 13785 }; 13786 13787 publishState(); 13788})(jQuery); 13789 13790// module: product compare 13791(function ($) { 13792 // ---- Compare State "API" ---- 13793 13794 // Separate cookie for WCPT native compare logic 13795 var WCPT_COMPARE_COOKIE = "wcpt_compare_products"; 13796 13797 // Get products in compare (array of strings) 13798 function getCompareIDs() { 13799 var val = getCookie(WCPT_COMPARE_COOKIE); 13800 if (!val || val === "null") return []; 13801 return val 13802 .split(",") 13803 .map(function (v) { 13804 return v.trim(); 13805 }) 13806 .filter(function (v) { 13807 return !!v; 13808 }); 13809 } 13810
13811 // Set products in compare (array of strings) 13812 function setCompareIDs(ids) { 13813 setCookie(WCPT_COMPARE_COOKIE, ids.join(","), 30); // 30 day expiry for native compare 13814 $(document).trigger("wcpt_compare_changed", [ids]); // Broadcast state change 13815 } 13816 13817 // Add/remove by id (API helpers) 13818 function addToCompare(id) { 13819 var ids = getCompareIDs(); 13820 id = String(id); 13821 if (ids.indexOf(id) === -1) { 13822 ids.push(id); 13823 setCompareIDs(ids); 13824 } 13825 } 13826 function removeFromCompare(id) { 13827 var ids = getCompareIDs(); 13828 id = String(id); 13829 var idx = ids.indexOf(id); 13830 if (idx !== -1) { 13831 ids.splice(idx, 1); 13832 setCompareIDs(ids); 13833 } 13834 } 13835 function isInCompare(id) { 13836 var ids = getCompareIDs(); 13837 return ids.indexOf(String(id)) !== -1; 13838 } 13839 function clearCompare() { 13840 setCompareIDs([]); 13841 } 13842 13843 // Expose public API 13844 window.wcptCompare = { 13845 getIDs: getCompareIDs, 13846 setIDs: setCompareIDs, 13847 add: addToCompare, 13848 remove: removeFromCompare, 13849 isIn: isInCompare, 13850 clear: clearCompare, 13851 }; 13852 13853 // --- Cookie utils 13854 function getCookie(name) { 13855 var nameEQ = name + "="; 13856 var ca = document.cookie.split(";"); 13857 for (var i = 0; i < ca.length; i++) { 13858 var c = ca[i].trim(); 13859 if (c.indexOf(nameEQ) === 0) 13860 return decodeURIComponent(c.substring(nameEQ.length)); 13861 } 13862 return ""; 13863 } 13864 13865 function setCookie(name, val, days) { 13866 var d = new Date(); 13867 d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000); 13868 var expires = "expires=" + d.toUTCString(); 13869 document.cookie = 13870 name + "=" + encodeURIComponent(val) + "; " + expires + "; path=/"; 13871 } 13872 13873 // --- UI STATE REFRESH --- 13874 13875 // Helper function to get product/variation ID from row 13876 function getProductIdFromRow($row) { 13877 // First check for variation_id (using .data() for variable products with selected variation) 13878 var variationId = $row.data("wcpt_variation_id"); 13879 if (variationId) { 13880 return String(variationId); 13881 } 13882 // Then check for data-wcpt-variation-id attribute (for variation products) 13883 var variationIdAttr = $row.attr("data-wcpt-variation-id"); 13884 if (variationIdAttr) { 13885 return String(variationIdAttr); 13886 } 13887 // Finally fall back to product ID 13888 var productId = $row.attr("data-wcpt-product-id"); 13889 if (productId) { 13890 return String(productId); 13891 } 13892 return ""; 13893 } 13894 13895 function updateCompareButtonStates(forceIDs) { 13896 // Optionally accept an override ids (for API parity/event-coalescing) 13897 var ids = forceIDs || getCompareIDs(); 13898 13899 $(".wcpt-compare-button").each(function () { 13900 var $btn = $(this); 13901 var $row = $btn.closest(".wcpt-row"); 13902 var id = getProductIdFromRow($row); 13903 var $count = $btn.find(".wcpt-compare-button__count"); 13904 var $input = $btn.find("input[type='checkbox']"); 13905 if (!id) return; 13906 13907 // Update button's data attribute to match current row state 13908 $btn.attr("data-wcpt-product-id", id); 13909 13910 var inCompare = ids.indexOf(id) !== -1; 13911 if (inCompare) { 13912 $btn.removeClass("wcpt-not-in-compare").addClass("wcpt-in-compare"); 13913 $input.prop("checked", true); 13914 } else { 13915 $btn.removeClass("wcpt-in-compare").addClass("wcpt-not-in-compare"); 13916 $input.prop("checked", false); 13917 } 13918 if ($count.length) { 13919 $count.text("(" + ids.length + ")"); 13920 } 13921 }); 13922 } 13923 13924 // ---- WCOSC SYNC API (completely separate from native) 13925 // When possible, keep woosc integration as *optionally* in sync for visuals 13926 function updateWooscButtonStatesSyncWithWCPT() { 13927 if (typeof woosc_vars == "undefined") return; 13928 // Set the WOOSC cookie value to our (WCPT) compare IDs 13929 var woosc_cookie_name = "woosc_products_" + woosc_vars.user_id; 13930 var wcpt_ids = getCompareIDs(); 13931 setCookie(woosc_cookie_name, wcpt_ids.join(","), 30); 13932 13933 // Use wcpt_ids for button state sync 13934 $(".woosc-btn").each(function () { 13935 var $btn = $(this); 13936 var id = $btn.attr("data-id"); 13937 if (wcpt_ids.indexOf(String(id)) !== -1) { 13938 $btn.addClass("woosc-btn-added woosc-added"); 13939 } else { 13940 $btn.removeClass("woosc-btn-added woosc-added"); 13941 } 13942 });
13943 } 13944 13945 // ---- UI/State Observers ---- 13946 13947 // Initial render/init: get compare state from cookie and update UI 13948 function initCompareUI() { 13949 updateCompareButtonStates(); 13950 updateWooscButtonStatesSyncWithWCPT(); 13951 } 13952 13953 $(function () { 13954 initCompareUI(); 13955 }); 13956 13957 // Also update on ajax table reloads 13958 $(document).on("wcpt_after_every_load", function () { 13959 updateCompareButtonStates(); 13960 updateWooscButtonStatesSyncWithWCPT(); 13961 }); 13962 13963 // Any time our compare state changes (API), refresh UI 13964 $(document).on("wcpt_compare_changed", function (e, ids) { 13965 if (!ids) ids = getCompareIDs(); 13966 updateCompareButtonStates(ids); 13967 updateWooscButtonStatesSyncWithWCPT(); 13968 // ^ Only for visual update, but NOT to modify woosc cookie. 13969 }); 13970 13971 // ---- Button Event Listeners ---- 13972 13973 // Main click handler: toggle state, update UI/state, NO interference with woosc 13974 $(document).on("click", ".wcpt-compare-button", function (e) { 13975 // If the event is from a checkbox (or woosc-integration), skip here 13976 if ( 13977 $(e.target).hasClass("wcpt-compare-button__external-link") || 13978 $(e.target).closest(".wcpt-compare-button__external-link").length || 13979 $(e.target).hasClass("woosc-btn") || 13980 $(e.target).closest(".woosc-btn").length || 13981 $(e.target).hasClass("wcpt-compare-button__icon") 13982 ) { 13983 return; 13984 } 13985 13986 var $btn = $(this); 13987 var $row = $btn.closest(".wcpt-row"); 13988 var id = getProductIdFromRow($row); 13989 if (!id) return; 13990 13991 // Update button's data attribute to match current row state 13992 $btn.attr("data-wcpt-product-id", id); 13993 13994 if (isInCompare(id)) { 13995 removeFromCompare(id); 13996 } else { 13997 addToCompare(id); 13998 setTimeout(function () { 13999 $(".woosc-btn.woosc-added").first().trigger("click"); 14000 }, 100); 14001 } 14002 // UI updates after wcpt_compare_changed event 14003 }); 14004 14005 // push changes from woosc to wcpt 14006 $(document).on("woosc_change_count", function (e, count) { 14007 var woosc_cookie_name = "woosc_products_" + woosc_vars.user_id; 14008 var woosc_compare_ids = getCookie(woosc_cookie_name) || []; 14009 14010 setCompareIDs( 14011 typeof woosc_compare_ids === "string" 14012 ? woosc_compare_ids.split(",") 14013 : woosc_compare_ids, 14014 ); 14015 updateCompareButtonStates(); 14016 }); 14017 14018 // Listen for select_variation event to update button ID and initialize state 14019 $(document).on("select_variation", ".wcpt-row", function (e, data) { 14020 var $row = $(this); 14021 var $btn = $row.find(".wcpt-compare-button"); 14022 14023 if (!$btn.length) return; 14024 14025 // Get the current ID from row (will prioritize variation_id if available) 14026 var id = getProductIdFromRow($row); 14027 14028 if (id) { 14029 // Update button's data attribute 14030 $btn.attr("data-wcpt-product-id", id); 14031 14032 // Initialize button state with current compare state 14033 var ids = getCompareIDs(); 14034 var inCompare = ids.indexOf(id) !== -1; 14035 var $count = $btn.find(".wcpt-compare-button__count"); 14036 var $input = $btn.find("input[type='checkbox']"); 14037 14038 if (inCompare) { 14039 $btn.removeClass("wcpt-not-in-compare").addClass("wcpt-in-compare"); 14040 $input.prop("checked", true); 14041 } else { 14042 $btn.removeClass("wcpt-in-compare").addClass("wcpt-not-in-compare"); 14043 $input.prop("checked", false); 14044 } 14045 14046 if ($count.length) { 14047 $count.text("(" + ids.length + ")"); 14048 } 14049 } 14050 }); 14051})(jQuery); 14052 14053// module: product quick view 14054(function ($) { 14055 $(document).on("click", ".wcpt-quick-view-button", function (e) { 14056 if ($(e.target).closest(".woosq-btn").length) { 14057 return; 14058 } 14059 14060 var $btn = $(this); 14061 $btn.find(".woosq-btn").click(); 14062 }); 14063})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.