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 656 $table.each(function () { 657 var column_count = $table.find(".wcpt-row").eq(0).children().length; 658 659 while (column_count) { 660 // check if all the cells in this column are empty 661 var $column_cells = $table.find( 662 ".wcpt-cell:nth-child(" + column_count + ")", 663 ); 664 665 if ($column_cells.filter(":empty").length == $column_cells.length) { 666 $column_cells 667 .add($table.find(".wcpt-heading:nth-child(" + column_count + ")")) 668 .addClass("wcpt-hide"); 669 } 670 671 --column_count; 672 } 673 }); 674 } 675 676 // lazy load 677 function lazy_load_start() { 678 if (!window.wcpt_lazy_loaded) { 679 $(".wcpt-lazy-load").each(function () { 680 var $this = $(this), 681 table_id = $this.attr("data-wcpt-table-id"), 682 // Prefer data-wcpt-query-string (set by wcpt_get_container_attributes). 683 // Fall back to legacy data-wcpt-query for older markup. 684 query = 685 $this.attr("data-wcpt-query-string") || 686 $this.attr("data-wcpt-query") || 687 ""; 688 689 if (query.charAt(0) === "?") { 690 query = query.substring(1); 691 } 692 693 $this 694 .addClass("wcpt wcpt-" + table_id) 695 .removeClass("wcpt-lazy-load") 696 .attr("id", "wcpt-" + table_id); 697 window.wcpt_attempt_ajax($this, query, false, "lazy_load"); 698 }); 699 window.wcpt_lazy_loaded = true; 700 } 701 } 702 703 // get rows including freeze 704 function get_product_rows($elm) { 705 var $row = $elm.closest(".wcpt-row"), 706 product_id = $row.attr("data-wcpt-product-id"), 707 variation_id = $row.attr("data-wcpt-variation-id"), 708 $scroll_wrapper = $elm.closest(".wcpt-table-scroll-wrapper-outer"), 709 row_selector; 710 711 if (variation_id) { 712 row_selector = 713 '[data-wcpt-variation-id="' + 714 variation_id + 715 '"].wcpt-row.wcpt-product-type-variation'; 716 } else { 717 row_selector = 718 '[data-wcpt-product-id="' + 719 product_id + 720 '"].wcpt-row:not(.wcpt-product-type-variation)'; 721 } 722 723 if ($scroll_wrapper && $scroll_wrapper.length) { 724 return $scroll_wrapper.find(row_selector); 725 } 726 727 return $(row_selector); 728 } 729 730 // button click listener 731 $("body").on("click", ".wcpt-button", button_click_handler); 732 733 function button_click_handler(e) { 734 var $button = $(this), 735 link_code = $button.attr("data-wcpt-link-code"), 736 $product_rows = get_product_rows($button), 737 product_id = $product_rows.attr("data-wcpt-product-id"), 738 is_variable = $product_rows.hasClass("wcpt-product-type-variable"), 739 complete_match = $product_rows.data("wcpt_complete_match"), 740 is_variation = $product_rows.hasClass("wcpt-product-type-variation"), 741 is_composite = $product_rows.hasClass("wcpt-product-type-composite"), 742 is_bundle = $product_rows.hasClass("wcpt-product-type-woosb"), 743 has_addons = $product_rows.hasClass("wcpt-product-has-addons"), 744 has_measurement = $product_rows.hasClass("wcpt-product-has-measurement"), 745 has_nyp = $product_rows.hasClass("wcpt-product-has-name-your-price"), 746 qty = "", 747 params = { 748 payload: { 749 products: {}, 750 variations: {}, 751 attributes: {}, 752 addons: {}, 753 measurement: {}, 754 nyp: {}, // name your price 755 }, 756 }; 757 758 if ($("body").hasClass("wcpt-photoswipe-visible")) { 759 e.preventDefault(); 760 return; 761 } 762 763 if ( 764 -1 !== 765 $.inArray(link_code, [ 766 "product_link", 767 "external_link", 768 "custom_field", 769 "custom_field_media_id", 770 "custom_field_acf", 771 "custom", 772 ]) 773 ) { 774 return; 775 } 776 777 e.preventDefault(); 778 779 // validation 780 // -- variable product 781 if (is_variable) { 782 var variation_found = $product_rows.data("wcpt_variation_found"), 783 variation_selected = $product_rows.data("wcpt_variation_selected"), 784 variation_available = $product_rows.data("wcpt_variation_available"), 785 variation_ops = $product_rows.data("wcpt_variation_ops"); 786 787 // if row has variation selection options but customer did not make selections, show error 788 if ( 789 variation_ops && 790 (!variation_selected || !variation_found || !variation_available) 791 ) { 792 return; 793 } 794 } 795 796 // -- disabled 797 if (!is_variable && $button.hasClass("wcpt-disabled")) { 798 return; 799 } 800 801 // -- name your price 802 if (has_nyp) {
803 var $nyp = get_nyp_input_element($product_rows); 804 if ($nyp.length) { 805 var error = false, 806 name = $nyp.attr("data-wcpt-product-name"), 807 min = $nyp.attr("min"), 808 max = $nyp.attr("max"); 809 810 if (!$nyp.val()) { 811 error = wcpt_nyp_error_message_templates["empty"]; 812 } else if (min && $nyp.val() < parseFloat(min)) { 813 error = wcpt_nyp_error_message_templates["minimum_js"].replace( 814 "%%MINIMUM%%", 815 woocommerce_nyp_format_price( 816 min, 817 woocommerce_nyp_params.currency_format_symbol, 818 true, 819 ), 820 ); 821 } else if (max && $nyp.val() > parseFloat(max)) { 822 error = wcpt_nyp_error_message_templates["maximum_js"].replace( 823 "%%MAXIMUM%%", 824 woocommerce_nyp_format_price( 825 max, 826 woocommerce_nyp_params.currency_format_symbol, 827 true, 828 ), 829 ); 830 } 831 832 if (error) { 833 alert(error); 834 return; 835 } 836 } 837 } 838 839 // prepare params 840 841 // -- quantity 842 var $wcpt_qty = $( 843 ".wcpt-quantity input.qty, .wcpt-quantity > select.wcpt-qty-select", 844 $product_rows, 845 ), 846 $wc_qty = $(".cart .qty", $product_rows); 847 848 if ($wc_qty.length) { 849 // from WooCommerce form's qty field 850 qty = $wc_qty.val(); 851 } 852 853 if ($wcpt_qty.length) { 854 // from WCPT's own qty element 855 var val = parseFloat($wcpt_qty.val()); 856 if (isNaN(val) || !parseFloat($wcpt_qty.val())) { 857 $wcpt_qty 858 .filter("input") 859 .first() 860 .each(function () { 861 var $this = $(this), 862 min = $this.attr("data-wcpt-min"); 863 864 $this.val(min); 865 limit_qty_controller($this.parent("wcpt-quantity")); 866 val = $this.val(); 867 }); 868 } 869 870 qty = val; 871 } 872 873 params.payload.products[product_id] = qty; 874 875 // -- addons (only when the addon UI is actually printed & visible in the row; 876 // select variation "attribute dropdowns" reuses cart_form markup and must not 877 // count as having addons already collected)
878 var has_inline_addons_form = wcpt_row_has_inline_addons_form($product_rows), 879 addons = {}; 880 if (has_addons && has_inline_addons_form) { 881 addons = wcpt_get_addons($product_rows); 882 if (!$.isEmptyObject(addons)) { 883 params.payload.addons[product_id] = addons; 884 } 885 } 886 887 // -- measurement 888 if (has_measurement) { 889 var measurement = wcpt_get_measurement($product_rows); 890 if (!$.isEmptyObject(measurement)) { 891 params.payload.measurement[product_id] = measurement; 892 } 893 } 894 895 // -- name your price 896 if (has_nyp) { 897 var nyp = wcpt_get_nyp($product_rows); 898 if (nyp) { 899 params.payload.nyp[product_id] = nyp; 900 } 901 } 902 903 // -- variation 904 if (is_variation) { 905 var variation_id = $product_rows.attr("data-wcpt-variation-id"), 906 variation_attributes = JSON.parse( 907 $product_rows.attr("data-wcpt-variation-attributes"), 908 ), 909 $missing_attribute_select = $( 910 ".wcpt-select-variation-attribute-term", 911 $product_rows, 912 ); 913 914 if ($missing_attribute_select.length) { 915 $missing_attribute_select.each(function () { 916 var $this = $(this), 917 attribute = $this.attr("data-wcpt-attribute"), 918 term = $this.val(); 919 920 if (term) { 921 variation_attributes[attribute] = term; 922 } 923 }); 924 } 925 926 if (typeof params.payload.variations[product_id] === "undefined") { 927 params.payload.variations[product_id] = {}; 928 } 929 params.payload.variations[product_id][variation_id] = qty; 930 params.payload.attributes[variation_id] = variation_attributes; 931 } else if ($product_rows.hasClass("wcpt-product-type-variable")) { 932 var variation_id = $product_rows.data("wcpt_variation_id"), 933 variation_attributes = $product_rows.data("wcpt_attributes"); 934 935 if (variation_id) { 936 if (typeof params.payload.variations[product_id] === "undefined") { 937 params.payload.variations[product_id] = {}; 938 } 939 params.payload.variations[product_id][variation_id] = qty; 940 } 941 942 if (variation_attributes) { 943 params.payload.attributes[variation_id] = variation_attributes; 944 } 945 } 946 947 // prepare 'ajax_data' (required for non-AJAX req, submit over POST) 948 var ajax_data = { 949 action: "wcpt_add_to_cart", 950 "add-to-cart": $product_rows.attr("data-wcpt-product-id"), 951 product_id: product_id, 952 quantity: qty, 953 }; 954 955 // -- addons 956 if (has_addons && has_inline_addons_form && !$.isEmptyObject(addons)) { 957 $.extend(ajax_data, addons); 958 } 959 960 // -- measurement (submit via post) 961 if (has_measurement) { 962 var measurement = wcpt_get_measurement($product_rows); 963 if (!$.isEmptyObject(measurement)) { 964 $.extend(ajax_data, measurement); 965 } 966 } 967 968 // -- name your price (submit via post) 969 if (has_nyp) { 970 var nyp = wcpt_get_nyp($product_rows); 971 if (nyp) { 972 ajax_data.nyp = nyp; 973 } 974 } 975 976 // -- variation (submit via post) 977 if (is_variable || is_variation) { 978 if (variation_id) { 979 ajax_data.variation_id = variation_id; 980 } 981 982 if (variation_attributes) { 983 $.extend(ajax_data, variation_attributes); 984 } 985 } 986 987 // receive notices from server? 988 ajax_data.return_notice = link_code == "cart_ajax"; 989 990 // modal required 991 if ( 992 is_composite || 993 is_bundle || 994 (is_variable && !complete_match) || 995 (is_variation && is_incomplete_variation(variation_attributes)) || 996 (has_addons && !has_inline_addons_form) || 997 (has_measurement && !params.payload.measurement[product_id]) || 998 (has_nyp && !params.payload.nyp[product_id]) 999 ) { 1000 // deploy modal immediately if it's in cache 1001 if (typeof window.wcpt_product_form[product_id] !== "undefined") { 1002 deploy_product_form_modal( 1003 window.wcpt_product_form[product_id], 1004 $button, 1005 ajax_data, 1006 ); 1007 1008 // else fetch modal from server and deploy 1009 } else { 1010 ajax_data.action = "wcpt_get_product_form_modal";
1011 ajax_data.lang = wcpt_i18n.lang; 1012 delete ajax_data["add-to-cart"]; 1013 1014 $.ajax({ 1015 url: wcpt_params.wc_ajax_url.replace( 1016 "%%endpoint%%", 1017 "wcpt_get_product_form_modal", 1018 ), 1019 method: "POST", 1020 beforeSend: function () { 1021 window.wcpt_modal__last_requested_product_id = product_id; 1022 deploy_loading_modal(); 1023 }, 1024 data: ajax_data, 1025 }).done(function (response) { 1026 window.wcpt_product_form[product_id] = response; 1027 1028 if (product_id === window.wcpt_modal__last_requested_product_id) { 1029 // skip if req. superseded 1030 $(".wcpt-product-form-loading-modal").trigger("wcpt_close"); // close loading modal 1031 deploy_product_form_modal(response, $button, ajax_data); 1032 } 1033 }); 1034 } 1035 1036 return false; 1037 } 1038 1039 // all required info already available, no need for modal 1040 if (link_code == "cart_ajax") { 1041 wcpt_cart(params); 1042 } else { 1043 submit_via_post($button.attr("href"), ajax_data); 1044 } 1045 } 1046 1047 function deploy_product_form_modal(markup, $button, ajax_data) { 1048 var $modal = $(markup); 1049 $modal.appendTo("body"); 1050 $("body").addClass("wcpt-modal-on"); 1051 prep_product_form($modal, $button, ajax_data); 1052 $("body").trigger("wcpt_product_modal_ready"); 1053 } 1054 1055 function deploy_loading_modal() { 1056 var $loading_modal = $($("#tmpl-wcpt-product-form-loading-modal").html()); 1057 $("body").append($loading_modal); 1058 $loading_modal.on("wcpt_close", function () { 1059 $loading_modal.remove(); 1060 }); 1061 } 1062 1063 function is_incomplete_variation(variation_attributes) { 1064 var is_incomplete_variation = false; 1065 $.each(variation_attributes, function (key, value) { 1066 if (!value) { 1067 is_incomplete_variation = true; 1068 return false; 1069 } 1070 }); 1071 1072 return is_incomplete_variation; 1073 } 1074 1075 function submit_via_post(href, data) { 1076 // redirect by form 1077 var $form = $( 1078 '<form method="POST" action="' + 1079 href + 1080 '" style="display: none;"></form>', 1081 ); 1082 $.each(data, function (key, val) { 1083 if (key == "action") return; // continue 1084 var $input = $('<input type="hidden" name="' + key + '" value="" />'); 1085 $input.val(val); 1086 $form.append($input); 1087 }); 1088 $form.append('<input type="hidden" name="wcpt_request" value="true" />'); 1089 $form.appendTo($("body")).submit(); 1090 } 1091 1092 function prep_product_form($modal, $button, pre_select) { 1093 var link_code = $button.attr("data-wcpt-link-code"), 1094 href = link_code == "cart_ajax" ? "" : $button.attr("href"); 1095 1096 $modal.on("wcpt_close", function () { 1097 $modal.remove(); 1098 $("body").removeClass("wcpt-modal-on"); 1099 }); 1100 1101 $(".cart", $modal).each(function () { 1102 var $form = $(this); 1103 1104 if ($form.hasClass("variations_form")) { 1105 $form.wc_variation_form(); 1106 } else { 1107 // simple product (probably with addon or measurement) 1108 $form.append( 1109 '<input name="add-to-cart" type="hidden" value="' + 1110 pre_select["product_id"] + 1111 '">', 1112 ); 1113 } 1114 1115 // init addons 1116 if (typeof WC_PAO === "object") { 1117 new WC_PAO.Form($form); 1118 } else { 1119 if ($.fn.init_addon_totals) { 1120 $form.init_addon_totals(); 1121 } 1122 if (typeof wcPaoInitAddonTotals === "object") { 1123 wcPaoInitAddonTotals.init($form); 1124 } 1125 } 1126 1127 // init measurement 1128 if (typeof wcpt_wc_mc_init_cart !== "undefined") { 1129 $form.each(wcpt_wc_mc_init_cart); 1130 } 1131 1132 // cart: name your price 1133 if (typeof jQuery.fn.wc_nyp_form !== "undefined") { 1134 $form.wc_nyp_form(); 1135 } 1136 1137 $form.attr("action", href); 1138 1139 $(".qty", $form).attr("autocomplete", "off"); 1140 1141 if (pre_select) { 1142 $.each(pre_select, function (key, val) { 1143 var $control = $form.find("[name='" + key + "']"); 1144 if ($control.is("input.qty")) { 1145 // working on input 1146 val = parseFloat(val); 1147 var min = $control.attr("min") 1148 ? parseFloat($control.attr("min")) 1149 : 0; 1150 var max = $control.attr("max") 1151 ? parseFloat($control.attr("max")) 1152 : false; 1153 1154 // respect min 1155 if (val < min || isNaN(val)) { 1156 val = min; 1157 } 1158 1159 // respect max 1160 if (max && val > max) { 1161 val = max; 1162 } 1163 } 1164 $control.val(val); 1165 }); 1166 } 1167 1168 // try and apply quantity on default variation 1169 if (pre_select.quantity) { 1170 $form.one("show_variation", function () { 1171 var $form_qty = $(".qty", $form), 1172 min = $form_qty.attr("min"), 1173 max = $form_qty.attr("max"); 1174 if ( 1175 (!min || min <= pre_select.quantity) && 1176 (!max || max >= pre_select.quantity) 1177 ) { 1178 $form_qty.val(pre_select.quantity); 1179 } 1180 }); 1181 } 1182 1183 if (link_code == "cart_ajax") { 1184 $form.on("submit", function (e) { 1185 e.preventDefault(); 1186 1187 var external_payload = {}; 1188 1189 $.each($form.serializeArray(), function (i, field) { 1190 if (typeof external_payload[field.name] === "undefined") { 1191 external_payload[field.name] = field.value; 1192 } else { 1193 // should be array 1194 if (typeof external_payload[field.name] !== "object") { 1195 external_payload[field.name] = [external_payload[field.name]]; 1196 } 1197 external_payload[field.name].push(field.value); 1198 } 1199 }); 1200 1201 wcpt_cart({ 1202 external_payload: external_payload, 1203 payload: { variation_form: true }, 1204 }); 1205 1206 $modal.trigger("wcpt_close"); 1207 }); 1208 } 1209 1210 // reset qty in row 1211 var $rows = wcpt_get_sibling_rows($button.closest(".wcpt-row")); 1212 $rows 1213 .find(".qty[data-wcpt-return-to-initial=1]") 1214 .val(0) 1215 .first() 1216 .trigger("change"); 1217 });
1218 } 1219 1220 function disable_button($button, add_condition) { 1221 if (add_condition) { 1222 $button.addClass(add_condition); 1223 } 1224 1225 $button.addClass("wcpt-disabled"); 1226 } 1227 1228 function enable_button($button, clear_condition) { 1229 if (clear_condition) { 1230 $button.removeClass(clear_condition); 1231 } 1232 1233 if ( 1234 // list of conditions 1235 !$button.hasClass("wcpt-all-variations-out-of-stock") && 1236 !$button.hasClass("wcpt-variation-out-of-stock") && 1237 !$button.hasClass("wcpt-no-variation-selected") && 1238 !$button.hasClass("wcpt-quantity-input-error") && 1239 !$button.hasClass("wcpt-out-of-stock") 1240 ) { 1241 $button.removeClass("wcpt-disabled"); 1242 } 1243 } 1244 1245 function loading_badge_on_button($button) { 1246 disable_button($button); 1247 if (!$button.find(".wcpt-cart-badge-refresh").length) { 1248 var svg = 1249 '<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>'; 1250 $button.append('<i class="wcpt-cart-badge-refresh">' + svg + "</i>"); 1251 } 1252 } 1253 1254 function add_count_badge_to_button(in_cart, $button) { 1255 if (!parseFloat(in_cart)) { 1256 $(".wcpt-cart-badge-number, .wcpt-cart-badge-refresh", $button).remove(); 1257 return; 1258 } 1259 1260 // if( $button.closest('.wcpt-row').hasClass('wcpt-sold-individually') ){ 1261 // in_cart = 'â'; 1262 // } 1263 1264 if (!$button.find(".wcpt-cart-badge-number").length) { 1265 $button.append('<i class="wcpt-cart-badge-number">' + in_cart + "</i>"); 1266 } else { 1267 $button.find(".wcpt-cart-badge-number").html(in_cart); 1268 } 1269 1270 if ($button.find(".wcpt-cart-badge-refresh").length) { 1271 $button.find(".wcpt-cart-badge-refresh").remove(); 1272 } 1273 } 1274 1275 // search 1276 // -- submit 1277 $("body").on("click", ".wcpt-search-submit", search_submit); 1278 $("body").on("keydown", ".wcpt-search-input", search_submit); 1279 function search_submit(e) { 1280 var $this = $(this), 1281 $search = $this.closest(".wcpt-search"), 1282 $input = $search.find(".wcpt-search-input"), 1283 table_id = $search.attr("data-wcpt-table-id"), 1284 $container = $("#wcpt-" + table_id), 1285 $nav_modal = $this.closest(".wcpt-nav-modal"), 1286 $nav = $this.closest(".wcpt-navigation"), 1287 keywords = $input.val().trim(), 1288 previous_keywords = $input.attr("data-wcpt-value"), 1289 query = $input.attr("name") + "=" + encodeURIComponent(keywords), 1290 $wrapper = $input.closest(".wcpt-search-wrapper"), 1291 append = !$wrapper.hasClass("wcpt-search--reset-others"); 1292 1293 if (previous_keywords === keywords) { 1294 return; 1295 } 1296 1297 if ( 1298 // submit button is clicked 1299 ($(e.target).closest(".wcpt-search-submit").length && 1300 e.type == "click") || // enter key pressed on input 1301 ($(e.target).is(".wcpt-search-input") && 1302 e.type == "keydown" && 1303 (e.keyCode == 13 || e.which == 13)) 1304 ) { 1305 if ($nav_modal.length) { 1306 $(".wcpt-nm-apply").click(); 1307 return; 1308 } 1309 1310 if (append) { 1311 $nav.trigger("change"); 1312 } else { 1313 // reset other filters 1314 attempt_ajax($container, query, append, "filter"); 1315 } 1316 1317 if ($nav_modal.length) { 1318 $nav_modal.trigger("wcpt_close"); 1319 } 1320 } 1321 } 1322 // -- clear 1323 $("body").on("click", ".wcpt-search-clear", function (e) { 1324 var $this = $(this), 1325 $search = $this.closest(".wcpt-search"), 1326 $input = $search.find(".wcpt-search-input"), 1327 table_id = $search.attr("data-wcpt-table-id"), 1328 $container = $("#wcpt-" + table_id), 1329 $nav_modal = $this.closest(".wcpt-nav-modal"), 1330 query = "&" + $input.attr("name") + "=", 1331 append = true; 1332 $input.val(""); 1333 1334 if ($nav_modal.length) { 1335 $(".wcpt-nm-apply").click(); 1336 return; 1337 } 1338 1339 attempt_ajax($container, query, append, "filter"); 1340 1341 if ($nav_modal.length) { 1342 $nav_modal.trigger("wcpt_close"); 1343 } 1344 }); 1345 1346 // download button - responsive fix 1347 if (wcpt_params.initial_device !== "laptop") { 1348 $("body").on("click", ".wcpt-button[download]", function (e) { 1349 e.preventDefault(); 1350 var $this = $(this), 1351 url = $this.attr("href"); 1352 1353 if (!url) { 1354 return; 1355 } 1356 1357 // Masked stream URLs: download in a hidden iframe to avoid a blank tab 1358 // and keep the real remote URL off the address bar. 1359 if (url.indexOf("wcpt_stream=") !== -1) { 1360 var iframe = document.createElement("iframe"); 1361 iframe.style.display = "none"; 1362 iframe.setAttribute("aria-hidden", "true"); 1363 iframe.src = url; 1364 document.body.appendChild(iframe); 1365 setTimeout(function () { 1366 if (iframe.parentNode) { 1367 iframe.parentNode.removeChild(iframe); 1368 } 1369 }, 120000); 1370 return; 1371 } 1372 1373 window.open(url, "_blank", false); 1374 });
1375 } 1376 1377 // dropdown / tooltip 1378 1379 // currently assigned trigger ev 1380 // -- default 1381 window.wcpt_global_tooltip_trigger_mode = "hover"; // hover (default) | click 1382 // -- switch to click 1383 $(window).on("touchstart", function () { 1384 window.wcpt_global_tooltip_trigger_mode = "click"; 1385 }); 1386 // -- switch to hover 1387 $(window).on("resize", function () { 1388 window.wcpt_global_tooltip_trigger_mode = "hover"; 1389 }); 1390 1391 var target_selector = 1392 ".wcpt-dropdown, .wcpt-tooltip:not(.wcpt-tooltip--inside-portal)", 1393 $body = $("body"); 1394 1395 $body.on("mouseenter", target_selector, dropdown_hover_open); 1396 $body.on("mouseleave", target_selector, dropdown_hover_close); 1397 $body.on("click", dropdown_touch_toggle); 1398 1399 function get_event_type_for_dropdown($dropdown) { 1400 var is_tooltip = !!$dropdown.closest(".wcpt-tooltip").length, 1401 is_in_sidebar = !!$dropdown.closest(".wcpt-left-sidebar").length; 1402 1403 // click ev type is forced 1404 if ( 1405 // -- in dropdown options 1406 $dropdown.hasClass("wcpt-tooltip--open-on-click") || 1407 // -- we are on touchscreen 1408 wcpt_global_tooltip_trigger_mode == "click" 1409 ) { 1410 return "click"; 1411 } 1412 1413 // filters in sidebar alway use click 1414 if (!is_tooltip && is_in_sidebar) { 1415 return "click"; 1416 } 1417 1418 return "hover"; 1419 } 1420 1421 function dropdown_hover_open(e) { 1422 var $this = $(this); 1423 1424 if ("hover" !== get_event_type_for_dropdown($this)) { 1425 return; 1426 } 1427 1428 // hover intent 1429 // if( $this.hasClass('wcpt-tooltip--hover-intent-enabled') ){ 1430 var clear_timeout = setTimeout(function () { 1431 // if( ! $this.is(':hover') ) return; 1432 $this.addClass("wcpt-open"); 1433 fix_tooltip_position($this); 1434 }, 200); 1435 1436 $this.data("wcpt_hover_intent_clear_timeout", clear_timeout); 1437 } 1438 1439 function dropdown_hover_close(e) { 1440 var $this = $(this); 1441 1442 if ("hover" !== get_event_type_for_dropdown($this)) { 1443 return; 1444 } 1445 1446 // hover intent 1447 var clear_timeout = $this.data("wcpt_hover_intent_clear_timeout"); 1448 if (clear_timeout) { 1449 clearTimeout(clear_timeout); 1450 } 1451 1452 $this.removeClass("wcpt-open"); 1453 } 1454 1455 function dropdown_touch_toggle(e) { 1456 var $target = $(e.target), 1457 container_selector = ".wcpt-dropdown, .wcpt-tooltip", 1458 content_selector = 1459 "> .wcpt-dropdown-menu, > .wcpt-tooltip-content-wrapper > .wcpt-tooltip-content", 1460 $dropdown = $target.closest(container_selector), 1461 $content = $dropdown.find(content_selector), 1462 $body = $("body"); 1463 1464 // clicked outside any tootltip / filter 1465 if (!$dropdown.length) { 1466 // close all dropdowns or tooltips not in nav modal or sidebar 1467 if (!$target.closest(".wcpt-nav-modal").length) { 1468 $body.find(container_selector).each(function () { 1469 if ( 1470 $(this).hasClass("wcpt-tooltip") || 1471 (!$(this).closest(".wcpt-nav-modal").length && 1472 !$(this).closest(".wcpt-left-sidebar").length) 1473 ) { 1474 $(this).removeClass("wcpt-open"); 1475 } 1476 }); 1477 } 1478 1479 return; 1480 } 1481 1482 if ("click" !== get_event_type_for_dropdown($dropdown)) { 1483 return; 1484 } 1485 1486 // clicked in dropdown 1487 if ($dropdown.length) { 1488 // -- clicked in content 1489 if ($target.closest($content).length) { 1490 // return; 1491 // -- clicked on trigger 1492 } else { 1493 // close all others 1494 1495 // -- except parents and other open sidebar filters 1496 var $parents = $dropdown.parents(container_selector), 1497 $sidebar_filters = $(".wcpt-left-sidebar .wcpt-dropdown"); 1498 1499 $body 1500 .find(container_selector) 1501 .not($dropdown.add($parents).add($sidebar_filters)) 1502 .removeClass("wcpt-open"); 1503 1504 $dropdown.toggleClass("wcpt-open"); 1505 1506 // close all children 1507 if (!$dropdown.hasClass("wcpt-open")) { 1508 $dropdown.find(container_selector).removeClass("wcpt-open"); 1509 } 1510 1511 // popup enabled 1512 if ($dropdown.hasClass("wcpt-tooltip--popup-enabled")) { 1513 if ($dropdown.hasClass("wcpt-open")) { 1514 // need to run open logic earlier 1515 $body.addClass("wcpt-tooltip-popup-displayed"); 1516 } else { 1517 $body.removeClass("wcpt-tooltip-popup-displayed"); 1518 } 1519 } 1520 } 1521 1522 fix_tooltip_position($dropdown); 1523 } 1524 } 1525 1526 function fix_tooltip_position($tooltip) { 1527 // correct position 1528 var $content = $tooltip.find( 1529 " > .wcpt-dropdown-menu, > .wcpt-tooltip-content-wrapper > .wcpt-tooltip-content", 1530 ), 1531 content_width = $content.outerWidth(false), 1532 offset_left = $content.offset().left, 1533 page_width = $(window).width(); 1534 1535 if ($tooltip.hasClass("wcpt-tooltip")) { 1536 // tooltip 1537 1538 // if tooltip is already in portal, don't move it 1539 if ($tooltip.closest(".wcpt-tooltip-portal").length) { 1540 return; 1541 } 1542 1543 // narrow width 1544 $content.css("max-width", ""); 1545 var $container, margin; 1546 1547 if ($tooltip.hasClass("wcpt-tooltip--popup-enabled")) { 1548 $container = $("body"); 1549 margin = 40; 1550 } else { 1551 $container = $tooltip.closest(".wcpt-table-scroll-wrapper-outer").length 1552 ? $tooltip.closest(".wcpt-table-scroll-wrapper-outer") 1553 : $("body"); 1554 margin = 20; 1555 } 1556 1557 var container_rect = $.extend({}, $container[0].getBoundingClientRect()), 1558 content_rect = $content[0].getBoundingClientRect(); 1559 1560 var $freezeTable = $(".frzTbl-table", $container).not( 1561 ".frzTbl-clone-table", 1562 ); 1563 1564 if ($content.closest($freezeTable).length) {
1565 // target is inside the original freeze_table 1566 var $left_freeze_column = 1567 $freezeTable.data("freezeTable").el.$frozenColumnsLeft, 1568 $right_freeze_column = 1569 $freezeTable.data("freezeTable").el.$frozenColumnsRight; 1570 1571 container_rect.left += $left_freeze_column.width(); 1572 container_rect.right -= $right_freeze_column.width(); 1573 container_rect.width = 1574 container_rect.width - 1575 $left_freeze_column.width() - 1576 $right_freeze_column.width(); 1577 } 1578 1579 if (container_rect.width < parseInt(content_rect.width) + margin * 2) { 1580 $content.css("max-width", container_rect.width - margin * 2); 1581 } 1582 1583 // vertical position 1584 var $content_wrapper = $content.parent(), 1585 content_wrapper_width = $content_wrapper.width(), 1586 viewport_height = $(window).height(); 1587 1588 $content_wrapper.attr("data-wcpt-position", ""); 1589 content_rect = $content[0].getBoundingClientRect(); // refresh 1590 1591 // Check if tooltip would go above viewport or container top 1592 if (content_rect.top - 150 < 0) { 1593 // Position above if not enough space below 1594 } else { 1595 $content.parent().attr("data-wcpt-position", "above"); 1596 } 1597 1598 // // Check if tooltip would go below viewport or container bottom 1599 // if ( 1600 // content_rect.bottom + 30 > viewport_height || 1601 // container_rect.bottom < content_rect.bottom + 30 1602 // ) { 1603 // // Position above if not enough space below 1604 // $content.parent().attr("data-wcpt-position", "above"); 1605 // } 1606 1607 // horizontal position 1608 $content.css({ left: "", right: "" }); 1609 content_rect = $content[0].getBoundingClientRect(); // refresh 1610 1611 var arrow_margin = 20; // how close arrow gets to left/ right edge 1612 1613 if (content_rect.left - 15 < container_rect.left) { 1614 // excess left 1615 var left = 15 + container_rect.left - content_rect.left, 1616 limit = content_wrapper_width / 2 - arrow_margin; 1617 1618 if (left > limit) { 1619 left = limit; 1620 } 1621 1622 $content.css("left", left); 1623 } else if (content_rect.right + 15 > container_rect.right) { 1624 // excess right 1625 var right = content_rect.right - container_rect.right + 15, 1626 limit = content_wrapper_width / 2 - arrow_margin; 1627 1628 if (right > limit) { 1629 right = limit; 1630 } 1631 1632 $content.css("right", right); 1633 } 1634 } else { 1635 // dropdown 1636 if (content_width + 30 > page_width) { 1637 $content.outerWidth(page_width - 30); 1638 var content_width = $content.outerWidth(false); 1639 } 1640 1641 if ($content.offset().left + content_width > page_width) { 1642 // offscreen right 1643 var offset_required = 1644 $content.offset().left + content_width - page_width; 1645 $content.css("left", "-=" + (offset_required + 15)); 1646 } else if ($content.offset().left < 0) { 1647 // offscreen left 1648 $content.css("left", Math.abs($content.offset().left - 15)); 1649 } 1650 } 1651 1652 // tooltip arrow 1653 if ($tooltip.hasClass("wcpt-tooltip")) { 1654 var $label = $tooltip.find("> .wcpt-tooltip-label"), 1655 offset_left = $label.offset().left, 1656 width = $label.outerWidth(), 1657 $arrow = $("> .wcpt-tooltip-arrow", $content); 1658 1659 $arrow.css( 1660 "left", 1661 offset_left - $content.offset().left + width / 2 + "px", 1662 ); 1663 } 1664 1665 // place tooltip in portal 1666 if ( 1667 $tooltip.hasClass("wcpt-tooltip") && 1668 $container.hasClass("wcpt-table-scroll-wrapper-outer") 1669 ) { 1670 var container_rect = $container[0].getBoundingClientRect(); 1671 var content_rect = $content[0].getBoundingClientRect(); 1672 1673 var $row = $tooltip.closest(".wcpt-row, .wcpt-heading-row"), 1674 $cell = $tooltip.closest(".wcpt-cell, .wcpt-heading"); 1675 1676 $container.children(".wcpt-tooltip-portal").remove(); 1677 1678 // Get all data attributes from the original row 1679 var rowAttributes = ""; 1680 $.each($row[0].attributes, function (i, attr) { 1681 if (attr.name.startsWith("data-")) { 1682 rowAttributes += " " + attr.name + '="' + attr.value + '"'; 1683 } 1684 }); 1685 1686 var $portal = $( 1687 '<div class="wcpt-tooltip-portal">' + 1688 '<div class="wcpt-table">' + 1689 '<div class="' + 1690 $row.attr("class") + 1691 '"' + 1692 rowAttributes + 1693 ">" + 1694 '<div class="' + 1695 $cell.attr("class") + 1696 '">' + 1697 '<div class="wcpt-item-row"></div>' + 1698 "</div>" + 1699 "</div>" + 1700 "</div>" + 1701 "</div>", 1702 ); 1703 $portal 1704 .find(".wcpt-item-row") 1705 .html($tooltip.clone().addClass("wcpt-tooltip--inside-portal")); 1706 1707 $portal.find(".wcpt-row").data($row.data()); 1708 1709 var wrapper_rect = $content_wrapper[0].getBoundingClientRect(); 1710 $portal.find(".wcpt-tooltip-content-wrapper").css({ 1711 position: "fixed", 1712 top: wrapper_rect.top + "px", 1713 left: wrapper_rect.left + "px", 1714 }); 1715 1716 var content_rect = $content[0].getBoundingClientRect(); 1717 1718 var $portal_content = $portal.find(".wcpt-tooltip-content"); 1719 $portal_content.css({ 1720 width: content_rect.width + "px", 1721 transform: "translateY(4px)", 1722 opacity: 0, 1723 }); 1724 $content.css({ 1725 display: "none", 1726 }); 1727 $container.append($portal); 1728 1729 // animate 1730 setTimeout(function () { 1731 $portal_content.css({ 1732 transform: "translateY(0px)", 1733 opacity: 1, 1734 }); 1735 }, 1); 1736 1737 var $sticky_table_scroll_wrapper = $tooltip.closest( 1738 ".frzTbl-table-wrapper", 1739 ); 1740 1741 // Generate unique namespace for this tooltip instance 1742 var portalNamespace = 1743 "wcpt_portal_" + Math.random().toString(36).substr(2, 9); 1744 1745 function remove_portal() { 1746 // Remove all event handlers using unique namespace 1747 $container.off("mousemove." + portalNamespace); 1748 $(window).off("scroll." + portalNamespace); 1749 $container.off("scroll." + portalNamespace);
1750 $("body").off("click." + portalNamespace); 1751 $sticky_table_scroll_wrapper.off("scroll." + portalNamespace); 1752 1753 $portal.remove(); 1754 $content.css("display", ""); 1755 $tooltip.removeClass("wcpt-open"); 1756 } 1757 1758 // Store namespace on portal element for future reference 1759 $portal.data("portalNamespace", portalNamespace); 1760 1761 // remove portal based on 1762 // -- mouse hover 1763 if (!$tooltip.hasClass("wcpt-tooltip--open-on-click")) { 1764 $container.on("mousemove." + portalNamespace, function (e) { 1765 var $target = $(e.target); 1766 if ( 1767 !$target.closest($portal).length && 1768 !$target.closest($tooltip).length 1769 ) { 1770 remove_portal(); 1771 } 1772 }); 1773 } else { 1774 // -- click outside 1775 $("body").on("click." + portalNamespace, function (e) { 1776 if (!$(e.target).closest($portal).length) { 1777 remove_portal(); 1778 } 1779 }); 1780 } 1781 // -- scroll 1782 $(window).on("scroll." + portalNamespace, function () { 1783 remove_portal(); 1784 }); 1785 $container.on("scroll." + portalNamespace, function () { 1786 remove_portal(); 1787 }); 1788 $sticky_table_scroll_wrapper.on("scroll." + portalNamespace, function () { 1789 remove_portal(); 1790 }); 1791 } 1792 } 1793 1794 // tooltip with content hover disabled 1795 $("body").on("click mouseover", ".wcpt-tooltip-content", function () { 1796 var $this = $(this), 1797 $tooltip = $this.closest(".wcpt-tooltip"); 1798 1799 if ($tooltip.hasClass("wcpt-tooltip--hover-disabled")) { 1800 $tooltip.removeClass("wcpt-open"); 1801 } 1802 }); 1803 1804 // close dropdown when grab and scroll starts 1805 $("body").on("freeze_table__grab_and_scroll__start", function () { 1806 $( 1807 ".wcpt-navigation:not(.wcpt-left-sidebar) .wcpt-dropdown.wcpt-open", 1808 ).removeClass("wcpt-open"); 1809 }); 1810 1811 // Unique key for matching the same filter/search across header + sidebar 1812 function get_nav_filter_dedupe_key($el) { 1813 if ($el.hasClass("wcpt-search-wrapper") || $el.hasClass("wcpt-search")) { 1814 var $input = $el 1815 .find(".wcpt-search-input") 1816 .addBack(".wcpt-search-input") 1817 .first(); 1818 var name = $input.attr("name") || ""; 1819 return name ? "search:" + name : ""; 1820 } 1821 1822 if (!$el.hasClass("wcpt-filter") && !$el.is(".wcpt-filter")) { 1823 return ""; 1824 } 1825 1826 var filter = $el.attr("data-wcpt-filter"); 1827 if (!filter) { 1828 return ""; 1829 } 1830 1831 return ( 1832 filter + 1833 "|" + 1834 ($el.attr("data-wcpt-taxonomy") || "") + 1835 "|" + 1836 ($el.attr("data-wcpt-meta-key") || "") 1837 ); 1838 } 1839 1840 // Match other instances of the same filter across header / sidebar / etc. 1841 function find_duplicate_filters($filter, $nav) { 1842 var filter = $filter.attr("data-wcpt-filter"); 1843 if (!filter) { 1844 return $(); 1845 } 1846 1847 var selector = '.wcpt-filter[data-wcpt-filter="' + filter + '"]', 1848 taxonomy = $filter.attr("data-wcpt-taxonomy"), 1849 meta_key = $filter.attr("data-wcpt-meta-key"); 1850 1851 if (taxonomy) { 1852 selector += '[data-wcpt-taxonomy="' + taxonomy + '"]'; 1853 } 1854 if (meta_key) { 1855 selector += '[data-wcpt-meta-key="' + meta_key + '"]'; 1856 } 1857 1858 return $nav.find(selector); 1859 } 1860 1861 // When phone/tablet flattens header + sidebar into one strip, hide later 1862 // copies of the same filter so each filter only appears once. 1863 function hide_responsive_duplicate_nav_filters($wcpt) { 1864 $wcpt 1865 .find(".wcpt-nav-duplicate-hidden") 1866 .removeClass("wcpt-nav-duplicate-hidden"); 1867 1868 if (get_device() === "laptop") { 1869 return; 1870 } 1871 1872 // Only needed when the sidebar was converted into the combined header strip 1873 if (!$wcpt.find(".wcpt-was-left-sidebar").length) { 1874 return; 1875 } 1876 1877 var seen = {}; 1878 $wcpt 1879 .find(".wcpt-navigation-wrapper") 1880 .find(".wcpt-filter, .wcpt-search-wrapper") 1881 .each(function () { 1882 var $el = $(this); 1883 if ($el.closest(".wcpt-nav-modal-tpl, .wcpt-nav-modal").length) { 1884 return; 1885 } 1886 1887 var key = get_nav_filter_dedupe_key($el); 1888 if (!key) { 1889 return; 1890 } 1891 1892 if (seen[key]) { 1893 $el.addClass("wcpt-nav-duplicate-hidden"); 1894 } else { 1895 seen[key] = true; 1896 } 1897 }); 1898 } 1899 1900 // Keep first of each unique filter/search; drop later duplicates from a set 1901 function dedupe_nav_elements($set) { 1902 var seen = {}; 1903 return $set.filter(function () { 1904 var key = get_nav_filter_dedupe_key($(this)); 1905 if (!key) { 1906 return true; 1907 } 1908 if (seen[key]) { 1909 return false; 1910 } 1911 seen[key] = true; 1912 return true; 1913 });
1914 } 1915 1916 // Copy checked/value state from one filter instance onto its duplicates 1917 function sync_duplicate_filters_from($source_filter, $nav) { 1918 var $duplicates = find_duplicate_filters($source_filter, $nav).not( 1919 $source_filter, 1920 ); 1921 if (!$duplicates.length) { 1922 return; 1923 } 1924 1925 $source_filter 1926 .find('input[type="checkbox"], input[type="radio"]') 1927 .each(function () { 1928 var $src = $(this), 1929 value = $src.attr("value"), 1930 checked = $src.prop("checked"), 1931 type = $src.attr("type"); 1932 1933 $duplicates.each(function () { 1934 $(this) 1935 .find('input[type="' + type + '"]') 1936 .filter(function () { 1937 return $(this).attr("value") === value; 1938 }) 1939 .each(function () { 1940 var $input = $(this), 1941 $option = $input.closest(".wcpt-option, .wcpt-dropdown-option"), 1942 $label = $input.closest("label"); 1943 1944 $input.prop("checked", checked); 1945 1946 if ($option.length) { 1947 $option.toggleClass("wcpt-active", !!checked); 1948 } 1949 if ($label.length) { 1950 $label.toggleClass("wcpt-active", !!checked); 1951 } 1952 }); 1953 }); 1954 }); 1955 1956 // Range / date text inputs (same class names across copies) 1957 [ 1958 ".wcpt-range-input-min", 1959 ".wcpt-range-input-max", 1960 ".wcpt-filter-date-picker--start-date", 1961 ".wcpt-filter-date-picker--end-date", 1962 ].forEach(function (sel) { 1963 var $src_inputs = $source_filter.find(sel);
1964 if (!$src_inputs.length) { 1965 return; 1966 } 1967 1968 $src_inputs.each(function (i) { 1969 var val = $(this).val(); 1970 $duplicates.each(function () { 1971 var $dup_input = $(this).find(sel).eq(i); 1972 if ($dup_input.length) { 1973 $dup_input.val(val); 1974 } 1975 }); 1976 }); 1977 }); 1978 } 1979 1980 // apply nav filters 1981 $("body").on("change", ".wcpt-navigation", navigation_change); 1982 function navigation_change(e) { 1983 var $target = $(e.target), 1984 $container = $target.closest(".wcpt"), 1985 // Header + left sidebar are nested under .wcpt-navigation-wrapper / 1986 // .wcpt-nav-main, so they are no longer siblings â collect all of them. 1987 $nav = $( 1988 "> .wcpt-navigation, > .wcpt-navigation-wrapper .wcpt-navigation", 1989 $container, 1990 ); 1991 1992 // skip search filter options input 1993 if ($target.closest(".wcpt-search-filter-options").length) { 1994 return; 1995 } 1996 1997 // skip date pickers 1998 if ($target.closest(".wcpt-filter-date-picker").length) { 1999 return; 2000 } 2001 2002 // taxonomy hierarchy 2003 if ($target.closest(".wcpt-hierarchy").length) { 2004 var checked = $target.prop("checked"); 2005 2006 // effect on child terms 2007 if ($target.hasClass("wcpt-hr-parent-term")) { 2008 var ct_selector = "input[type=checkbox], input[type=radio]", 2009 $child_terms = $target 2010 .closest("label") 2011 .siblings(".wcpt-hr-child-terms-wrapper") 2012 .find(ct_selector); 2013 $child_terms.prop("checked", false); 2014 } 2015 2016 // effect on parent terms 2017 var $ancestors = $target.parents(".wcpt-hr-child-terms-wrapper"); 2018 if ($ancestors.length) { 2019 $ancestors.each(function () { 2020 var $parent_term = $(this) 2021 .siblings("label") 2022 .find(".wcpt-hr-parent-term"); 2023 $parent_term.prop("checked", false); 2024 }); 2025 } 2026 } 2027 2028 // range filter 2029 if ($target.closest(".wcpt-range-filter")) { 2030 // -- number and range inputs shouldn't cause filtering 2031 if ( 2032 $target.hasClass("wcpt-range-input-min") || 2033 $target.hasClass("wcpt-range-input-max") || 2034 $target.hasClass("wcpt-range-slider") 2035 ) { 2036 // Still sync typed values onto duplicate filter copies 2037 var $range_source = $target.closest(".wcpt-filter"); 2038 if ($range_source.length) { 2039 sync_duplicate_filters_from($range_source, $nav); 2040 } 2041 return; 2042 } 2043 2044 // apply selected radio range onto the range slider inputs 2045 if ($target.is("input[type=radio]")) { 2046 var min = $target.attr("data-wcpt-range-min") || "", 2047 max = $target.attr("data-wcpt-range-max") || "", 2048 $range_filter = $target.closest(".wcpt-range-filter"), 2049 $min = $range_filter.find(".wcpt-range-input-min"), 2050 $max = $range_filter.find(".wcpt-range-input-max"), 2051 $range_slider = $range_filter.find(".wcpt-range-slider.original"); 2052 2053 $min.val(min); 2054 $max.val(max); 2055 if (!min) { 2056 min = $range_slider.attr("min"); 2057 } 2058 if (!max) { 2059 max = $range_slider.attr("max"); 2060 } 2061 2062 $range_slider.val(min + "," + max); 2063 } 2064 } 2065 2066 // Keep duplicate copies of the same filter in sync (e.g. header + sidebar). 2067 // Without this, serialize() still picks up a checked option from the other 2068 // copy after deselection, so the filter stays applied. 2069 var $source_filter = $target.closest(".wcpt-filter"); 2070 if ($source_filter.length) { 2071 sync_duplicate_filters_from($source_filter, $nav); 2072 } 2073 2074 // return if current control doesn't trigger immediate filtering 2075 // -- search 2076 if ($target.closest(".wcpt-search").length) { 2077 return; 2078 } 2079 2080 // -- nav modal 2081 if ($target.closest(".wcpt-nav-modal").length) { 2082 return; 2083 } 2084 2085 // nav clone 2086 var table_id = $container.attr("id").substring(5), 2087 $nav_clone = $nav.clone(); 2088 2089 nav_clone_operations($nav_clone); 2090 2091 // build query 2092 var query = $("<form>").append($nav_clone).serialize(); 2093 2094 // include column sort 2095 if (!$(e.target).closest('[data-wcpt-filter="sort_by"]').length) {
2096 var $table = wcpt_get_container_original_table($container), 2097 $sortable_headings = $(".wcpt-heading.wcpt-sortable:visible", $table), 2098 $current_sort_col = $sortable_headings.filter(function () { 2099 return $(this).find( 2100 ".wcpt-sorting-icons.wcpt-sorting-asc, .wcpt-sorting-icons.wcpt-sorting-desc", 2101 ).length; 2102 }); 2103 if ($current_sort_col.length) { 2104 var sort_id = $current_sort_col.attr("data-wcpt-sort-id"), 2105 order = $current_sort_col.find(".wcpt-sorting-icons.wcpt-sorting-asc") 2106 .length 2107 ? "ASC" 2108 : "DESC", 2109 $sorting_icons = $current_sort_col 2110 .find(".wcpt-sorting-icons") 2111 .first(), 2112 column_orderby = $sorting_icons.attr("data-wcpt-orderby") || "", 2113 column_meta_key = $sorting_icons.attr("data-wcpt-meta-key") || "", 2114 column_orderby_attribute = 2115 $sorting_icons.attr("data-wcpt-orderby-attribute") || "", 2116 column_orderby_taxonomy = 2117 $sorting_icons.attr("data-wcpt-orderby-taxonomy") || ""; 2118 // Skip incomplete sort ids (e.g. "column_" with no index) â those 2119 // produce undefined orderby warnings on the server during filter AJAX. 2120 if ( 2121 sort_id && 2122 sort_id !== "column_" && 2123 sort_id.indexOf("column_") === 0 2124 ) { 2125 // Prefer explicit heading criteria when available 2126 if (column_orderby) { 2127 sort_id = "column_heading"; 2128 } 2129 2130 query += 2131 "&" + 2132 table_id + 2133 "_orderby=" + 2134 sort_id + 2135 "&" + 2136 table_id + 2137 "_order=" + 2138 order; 2139 2140 if (column_orderby) { 2141 query += 2142 "&" + 2143 table_id + 2144 "_column_orderby=" + 2145 encodeURIComponent(column_orderby); 2146 query += 2147 "&" + 2148 table_id + 2149 "_column_meta_key=" + 2150 encodeURIComponent(column_meta_key); 2151 if (column_orderby_attribute) { 2152 query += 2153 "&" + 2154 table_id + 2155 "_column_orderby_attribute=" + 2156 encodeURIComponent(column_orderby_attribute); 2157 } 2158 if (column_orderby_taxonomy) { 2159 query += 2160 "&" + 2161 table_id + 2162 "_column_orderby_taxonomy=" + 2163 encodeURIComponent(column_orderby_taxonomy); 2164 } 2165 } 2166 } 2167 } 2168 } 2169 2170 nav_filter_feedback($nav); 2171 2172 var data = { 2173 callback: function () { 2174 attempt_ajax($container, query, false, "filter", e); 2175 }, 2176 proceed: true, 2177 event: e, 2178 }; 2179 2180 $container.trigger("wcpt_after_navigation_change", data); 2181 2182 if (data.proceed) { 2183 data.callback(); 2184 } 2185 } 2186 2187 function nav_clone_operations($nav_clone) { 2188 var $reverse_check = $(); 2189 2190 $("[data-wcpt-reverse-value]:not(:checked)", $nav_clone).each(function () { 2191 var $this = $(this); 2192 $this.attr("value", $this.attr("data-wcpt-reverse-value")); 2193 $this.prop("checked", "checked"); 2194 $reverse_check = $reverse_check.add($this.clone()); 2195 }); 2196 $nav_clone = $nav_clone.add($reverse_check); 2197 2198 // get values from duplicate radio buttons and clean up the names 2199 $('input[type="radio"]', $nav_clone).each(function () { 2200 var $this = $(this), 2201 name = $this.attr("name"); 2202 2203 if (-1 !== name.indexOf("--")) { 2204 var is_array = name.indexOf("[]"); 2205 name = name.substr(0, name.indexOf("--")) + (is_array ? "[]" : ""); 2206 $this.attr("name", name); 2207 } 2208 }); 2209 2210 // remove redundant range values 2211 $nav_clone.find(".wcpt-range-options-wrapper").each(function () { 2212 var $this = $(this), 2213 $min_input = $this.find(".wcpt-range-input-min"), 2214 $max_input = $this.find(".wcpt-range-input-max"), 2215 $range_slider = $this.find(".wcpt-range-slider"), 2216 $filter = $this.closest(".wcpt-filter"), 2217 $selected_option = $filter.find("input[type=radio]:checked"); 2218 2219 // enforce option selected by user 2220 if ($selected_option.length && $selected_option.attr("value")) { 2221 var $selected_option_min = $selected_option.attr("data-wcpt-range-min"), 2222 $selected_option_max = $selected_option.attr("data-wcpt-range-max"); 2223 2224 $min_input.val($selected_option_min); 2225 $max_input.val($selected_option_max); 2226 return; 2227 } 2228 2229 var prev_min = $min_input.attr("value"), 2230 prev_max = $max_input.attr("value"); 2231 2232 $min_input.each(function () { 2233 var $this = $(this), 2234 min = $this.attr("min"), 2235 current_value = $this.val(); 2236 if (min == current_value) { 2237 $this.val(prev_min); 2238 } 2239 }); 2240 2241 $max_input.each(function () { 2242 var $this = $(this), 2243 max = $this.attr("max"), 2244 current_value = $this.val(); 2245 if (max == current_value) { 2246 $this.val(prev_max); 2247 } 2248 }); 2249 2250 $range_slider.remove(); 2251 });
2252 } 2253 2254 function nav_filter_feedback($nav) { 2255 // header nav 2256 $(".wcpt-filter", $nav.filter(".wcpt-header")).each(function () { 2257 var $this = $(this), 2258 filter = $this.attr("data-wcpt-filter"), 2259 $filter = $this.closest(".wcpt-filter"), 2260 format = $this.attr("data-wcpt-heading_format__op_selected"), 2261 radio = 2262 $this.find("input[type=radio]").length || 2263 $this.hasClass("wcpt-range-filter"), 2264 checkbox = $this.find("input[type=checkbox]").length, 2265 $selected = $this.find("input[type=radio]:checked"), 2266 $checked = $this.find("input[type=checkbox]:checked"), 2267 checked_count = $checked.length, 2268 $active_count = $this.find(".wcpt-active-count"), 2269 radio_permit = false, 2270 label_append = "", 2271 $multi_range = $(".wcpt-range-options-wrapper", $filter), 2272 $multi_range__min = $( 2273 ".wcpt-range-options-wrapper .wcpt-range-input-min", 2274 $filter, 2275 ), 2276 $multi_range__max = $( 2277 ".wcpt-range-options-wrapper .wcpt-range-input-max", 2278 $filter, 2279 ), 2280 in_filter_grid = !!$this.closest(".wcpt-ratio-filter_grid").length; 2281 2282 // Skip options-row filters outside filter grid (counts aren't used there) 2283 if ($this.hasClass("wcpt-options-row") && !in_filter_grid) { 2284 return; 2285 } 2286 2287 if ( 2288 -1 == 2289 $.inArray(filter, [ 2290 "custom_field", 2291 "attribute", 2292 "category", 2293 "taxonomy", 2294 "price_range", 2295 "rating", 2296 "sort_by", 2297 "results_per_page", 2298 "on_sale", 2299 "availability", 2300 ]) 2301 ) { 2302 return; 2303 } 2304 2305 // mark active filters 2306 if ( 2307 checked_count || 2308 ($selected.val() && 2309 !$selected.closest(".wcpt-default-option").length) || // sort by 2310 ($multi_range.length && 2311 ($multi_range__min.val() != $multi_range__min.attr("min") || 2312 $multi_range__max.val() != $multi_range__max.attr("max"))) 2313 ) { 2314 $this.closest(".wcpt-filter").addClass("wcpt-filter--active"); 2315 } else { 2316 $this.closest(".wcpt-filter").removeClass("wcpt-filter--active"); 2317 } 2318 2319 // modify dropdown heading 2320 2321 // -- filter grid: radio filters show (0)/(1) count like checkboxes 2322 if ( 2323 radio && 2324 in_filter_grid && 2325 filter !== "sort_by" && 2326 filter !== "results_per_page" 2327 ) { 2328 $this.find(".wcpt-radio-op-selected__heading-append").remove(); 2329 $active_count.remove(); 2330 2331 var radio_count = 0; 2332 if ($selected.length && $selected.attr("value")) { 2333 // Non-"show all" option selected 2334 radio_count = 1; 2335 } else if ( 2336 $multi_range.length && 2337 ($multi_range__min.val() != $multi_range__min.attr("min") || 2338 $multi_range__max.val() != $multi_range__max.attr("max")) 2339 ) { 2340 radio_count = 1; 2341 } 2342 2343 var radio_count_class = 2344 "wcpt-active-count wcpt-active-count--filter-grid"; 2345 if (!radio_count) { 2346 radio_count_class += " wcpt-active-count--zero"; 2347 } 2348 2349 $active_count = $( 2350 '<span class="' + radio_count_class + '">' + radio_count + "</span>", 2351 ); 2352 $( 2353 ".wcpt-filter-heading .wcpt-dropdown-label, .wcpt-filter-heading .wcpt-options-heading", 2354 $this, 2355 ).after($active_count); 2356 2357 // -- radio append option label 2358 } else if (radio && format !== "only_heading") { 2359 $this.find(".wcpt-radio-op-selected__heading-append").remove(); 2360 2361 if ( 2362 !$selected.length || 2363 !$selected.attr("value") // 'show all' op selected 2364 ) { 2365 $this.removeClass("wcpt-radio-op-selected"); 2366 } else { 2367 // selected and has value 2368 $this.addClass("wcpt-radio-op-selected"); 2369 label_append = $selected.next()[0].outerHTML; 2370 radio_permit = true; 2371 } 2372 2373 if (!$selected.length && filter == "price_range") { 2374 var min = 2375 wcpt_params.currency_symbol + 2376 $(".wcpt-range-input-min", $this).val() || 0, 2377 max = 2378 wcpt_params.currency_symbol + 2379 $(".wcpt-range-input-max", $this).val() || 0; 2380 2381 label_append = "<span>" + min + " - " + max + "<span>"; 2382 2383 if ( 2384 $(".wcpt-range-input-min", $this).val() != 2385 $(".wcpt-range-input-min", $this).attr("min") || 2386 $(".wcpt-range-input-max", $this).val() != 2387 $(".wcpt-range-input-max", $this).attr("max") 2388 ) { 2389 $this.addClass("wcpt-radio-op-selected"); 2390 radio_permit = true; 2391 } 2392 } else if (!$selected.length && filter == "custom_field") { 2393 var min = $(".wcpt-range-input-min", $this).val() || 0, 2394 max = $(".wcpt-range-input-max", $this).val() || 0; 2395 2396 label_append = "<span>" + min + " - " + max + "<span>"; 2397 2398 if ( 2399 min != $(".wcpt-range-input-min", $this).attr("min") || 2400 max != $(".wcpt-range-input-max", $this).attr("max") 2401 ) { 2402 $this.addClass("wcpt-radio-op-selected"); 2403 radio_permit = true; 2404 } 2405 } else if ($selected.length && filter == "sort_by") { 2406 var $icon = $this.find(".wcpt-dropdown-label > .wcpt-icon"); 2407 if ($icon.length) { 2408 label_append = 2409 $icon[0].outerHTML + " " + $selected.next()[0].outerHTML; 2410 2411 $icon.remove(); 2412 } 2413 } 2414 2415 if (radio_permit) { 2416 $this 2417 .find(".wcpt-dropdown-label") 2418 .append( 2419 '<div class="wcpt-radio-op-selected__heading-append">' + 2420 label_append + 2421 "</div>", 2422 ); 2423 } 2424 2425 // -- checkbox append selected option count 2426 } else if (checkbox) { 2427 $active_count.remove(); 2428 2429 // In filter grid rows, always show the count (including 0) 2430 // so empty checkbox dropdowns still read as (0). 2431 var always_show_count = in_filter_grid; 2432 2433 if (checked_count || always_show_count) { 2434 var active_count_class = "wcpt-active-count"; 2435 var active_count_text = checked_count; 2436 2437 if (always_show_count) { 2438 active_count_class += " wcpt-active-count--filter-grid"; 2439 2440 if (!checked_count) { 2441 active_count_class += " wcpt-active-count--zero"; 2442 } 2443 } 2444 2445 $active_count = $( 2446 '<span class="' + 2447 active_count_class + 2448 '">' + 2449 active_count_text + 2450 "</span>", 2451 ); 2452 $( 2453 ".wcpt-filter-heading .wcpt-dropdown-label, .wcpt-filter-heading .wcpt-options-heading", 2454 $this, 2455 ).after($active_count); 2456 } 2457 } 2458 });
2459 } 2460 2461 // submit range by enter 2462 $("body").on( 2463 "keyup", 2464 ".wcpt-range-input-min, .wcpt-range-input-max", 2465 function (e) { 2466 var $this = $(this), 2467 $filters = $this.closest(".wcpt-navigation"), 2468 code = e.keyCode ? e.keyCode : e.which; 2469 2470 if (code == 13) { 2471 var $filter = $this.closest(".wcpt-filter"), 2472 $container = $this.closest(".wcpt"), 2473 $nav = $( 2474 "> .wcpt-navigation, > .wcpt-navigation-wrapper .wcpt-navigation", 2475 $container, 2476 ); 2477 if ($filter.length) { 2478 sync_duplicate_filters_from($filter, $nav); 2479 } 2480 $filters.trigger("change"); 2481 } 2482 }, 2483 ); 2484 2485 // submit range 2486 $("body").on("click", ".wcpt-range-submit-button", function (e) { 2487 var $this = $(this), 2488 $range_wrapper = $this.closest(".wcpt-range-options-wrapper"), 2489 $min = $(".wcpt-range-input-min", $range_wrapper), 2490 $max = $(".wcpt-range-input-max", $range_wrapper), 2491 min_val = parseFloat($min.val()), 2492 max_val = parseFloat($max.val()); 2493 2494 // ensure max stays greater than min 2495 if (max_val < min_val) { 2496 $max.val(min_val); 2497 $min.val(max_val); 2498 } 2499 2500 $this.trigger("change"); 2501 }); 2502 2503 // date picker 2504 2505 // -- submit 2506 $("body").on("click", ".wcpt-date-picker-submit-button", function (e) { 2507 var $this = $(this), 2508 $filter = $this.closest('[data-wcpt-filter="date_picker"]'), 2509 $start_date = $(".wcpt-filter-date-picker--start-date", $filter), 2510 $end_date = $(".wcpt-filter-date-picker--end-date", $filter), 2511 $filters = $this.closest(".wcpt-navigation"), 2512 $container = $this.closest(".wcpt"), 2513 $nav = $( 2514 "> .wcpt-navigation, > .wcpt-navigation-wrapper .wcpt-navigation", 2515 $container, 2516 ); 2517 2518 if ($start_date.val() && $end_date.val()) { 2519 var start_date = new Date($start_date.val()), 2520 end_date = new Date($end_date.val()); 2521 2522 if (start_date > end_date) { 2523 var start_date_val = $start_date.val(), 2524 end_date_val = $end_date.val(); 2525 2526 $start_date.val(end_date_val); 2527 $end_date.val(start_date_val); 2528 } 2529 } 2530 2531 if ($filter.length) { 2532 sync_duplicate_filters_from($filter, $nav); 2533 } 2534 $filters.trigger("change"); 2535 }); 2536 // -- -- enter key 2537 $("body").on("keydown", ".wcpt-filter-date-picker", function (e) { 2538 if (e.keyCode === 13 || e.which === 13) { 2539 var $this = $(this), 2540 $filters = $this.closest(".wcpt-navigation"), 2541 $filter = $this.closest(".wcpt-filter"), 2542 $container = $this.closest(".wcpt"), 2543 $nav = $( 2544 "> .wcpt-navigation, > .wcpt-navigation-wrapper .wcpt-navigation", 2545 $container, 2546 ); 2547 2548 if ($filter.length) { 2549 sync_duplicate_filters_from($filter, $nav); 2550 } 2551 $filters.trigger("change"); 2552 } 2553 }); 2554 2555 // -- reset 2556 $("body").on("click", ".wcpt-date-picker-reset-button", function (e) { 2557 e.preventDefault(); 2558 2559 var $this = $(this), 2560 $filter = $this.closest('[data-wcpt-filter="date_picker"]'), 2561 $date_picker = $(".wcpt-filter-date-picker", $filter), 2562 $filters = $this.closest(".wcpt-navigation"), 2563 $container = $this.closest(".wcpt"), 2564 $nav = $( 2565 "> .wcpt-navigation, > .wcpt-navigation-wrapper .wcpt-navigation", 2566 $container, 2567 ); 2568 2569 $date_picker.val(""); 2570 if ($filter.length) { 2571 sync_duplicate_filters_from($filter, $nav); 2572 } 2573 $filters.trigger("change"); 2574 }); 2575 2576 // -- validate 2577 $("body").on("keyup", ".wcpt-filter-date-picker", function (e) { 2578 var $this = $(this); 2579 if ( 2580 $this.val().length > 10 || 2581 -1 == $.inArray($this.val()[0], ["1", "2"]) 2582 ) { 2583 $this.css("border-color", "red"); 2584 } else { 2585 $this.css("border-color", ""); 2586 } 2587 }); 2588 2589 // clear filter 2590 $("body").on("click", ".wcpt-clear-filter", function (e) { 2591 var $clear_filter = $(this), 2592 $target = $(e.target); 2593 2594 if ($target.closest(".wcpt-dropdown-menu")) { 2595 var $sub_option = $target.closest(".wcpt-dropdown-option"); 2596 } else { 2597 $sub_option = false; 2598 } 2599 2600 var $container = $clear_filter.closest(".wcpt"), 2601 filter = $clear_filter.attr("data-wcpt-filter"), 2602 $navs = $( 2603 "> .wcpt-navigation, >
2603 .wcpt-navigation-wrapper .wcpt-navigation", 2604 $container, 2605 ), 2606 $inputs = $(); 2607 2608 if (filter == "search") { 2609 var name = $clear_filter.attr("data-wcpt-search-name"), 2610 $inputs = $('.wcpt-search-input[name="' + name + '"]', $navs); 2611 } else if ( 2612 filter == "attribute" || 2613 filter == "category" || 2614 filter == "taxonomy" 2615 ) { 2616 var taxonomy = $clear_filter.attr("data-wcpt-taxonomy"), 2617 term = $clear_filter.attr("data-wcpt-value"), 2618 $inputs = $navs 2619 .find( 2620 '.wcpt-filter[data-wcpt-filter="' + 2621 filter + 2622 '"][data-wcpt-taxonomy="' + 2623 taxonomy + 2624 '"]', 2625 ) 2626 .find('input[value="' + term + '"]'); 2627 } else if (filter == "custom_field") { 2628 var meta_key = $clear_filter.attr("data-wcpt-meta-key"), 2629 value = $clear_filter.attr("data-wcpt-value"), 2630 $filter = $navs.find( 2631 '.wcpt-filter[data-wcpt-filter="' + 2632 filter + 2633 '"][data-wcpt-meta-key="' + 2634 meta_key + 2635 '"]', 2636 ); 2637 2638 if ($filter.hasClass("wcpt-range-filter")) { 2639 $inputs = $filter.find("input"); 2640 } else { 2641 $inputs = $navs 2642 .find( 2643 '.wcpt-filter[data-wcpt-filter="' + 2644 filter + 2645 '"][data-wcpt-meta-key="' + 2646 meta_key + 2647 '"]', 2648 ) 2649 .find('input[value="' + value + '"]'); 2650 } 2651 } else if (filter == "price_range") { 2652 $inputs = $navs 2653 .find( 2654 '.wcpt-filter[data-wcpt-filter="custom_field"][data-wcpt-meta-key="_price"]', 2655 ) 2656 .find("input"); 2657 } else if (filter == "search") { 2658 $inputs = $navs.find( 2659 'input[type=search][data-wcpt-value="' + 2660 htmlentity($clear_filter.attr("data-wcpt-value")) + 2661 '"]', 2662 ); 2663 } else if (filter == "rating") { 2664 $inputs = $navs 2665 .find('.wcpt-filter[data-wcpt-filter="rating"]') 2666 .find("input"); 2667 } else if (filter == "date_picker") { 2668 var val = $clear_filter.attr("data-wcpt-value"), 2669 $inputs = $navs 2670 .find('.wcpt-filter[data-wcpt-filter="date_picker"]') 2671 .find('input[value="' + val + '"]'); 2672 } else { 2673 var value = $clear_filter.attr("data-wcpt-value"), 2674 $inputs = $navs 2675 .find('.wcpt-filter[data-wcpt-filter="' + filter + '"]') 2676 .find('input[value="' + value + '"]'); 2677 } 2678 2679 $inputs 2680 .filter(":input[type=checkbox], :input[type=radio]") 2681 .prop("checked", false) 2682 .closest("label.wcpt-active") 2683 .removeClass("wcpt-active"); 2684 2685 $inputs 2686 .filter( 2687 ":input[type=text], :input[type=number], :input[type=search], :input[type=date]", 2688 ) 2689 .val(""); // search and range input 2690 2691 $navs.first().trigger("change"); 2692 2693 // remove clear filter 2694 if (!$clear_filter.siblings(".wcpt-clear-filter").length) { 2695 $clear_filter.closest(".wcpt-clear-filters-wrapper").remove(); 2696 } else { 2697 $clear_filter.remove(); 2698 } 2699 }); 2700 2701 // clear all filters 2702 $("body").on( 2703 "click", 2704 ".wcpt-clear-filters, .wcpt-clear-all-filters, .wcpt-reset", 2705 function (e) { 2706 e.preventDefault(); 2707 var $this = $(this), 2708 $container = $this.closest(".wcpt"), 2709 query = "", 2710 append = false, 2711 purpose = "filter"; 2712 2713 if (!$this.hasClass("wcpt-disabled")) { 2714 attempt_ajax($container, query, append, purpose); 2715 } 2716 }, 2717 ); 2718 2719 // sort by column heading 2720 window.wcpt_column_heading_sort_handler = function (e) { 2721 var $this = $(this), 2722 $sorting = $this.find(".wcpt-sorting-icons"); 2723 2724 if ($(e.target).closest(".wcpt-tooltip").length) { 2725 return; 2726 } 2727 2728 if (!$sorting.length) { 2729 return; 2730 } 2731 2732 // maintain scroll left upon column header sorting 2733 if ($this.closest(".frzTbl").length) { 2734 var $container = $this.closest(".wcpt"), 2735 $scrollOverlay = $this 2736 .closest(".frzTbl-content-wrapper") 2737 .siblings(".frzTbl-scroll-overlay"), 2738 scrollLeft = $scrollOverlay[0].scrollLeft; 2739 2740 $("body").one( 2741 "after_freeze_table_build", 2742 "#" + $container.attr("id") + " .frzTbl-table", 2743 function (e, frzTbl) { 2744 frzTbl.el.$scrollOverlay[0].scrollLeft = scrollLeft; 2745 }, 2746 ); 2747 } 2748 2749 var order = $sorting.hasClass("wcpt-sorting-asc") ? "desc" : "asc", 2750 sort_id = $this.attr("data-wcpt-sort-id") || "", 2751 $container = $this.closest(".wcpt"), 2752 table_id = $container.attr("id").substring(5), 2753 device = get_device(), 2754 // Explicit criteria from the heading icons â preferred server-side 2755 // source of truth (does not rely on column_{index} resolution). 2756 column_orderby = $sorting.attr("data-wcpt-orderby") || "", 2757 column_meta_key = $sorting.attr("data-wcpt-meta-key") || "", 2758 column_orderby_attribute = 2759 $sorting.attr("data-wcpt-orderby-attribute") || "", 2760 column_orderby_taxonomy = 2761 $sorting.attr("data-wcpt-orderby-taxonomy") || ""; 2762 2763 if ($(".wcpt-sorting-" + order + "-icon", $sorting).hasClass("wcpt-hide")) { 2764 if ( 2765 $(".wcpt-sorting-" + order + "-icon", $sorting) 2766 .siblings() 2767 .hasClass("wcpt-active") 2768 ) { 2769 return; 2770 } else { 2771 order = order == "asc" ? "desc" : "asc"; 2772 } 2773 } 2774 2775 // When the heading icons expose explicit sort criteria, use a stable 2776 // orderby token and send those criteria as the source of truth. This 2777 // avoids fragile column_{index} resolution (layout / original_index mismatch). 2778 if (column_orderby) { 2779 sort_id = "column_heading"; 2780 } 2781 2782 if (!sort_id || sort_id === "column_") { 2783 return; 2784 } 2785 2786 var query = 2787 table_id + 2788 "_orderby=" + 2789 encodeURIComponent(sort_id) + 2790 "&" + 2791 table_id + 2792 "_order=" + 2793 order + 2794 "&" + 2795 table_id + 2796 "_device=" + 2797 device + 2798 "&" + 2799 table_id + 2800 "_paged=1"; 2801 2802 if (column_orderby) { 2803 query += 2804 "&" + 2805 table_id + 2806 "_column_orderby=" + 2807 encodeURIComponent(column_orderby); 2808 // Always send meta_key key (even empty) so server can clear stale keys 2809 // when switching away from a custom-field sort. 2810 query += 2811 "&" + 2812 table_id + 2813 "_column_meta_key=" + 2814 encodeURIComponent(column_meta_key); 2815 if (column_orderby_attribute) { 2816 query += 2817 "&" + 2818 table_id + 2819 "_column_orderby_attribute=" + 2820 encodeURIComponent(column_orderby_attribute); 2821 } 2822 if (column_orderby_taxonomy) { 2823 query += 2824 "&" + 2825 table_id + 2826 "_column_orderby_taxonomy=" + 2827 encodeURIComponent(column_orderby_taxonomy); 2828 } 2829 } 2830 2831 attempt_ajax($container, query, true, "column_heading_sort"); 2832 }; 2833 2834 // ajax 2835 var attempt_ajax = (window.wcpt_attempt_ajax = ( 2836 $container, 2837 new_query, 2838 append, 2839 purpose, 2840 ) => { 2841 const query = build_ajax_query_string( 2842 $container, 2843 new_query, 2844 append, 2845 purpose, 2846 ); 2847 2848 // trigger body event to hook in 2849 var data = { 2850 query: query, 2851 $wcpt: $container, 2852 purpose: purpose, 2853 proceed: true, 2854 }; 2855 2856 var prev_nav_query = $container.attr("data-wcpt-nav-query"); 2857 2858 if (prev_nav_query === query) { 2859 data.proceed = false; 2860 } 2861 2862 $("body").trigger("wcpt_before_ajax_query", data); 2863 2864 $container.attr("data-wcpt-nav-query", data.query); 2865 2866 if (data.proceed) { 2867 fetch_markup_and_apply_callback( 2868 $container, 2869 data.query, 2870 purpose, 2871 ajax_success, 2872 ); 2873 } 2874 }); 2875 2876 function get_response_container(response) { 2877 var $response = $(response), 2878 $responseContainer = $response.filter(".wcpt").first(); 2879 2880 if (!$responseContainer.length) { 2881 $responseContainer = $response.find(".wcpt").first(); 2882 } 2883 2884 return $responseContainer.length ? $responseContainer : $response; 2885 } 2886 2887 function sync_container_attributes($container, $new_container) { 2888 if (!$new_container.length || !$new_container[0].attributes) { 2889 return; 2890 } 2891
2892 [...$new_container[0].attributes].forEach((attr) => { 2893 if ( 2894 attr.nodeName === "class" || 2895 attr.nodeName.substr(0, 10) === "data-wcpt-" 2896 ) { 2897 $container[0].setAttribute(attr.nodeName, attr.nodeValue); 2898 } 2899 }); 2900 } 2901 2902 function replace_paginated_content($container, $new_container) { 2903 var $target_scope = $container.find(".wcpt-nav-main").first(), 2904 $source_scope = $new_container.find(".wcpt-nav-main").first(), 2905 content_selector = [ 2906 ".wcpt-table-scroll-wrapper-outer", 2907 ".wcpt-pagination", 2908 ".wcpt-no-results", 2909 ".wcpt-in-footer", 2910 ".wcpt-infinite-scroll-dots", 2911 ".wcpt-add-selected-to-cart-with-pagination", 2912 ".wcpt-loading-screen", 2913 ].join(", "), 2914 previous_min_height, 2915 lock_height, 2916 $fragment; 2917 2918 if (!$target_scope.length) { 2919 $target_scope = $container; 2920 } 2921 2922 if (!$source_scope.length) { 2923 $source_scope = $new_container; 2924 } 2925 2926 // Keep height while swapping results so the page does not collapse and 2927 // jump the viewport to the top before the new table is inserted. 2928 previous_min_height = $target_scope[0].style.minHeight; 2929 lock_height = $target_scope.outerHeight(); 2930 if (lock_height) { 2931 $target_scope.css("min-height", lock_height + "px"); 2932 } 2933 2934 $fragment = $(document.createDocumentFragment()); 2935 $source_scope.children().each(function () { 2936 var $child = $(this); 2937 if ($child.is(content_selector)) { 2938 $fragment.append($child.clone(true, true)); 2939 } 2940 }); 2941 2942 $target_scope.children(content_selector).remove(); 2943 $target_scope.append($fragment); 2944 $target_scope.css("min-height", previous_min_height || ""); 2945 2946 // Paginate-only responses keep the outer container; merge variable-switch 2947 // price/availability templates so variation resets can restore prices. 2948 if ( 2949 typeof wcpt_util !== "undefined" && 2950 wcpt_util.merge_variable_switch_templates_from_response 2951 ) { 2952 wcpt_util.merge_variable_switch_templates_from_response( 2953 $new_container, 2954 $container, 2955 ); 2956 } 2957 } 2958 2959 // Header pagination lives inside preserved .wcpt-navigation, so update it 2960 // separately after the footer/table swap on paginate-only responses. 2961 function update_header_pagination($container, $new_container) { 2962 var $newPagination = $new_container 2963 .find( 2964 ".wcpt-add-selected-to-cart-with-pagination .wcpt-pagination, .wcpt-pagination", 2965 ) 2966 .first(); 2967 2968 if (!$newPagination.length) { 2969 return; 2970 } 2971 2972 $container.find(".wcpt-navigation .wcpt-pagination").each(function () { 2973 $(this).replaceWith($newPagination.clone(true, true)); 2974 }); 2975 } 2976 2977 function replace_result_count_placeholders(template, context) { 2978 if (typeof template !== "string") { 2979 return ""; 2980 } 2981 2982 [ 2983 ["displayed_results", context.displayed_results || 0], 2984 ["total_results", context.total_results || 0], 2985 ["first_result", context.first_result || 0], 2986 ["last_result", context.last_result || 0], 2987 ].forEach(function (entry) { 2988 var key = entry[0], 2989 value = String(entry[1]); 2990 2991 template = template 2992 .split("[" + key + "]") 2993 .join(value) 2994 .split("[wcpt_" + key + "]") 2995 .join(value); 2996 }); 2997 2998 return template; 2999 } 3000 3001 function update_result_count($container) { 3002 var contextAttr = $container.attr("data-wcpt-result-count-context"), 3003 $resultCounts = $container.find(".wcpt-result-count"); 3004 3005 if (!contextAttr || !$resultCounts.length) { 3006 return; 3007 } 3008 3009 var context = {}; 3010 try { 3011 context = JSON.parse(contextAttr); 3012 } catch (e) { 3013 return; 3014 } 3015 3016 $resultCounts.each(function () { 3017 var $resultCount = $(this); 3018 3019 $resultCount.removeClass( 3020 "wcpt-single-result wcpt-no-results wcpt-single-page", 3021 ); 3022 3023 if (context.html_class) { 3024 $resultCount.addClass(context.html_class); 3025 } 3026 3027 $resultCount 3028 .find(".wcpt-result-message") 3029 .text( 3030 replace_result_count_placeholders( 3031 $resultCount.attr("data-wcpt-message-template"), 3032 context, 3033 ), 3034 ); 3035 3036 $resultCount 3037 .find(".wcpt-single-page-message") 3038 .text( 3039 replace_result_count_placeholders( 3040 $resultCount.attr("data-wcpt-single-page-template"), 3041 context, 3042 ), 3043 ); 3044 3045 $resultCount 3046 .find(".wcpt-single-result-message") 3047 .text( 3048 replace_result_count_placeholders( 3049 $resultCount.attr("data-wcpt-single-result-template"), 3050 context, 3051 ), 3052 ); 3053 3054 $resultCount 3055 .find(".wcpt-no-results-message") 3056 .text( 3057 replace_result_count_placeholders( 3058 $resultCount.attr("data-wcpt-no-results-template"), 3059 context, 3060 ), 3061 ); 3062 });
3063 } 3064 3065 // ajax successful 3066 function ajax_success(response, $container, purpose) { 3067 $("body").trigger("wcpt_before_ajax_container_replace", { 3068 response: response, 3069 $container: $container, 3070 }); 3071 3072 // replace $container inner html 3073 var $new_container = get_response_container(response); 3074 if (purpose === "paginate" && $container.find(".wcpt-navigation").length) { 3075 replace_paginated_content($container, $new_container); 3076 update_header_pagination($container, $new_container); 3077 } else { 3078 $container.html($new_container.html()); 3079 } 3080 3081 sync_container_attributes($container, $new_container); 3082 update_result_count($container); 3083 $container.data("wcpt_last_ajax_purpose", purpose); 3084 3085 // scroll 3086 // -- get params 3087 var sc_attrs_string = $container.attr("data-wcpt-sc-attrs"), 3088 default_offset = 40, 3089 sc_attrs = 3090 sc_attrs_string && sc_attrs_string !== "{}" 3091 ? JSON.parse(sc_attrs_string) 3092 : {}, 3093 offset = { 3094 laptop: 3095 typeof sc_attrs.laptop_scroll_offset == "undefined" || 3096 sc_attrs.laptop_scroll_offset == "" 3097 ? default_offset 3098 : sc_attrs.laptop_scroll_offset, 3099 tablet: 3100 typeof sc_attrs.tablet_scroll_offset == "undefined" || 3101 sc_attrs.tablet_scroll_offset == "" 3102 ? default_offset 3103 : sc_attrs.tablet_scroll_offset, 3104 phone: 3105 typeof sc_attrs.phone_scroll_offset == "undefined" || 3106 sc_attrs.phone_scroll_offset == "" 3107 ? default_offset 3108 : sc_attrs.phone_scroll_offset, 3109 }; 3110 3111 // -- do scroll 3112 var device = wcpt_get_device_2($container); 3113 if ( 3114 (sc_attrs["auto_scroll"] === "true" || 3115 sc_attrs[device + "_auto_scroll"] === "true") && 3116 ["lazy_load", "device_switch", "refresh_table"].indexOf(purpose) === -1 3117 ) { 3118 var offset = offset[device]; 3119 3120 if (isNaN(offset)) { 3121 if (typeof offset === "string") { 3122 // selector 3123 offset = $(offset).height(); 3124 } else if (typeof offset === "object") { 3125 // jQuery object 3126 offset = offset.height(); 3127 } 3128 } 3129 3130 var containerTop = $container.offset().top - parseFloat(offset); 3131 var windowTop = $(window).scrollTop(); 3132 var windowBottom = windowTop + $(window).height(); 3133 3134 if (containerTop < windowTop || containerTop > windowBottom) { 3135 $("html, body").animate( 3136 { 3137 scrollTop: Math.max(0, containerTop), 3138 }, 3139 200, 3140 ); 3141 } 3142 } 3143 } 3144 3145 var build_ajax_query_string = (window.wcpt_build_ajax_query_string = ( 3146 $container, 3147 new_query, 3148 append, 3149 purpose, 3150 ) => { 3151 var table_id = $container.attr("data-wcpt-table-id"); 3152 3153 if (typeof purpose == "undefined") { 3154 throw "WCPT: Define AJAX purpose"; 3155 } 3156 3157 // combine earlier query 3158 var query = "", 3159 earlier_query = $container.attr("data-wcpt-query-string"); 3160 if (append && earlier_query) { 3161 earlier_query = earlier_query.substring(1); 3162 query = "?"; 3163 3164 $.each( 3165 $.extend( 3166 {}, 3167 wcpt_util.parse_query_string(earlier_query), 3168 wcpt_util.parse_query_string(new_query), 3169 ), 3170 function (key, val) { 3171 if (val !== "undefined") { 3172 query += key + "=" + encodeURIComponent(val) + "&"; 3173 } 3174 }, 3175 ); 3176 query = query.substring(0, query.length - 1); 3177 } else { 3178 query = "?" + new_query; 3179 } 3180 3181 // lazy load 3182 if (purpose == "lazy_load") { 3183 query += "&" + window.location.search.substr(1); 3184 } 3185 3186 // persist params 3187 var parsed_params = wcpt_util.parse_query_string( 3188 window.location.search.substring(1), 3189 ); 3190 var query_obj = wcpt_util.parse_query_string(query.substring(1)); 3191 3192 if (typeof window.wcpt_persist_params !== "undefined") { 3193 $.each(wcpt_persist_params, function (index, i) { 3194 if ( 3195 parsed_params[i] !== "undefined" && 3196 typeof parsed_params[i] !== "undefined" && 3197 typeof query_obj[i] == "undefined" 3198 ) { 3199 query += "&" + i + "=" + parsed_params[i]; 3200 } 3201 }); 3202 } 3203 3204 // device 3205 var device = wcpt_get_device_2($container); 3206 3207 var query_obj = wcpt_util.parse_query_string(query); 3208 if (query_obj[table_id + "_device"] !== device) { 3209 query += "&" + table_id + "_device=" + device; 3210 } 3211 3212 // switching device 3213 if (purpose === "device_switch") { 3214 // remove column sort 3215 // first find out if column sort is present 3216 if (query.indexOf(table_id + "_orderby=column_") !== -1) { 3217 query = query.replace( 3218 new RegExp(table_id + "_orderby=column_\\d+", "g"), 3219 "", 3220 ); 3221 query = query.replace( 3222 new RegExp(table_id + "_order=(ASC|DESC)", "g"), 3223 "", 3224 ); 3225 } 3226 } 3227 3228 // shortcode attributes 3229 var sc_attrs = get_sc_attrs($container); 3230 3231 // search - orderby relevance 3232 var new_query_p = new_query ? wcpt_util.parse_query_string(new_query) : {}, 3233 earlier_query_p = earlier_query 3234 ? wcpt_util.parse_query_string(earlier_query.substring(1)) 3235 : {}, 3236 search_orderby = sc_attrs.search_orderby ? "search_orderby" : "relevance", 3237 search_order = sc_attrs.search_order ? "search_order" : ""; 3238 3239 // detect if a new search is taking place in this query 3240 $.each(new_query_p, function (key, val) { 3241 if ( 3242 key.indexOf("search") !== -1 && 3243 val && 3244 earlier_query_p[key] !== val.replace(/\+/g, " ") 3245 ) { 3246 // reset search orderby and order 3247 query += 3248 "&" + 3249 table_id + 3250 "_orderby=" + 3251 search_orderby + 3252 "&" + 3253 table_id + 3254 "_order=" + 3255 search_order; 3256 return false; 3257 } 3258 });
3259 3260 // flag table has been filtered 3261 if (purpose == "filter") { 3262 query += "&" + table_id + "_filtered=true"; 3263 } 3264 3265 // coming from shop? 3266 if (parsed_params[table_id + "_from_shop"]) { 3267 query += "&" + table_id + "_from_shop=true"; 3268 } 3269 3270 // add shortcode attributes 3271 if (!$.isEmptyObject(sc_attrs)) { 3272 // applying fix % conflict with % -> %25 3273 // query += '&' + table_id + '_sc_attrs=' + encodeURIComponent( _sc_attrs.replaceAll('%', '%25') ); 3274 query += 3275 "&" + 3276 table_id + 3277 "_sc_attrs=" + 3278 encodeURIComponent(JSON.stringify(sc_attrs)); 3279 } 3280 3281 if (purpose === "paginate") { 3282 query += "&wcpt_paginate_only=1"; 3283 } 3284 3285 return query; 3286 }); 3287 3288 function get_sc_attrs($container) { 3289 var _sc_attrs = $container.attr("data-wcpt-sc-attrs"), 3290 sc_attrs = _sc_attrs && _sc_attrs !== "{}" ? JSON.parse(_sc_attrs) : {}; 3291 return sc_attrs; 3292 } 3293 3294 function get_column_layout_source_map($container) { 3295 var raw = $container.attr("data-wcpt-column-layout-source-map"); 3296 if (!raw || raw === "{}") return {}; 3297 try { 3298 return JSON.parse(raw); 3299 } catch (e) { 3300 return {}; 3301 } 3302 } 3303 3304 function get_layout_source_for_device(layoutMap, device) { 3305 if (!layoutMap || typeof layoutMap !== "object") return device; 3306 return layoutMap[device] ? layoutMap[device] : device; 3307 } 3308 3309 // get device based on device column container being displayed 3310 function wcpt_get_device_2($container) { 3311 var device = "laptop", 3312 // Single-device container system: device wrapper should always exist. 3313 // Avoid :visible here to prevent timing/CSS interactions causing 3314 // an incorrect "current device" and triggering unnecessary reloads. 3315 $scroll_outer = $container 3316 .find(".wcpt-table-scroll-wrapper-outer") 3317 .first(); 3318 3319 if ($scroll_outer.length) { 3320 if ($scroll_outer.hasClass("wcpt-device-phone")) { 3321 device = "phone"; 3322 } else if ($scroll_outer.hasClass("wcpt-device-tablet")) { 3323 device = "tablet"; 3324 } 3325 } else if ($("body").hasClass("wcpt-nav-modal-on")) { 3326 device = $(".wcpt-nav-modal").attr("data-wcpt-device"); 3327 } else if ( 3328 $( 3329 ".wcpt-required-but-missing-nav-filter-message, .wcpt-no-results", 3330 $container, 3331 ).length 3332 ) { 3333 device = $( 3334 ".wcpt-required-but-missing-nav-filter-message, .wcpt-no-results", 3335 $container, 3336 ).attr("data-wcpt-device"); 3337 } 3338 3339 return device; 3340 } 3341 3342 // request markup from sever and apply callback 3343 var fetch_markup_and_apply_callback = 3344 (window.wcpt_fetch_markup_and_apply_callback = ( 3345 $container, 3346 query, 3347 purpose, 3348 callback, 3349 ) => { 3350 var table_id = $container.attr("data-wcpt-table-id"), 3351 sc_attrs = wcpt_util.get_sc_attrs($container); 3352 3353 // prep url 3354 var url = 3355 wcpt_params.wc_ajax_url.replace("%%endpoint%%", "wcpt_ajax") + 3356 "&" + 3357 query.slice(1), 3358 data = { 3359 wcpt_encrypted_attrs: wcpt_util.get_encrypted_attrs($container), 3360 id: table_id, // @TODO -- remove this 3361 }; 3362 3363 // reload if ajax is disabled 3364 if (sc_attrs.disable_ajax && sc_attrs.disable_ajax !== "false") { 3365 var wcpt_redirect_params = { 3366 $container: $container, 3367 query: query, 3368 base_url: window.location.href.split("?")[0], 3369 purpose: purpose, 3370 proceed: true, 3371 }; 3372 $("body").trigger("wcpt_before_nav_redirect", wcpt_redirect_params); 3373 if (wcpt_redirect_params.proceed) { 3374 window.location.href = wcpt_redirect_params.base_url + "?" + query; 3375 } 3376 return; 3377 } 3378 3379 // allow table from cache @TODO -- convert to query param 3380 var permit_cache = 3381 purpose !== "refresh_table" && typeof WavePlayer === "undefined"; 3382 3383 if ( 3384 // use cache 3385 permit_cache && 3386 window.wcpt_cache.exist(query) 3387 ) { 3388 callback(window.wcpt_cache.get(query), $container, purpose); 3389 $container.data("wcpt_last_ajax_purpose", purpose); 3390 after_every_load($container); 3391 return; 3392 } else { 3393 // else AJAX 3394 $.ajax({
3395 url: url, 3396 method: "POST", 3397 beforeSend: function () { 3398 $container.addClass("wcpt-loading"); 3399 $container.trigger("wcpt_before_ajax"); 3400 return true; 3401 }, 3402 data: data, 3403 }).done(function (response) { 3404 // success 3405 if (response && response.indexOf("wcpt-table") !== -1) { 3406 window.wcpt_cache.set(query, response); // update cache 3407 callback(window.wcpt_cache.get(query), $container, purpose); 3408 $container.data("wcpt_last_ajax_purpose", purpose); 3409 after_every_load($container); 3410 3411 // fail 3412 } else { 3413 console.log("wcpt notice: query fail"); 3414 } 3415 3416 $container.removeClass("wcpt-loading"); 3417 }); 3418 } 3419 }); 3420 3421 // variable product modal form 3422 //-- close modal 3423 $("body").on("click", ".wcpt-modal, .wcpt-close-modal", function (e) { 3424 var $target = $(e.target), 3425 $modal = $(this).closest(".wcpt-modal"); 3426 if ( 3427 $target.hasClass("wcpt-modal") || 3428 $target.closest(".wcpt-close-modal").length 3429 ) { 3430 $modal.trigger("wcpt_close"); 3431 } 3432 }); 3433 3434 window.wcpt_update_cart_items = function (cart) { 3435 var cart_products = {}, 3436 total = 0; 3437 $.each(cart, function (key, item) { 3438 if (!cart_products[item.product_id]) { 3439 cart_products[item.product_id] = 0; 3440 } 3441 3442 if (item.variation_id && !cart_products[item.variation_id]) { 3443 cart_products[item.variation_id] = 0; 3444 } 3445 3446 cart_products[item.product_id] += item.quantity; 3447 3448 if (item.variation_id) { 3449 cart_products[item.variation_id] += item.quantity; 3450 } 3451 3452 total += item.quantity; 3453 }); 3454 3455 // -- update each product row 3456 $(".wcpt-row").each(function () { 3457 var $this = $(this), 3458 id = $this.attr("data-wcpt-variation-id") 3459 ? $this.attr("data-wcpt-variation-id") 3460 : $this.attr("data-wcpt-product-id"), 3461 qty = cart_products[id] ? cart_products[id] : 0, 3462 $badge = $this.find(".wcpt-cart-badge-number"), 3463 $remove = $this.find(".wcpt-remove"); 3464 3465 $this.attr("data-wcpt-in-cart", qty); 3466 3467 if (qty) { 3468 add_count_badge_to_button(qty, $badge.closest(".wcpt-button")); 3469 } else { 3470 $badge.text(""); 3471 } 3472 }); 3473 }; 3474 3475 // anchor tag 3476 $("body").on("click touchstart", "[data-wcpt-href]", function () { 3477 window.location = $(this).attr("data-wcpt-href"); 3478 }); 3479 3480 // accordion 3481 //-- filters 3482 // $('body').on('click', '.wcpt-left-sidebar .wcpt-filter > .wcpt-filter-heading', function(e){ 3483 // if( $(e.target).closest('.wcpt-tooltip').length ){ 3484 // return; 3485 // } 3486 3487 // var $this = $(this), 3488 // $filter = $this.closest('.wcpt-filter'); 3489 // $filter.toggleClass('wcpt-filter-open wcpt-open'); 3490 // }) 3491 // //-- filter clicked directly outside of heading 3492 // $('body').on('click', '.wcpt-left-sidebar .wcpt-filter:not(.wcpt-filter-open)', function(e){ 3493 // var $this = $(this); 3494 // if( e.target === this ){ 3495 // $this.addClass('wcpt-filter-open'); 3496 // } 3497 // }) 3498 //-- taxonomy parent 3499 $("body").on( 3500 "click", 3501 ".wcpt-filter-hierarchy-accordion__trigger", 3502 function (e) { 3503 var $this = $(this); 3504 $this 3505 .closest(".wcpt-filter-hierarchy-accordion") 3506 .toggleClass("wcpt-filter-hierarchy-accordion--open"); 3507 e.stopPropagation(); 3508 return false; 3509 }, 3510 ); 3511 3512 // nav modal 3513 function nav_modal(e) { 3514 var $button = $(e.target).closest(".wcpt-rn-button"), 3515 modal_type = $button.attr("data-wcpt-modal"), 3516 $wcpt = $button.closest(".wcpt"), 3517 wcpt_id = $wcpt.attr("id"), 3518 $nav_modal = $($wcpt.find(".wcpt-nav-modal-tpl").html()), 3519 // Header + sidebar may both contain the same filters â keep one of each 3520 $filters = dedupe_nav_elements( 3521 $wcpt.find(".wcpt-filter").not('[data-wcpt-filter="sort_by"]'), 3522 ), 3523 $search = dedupe_nav_elements($wcpt.find(".wcpt-search-wrapper")), 3524 $sort = dedupe_nav_elements( 3525 $wcpt.find('[data-wcpt-filter="sort_by"].wcpt-filter'), 3526 ), 3527 radios = {}; 3528 3529 $(".wcpt-nm-sort-placeholder", $nav_modal).replaceWith($sort.clone()); 3530 $(".wcpt-nm-filters-placeholder", $nav_modal).replaceWith( 3531 $search.clone().add($filters.clone()), 3532 ); 3533 3534 $( 3535 '.wcpt-nm-sort-placeholder [data-wcpt-filter="sort_by"]', 3536 $nav_modal, 3537 ).addClass("wcpt-open"); 3538 3539 if (modal_type == "sort") { 3540 $nav_modal.addClass("wcpt-show-sort").removeClass("wcpt-show-filters"); 3541 } else { 3542 // filter 3543 $nav_modal.addClass("wcpt-show-filters").removeClass("wcpt-show-sort"); 3544 } 3545 3546 // record radios 3547 $wcpt.find("input[type=radio]:checked").each(function () { 3548 var $this = $(this); 3549 radios[$this.attr("name")] = $this.val(); 3550 });
3551 $nav_modal.data("wcpt-radios", radios); 3552 3553 // ':' at the end of row labels 3554 $nav_modal 3555 .find( 3556 ".wcpt-filter.wcpt-options-row > .wcpt-filter-heading > .wcpt-options-heading > .wcpt-item-row > .wcpt-text:last-child", 3557 ) 3558 .each(function () { 3559 var $this = $(this), 3560 text = $this.text().trim(); 3561 3562 if (text.substr(-1) === ":") { 3563 $this.text(text.substr(0, text.length - 1)); 3564 } 3565 }); 3566 3567 // duplicate header 3568 // if( ! $nav_modal.find('.wcpt-nm-heading--sticky').length ){ 3569 // var $header = $nav_modal.find('.wcpt-nm-heading'); 3570 // $header.clone().addClass('wcpt-nm-heading--sticky').insertAfter( $header ); 3571 // } 3572 3573 // switch option row to dropdown 3574 const switch_classes = { 3575 "wcpt-options-row": "wcpt-dropdown", 3576 "wcpt-option": "wcpt-dropdown-option", 3577 "wcpt-options-heading": "wcpt-dropdown-label", 3578 "wcpt-options": " wcpt-dropdown-menu", 3579 }; 3580 $.each(switch_classes, function (key, value) { 3581 $nav_modal 3582 .find("." + key) 3583 .removeClass(key) 3584 .addClass(value); 3585 }); 3586 3587 // append 3588 window.wcpt_nav_modal_scroll = window.scrollY; 3589 3590 $("body") 3591 .trigger("wcpt-nav-modal-on") 3592 .addClass("wcpt-nav-modal-on") 3593 .append('<div class="wcpt-nav-modal-backdrop"></div>') 3594 .append($nav_modal); 3595 3596 $(".wcpt-nav-modal-backdrop").one("click", function () { 3597 $nav_modal.trigger("wcpt_close"); 3598 }); 3599 3600 // open filter dropdown if its alone 3601 $other_filters = $nav_modal.find( 3602 '.wcpt-filter:not([data-wcpt-filter="sort_by"])', 3603 ); 3604 3605 if ($other_filters.length === 1) { 3606 $other_filters.addClass("wcpt-filter-open wcpt-open"); 3607 } 3608 3609 // multirange 3610 $(".wcpt-range-slider-wrapper", $nav_modal).each(function () { 3611 var $this = $(this), 3612 $original = $this.children(".original"), 3613 $ghost = $this.children(".ghost"), 3614 $new_slider = $("<input/>").attr({ 3615 type: "range", 3616 class: "wcpt-range-slider", 3617 min: $original.attr("min"), 3618 max: $original.attr("max"), 3619 step: $original.attr("step"), 3620 value: $original.attr("data-wcpt-initial-value"), 3621 }); 3622 3623 $original.add($ghost).remove(); 3624 3625 $this.append($new_slider); 3626 wcpt__multirange($new_slider[0]); 3627 }); 3628 3629 // apply 3630 //-- filter 3631 $nav_modal.find(".wcpt-nm-apply").on("click", function () { 3632 var $nav_clone = $nav_modal.clone(); 3633 3634 nav_clone_operations($nav_clone); 3635 3636 var query = $("<form>").append($nav_clone).serialize(), 3637 $container = $("#" + wcpt_id); 3638 3639 $nav_modal.trigger("wcpt_close"); 3640 3641 attempt_ajax($container, query, false, "filter"); 3642 }); 3643 //-- sort 3644 $nav_modal.filter(".wcpt-show-sort").on("change", function () { 3645 var $nav_clone = $nav_modal.clone(); 3646 3647 nav_clone_operations($nav_clone); 3648 3649 var query = $("<form>").append($nav_clone).serialize(), 3650 $container = $("#" + wcpt_id); 3651 3652 $nav_modal.trigger("wcpt_close"); 3653 3654 attempt_ajax($container, query, false, "filter"); 3655 }); 3656 3657 // clear 3658 $nav_modal.find(".wcpt-nm-reset").on("click", function () { 3659 var query = $("<form>").append($nav_modal.clone()).serialize(), 3660 $container = $("#" + wcpt_id), 3661 query = ""; 3662 3663 $nav_modal.trigger("wcpt_close"); 3664 3665 attempt_ajax($container, query, false, "filter"); 3666 }); 3667 3668 // close 3669 $nav_modal.find(".wcpt-nm-close").on("click", function (e) { 3670 e.preventDefault(); 3671 3672 var $container = $("#" + wcpt_id), 3673 radios = $.extend({}, $nav_modal.data("wcpt-radios")); 3674 3675 $nav_modal.trigger("wcpt_close"); 3676 3677 $.each(radios, function (name, val) { 3678 $wcpt 3679 .find('input[type=radio][name="' + name + '"][value="' + val + '"]') 3680 .each(function () { 3681 $(this).prop("checked", "checked"); 3682 }); 3683 }); 3684 }); 3685 3686 // scroll fix 3687 // var prev_y = false; 3688 // $(".wcpt-nav-modal") 3689 // .on("touchstart", function (e) { 3690 // prev_y = e.originalEvent.touches[0].clientY; 3691 // }) 3692 // .on("touchmove", function (e) { 3693 // if ( 3694 // (e.originalEvent.touches[0].clientY > prev_y && !this.scrollTop) || 3695 // (e.originalEvent.touches[0].clientY < prev_y && 3696 // this.scrollTop === this.scrollHeight - this.offsetHeight) 3697 // ) { 3698 // e.preventDefault(); 3699 // } 3700 // }); 3701 } 3702 3703 $("body").on("wcpt_close", ".wcpt-nav-modal", function () { 3704 var $this = $(this), 3705 table_id = $this.attr("data-wcpt-table-id"), 3706 $container = $("#wcpt-" + table_id); 3707 3708 $("body").trigger("wcpt-nav-modal-off").removeClass("wcpt-nav-modal-on"); 3709 3710 $(".wcpt-nav-modal-backdrop").remove(); 3711 $this.remove(); 3712 window.scroll(null, wcpt_nav_modal_scroll); 3713 }); 3714 3715 // row option change ev 3716 $("body").on("change", ".wcpt-options-row .wcpt-option input", function () { 3717 var $this = $(this), 3718 $label = $this.closest(".wcpt-option "); 3719 3720 if ($this.is(":radio")) { 3721 if (this.checked) { 3722 $label.addClass("wcpt-active").siblings().removeClass("wcpt-active"); 3723 } 3724 } else { 3725 // checkbox 3726 if (this.checked) { 3727 $label.addClass("wcpt-active"); 3728 } else { 3729 $label.removeClass("wcpt-active"); 3730 } 3731 } 3732 }); 3733 3734 // toggle 3735 $("body").on("click", ".wcpt-tg-trigger", function () { 3736 var $this = $(this), 3737 $toggle = $this.closest(".wcpt-toggle"), 3738 $table = $this.closest(".wcpt-table"); 3739 3740 $toggle.toggleClass(" wcpt-tg-on wcpt-tg-off "); 3741 }); 3742 3743 $("body").on("click", ".wcpt-rn-filter, .wcpt-rn-sort", nav_modal); 3744 3745 // photoswipe 3746 3747 // -- open 3748 function init_photoswipe($this, index, append_item) { 3749 // photoswipe gallery 3750 if ( 3751 typeof PhotoSwipe !== "undefined" && 3752 typeof PhotoSwipeUI_Default !== "undefined" 3753 ) { 3754 var items = JSON.parse($this.attr("data-wcpt-photoswipe-items")), 3755 index = typeof index == "undefined" || !index ? 0 : parseInt(index), 3756 color_theme = $this.attr("data-wcpt-lightbox-color-theme") || "black"; 3757 3758 // append items 3759 if (append_item) { 3760 items.push(append_item); 3761 } 3762 3763 var index_src = items[index].src; 3764 3765 // remove duplicates 3766 var unique_src = [], 3767 _items = []; 3768 3769 $.each(items, function (index2, item) { 3770 if (-1 === $.inArray(item.src, unique_src)) { 3771 _items.push(item); 3772 unique_src.push(item.src); 3773 } 3774 }); 3775 items = _items; 3776 3777 var options = JSON.parse($this.attr("data-wcpt-photoswipe-options")), 3778 photoswipe = new PhotoSwipe( 3779 $(".pswp")[0], 3780 PhotoSwipeUI_Default, 3781 items, 3782 options, 3783 ); 3784 3785 photoswipe.init(); 3786 3787 // add wcpt-photoswipe class and trigger selector attr 3788 var color_theme_class = "wcpt-photoswipe--theme-" + color_theme; 3789 $(".pswp").addClass("wcpt-photoswipe " + color_theme_class); 3790 3791 // add wcpt class on photoswipe and trigger selector 3792 3793 // reposition index in case item was deleted 3794 $.each(items, function (index3, item) {
3795 if (item.src === index_src) { 3796 index = index3; 3797 } 3798 }); 3799 3800 photoswipe.goTo(index); 3801 3802 var $body = $("body"); 3803 3804 $body.addClass("wcpt-photoswipe-visible"); 3805 photoswipe.listen("close", function () { 3806 setTimeout(function () { 3807 // fix photoswipe click through bug 3808 $body.removeClass("wcpt-photoswipe-visible"); 3809 }, 10); 3810 }); 3811 3812 // remove wcpt-photoswipe class and trigger selector attr after closing 3813 photoswipe.listen("destroy", function () { 3814 $(".pswp").removeClass("wcpt-photoswipe " + color_theme_class); 3815 }); 3816 3817 $(photoswipe.container).data("wcpt_photoswipe", photoswipe); 3818 3819 return true; 3820 } else { 3821 return false; 3822 } 3823 } 3824 3825 // -- close 3826 $("body").on("click", ".pswp__container", function (e) { 3827 var $this = $(this), 3828 photoswipe = $this.data("wcpt_photoswipe"); 3829 3830 if (window.innerWidth < 720 && photoswipe) { 3831 var $target = $(e.target); 3832 if (!$target.closest(".pswp__button").length) { 3833 photoswipe.close(); 3834 } 3835 } 3836 }); 3837 3838 // gallery strip 3839 3840 // -- image 3841 $("body").on("click", ".wcpt-gallery__item", function () { 3842 var $this = $(this), 3843 index = parseInt($this.attr("data-wcpt-gallery-item")), 3844 $gallery = $this.closest(".wcpt-gallery"); 3845 3846 if (!$gallery.hasClass("wcpt-gallery--include-featured")) { 3847 index += 1; 3848 } 3849 3850 init_photoswipe($gallery, index); 3851 }); 3852 3853 // -- link 3854 $("body").on("click", ".wcpt-gallery a", function (e) { 3855 e.preventDefault(); 3856 3857 var $this = $(this), 3858 $gallery = $this.closest(".wcpt-gallery"); 3859 3860 init_photoswipe($gallery); 3861 }); 3862 3863 // image lightbox 3864 $("body").on("click", ".wcpt-lightbox-enabled", function () { 3865 destroy_offset_zoom_containers(); 3866 3867 var $this = $(this); 3868 if ( 3869 $this 3870 .closest(".wcpt") 3871 .hasClass("wcpt-quick-view-trigger--product-image") && 3872 !$this 3873 .closest(".wcpt-row") 3874 .hasClass("wcpt-quick-view-trigger__disabled-for-product") 3875 ) { 3876 return; 3877 } 3878 3879 var index = 0, 3880 $row = get_product_rows($this), 3881 src = $this.attr("data-wcpt-lightbox"), 3882 pswp_items = JSON.parse($this.attr("data-wcpt-photoswipe-items")), 3883 append_item = false; 3884 3885 if ( 3886 $row.attr("data-wcpt-type") === "variable" && 3887 $row.data("wcpt_variation_selected") 3888 ) { 3889 var variation = $row.data("wcpt_variation"), 3890 src = variation.image.full_src, 3891 found = false; 3892 3893 $.each(pswp_items, function (_index, item) { 3894 if (item.src == src) { 3895 index = _index; 3896 found = true; 3897 return false; 3898 } 3899 }); 3900 3901 if (!found) { 3902 append_item = { 3903 src: variation.image.full_src, 3904 w: variation.image.full_src_w, 3905 h: variation.image.full_src_h, 3906 title: variation.image.title, 3907 }; 3908 3909 index = pswp_items.length; 3910 } 3911 } else { 3912 // get starting index 3913 if ($this.attr("data-wcpt-photoswipe-items")) { 3914 $.each(pswp_items, function (_index, item) { 3915 if (item.src == src) { 3916 index = _index; 3917 return false; 3918 } 3919 }); 3920 } 3921 } 3922 3923 if (!init_photoswipe($this, index, append_item)) { 3924 var $el = $( 3925 '<div class="wcpt-lightbox-screen"><div class="wcpt-lightbox-loader"></div><div class="wcpt-lightbox-close"></div><img class="wcpt-lightbox-image" src="' + 3926 src + 3927 '"></div>', 3928 ); 3929 $("body").append($el); 3930 $el.on("click ", function () { 3931 $el.remove(); 3932 }); 3933 } 3934 }); 3935 3936 // image zoom 3937 //-- image hover 3938 $("body").on( 3939 "mouseenter", 3940 '.wcpt-zoom-enabled[data-wcpt-zoom-trigger="image_hover"]', 3941 function () { 3942 var $this = $(this), 3943 level = $this.attr("data-wcpt-zoom-level"); 3944 if (!level) { 3945 level = "1.5"; 3946 } 3947 3948 if ($this.closest(".wcpt-device-tablet, .wcpt-device-phone").length) { 3949 return; 3950 } 3951 3952 $this.css({ 3953 transform: "scale(" + level + ")", 3954 "z-index": "2", 3955 }); 3956 3957 $this.one("mouseleave", function () { 3958 $this.css({ 3959 transform: "", 3960 "z-index": "", 3961 }); 3962 }); 3963 }, 3964 ); 3965 //-- row hover 3966 $("body").on("mouseenter", ".wcpt-row", function () { 3967 var $row = $(this); 3968 $row 3969 .find('.wcpt-zoom-enabled[data-wcpt-zoom-trigger="row_hover"]') 3970 .each(function () { 3971 var $zoom_me = $(this), 3972 level = $zoom_me.attr("data-wcpt-zoom-level"); 3973 if (!level) { 3974 level = "1.5"; 3975 } 3976 3977 if ( 3978 $zoom_me.closest(".wcpt-device-tablet, .wcpt-device-phone").length 3979 ) { 3980 return; 3981 } 3982 3983 $zoom_me.css({ 3984 transform: "scale(" + level + ")", 3985 "z-index": "2", 3986 }); 3987 3988 $row.one("mouseleave", function () { 3989 $zoom_me.css({ 3990 transform: "", 3991 "z-index": "", 3992 }); 3993 }); 3994 }); 3995 }); 3996 3997 // product image offset zoom 3998 // -- attach hover handler 3999 $("body").on(
4000 "mouseenter.wcpt_offset_zoom", 4001 ".wcpt-product-image-wrapper--offset-zoom-enabled, .wcpt-gallery--offset-zoom-enabled .wcpt-gallery__item-wrapper", 4002 function (e) { 4003 var $this = $(this), 4004 $img = $this.find("img").not(".wcpt-product-image-on-hover").first(), 4005 zoom_src = $this.attr("data-wcpt-offset-zoom-image-src"), 4006 $offset_zoom_img = $("<img>", { 4007 class: "wcpt-offset-zoom-container__image", 4008 src: zoom_src || $img.attr("src"), 4009 }); 4010 4011 var $offset_zoom = $("<div>", { 4012 class: 4013 "wcpt-offset-zoom-container " + 4014 $this.attr("data-wcpt-offset-zoom-image-html-class"), 4015 }).append($offset_zoom_img), 4016 $wcpt = $this.closest(".wcpt"), 4017 original_title = $img.attr("title"); 4018 4019 // Remove the title on mouseenter and store original in data attr 4020 $img.data("wcpt-original-title", original_title); 4021 $img.removeAttr("title"); 4022 4023 if ($this.closest(".frzTbl--grab-and-scroll--grabbing").length) { 4024 return; 4025 } 4026 4027 destroy_offset_zoom_containers(); 4028 4029 $wcpt.append($offset_zoom).on("mousemove.wcpt_offset_zoom", function (e) { 4030 position_offset_zoom_container(e, $offset_zoom, $this); 4031 }); 4032 4033 setTimeout(function () { 4034 $offset_zoom.addClass("wcpt-offset-zoom-container--fade-in"); 4035 }, 50); 4036 4037 $this.on("mouseleave", function () { 4038 // Restore the original title on mouseleave 4039 var $img = $this.find("img"), 4040 original_title = $img.data("wcpt-original-title"); 4041 if (typeof original_title !== "undefined") { 4042 $img.attr("title", original_title); 4043 } 4044 destroy_offset_zoom_containers(); 4045 }); 4046 }, 4047 ); 4048 // -- turn off hover handler on touch screens 4049 $("body").on("touchstart", function () { 4050 $("body").off("mouseenter.wcpt_offset_zoom"); 4051 }); 4052 4053 function position_offset_zoom_container(e, $offset_zoom, $trigger) { 4054 var left = e.originalEvent.clientX + 40, 4055 top = e.originalEvent.clientY, 4056 position = "right"; 4057 4058 $offset_zoom.css({ 4059 left: left, 4060 top: top, 4061 }); 4062 4063 var rect = $offset_zoom.get(0).getBoundingClientRect(), 4064 viewport_width = 4065 window.innerWidth || document.documentElement.clientWidth, 4066 viewport_height = 4067 window.innerHeight || document.documentElement.clientHeight; 4068 4069 if (rect.right > viewport_width) { 4070 position = "left"; 4071 } 4072 4073 if (position == "left") { 4074 left = $trigger.get(0).getBoundingClientRect().left - 40 - rect.width; 4075 } 4076 4077 if (rect.top < 0) { 4078 top = 0 + 0.25 * rect.height; 4079 } else if (rect.bottom > viewport_height) { 4080 top -= rect.bottom - viewport_height; 4081 } 4082 4083 $offset_zoom.css({ 4084 left: left, 4085 top: top, 4086 }); 4087 } 4088 4089 function destroy_offset_zoom_containers() { 4090 $(".wcpt-offset-zoom-container").remove(); 4091 $body.off("mousemove.wcpt_offset_zoom"); 4092 } 4093 4094 // uncheck variation radio 4095 $("body").on("click", ".wcpt-variation-radio", function (e) { 4096 var $this = $(this), 4097 $variation = $this.closest(".wcpt-select-variation"), 4098 $row = $this.closest(".wcpt-row"); 4099 4100 if ( 4101 $variation.hasClass("wcpt-selected") && 4102 window.navigator.userAgent.indexOf("Edge") == -1 4103 ) { 4104 $this.prop("checked", false); 4105 $this.change(); 4106 4107 $row.trigger("select_variation", { 4108 variation_id: false, 4109 complete_match: false, 4110 attributes: false, 4111 variation: false, 4112 variation_found: false, 4113 variation_selected: false, 4114 variation_available: false, 4115 }); 4116 } 4117 }); 4118 4119 // variation selected class toggle 4120 $("body").on("change", ".wcpt-variation-radio", function () { 4121 var $this = $(this), 4122 $others = $( 4123 '.wcpt-variation-radio[name="' + $(this).attr("name") + '"]', 4124 ).not($(this)), 4125 $variation = $this.closest(".wcpt-select-variation"); 4126 4127 if ($this.is(":checked")) { 4128 $variation.addClass("wcpt-selected"); 4129 } else { 4130 $variation.removeClass("wcpt-selected"); 4131 } 4132 4133 $others 4134 .not(":checked") 4135 .closest(".wcpt-select-variation") 4136 .removeClass("wcpt-selected"); 4137 }); 4138 4139 // select variation (main) 4140 //-- sync 4141 $("body").on( 4142 "select_variation", 4143 ".wcpt-product-type-variable", 4144 function (e, data) { 4145 var $row = get_product_rows($(this)); 4146 4147 // update dropdown 4148 var $variation_dropdown = $row.find(".wcpt-select-variation-dropdown"); 4149 $variation_dropdown.val(data.variation_id ? data.variation_id : ""); 4150 4151 // update radio 4152 $row 4153 .find('.wcpt-variation-radio[value="' + data.variation_id + '"]') 4154 .prop("checked", true); 4155 4156 // update form 4157 $row.find(".variations_form").each(function () { 4158 var $this = $(this); 4159 current_variation_id = 4160 $(".variation_id", $this).val() === "0" 4161 ? "" 4162 : $(".variation_id", $this).val(); 4163 4164 if (data.variation_id != current_variation_id) { 4165 window.wcpt_form_reset_flag = true; 4166 $(".reset_variations", $this).trigger("click.wc-variation-form"); 4167 window.wcpt_form_reset_flag = false; 4168 4169 // select variation in form 4170 if (data.variation_id) { 4171 $(".variations select", $this).each(function () { 4172 var $this = $(this), 4173 name = $this.attr("name"); 4174 4175 if (typeof data.attributes[name] !== "undefined") { 4176 $this.val(data.attributes[name]); 4177 } else { 4178 $this.val(""); 4179 } 4180 }); 4181 4182 $this.trigger("check_variations"); 4183 } 4184 } 4185 }); 4186 4187 // update row 4188 $row.data("wcpt_variation", data.variation); 4189 $row.data("wcpt_variation_id", data.variation_id); 4190 $row.data("wcpt_complete_match", data.complete_match); 4191 $row.data("wcpt_attributes", data.attributes); 4192 $row.data("wcpt_variation_found", data.variation_found); 4193 $row.data("wcpt_variation_selected", data.variation_selected); 4194 $row.data("wcpt_variation_available", data.variation_available); 4195 $row.data("wcpt_variation_qty", data.variation_qty); 4196 4197 // update total 4198 update_row_total($row); 4199 update_table_add_selected_to_cart.call($row.get(0)); 4200 4201 // reset 4202 if (!data.variation_selected) { 4203 // -- add to cart buton 4204 var $button = $row.find('[data-wcpt-link-code^="cart"]'); 4205 4206 // -- -- no variation selected 4207 if ( 4208 $row.find(".wcpt-add-to-cart-wrapper").length || // cart form 4209 $variation_dropdown.length || // select variation -- dropdown 4210 $row.find(".wcpt-variation-radio").length // select variation -- radio 4211 ) { 4212 disable_button($button, "wcpt-no-variation-selected"); 4213 } 4214 4215 // -- -- out of stock 4216 if ($row.hasClass("wcpt-all-variations-out-of-stock")) { 4217 disable_button($button, "wcpt-all-variations-out-of-stock"); 4218 } else { 4219 enable_button($button, "wcpt-all-variations-out-of-stock"); 4220 } 4221 4222 // -- checkbox (UI only â do not clear cross-page persisted checkbox state) 4223 $row.first().trigger("wcpt_checkbox_change", false); 4224 4225 // -- qty input 4226 var $qty_wrapper = $row.find(".wcpt-quantity"), 4227 $qty = $qty_wrapper.find("input[type=number].qty"); 4228 4229 // -- -- disabled 4230 $qty_wrapper.addClass("wcpt-disabled"); 4231 $qty.attr("disabled", "disabled"); 4232 4233 if ($qty.length) { 4234 $qty.each(function () { 4235 var $this = $(this), 4236 inital_value = $this.attr("data-wcpt-initial-value"), 4237 value = ""; 4238 4239 if (inital_value === "0") { 4240 value = 0; 4241 } else if (inital_value == "min") { 4242 // No variation selected â max is 0 4243 value = 0; 4244 } else { 4245 value = ""; 4246 } 4247 4248 $this.attr({ 4249 min: "0", 4250 max: "0", 4251 step: "", 4252 value: value, 4253 }); 4254 4255 $this.val(value); 4256 4257 limit_qty_controller($this.closest(".wcpt-quantity")); 4258 }); 4259 } 4260 4261 // -- product image 4262 var $product_image_wrapper = $(".wcpt-product-image-wrapper", $row), 4263 $product_image = $( 4264 ".wcpt-product-image-wrapper > img:not(.wcpt-product-image-on-hover)", 4265 $row, 4266 ), 4267 $original_row = wcpt_get_original_row($row); 4268 4269 if ($product_image_wrapper.length) { 4270 if (!$original_row.data("wcpt_default_image")) { 4271 if ($product_image[0]) { 4272 // lazy load fix 4273 $original_row.data( 4274 "wcpt_default_image", 4275 $product_image[0].outerHTML, 4276 ); 4277 } else { 4278 handle_product_image_lazy_load($product_image_wrapper); 4279 } 4280 } else { 4281 $product_image.replaceWith( 4282 $original_row.data("wcpt_default_image"), 4283 ); 4284 } 4285 4286 if ($product_image_wrapper.hasClass("wcpt-lightbox-enabled")) { 4287 if (!$product_image_wrapper.attr("data-wcpt-lightbox--original")) { 4288 $product_image_wrapper.attr( 4289 "data-wcpt-lightbox--original", 4290 $product_image_wrapper.attr("data-wcpt-lightbox"), 4291 ); 4292 } else { 4293 $product_image_wrapper.attr( 4294 "data-wcpt-lightbox", 4295 $product_image_wrapper.attr("data-wcpt-lightbox--original"), 4296 ); 4297 } 4298 } 4299 4300 if ( 4301 $product_image_wrapper.hasClass( 4302 "wcpt-product-image-wrapper--offset-zoom-enabled", 4303 ) 4304 ) { 4305 if ( 4306 !$product_image_wrapper.attr( 4307 "data-wcpt-offset-zoom-image-src--original", 4308 ) 4309 ) { 4310 $product_image_wrapper.attr( 4311 "data-wcpt-offset-zoom-image-src--original", 4312 $product_image_wrapper.attr("data-wcpt-offset-zoom-image-src"), 4313 ); 4314 } else { 4315 $product_image_wrapper.attr( 4316 "data-wcpt-offset-zoom-image-src", 4317 $product_image_wrapper.attr( 4318 "data-wcpt-offset-zoom-image-src--original", 4319 ), 4320 ); 4321 } 4322 } 4323 } 4324 4325 // -- sku 4326 $row.find(".wcpt-sku").each(function () { 4327 var $sku = $(this), 4328 sku = $sku.attr("data-wcpt-sku"); 4329 var $label = $sku.find(".wcpt-property-label"); 4330 if ($label.length) { 4331 $sku.html($label.prop("outerHTML") + sku); 4332 } else { 4333 $sku.text(sku); 4334 } 4335 }); 4336 4337 // -- gtin 4338 $row.find(".wcpt-gtin").each(function () { 4339 var $gtin = $(this), 4340 gtin = $gtin.attr("data-wcpt-gtin"); 4341 $gtin.text(gtin); // revert to variable product gtin 4342 }); 4343 4344 // -- product id 4345 $row.find(".wcpt-product-id").each(function () { 4346 var $product_id = $(this), 4347 product_id = $product_id.attr("data-wcpt-product-id"); 4348 $product_id.text(product_id); // revert to variable product ID 4349 }); 4350 4351 // -- price 4352 4353 // -- -- wcpt price element 4354 $row 4355 .filter(".wcpt-product-type-variable") 4356 .find(".wcpt-price.wcpt-variable-switch") 4357 .each(function () { 4358 var $this = $(this), 4359 id = $this.attr("data-wcpt-element-id"), 4360 tpl = $this.attr("data-wcpt-variable-template"), 4361 template_html = $( 4362 "[data-wcpt-element-id=" + 4363 id + 4364 "][data-wcpt-price-type=" + 4365 tpl + 4366 "]", 4367 ).html(), 4368 $html, 4369 o = [ 4370 "highest-price", 4371 "lowest-price", 4372 "sale-price", 4373 "regular-price", 4374 ]; 4375 4376 // No template yet (paginate / infinite scroll before merge) â keep 4377 // the server-rendered default price instead of wiping it blank. 4378 if (!template_html) { 4379 return; 4380 } 4381 4382 $html = $(template_html); 4383 4384 $.each(o, function (index, val) { 4385 $(".wcpt-" + val + " .wcpt-amount", $html).text( 4386 $this.attr("data-wcpt-" + val), 4387 ); 4388 }); 4389 4390 $this.html($html); 4391 4392 if (tpl == "sale") { 4393 $this.addClass("wcpt-product-on-sale"); 4394 } else { 4395 $this.removeClass("wcpt-product-on-sale"); 4396 } 4397 }); 4398 4399 // -- -- default woocommerce template 4400 $row 4401 .filter(".wcpt-product-type-variable") 4402 .find(".wcpt-variable-price-default-woocommerce-template") 4403 .each(function () { 4404 var $this = $(this); 4405 $default = $(".wcpt-variable-switch__default", $this); 4406 $default.show().next(".price").remove(); 4407 }); 4408 4409 // -- -- price attribute 4410 $row.attr("data-wcpt-price", ""); 4411 4412 // -- on sale 4413 $row 4414 .filter(".wcpt-product-type-variable") 4415 .find(".wcpt-on-sale.wcpt-variable-switch") 4416 .each(function () { 4417 var $this = $(this); 4418 $this.addClass("wcpt-hide"); 4419 }); 4420 4421 // -- availability 4422 $row 4423 .filter(".wcpt-product-type-variable") 4424 .find(".wcpt-availability.wcpt-variable-switch") 4425 .each(function () { 4426 var $this = $(this), 4427 id = $this.attr("data-wcpt-element-id"), 4428 stock = $this.attr("data-wcpt-stock"), 4429 message_tpl = $this.attr("data-wcpt-default-message_tpl"), 4430 stock_class = $this.attr("data-wcpt-default-stock_class"), 4431 message = $( 4432 "[data-wcpt-element-id=" + 4433 id + 4434 '][data-wcpt-availability-message="' + 4435 message_tpl + 4436 '"]', 4437 ).html(), 4438 $label = $this.find(".wcpt-property-label"), 4439 label_html = $label.length ? $label.prop("outerHTML") : "", 4440 $message = $($.parseHTML("<span>" + message + "</span>")); 4441 4442 $this.html( 4443 label_html + 4444 "<span class='" + 4445 stock_class + 4446 "'>" + 4447 $message 4448 .find(".wcpt-stock-placeholder") 4449 .text(stock) 4450 .end() 4451 .html() + 4452 "</span>", 4453 ); 4454 }); 4455 4456 // -- stock 4457 $row 4458 .filter(".wcpt-product-type-variable") 4459 .find(".wcpt-stock") 4460 .each(function () { 4461 var $this = $(this), 4462 stock = $this.attr("data-wcpt-stock"), 4463 rules = $this.attr("data-wcpt-stock-range-labels"), 4464 parsed_rules = !rules || rules == "{}" ? [] : JSON.parse(rules), 4465 $label = $this.find(".wcpt-property-label"), 4466 label_html = $label.length ? $label.prop("outerHTML") : "", 4467 label = stock; 4468 4469 var found_rule = false; 4470 4471 if (stock && parsed_rules.length) { 4472 $.each(parsed_rules, function (index, rule) { 4473 if (rule[0] <= stock && rule[1] >= stock) { 4474 label = rule[2]; 4475 found_rule = true; 4476 } 4477 }); 4478 } 4479 4480 if (!found_rule && stock < 0) { 4481 stock = ""; 4482 label = ""; 4483 } 4484 4485 $this.html(label_html + (label + "").replace("[stock]", stock)); 4486 }); 4487 4488 // -- dimensions 4489 $row 4490 .filter(".wcpt-product-type-variable") 4491 .find(".wcpt-dimensions") 4492 .each(function () { 4493 var $this = $(this); 4494
4494 $this.html($this.attr("data-wcpt-default-dimensions")); 4495 }); 4496 4497 // -- height 4498 $row 4499 .filter(".wcpt-product-type-variable") 4500 .find(".wcpt-height") 4501 .each(function () { 4502 var $this = $(this), 4503 template = $this.attr("data-wcpt-template"), 4504 empty_template = $this.attr("data-wcpt-empty-template"), 4505 height = $this.attr("data-wcpt-default-height"); 4506 4507 if (!height) { 4508 $this.html(empty_template ? empty_template : ""); 4509 } else { 4510 $this.html( 4511 template ? template.replace("{n}", height) : empty_template, 4512 ); 4513 } 4514 }); 4515 4516 // -- width 4517 $row 4518 .filter(".wcpt-product-type-variable") 4519 .find(".wcpt-width") 4520 .each(function () { 4521 var $this = $(this), 4522 template = $this.attr("data-wcpt-template"), 4523 empty_template = $this.attr("data-wcpt-empty-template"), 4524 width = $this.attr("data-wcpt-default-width"); 4525 4526 if (!width) { 4527 $this.html(empty_template ? empty_template : ""); 4528 } else { 4529 $this.html(template ? template.replace("{n}", width) : width); 4530 } 4531 }); 4532 4533 // -- length 4534 $row 4535 .filter(".wcpt-product-type-variable") 4536 .find(".wcpt-length") 4537 .each(function () { 4538 var $this = $(this), 4539 template = $this.attr("data-wcpt-template"), 4540 empty_template = $this.attr("data-wcpt-empty-template"), 4541 length = $this.attr("data-wcpt-default-length"); 4542 4543 if (!length) { 4544 $this.html(empty_template ? empty_template : ""); 4545 } else { 4546 $this.html(template ? template.replace("{n}", length) : length); 4547 } 4548 }); 4549 4550 // -- weight 4551 $row 4552 .filter(".wcpt-product-type-variable") 4553 .find(".wcpt-weight") 4554 .each(function () { 4555 var $this = $(this), 4556 template = $this.attr("data-wcpt-template"), 4557 empty_template = $this.attr("data-wcpt-empty-template"), 4558 weight = $this.attr("data-wcpt-default-weight"); 4559 4560 if (!weight) { 4561 $this.html(empty_template ? empty_template : ""); 4562 } else { 4563 $this.html(template ? template.replace("{n}", weight) : weight); 4564 } 4565 }); 4566 4567 // -- custom field 4568 $row 4569 .filter(".wcpt-product-type-variable") 4570 .find(".wcpt-custom-field.wcpt-variable-switch") 4571 .each(function () { 4572 var $this = $(this), 4573 element_id = $this.attr("data-wcpt-element-id"), 4574 product_id = $this 4575 .closest(".wcpt-row") 4576 .attr("data-wcpt-product-id"), 4577 table_id = $this.closest(".wcpt").attr("data-wcpt-table-id"), 4578 $label = $this.find(".wcpt-property-label"), 4579 label_html = $label.length ? $label.prop("outerHTML") : ""; 4580 4581 if ( 4582 "undefined" !== 4583 typeof window["wcpt_" + table_id + "_variable_switch_cf"] 4584 ) { 4585 var cf_vals = window["wcpt_" + table_id + "_variable_switch_cf"]; 4586 if ("undefined" !== typeof cf_vals[element_id]) { 4587 $this.html(label_html + cf_vals[element_id][product_id]); 4588 } 4589 } 4590 }); 4591 } 4592 4593 if (!$.isEmptyObject(data.variation)) { 4594 // -- add to cart buton 4595 var $button = $row.find('[data-wcpt-link-code^="cart"]'); 4596 4597 // -- -- no variation selected 4598 enable_button($button, "wcpt-no-variation-selected"); 4599 4600 // -- -- out of stock 4601 if (data.variation.is_in_stock) { 4602 enable_button($button, "wcpt-variation-out-of-stock"); 4603 } else { 4604 disable_button($button, "wcpt-variation-out-of-stock"); 4605 } 4606 4607 // -- qty input 4608 var $qty_wrapper = $row.find(".wcpt-quantity"), 4609 $qty = $qty_wrapper.find("input[type=number].qty"); 4610 4611 // -- -- disabled 4612 if (data.variation.is_in_stock) { 4613 $qty_wrapper.removeClass("wcpt-disabled"); 4614 $qty.removeAttr("disabled"); 4615 } else { 4616 $qty_wrapper.addClass("wcpt-disabled"); 4617 $qty.attr("disabled", "disabled"); 4618 } 4619 4620 if ($qty.length) { 4621 $qty.each(function () { 4622 var $this = $(this), 4623 $qty_wrapper = $this.closest(".wcpt-quantity"), 4624 inital_value = $this.attr("data-wcpt-initial-value"), 4625 min = data.variation.min_qty 4626 ? parseFloat(data.variation.min_qty) 4627 : 1, 4628 max = data.variation.max_qty 4629 ? parseFloat(data.variation.max_qty) 4630 : "", 4631 step = data.variation.step ? parseFloat(data.variation.step) : "", 4632 value = ""; 4633 4634 // validate val 4635 var current_val = parseFloat($this.val()); 4636 if (current_val && current_val !== NaN) { 4637 // neither empty, nor 0 4638 if (current_val < min) { 4639 value = min; 4640 } else if (max && current_val > max) { 4641 value = max; 4642 } else { 4643 value = current_val; 4644 } 4645 } 4646 4647 if ( 4648 inital_value === "min" && 4649 $this.attr("data-wcpt-reset-on-variation-change") 4650 ) { 4651 value = min; 4652 } 4653 4654 var wcpt_min = min; 4655 4656 if (inital_value === "0") { 4657 min = "0"; 4658 } 4659 4660 $this.attr({ 4661 value: value, 4662 min: min, 4663 "data-wcpt-min": wcpt_min, 4664 max: max, 4665 step: step, 4666 }); 4667 4668 $this.val(value); 4669 4670 $.each(["min", "max", "step"], function (index, attr) { 4671 $qty_wrapper 4672 .find(".wcpt-quantity-error-placeholder--" + attr) 4673 .text($this.attr(attr)); 4674 }); 4675 4676 $this.trigger("change"); 4677 }); 4678 } 4679 4680 // -- qty select 4681 var $select = $row.find(".wcpt-quantity > select.wcpt-qty-select"); 4682 if ($select.length) { 4683 // re-create select 4684 var qty_label = $select.attr("data-wcpt-qty-label"), 4685 max_qty = parseInt($select.attr("data-wcpt-max-qty")), 4686 val = data.variation.min_qty, 4687 options = 4688 '<option value="' + 4689 data.variation.min_qty + 4690 '" selected="selected">' + 4691 qty_label + 4692 data.variation.min_qty + 4693 "</option>"; 4694 if (data.variation.max_qty) { 4695 max_qty = data.variation.max_qty; 4696 } 4697 4698 while (val < max_qty) { 4699 val += data.variation.step || 1; 4700 options += "<option>" + val + "</option>"; 4701 } 4702 $select.html(options); 4703 $select.attr("min", data.variation.min_qty); 4704 } 4705 4706 // -- product image 4707 var $product_image_wrapper = $(".wcpt-product-image-wrapper", $row), 4708 $product_image = $( 4709 ".wcpt-product-image-wrapper img:not(.wcpt-product-image-on-hover)", 4710 $row, 4711 ), 4712 $original_row = wcpt_get_original_row($row); 4713 4714 if ($product_image[0]) { 4715 if (!$original_row.data("wcpt_default_image")) { 4716 $original_row.data( 4717 "wcpt_default_image", 4718 $product_image[0].outerHTML, 4719 ); 4720 } 4721 4722 // replace variation image accoring to 'product image' element size settings 4723 if (data.variation.wcpt_image && data.variation.wcpt_image.src) { 4724 data.variation.image.src = data.variation.image.srcset = 4725 data.variation.wcpt_image.src; 4726 } 4727 4728 if ( 4729 $product_image.length && 4730 data.variation.image && 4731 data.variation.image.src 4732 ) { 4733 $product_image.attr({ 4734 src: data.variation.image.src, 4735 srcset: data.variation.image.srcset 4736 ? data.variation.image.srcset 4737 : "", 4738 sizes: data.variation.image.sizes, 4739 }); 4740 4741 if ($product_image_wrapper.hasClass("wcpt-lightbox-enabled")) { 4742 if ( 4743 !$product_image_wrapper.attr("data-wcpt-lightbox--original") 4744 ) { 4745 $product_image_wrapper.attr( 4746 "data-wcpt-lightbox--original", 4747 $product_image_wrapper.attr("data-wcpt-lightbox"), 4748 ); 4749 } 4750 4751 $product_image_wrapper.attr( 4752 "data-wcpt-lightbox", 4753 data.variation.image.full_src, 4754 ); 4755 } 4756 4757 if ( 4758 $product_image_wrapper.hasClass( 4759 "wcpt-product-image-wrapper--offset-zoom-enabled", 4760 ) 4761 ) { 4762 if ( 4763 !$product_image_wrapper.attr( 4764 "data-wcpt-offset-zoom-image-src--original", 4765 ) 4766 ) { 4767 $product_image_wrapper.attr( 4768 "data-wcpt-offset-zoom-image-src--original", 4769 $product_image_wrapper.attr( 4770 "data-wcpt-offset-zoom-image-src", 4771 ), 4772 ); 4773 } 4774 4775 $product_image_wrapper.attr( 4776 "data-wcpt-offset-zoom-image-src", 4777 data.variation.image.full_src, 4778 ); 4779 } 4780 } 4781 } else { 4782 handle_product_image_lazy_load($product_image_wrapper); 4783 } 4784 4785 // -- sku 4786 if (data.variation.sku) { 4787 $row.find(".wcpt-sku").each(function () { 4788 var $this = $(this); 4789 if ($this.hasClass("wcpt-variable-switch")) { 4790 var $label = $this.find(".wcpt-property-label"); 4791 if ($label.length) { 4792 $this.html($label.prop("outerHTML") + data.variation.sku); 4793 } else { 4794 $this.text(data.variation.sku); 4795 } 4796 } 4797 }); 4798 } 4799 4800 // -- gtin 4801 if (data.variation.wcpt_gtin) { 4802 $row.find(".wcpt-gtin").each(function () { 4803 var $this = $(this); 4804 if ($this.hasClass("wcpt-variable-switch")) { 4805 $this.text(data.variation.wcpt_gtin); 4806 } 4807 }); 4808 } 4809 4810 // -- product id 4811 if (data.variation.variation_id) { 4812 $row.find(".wcpt-product-id").each(function () { 4813 var $this = $(this); 4814 if ($this.hasClass("wcpt-variable-switch")) { 4815 $this.text(data.variation.variation_id); 4816 } 4817 }); 4818 } 4819 4820 // -- price 4821 4822 // -- -- wcpt price element 4823 $row 4824 .filter(".wcpt-product-type-variable") 4825 .find(".wcpt-price.wcpt-variable-switch") 4826 .each(function () { 4827 var $this = $(this), 4828 id = $this.attr("data-wcpt-element-id"), 4829 tpl = 4830 parseFloat(data.variation.display_price) < 4831 parseFloat(data.variation.display_regular_price) 4832 ? "sale" 4833 : "regular", 4834 template_html = $( 4835 "[data-wcpt-element-id=" + 4836 id + 4837 "][data-wcpt-price-type=" + 4838 tpl + 4839 "]", 4840 ).html(), 4841 $html; 4842 4843 if (!template_html) { 4844 return; 4845 } 4846 4847 $html = $(template_html); 4848 4849 $html 4850 .find(".wcpt-regular-price .wcpt-amount") 4851 .text(data.variation.display_regular_price) 4852 .end() 4853 .find(".wcpt-sale-price .wcpt-amount") 4854 .text(data.variation.display_price); 4855 $this.html($html); 4856 4857 if (tpl == "sale") { 4858 $this.addClass("wcpt-product-on-sale"); 4859 } else { 4860 $this.removeClass("wcpt-product-on-sale"); 4861 } 4862 }); 4863 4864 // -- -- default woocommerce template 4865 $row 4866 .filter(".wcpt-product-type-variable") 4867 .find(".wcpt-variable-price-default-woocommerce-template") 4868 .each(function () { 4869 var $this = $(this); 4870 $default = $(".wcpt-variable-switch__default", $this); 4871 4872 if (!data.variation.price_html) { 4873 // single variation won't have price html 4874 return; 4875 } 4876 4877 $default 4878 .hide() 4879 .nextAll(".price") 4880 .remove() 4881 .end() 4882 .after(data.variation.price_html); 4883 }); 4884 4885 // -- -- price attribute 4886 $row.attr("data-wcpt-price", data.variation.display_price); 4887 4888 // -- on sale 4889 $row 4890 .filter(".wcpt-product-type-variable") 4891 .find(".wcpt-on-sale.wcpt-variable-switch") 4892 .each(function () { 4893 var $this = $(this), 4894 precision = $this.attr("data-wcpt-precision"), 4895 is_on_sale = 4896 parseFloat(data.variation.display_price) < 4897 parseFloat(data.variation.display_regular_price), 4898 $price_diff = $this.find(".wcpt-on-sale__price-diff"), 4899 $percent_diff = $this.find(".wcpt-on-sale__percent-diff"); 4900
4901 if (is_on_sale) { 4902 $this.removeClass("wcpt-hide"); 4903 4904 var price_diff = 4905 data.variation.display_regular_price - 4906 data.variation.display_price; 4907 var percent_diff = parseFloat( 4908 ( 4909 (price_diff / data.variation.display_regular_price) * 4910 100 4911 ).toFixed(precision), 4912 ); 4913 4914 $price_diff.html(format_price(price_diff)); 4915 $percent_diff.text(percent_diff); 4916 } else { 4917 $this.addClass("wcpt-hide"); 4918 } 4919 }); 4920 4921 // -- availability 4922 $row 4923 .filter(".wcpt-product-type-variable") 4924 .find(".wcpt-availability.wcpt-variable-switch") 4925 .each(function () { 4926 var $this = $(this), 4927 id = $this.attr("data-wcpt-element-id"), 4928 message_tpl = "", 4929 stock_class = "", 4930 out_of_stock_message = $( 4931 "[data-wcpt-element-id=" + 4932 id + 4933 '][data-wcpt-availability-message="out_of_stock_message"]', 4934 ).html(), 4935 low_stock_message = $( 4936 "[data-wcpt-element-id=" + 4937 id + 4938 '][data-wcpt-availability-message="low_stock_message"]', 4939 ).html(), 4940 single_stock_message = $( 4941 "[data-wcpt-element-id=" + 4942 id + 4943 '][data-wcpt-availability-message="single_stock_message"]', 4944 ).html(), 4945 in_stock_message = $( 4946 "[data-wcpt-element-id=" + 4947 id + 4948 '][data-wcpt-availability-message="in_stock_message"]', 4949 ).html(), 4950 in_stock_managed_message = $( 4951 "[data-wcpt-element-id=" + 4952 id + 4953 '][data-wcpt-availability-message="in_stock_managed_message"]', 4954 ).html(), 4955 on_backorder_message = $( 4956 "[data-wcpt-element-id=" + 4957 id + 4958 '][data-wcpt-availability-message="on_backorder_message"]', 4959 ).html(), 4960 on_backorder_managed_message = $( 4961 "[data-wcpt-element-id=" + 4962 id + 4963 '][data-wcpt-availability-message="on_backorder_managed_message"]', 4964 ).html(), 4965 low_stock_threshold = $this.attr("data-wcpt-low_stock_threshold"); 4966 4967 $this.show(); 4968 4969 if (!data.variation.is_in_stock) { 4970 message_tpl = "out_of_stock_message"; 4971 stock_class = "wcpt-out-of-stock"; 4972 } else if ( 4973 ((data.variation.managing_stock && 4974 data.variation.is_on_backorder && 4975 data.variation.backorders_require_notification) || 4976 (!data.variation.managing_stock && 4977 data.variation.is_on_backorder)) && 4978 on_backorder_message 4979 ) { 4980 message_tpl = "on_backorder_message"; 4981 stock_class = "wcpt-on-backorder"; 4982 } else if (data.variation.managing_stock) { 4983 // in stock, managed 4984 message_tpl = "in_stock_managed_message"; 4985 stock_class = "wcpt-in-stock"; 4986 4987 if (!data.variation.backorders_allowed) { 4988 if ( 4989 // low stock 4990 data.variation.stock == 1 && 4991 single_stock_message 4992 ) { 4993 message_tpl = "single_stock_message"; 4994 stock_class = "wcpt-low-stock"; 4995 } else if ( 4996 // single stock 4997 low_stock_message && 4998 low_stock_threshold && 4999 data.variation.stock <= low_stock_threshold 5000 ) { 5001 message_tpl = "low_stock_message"; 5002 stock_class = "wcpt-low-stock"; 5003 } 5004 } else if ( 5005 data.variation.backorders_require_notification && 5006 on_backorder_managed_message 5007 ) { 5008 // backorder allowed, managed stock, greater than 0 5009 message_tpl = "on_backorder_managed_message"; 5010 stock_class = "wcpt-on-backorder"; 5011 } else if (data.variation.stock <= 0) { 5012 // backorder allowed, managed stock, 0 or less 5013 message_tpl = "on_backorder_message"; 5014 stock_class = ""; 5015 $this.hide(); 5016 } else { 5017 // backorder allowed, managed stock, greater than 0 5018 message_tpl = "in_stock_message"; 5019 stock_class = "wcpt-in-stock"; 5020 } 5021 } else { 5022 // in stock, not managed 5023 message_tpl = ""; 5024 stock_class = ""; 5025 5026 if (in_stock_message) { 5027 message_tpl = "in_stock_message"; 5028 stock_class = "wcpt-in-stock"; 5029 } 5030 } 5031 5032 const message_tpl_selector = `[data-wcpt-element-id="${id}"][data-wcpt-availability-message="${message_tpl}"]`; 5033 const $message = $( 5034 $.parseHTML( 5035 "<span>" + $(message_tpl_selector).html() + "</span>", 5036 ), 5037 ); 5038 const $label = $this.find(".wcpt-property-label"); 5039 const label_html = $label.length ? $label.prop("outerHTML") : ""; 5040 5041 $this.html( 5042 label_html + 5043 "<span class='" + 5044 stock_class + 5045 "'>" + 5046 $message 5047 .find(".wcpt-stock-placeholder") 5048 .text(data.variation.stock ? data.variation.stock : "") 5049 .end() 5050 .html() + 5051 "</span>", 5052 ); 5053 }); 5054 5055 // -- stock 5056 $row 5057 .filter(".wcpt-product-type-variable") 5058 .find(".wcpt-stock.wcpt-variable-switch") 5059 .each(function () { 5060 var $this = $(this), 5061 stock = data.variation.stock, 5062 rules = $this.attr("data-wcpt-stock-range-labels"), 5063 parsed_rules = !rules || rules == "{}" ? [] : JSON.parse(rules), 5064 $label = $this.find(".wcpt-property-label"), 5065 label_html = $label.length ? $label.prop("outerHTML") : "", 5066 label = stock; 5067 5068 if ( 5069 typeof label === "undefined" || 5070 label === null || 5071 label == NaN 5072 ) { 5073 $this.hide(); 5074 return; 5075 } else { 5076 $this.show(); 5077 } 5078 5079 var found_rule = false; 5080 if (stock && parsed_rules.length) { 5081 $.each(parsed_rules, function (index, rule) { 5082 if (rule[0] <= stock && rule[1] >= stock) { 5083 label = rule[2]; 5084 found_rule = true; 5085 } 5086 }); 5087 } 5088 5089 if (!found_rule && stock < 0) { 5090 stock = ""; 5091 label = ""; 5092 } 5093 5094 $this.html(label_html + (label + "").replace("[stock]", stock)); 5095 }); 5096 5097 // -- dimensions 5098 $row 5099 .filter(".wcpt-product-type-variable") 5100 .find(".wcpt-dimensions.wcpt-variable-switch") 5101 .each(function () { 5102 var $this = $(this); 5103 $this.html(data.variation.dimensions_html); 5104 }); 5105 5106 // -- height 5107 $row 5108 .filter(".wcpt-product-type-variable") 5109 .find(".wcpt-height.wcpt-variable-switch") 5110 .each(function () { 5111 var $this = $(this), 5112 template = $this.attr("data-wcpt-template"), 5113 empty_template = $this.attr("data-wcpt-empty-template"), 5114 height = data.variation.dimensions.height; 5115 5116 if (!height) { 5117 $this.html(empty_template ? empty_template : ""); 5118 } else { 5119 $this.html( 5120 template ? template.replace("{n}", height) : empty_template, 5121 ); 5122 } 5123 }); 5124
5125 // -- width 5126 $row 5127 .filter(".wcpt-product-type-variable") 5128 .find(".wcpt-width.wcpt-variable-switch") 5129 .each(function () { 5130 var $this = $(this), 5131 template = $this.attr("data-wcpt-template"), 5132 empty_template = $this.attr("data-wcpt-empty-template"), 5133 width = data.variation.dimensions.width; 5134 5135 if (!width) { 5136 $this.html(empty_template ? empty_template : ""); 5137 } else { 5138 $this.html(template ? template.replace("{n}", width) : width); 5139 } 5140 }); 5141 5142 // -- length 5143 $row 5144 .filter(".wcpt-product-type-variable") 5145 .find(".wcpt-length.wcpt-variable-switch") 5146 .each(function () { 5147 var $this = $(this), 5148 template = $this.attr("data-wcpt-template"), 5149 empty_template = $this.attr("data-wcpt-empty-template"), 5150 length = data.variation.dimensions.length; 5151 5152 if (!length) { 5153 $this.html(empty_template ? empty_template : ""); 5154 } else { 5155 $this.html(template ? template.replace("{n}", length) : length); 5156 } 5157 }); 5158 5159 // -- weight 5160 $row 5161 .filter(".wcpt-product-type-variable") 5162 .find(".wcpt-weight.wcpt-variable-switch") 5163 .each(function () { 5164 var $this = $(this), 5165 template = $this.attr("data-wcpt-template"), 5166 empty_template = $this.attr("data-wcpt-empty-template"), 5167 weight = data.variation.weight; 5168 5169 if (!weight) { 5170 $this.html(empty_template ? empty_template : ""); 5171 } else { 5172 $this.html(template ? template.replace("{n}", weight) : weight); 5173 } 5174 }); 5175 5176 // -- custom field 5177 $row 5178 .filter(".wcpt-product-type-variable") 5179 .find(".wcpt-custom-field.wcpt-variable-switch") 5180 .each(function () { 5181 var $this = $(this), 5182 element_id = $this.attr("data-wcpt-element-id"), 5183 table_id = $this.closest(".wcpt").attr("data-wcpt-table-id"); 5184 (($label = $this.find(".wcpt-property-label")), 5185 (label_html = $label.length ? $label.prop("outerHTML") : "")); 5186 5187 if ( 5188 "undefined" !== 5189 typeof window["wcpt_" + table_id + "_variable_switch_cf"] 5190 ) { 5191 var cf_vals = window["wcpt_" + table_id + "_variable_switch_cf"]; 5192 if ("undefined" !== typeof cf_vals[element_id]) { 5193 $this.html( 5194 label_html + cf_vals[element_id][data.variation.variation_id], 5195 ); 5196 } 5197 } 5198 }); 5199 } 5200 }, 5201 ); 5202 5203 //-- -- update from dropdown 5204 $("body").on("change", ".wcpt-select-variation-dropdown", function (e) { 5205 var $this = $(this), 5206 $selected = $this.find("option:selected"), 5207 $row = $this.closest(".wcpt-row"); 5208 5209 $row.trigger("select_variation", { 5210 variation_id: $this.val(), 5211 complete_match: $selected.hasClass("wcpt-complete_match"), 5212 attributes: $selected.attr("data-wcpt-attributes") 5213 ? JSON.parse($selected.attr("data-wcpt-attributes")) 5214 : "", 5215 variation: $selected.attr("data-wcpt-variation") 5216 ? JSON.parse($selected.attr("data-wcpt-variation")) 5217 : "", 5218 variation_found: !!$selected.attr("value"), 5219 variation_selected: !!$selected.attr("value"), 5220 variation_available: 5221 !$selected.is(":disabled") && !!$selected.attr("value"), 5222 }); 5223 }); 5224 5225 //-- -- update from radio 5226 $("body").on("change", ".wcpt-variation-radio", function (e) { 5227 var $this = $(this), 5228 $wrapper = $this.closest(".wcpt-select-variation"), 5229 $row = $this.closest(".wcpt-row"); 5230 5231 if ($this.is(":checked")) { 5232 $row.trigger("select_variation", { 5233 variation_id: $this.val(), 5234 complete_match: $wrapper.hasClass("wcpt-complete_match"), 5235 attributes: JSON.parse($wrapper.attr("data-wcpt-attributes")), 5236 variation: JSON.parse($wrapper.attr("data-wcpt-variation")), 5237 variation_found: true, 5238 variation_selected: true, 5239 variation_available: !$this.is(":disabled"), 5240 }); 5241 } 5242 }); 5243 5244 //-- -- update from form 5245 $("body").on( 5246 "woocommerce_variation_has_changed", 5247 ".wcpt-row .variations_form", 5248 function (e) { 5249 get_select_variation_from_cart_form($(this)); 5250 }, 5251 ); 5252 5253 function get_select_variation_from_cart_form($form) { 5254 if (window.wcpt_form_reset_flag) { 5255 // avoid infinite loop 5256 return; 5257 } 5258 5259 var variations = JSON.parse($form.attr("data-product_variations")), 5260 $row = $form.closest(".wcpt-row"), 5261 $variation_id = $(".variation_id", $form), 5262 variation = {}, 5263 attributes = {}, 5264 selected_variation = $variation_id.val(); 5265 5266 $.each(variations, function (index, value) { 5267 if (parseInt(value["variation_id"]) == selected_variation) { 5268 variation = value; 5269 return false; 5270 } 5271 });
5272 5273 var variation_selected = true; 5274 $(".variations select", $form).each(function () { 5275 var $this = $(this); 5276 attributes[$this.attr("name")] = $this.val(); 5277 5278 if (!$this.val()) { 5279 variation_selected = false; 5280 } 5281 }); 5282 5283 var variation_available = false; 5284 if ( 5285 variation && 5286 !$.isEmptyObject(variation) && 5287 variation.is_purchasable && 5288 variation.is_in_stock && 5289 variation.variation_is_visible 5290 ) { 5291 variation_available = true; 5292 } 5293 5294 $row.trigger("select_variation", { 5295 variation: variation, 5296 variation_id: selected_variation, 5297 complete_match: true, 5298 attributes: attributes, 5299 variation_found: !!selected_variation, 5300 variation_selected: variation_selected, 5301 variation_available: variation_available, 5302 }); 5303 } 5304 5305 // prepare variation options 5306 function prep_variation_options($new_rows) { 5307 function mark_row_prep_variation_complete($row) { 5308 $row.data("wcpt_prep_variation_complete", true); 5309 $row.trigger("wcpt_row_prep_variation_complete"); 5310 } 5311 5312 function shouldSkipPrepVariationReset($row) { 5313 var productId = $row.attr("data-wcpt-product-id"), 5314 table_id = 5315 typeof wcpt_util !== "undefined" 5316 ? wcpt_util.get_table_id($row.closest(".wcpt")) 5317 : ""; 5318 5319 return !!( 5320 productId && 5321 table_id && 5322 typeof window.wcpt_cart_checkbox_persist !== "undefined" && 5323 typeof window.wcpt_cart_checkbox_persist.hasPersistedVariableProduct === 5324 "function" && 5325 window.wcpt_cart_checkbox_persist.hasPersistedVariableProduct( 5326 table_id, 5327 productId, 5328 ) 5329 ); 5330 } 5331 5332 $($new_rows) 5333 .filter(".wcpt-product-type-variable") 5334 .each(function () { 5335 var $row = $(this), 5336 $dropdown = $(".wcpt-select-variation-dropdown", $row), 5337 $radio = $(".wcpt-variation-radio", $row), 5338 $form = $(".variations_form", $row), 5339 $options = $dropdown.add($radio).add($form); 5340 5341 $row.data("wcpt_prep_variation_complete", false); 5342 5343 // flag availability of options in row 5344 if ($options.length) { 5345 $row.data("wcpt_variation_ops", true); 5346 } 5347 5348 // trigger 'select_variation' 5349 5350 // cart form element 5351 if ($form.length) { 5352 $form.each(function () { 5353 var $form = $(this), 5354 skipPrepReset = shouldSkipPrepVariationReset($row); 5355 5356 setTimeout(function () { 5357 // need to match setTimeout in add-to-cart-variation.js that delays form init 5358 // this init syncs the WC form with WCPT native controls including add-to-cart button 5359 if (!skipPrepReset) { 5360 $form.find("select").first().change(); 5361 } 5362 mark_row_prep_variation_complete($row); 5363 }, 200); 5364 }); 5365 5366 // select variation element 5367 5368 // -- dropdown 5369 } else if ($dropdown.length) { 5370 if (!shouldSkipPrepVariationReset($row)) { 5371 $dropdown.trigger("change"); 5372 } 5373 mark_row_prep_variation_complete($row); 5374 5375 // -- radio 5376 } else if ($radio.length) { 5377 if (!shouldSkipPrepVariationReset($row)) { 5378 var $checked = $radio.filter(":checked"); 5379 if ($checked.length) { 5380 $checked.trigger("change"); 5381 } else { 5382 $row.trigger("select_variation", { 5383 variation_id: false, 5384 complete_match: false, 5385 attributes: false, 5386 variation: false, 5387 variation_found: false, 5388 variation_selected: false, 5389 variation_available: false, 5390 }); 5391 } 5392 } 5393 mark_row_prep_variation_complete($row); 5394 } else { 5395 mark_row_prep_variation_complete($row); 5396 } 5397 }); 5398 } 5399 5400 // lazy loaded product image 5401 function handle_product_image_lazy_load($product_image_wrapper) { 5402 if ( 5403 $product_image_wrapper.length && 5404 !$product_image_wrapper.hasClass("wcpt-awaiting-image-lazy-load") 5405 ) { 5406 $product_image_wrapper.addClass("wcpt-awaiting-image-lazy-load"); 5407 5408 $product_image_wrapper[0].addEventListener( 5409 "load", 5410 function (event) { 5411 if ( 5412 event.target.tagName === "IMG" && 5413 $product_image_wrapper.hasClass("wcpt-awaiting-image-lazy-load") 5414 ) { 5415 $product_image_wrapper.removeClass("wcpt-awaiting-image-lazy-load"); 5416 5417 var $row = wcpt_get_original_row( 5418 $product_image_wrapper.closest(".wcpt-row"), 5419 ), 5420 data = $row.data(), 5421 $product_image = $product_image_wrapper.find(".wp-post-image"); 5422 5423 $row.data("wcpt_default_image", $product_image[0].outerHTML); 5424 5425 if ( 5426 !$.isEmptyObject(data.wcpt_variation) && 5427 $product_image.length && 5428 data.wcpt_variation.image && 5429 data.wcpt_variation.image.src 5430 ) { 5431 $product_image.attr({ 5432 src: data.wcpt_variation.image.src, 5433 srcset: data.wcpt_variation.image.srcset 5434 ? data.wcpt_variation.image.srcset 5435 : "", 5436 }); 5437 5438 if ($product_image_wrapper.hasClass("wcpt-lightbox-enabled")) { 5439 $product_image_wrapper.attr( 5440 "data-wcpt-lightbox", 5441 data.wcpt_variation.image.full_src, 5442 ); 5443 } 5444 5445 if ( 5446 $product_image_wrapper.hasClass( 5447 "wcpt-product-image-wrapper--offset-zoom-enabled", 5448 ) 5449 ) { 5450 $product_image_wrapper.attr( 5451 "data-wcpt-offset-zoom-image-src", 5452 data.variation.image.full_src, 5453 ); 5454 } 5455 } 5456 } 5457 }, 5458 true, 5459 ); 5460 } 5461 } 5462 5463 if (window.innerWidth < 1200) { 5464 $("body").on("contextmenu", ".wcpt-noselect", function () { 5465 return false; 5466 });
5467 } 5468 5469 /** 5470 * Device interaction type detection 5471 * Sets appropriate mouse event types based on device capabilities 5472 */ 5473 if ("ontouchstart" in document.documentElement) { 5474 var mousedown = "touchstart", 5475 mouseup = "touchend"; 5476 } else { 5477 var mousedown = "mousedown", 5478 mouseup = "mouseup"; 5479 } 5480 5481 /** 5482 * Quantity Controller Module 5483 * 5484 * This module handles quantity input functionality for WooCommerce product tables, 5485 * including touch screen support, increment/decrement controls, validation, and 5486 * cross-table synchronization. 5487 */ 5488 5489 /** 5490 * Touch Screen Handler 5491 * Automatically moves cursor to end of input field on touch devices 5492 * for better user experience when editing quantities 5493 */ 5494 $("body").on("touchend", ".wcpt-quantity .qty", function () { 5495 var $this = $(this), 5496 _ = this; 5497 if (!$this.is(":focus")) { 5498 $this.one("focus", function () { 5499 var val = _.value; 5500 _.value = ""; 5501 setTimeout(function () { 5502 _.value = val; 5503 }, 1); 5504 }); 5505 } 5506 }); 5507 5508 /** 5509 * Quantity Controller Click Handler 5510 * Manages increment/decrement button interactions including: 5511 * - Initial value changes 5512 * - Continuous increment/decrement on hold 5513 * - Min/max boundary enforcement 5514 * - Touch device support 5515 */ 5516 $("body").on("click", ".wcpt-qty-controller", handleQuantityChange); 5517 $("body").on("wcptPressing", ".wcpt-qty-controller", handleQuantityChange); 5518 $("body").on("dblclick", ".wcpt-qty-controller", function (e) { 5519 e.preventDefault(); 5520 }); 5521 5522 function handleQuantityChange(e) { 5523 var $this = $(this), 5524 $wrapper = $this.closest(".wcpt-quantity"), 5525 $qty = $wrapper.find(".qty"), 5526 visualMin = $qty.attr("min") ? $qty.attr("min") : 0, 5527 realMin = $qty.attr("data-wcpt-min") 5528 ? $qty.attr("data-wcpt-min") 5529 : visualMin, 5530 max = $qty.attr("max") ? $qty.attr("max") : false, 5531 step = $qty.attr("step") ? $qty.attr("step") : 1, 5532 val = $qty.val() > -1 ? $qty.val() : visualMin; 5533 5534 if ($wrapper.hasClass("wcpt-disabled")) { 5535 return; 5536 } 5537 5538 if ($this.hasClass("wcpt-disabled")) { 5539 return; 5540 } 5541 5542 if (step % 1 === 0) { 5543 visualMin = parseInt(visualMin); 5544 realMin = parseInt(realMin); 5545 max = parseInt(max); 5546 step = parseInt(step); 5547 val = parseInt(val); 5548 } else { 5549 visualMin = parseFloat(visualMin); 5550 realMin = parseFloat(realMin); 5551 max = parseFloat(max); 5552 step = parseFloat(step); 5553 val = parseFloat(val); 5554 } 5555 5556 if (isNaN(val)) { 5557 val = 0; 5558 } 5559 5560 // update quantity value 5561 if ($this.hasClass("wcpt-plus")) { 5562 var next = (val * 1e12 + step * 1e12) / 1e12; 5563 // If increasing from below realMin, jump to realMin 5564 if (val < realMin && next >= visualMin) { 5565 next = realMin; 5566 } 5567 } else { 5568 // wcpt-minus 5569 var next = (val * 1e12 - step * 1e12) / 1e12; 5570 // If decreasing from above or equal to realMin, drop straight to visualMin or 0 5571 if (val >= realMin && next < realMin) { 5572 next = visualMin > 0 ? 0 : visualMin; 5573 } else if (next < visualMin) { 5574 next = 0; 5575 } 5576 } 5577 $qty.val(next).change(); 5578 5579 limit_qty_controller($wrapper); 5580 } 5581 5582 function startContinuousQuantityChange(e) { 5583 $("body").addClass("wcpt-noselect--qty-increment"); 5584 } 5585 5586 function stopContinuousQuantityChange(e) { 5587 $("body").removeClass("wcpt-noselect--qty-increment"); 5588 } 5589 5590 /** 5591 * Quantity Input Validator 5592 * Ensures quantity values respect: 5593 * - Minimum and maximum limits 5594 * - Step increments 5595 * - Initial value rules 5596 * @param {Event} e - Change event 5597 */ 5598 $("body").on( 5599 "change", 5600 ".wcpt-quantity .qty", 5601 function qty_controller_validate(e) { 5602 var $this = $(this), 5603 min = $this.attr("min") ? parseFloat($this.attr("min")) : 0, 5604 max = 5605 $this.attr("max") !== undefined && $this.attr("max") !== "" 5606 ? parseFloat($this.attr("max")) 5607 : 1e12, 5608 initial = $this.attr("data-wcpt-initial-value"), 5609 step = $this.attr("step") ? parseFloat($this.attr("step")) : 1, 5610 raw = $this.val(), 5611 val = parseFloat(raw); 5612 5613 // Empty field only â do not treat 0 as empty (OOS uses 0 with max 0). 5614 if (raw === "" || isNaN(val)) { 5615 if (initial === "min") { 5616 $this.val(!isNaN(max) && max < min ? Math.max(0, max) : min); 5617 } else if (initial === "empty") { 5618 $this.val(""); 5619 } else if (initial === "0") { 5620 $this.val("0"); 5621 } 5622 val = parseFloat($this.val()); 5623 } 5624 5625 // enforce min 5626 if (val && val < min) { 5627 $this.val(min); 5628 val = min; 5629 } 5630 5631 // enforce max 5632 if (!isNaN(val) && val > max) { 5633 $this.val(max); 5634 val = max; 5635 } 5636 5637 // enforce step 5638 if (val && (val * 1e12) % (step * 1e12)) { 5639 var _val = val * 1e12, 5640 _step = step * 1e12, 5641 new_val = (_val + _step - (val % step) * 1e12) / 1e12; 5642 5643 $this.val(new_val); 5644 } 5645 5646 limit_qty_controller($(this).parent()); 5647 }, 5648 ); 5649 5650 /** 5651 * Enter Key Cart Addition Handler 5652 * Allows users to press Enter while focused on quantity input 5653 * to trigger the add-to-cart action for that product 5654 * 5655 * @param {KeyboardEvent} e - Keyboard event object 5656 */ 5657 $("body").on("keypress", ".wcpt-quantity .qty", function (e) { 5658 if (e.keyCode == 13) { 5659 var $rows = get_product_rows($(this)); 5660 $rows.find('.wcpt-button[data-wcpt-link-code^="cart"]').eq(0).click(); 5661 } 5662 }); 5663 5664 /** 5665 * Quantity Controller State Manager 5666 * Updates the enabled/disabled state of increment/decrement buttons based on: 5667 * - Current quantity value 5668 * - Minimum and maximum limits 5669 * - Step size 5670 * @param {jQuery} $qty_wrapper - The quantity input wrapper element 5671 */ 5672 function limit_qty_controller($qty_wrapper) { 5673 $qty_wrapper.each(function () { 5674 var $this = $(this),
5675 $minus = $this.children(".wcpt-minus"), 5676 $plus = $this.children(".wcpt-plus"), 5677 $qty = $this.find(".qty"); 5678 5679 if ($this.hasClass("wcpt-disabled")) { 5680 $minus.addClass("wcpt-disabled"); 5681 $plus.addClass("wcpt-disabled"); 5682 return; 5683 } 5684 5685 var initial = $qty.attr("data-wcpt-initial-value"), 5686 min = $qty.attr("min") ? parseFloat($qty.attr("min")) : 1, 5687 max = $qty.attr("max") ? parseFloat($qty.attr("max")) : false, 5688 step = $qty.attr("step") ? parseFloat($qty.attr("step")) : 1, 5689 val = parseFloat($qty.val()); 5690 5691 if (!val || isNaN(val)) { 5692 val = 0; 5693 } 5694 5695 if (-1 !== $.inArray(initial, ["empty", "0"])) { 5696 min = 0; 5697 } 5698 5699 $minus.removeClass("wcpt-disabled"); 5700 if (val - step < min) { 5701 $minus.addClass("wcpt-disabled"); 5702 } 5703 5704 $plus.removeClass("wcpt-disabled"); 5705 if (false !== max && val + step > max) { 5706 $plus.addClass("wcpt-disabled"); 5707 } 5708 }); 5709 } 5710 5711 /** 5712 * Initial Quantity Value Handler 5713 * Sets up initial quantity values after table load based on configuration: 5714 * - Can set to minimum value 5715 * - Can set to empty 5716 * - Can set to zero 5717 * - Triggers appropriate events for other components 5718 */ 5719 $("body").on( 5720 "wcpt_after_every_load.wcpt_initial_qty_update", 5721 ".wcpt", 5722 function () { 5723 var $this__container = $(this), 5724 $target_qty_elms = $( 5725 '.wcpt-quantity input[type="number"].qty', 5726 wcpt_util.get_uninit_rows($this__container), 5727 ); 5728 5729 $target_qty_elms.each(function () { 5730 var $this__input = $(this), 5731 initial_value = $this__input.attr("data-wcpt-initial-value"); 5732 5733 if (initial_value === "0") { 5734 $this__input.val(0); 5735 } else if (initial_value === "empty") { 5736 $this__input.val(""); 5737 } else if (initial_value === "min") { 5738 var min = $this__input.attr("min") 5739 ? parseFloat($this__input.attr("min")) 5740 : 1, 5741 maxAttr = $this__input.attr("max"), 5742 max = 5743 maxAttr !== undefined && maxAttr !== "" 5744 ? parseFloat(maxAttr) 5745 : NaN; 5746 // OOS: max is 0 â don't leave purchase min in the field (WC page-load 5747 // script will also re-bump value if min stays above max). 5748 if (!isNaN(max) && max < min) { 5749 $this__input.attr("min", Math.max(0, max)); 5750 $this__input.val(Math.max(0, max)); 5751 } else { 5752 $this__input.val(min); 5753 } 5754 } 5755 5756 $this__input 5757 .trigger("wcpt_updating_initial_quantity") 5758 .trigger("change") 5759 .trigger("wcpt_initial_quantity_updated"); 5760 }); 5761 }, 5762 ); 5763 5764 /** 5765 * Quantity Error Handler 5766 * Manages error states and visual feedback for: 5767 * - Values below minimum 5768 * - Values above maximum 5769 * - Invalid step increments 5770 * Updates associated cart buttons accordingly 5771 */ 5772 $("body").on("keyup change", ".wcpt-quantity .qty", function (e) { 5773 var $this = $(this), 5774 max = $this.attr("max") ? parseFloat($this.attr("max")) : 1e9, 5775 min = $this.attr("min") ? parseFloat($this.attr("min")) : 1, 5776 step = $this.attr("step") ? parseFloat($this.attr("step")) : 1, 5777 val = isNaN(parseFloat($this.val())) ? 0 : parseFloat($this.val()), 5778 $wrapper = $this.closest(".wcpt-quantity"); 5779 5780 $wrapper.removeClass( 5781 "wcpt-quantity-error wcpt-quantity-error--min wcpt-quantity-error--max wcpt-quantity-error--step", 5782 ); 5783 5784 if (!val) { 5785 return; 5786 } 5787 5788 if (val < min) { 5789 $wrapper.addClass("wcpt-quantity-error wcpt-quantity-error--min"); 5790 } else if (val > max) { 5791 $wrapper.addClass("wcpt-quantity-error wcpt-quantity-error--max"); 5792 } else if ((val * 1e12) % (step * 1e12)) { 5793 $wrapper.addClass("wcpt-quantity-error wcpt-quantity-error--step"); 5794 } 5795 5796 var $row = $this.closest(".wcpt-row"), 5797 $button = $row.find('.wcpt-button[data-wcpt-link-code^="cart_"]'); 5798 5799 if ($wrapper.hasClass("wcpt-quantity-error")) { 5800 disable_button($button, "wcpt-quantity-input-error"); 5801 } else { 5802 enable_button($button, "wcpt-quantity-input-error"); 5803 } 5804 5805 limit_qty_controller($wrapper); 5806 }); 5807 5808 /** 5809 * Cross-Table Quantity Synchronization Handler 5810 * Ensures quantity values remain synchronized across: 5811 * - Multiple instances of the same product in different tables 5812 * - WooCommerce default cart buttons 5813 * - Custom quantity select dropdowns 5814 * 5815 * Updates: 5816 * - All quantity inputs for the same product 5817 * - WooCommerce add-to-cart button data attributes 5818 * 5819 * @param {Event} e - Change event object 5820 */ 5821 $("body").on( 5822 "change", 5823 ".wcpt-quantity input.qty, select.wcpt-qty-select", 5824 function (e) { 5825 var $input = $(this), 5826 $product_rows = get_product_rows($input), 5827 $siblings = $product_rows 5828 .find("input.qty, select.wcpt-qty-select") 5829 .not(this), 5830 val = $input.val(); 5831 5832 $siblings.val(val); 5833 5834 if ($input.closest(".wcpt-add-to-cart-wrapper").length) { 5835 return; 5836 } 5837 5838 var $wc_default_button = $(".add_to_cart_button", $product_rows); 5839 $wc_default_button.data("quantity", val); 5840 $wc_default_button.attr("data-quantity", val); 5841 }, 5842 ); 5843 5844 /** 5845 * Quantity Input Keyboard Navigation Handler 5846 * Manages up/down arrow key interactions for quantity adjustments:
5847 * - ArrowUp: Increments value by step amount 5848 * - ArrowDown: Decrements value by step amount 5849 * - Respects min/max boundaries 5850 * - Handles special cases for real vs visual minimum values 5851 * 5852 * @param {KeyboardEvent} e - The keyboard event object 5853 */ 5854 $("body").on("keydown", ".wcpt-quantity .qty", function (e) { 5855 if (e.key !== "ArrowUp" && e.key !== "ArrowDown") return; 5856 5857 var $qty = $(this); 5858 var visualMin = $qty.attr("min") ? parseFloat($qty.attr("min")) : 0; 5859 var realMin = $qty.attr("data-wcpt-min") 5860 ? parseFloat($qty.attr("data-wcpt-min")) 5861 : visualMin; 5862 var step = $qty.attr("step") ? parseFloat($qty.attr("step")) : 1; 5863 var currentVal = parseFloat($qty.val()) || 0; 5864 5865 // Calculate next value 5866 var next = 5867 e.key === "ArrowUp" 5868 ? (currentVal * 1e12 + step * 1e12) / 1e12 5869 : (currentVal * 1e12 - step * 1e12) / 1e12; 5870 5871 // Apply same logic as plus/minus buttons 5872 if (e.key === "ArrowUp") { 5873 if (currentVal < realMin && next >= visualMin) { 5874 next = realMin; 5875 } 5876 } else { 5877 // ArrowDown 5878 if (currentVal >= realMin && next < realMin) { 5879 next = visualMin > 0 ? 0 : visualMin; 5880 } else if (next < visualMin) { 5881 next = 0; 5882 } 5883 } 5884 5885 $qty.val(next).change(); 5886 e.preventDefault(); 5887 }); 5888 5889 /** 5890 * Handles clicks on disabled cart-related buttons and quantity controls. 5891 * Shows appropriate error messages when users try to interact with: 5892 * 5893 * For variable products: 5894 * - Products missing variation selection 5895 * - Products with no matching variations 5896 * - Products with unavailable variations 5897 * 5898 * For simple products: 5899 * - Out of stock products 5900 * 5901 * @param {Event} event - The click event object 5902 */ 5903 $("body").on( 5904 "click", 5905 ".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", 5906 function () { 5907 var $product_row = $(this).closest(".wcpt-row"), 5908 product_type = $product_row.attr("data-wcpt-type"); 5909 5910 if (product_type === "variable") { 5911 var product_data = $product_row.data(); 5912 5913 if (!product_data.wcpt_variation_selected) { 5914 alert(wcpt_i18n.i18n_make_a_selection_text); 5915 return; 5916 } 5917 5918 if (!product_data.wcpt_variation_found) { 5919 alert(wcpt_i18n.i18n_no_matching_variations_text); 5920 return; 5921 } 5922 5923 if ( 5924 !product_data.wcpt_variation || 5925 !product_data.wcpt_variation.is_in_stock 5926 ) { 5927 alert(wcpt_i18n.i18n_unavailable_variation_text); 5928 return; 5929 } 5930 } else if ($product_row.hasClass("wcpt-out-of-stock")) { 5931 alert(wcpt_i18n.i18n_out_of_stock_text); 5932 } 5933 }, 5934 ); 5935 5936 // total 5937 $("body").on("keyup mouseup change", ".wcpt-quantity .qty", function () { 5938 update_row_total($(this).closest(".wcpt-row")); 5939 }); 5940 5941 // -- name your price: ensure qty if name your price is used 5942 $("body").on( 5943 "keyup change", 5944 ".wcpt .wcpt-name-your-price--input", 5945 function (e) { 5946 var $nyp = $(this), 5947 nyp_val = $nyp.val(), 5948 $rows = wcpt_get_sibling_rows($nyp.closest(".wcpt-row")), 5949 $qty = $(".wcpt-quantity input.qty", $rows), 5950 qty_val = $qty.val(), 5951 $cb = $(".wcpt-cart-checkbox ", $rows); 5952 5953 nyp_validate($nyp); 5954 5955 // added val - add qty 5956 if (nyp_val && !qty_val) { 5957 $qty.val($qty.attr("min")); 5958 5959 // removed val - remove qty 5960 } else if (!nyp_val && qty_val) { 5961 var inital_val_type = $qty.attr("data-wcpt-nyp-initial-value"), 5962 val = $qty.attr("min"); 5963 5964 if (inital_val_type === "empty") { 5965 val = ""; 5966 } else if (inital_val_type === "0") { 5967 val = "0"; 5968 } 5969 5970 $qty.val(val); 5971 } 5972 5973 update_row_total($(this).closest(".wcpt-row")); 5974 5975 if ($qty.length) { 5976 $qty.change(); 5977 } else if ($cb.length) { 5978 $rows.trigger("wcpt_checkbox_change", !!$nyp.val()); 5979 } 5980 }, 5981 ); 5982 5983 function update_row_total($row, force_qty) { 5984 var $rows = $row, 5985 $qty = $rows.find(".wcpt-quantity .qty").eq(0), 5986 qty = $qty.length ? parseFloat($qty.val() ? $qty.val() : 0) : 1, 5987 $total = $(".wcpt-total", $rows), 5988 $cb = $(".wcpt-cart-checkbox ", $rows), 5989 prev_total = parseFloat($total.attr("data-wcpt-in-cart-total") || 0), 5990 price = $rows.attr("data-wcpt-price") 5991 ? parseFloat($rows.attr("data-wcpt-price")) 5992 : 0, 5993 total = 0; 5994 5995 if (force_qty) { 5996 qty = force_qty; 5997 } 5998 5999 // variable 6000 if ($rows.hasClass("wcpt-product-type-variable")) { 6001 if ( 6002 $rows.data("wcpt_variation_found") && 6003 $rows.data("wcpt_variation") && 6004 $rows.data("wcpt_variation").display_price 6005 ) { 6006 price = unformat_price_figure( 6007 $rows.data("wcpt_variation").display_price, 6008 ); 6009 } else { 6010 price = 0; 6011 } 6012 } 6013 6014 // name your price 6015 if ($row.hasClass("wcpt-product-has-name-your-price")) { 6016 var $nyp = $(".wcpt-name-your-price--input", $rows); 6017 if ($nyp.filter(":visible").length) { 6018 // variable product compatibility 6019 price = $nyp.val() ? parseFloat($nyp.val()) : 0; 6020 } 6021 } 6022 6023 total = (qty * price).toFixed(2); 6024 6025 // if checkbox is unchecked and qty is not min then no total 6026 if ( 6027 $cb.length && 6028 !$rows.data("wcpt_checked") && 6029 $qty.attr("data-wcpt-initial-value") !== "min" 6030 ) { 6031 total = 0; 6032 } 6033 6034 $total.each(function () { 6035 var $this = $(this), 6036 _total = total; 6037 6038 if ($this.hasClass("wcpt-total--include-total-in-cart")) { 6039 _total = parseFloat(_total) + parseFloat(prev_total); 6040 } 6041 6042 if (parseFloat(_total)) { 6043 $this 6044 .removeClass("wcpt-total--empty") 6045 .find(".wcpt-amount") 6046 .text(format_price_figure(_total)); 6047 } else { 6048 $this.addClass("wcpt-total--empty"); 6049 } 6050 });
6051 6052 $row.data("wcpt-total", total); 6053 6054 $row.trigger("wcpt_total_updated"); 6055 } 6056 6057 // audio player 6058 $("body").on("click", ".wcpt-player__button", function () { 6059 var $button = $(this), 6060 $container = $button.closest(".wcpt-player"), 6061 src = $container.attr("data-wcpt-src"), 6062 loop = $container.attr("data-wcpt-loop"), 6063 $el = $container.data("wcpt-media-el"); 6064 6065 if (!$el) { 6066 $el = $('<audio class="wcpt-audio-elm" src="' + src + '"></audio>'); 6067 $container.append($el); 6068 $container.data("wcpt-media-el", $el); 6069 6070 if (loop) { 6071 $el.prop("loop", true); 6072 } else { 6073 $el.on("ended", function () { 6074 $container.toggleClass("wcpt-player--playing"); 6075 }); 6076 } 6077 } 6078 6079 if ($button.hasClass("wcpt-player__play-button")) { 6080 $el[0].play(); 6081 6082 if (!$container.hasClass("wcpt-media-loaded")) { 6083 $el.on("canplay", function () { 6084 $container.addClass("wcpt-media-loaded"); 6085 }); 6086 } 6087 6088 $("audio.wcpt-audio-elm") 6089 .not($el) 6090 .each(function () { 6091 this.currentTime = 0; 6092 this.pause(); 6093 }); 6094 6095 // pause others 6096 var $other_players = $(".wcpt-player.wcpt-player--playing").not( 6097 $container, 6098 ); 6099 $other_players.find(".wcpt-player__playing-button").click(); 6100 } else if ($button.hasClass("wcpt-player__pause-button")) { 6101 $el[0].pause(); 6102 } else if ($button.hasClass("wcpt-player__stop-button")) { 6103 $el[0].pause(); 6104 $el[0].currentTime = 0; 6105 } 6106 6107 $container.toggleClass("wcpt-player--playing"); 6108 }); 6109 6110 // term click 6111 // -- trigger filter 6112 $("body").on("click", ".wcpt-trigger_filter > [data-wcpt-slug]", function () { 6113 var $this = $(this), 6114 slug = $this.attr("data-wcpt-slug"), 6115 taxonomy = $this.parent().attr("data-wcpt-taxonomy"), 6116 $container = $this.closest(".wcpt"), 6117 $nav = $container.find(".wcpt-navigation"), 6118 $option = $nav.find( 6119 '[data-wcpt-taxonomy="' + 6120 taxonomy + 6121 '"] [data-wcpt-slug="' + 6122 slug + 6123 '"]', 6124 ), 6125 $input = $("input", $option); 6126 6127 if (!$option.length) { 6128 return; 6129 } 6130 6131 // $nav.addClass("wcpt-force-hide-dropdown-menus"); 6132 $input.prop("checked", !$input.prop("checked")); 6133 $nav.first().trigger("change"); 6134 }); 6135 // -- archive redirect 6136 $("body").on( 6137 "click", 6138 ".wcpt-archive_redirect > [data-wcpt-slug]", 6139 function () { 6140 var $this = $(this), 6141 url = $this.attr("data-wcpt-archive-url"); 6142 if (!$this.is("a")) { 6143 window.location = url; 6144 } 6145 }, 6146 ); 6147 6148 // remove 6149 $("body").on( 6150 "click", 6151 ".wcpt-row:not(.wcpt-removing-product) .wcpt-remove:not(.wcpt-disabled)", 6152 function () { 6153 var $this = $(this), 6154 $row = ($product_row = get_product_rows($this)), 6155 product_id = $row.attr("data-wcpt-product-id"), 6156 variation_id = $row.attr("data-wcpt-variation-id"), 6157 params = { 6158 payload: { 6159 products: {}, 6160 variations: {}, 6161 overwrite_cart_qty: true, 6162 }, 6163 }; 6164 6165 if ($this.hasClass("wcpt-refresh-enabled")) { 6166 params.redirect = window.location.href; 6167 } 6168 6169 params.payload.products[product_id] = 0; 6170 6171 // variation 6172 if (variation_id) { 6173 params.payload.variations[product_id] = {}; 6174 params.payload.variations[product_id][variation_id] = 0; 6175 } 6176 6177 // variable product 6178 if ($row.hasClass("wcpt-product-type-variable")) { 6179 params.payload.variations[product_id] = $.extend( 6180 {}, 6181 wcpt_cart_result_cache.in_cart[product_id], 6182 ); 6183 $.each( 6184 params.payload.variations[product_id], 6185 function (variation_id, qty) { 6186 params.payload.variations[product_id][variation_id] = 0; 6187 }, 6188 ); 6189 } 6190 6191 wcpt_cart(params); 6192 }, 6193 ); 6194 6195 // toggle content (show more / less) 6196 $("body").on("click", ".wcpt-toggle-trigger", function (e) { 6197 e.preventDefault(); 6198 var $this = $(this); 6199 $this.closest(".wcpt-toggle-enabled").toggleClass("wcpt-toggle"); 6200 }); 6201 6202 // freeze table selectors for originals 6203 function wcpt_get_container_original_table($container) { 6204 return $container.find(".wcpt-table:visible").not(".frzTbl-clone-table"); 6205 } 6206 6207 // get sibling tables under freeze table 6208 function wcpt_get_sibling_tables($table) { 6209 var $freeze_table = $table.closest(".frzTbl"); 6210 if (!$freeze_table.length) { 6211 return $table; 6212 } 6213 6214 return $(".wcpt-table", $freeze_table); 6215 } 6216 6217 // get sibling rows under freeze table 6218 window.wcpt_get_sibling_rows = function ($row) { 6219 var $freeze_table = $row.closest(".frzTbl"); 6220 if (!$freeze_table.length) { 6221 return $row; 6222 } 6223 6224 var product_id = $row.attr("data-wcpt-product-id"), 6225 variation_id = $row.attr("data-wcpt-variation-id"), 6226 row_selector; 6227 6228 if (variation_id) { 6229 row_selector = 6230 '[data-wcpt-variation-id="' + 6231 variation_id + 6232 '"].wcpt-row.wcpt-product-type-variation'; 6233 } else { 6234 row_selector = 6235 '[data-wcpt-product-id="' + 6236 product_id + 6237 '"].wcpt-row:not(.wcpt-product-type-variation)'; 6238 } 6239 6240 return $(row_selector, $freeze_table); 6241 }; 6242 6243 // get original row under freeze table 6244 function wcpt_get_original_row($row) { 6245 var $sibling_rows = wcpt_get_sibling_rows($row); 6246 6247 $sibling_rows.each(function () { 6248 var $row = $(this); 6249 if (!$row.closest("table").hasClass("frzTbl-clone-table")) { 6250 $original = $row; 6251 return false; 6252 } 6253 });
6254 6255 return $original; 6256 } 6257 6258 // unique key per row (variation first, then product) 6259 function wcpt_get_row_key($row) { 6260 var variationId = $row.data("wcpt_variation_id"); 6261 if (variationId) { 6262 return "v:" + String(variationId); 6263 } 6264 var variationIdAttr = $row.attr("data-wcpt-variation-id"); 6265 if (variationIdAttr) { 6266 return "v:" + String(variationIdAttr); 6267 } 6268 var productId = $row.attr("data-wcpt-product-id"); 6269 if (productId) { 6270 return "p:" + String(productId); 6271 } 6272 return ""; 6273 } 6274 6275 window.wcpt_get_original_row = wcpt_get_original_row; 6276 window.wcpt_get_row_key = wcpt_get_row_key; 6277 6278 // checkbox 6279 6280 // -- $cb 'change' handler -> triggers '_wcpt_checkbox_change' on $row 6281 $("body").on("change", ".wcpt-cart-checkbox", function (e) { 6282 var $this = $(this), 6283 $row = $this.closest(".wcpt-row"); 6284 6285 $row.trigger("_wcpt_checkbox_change", $this.prop("checked")); 6286 }); 6287 6288 // -- $row '_wcpt_checkbox_change' handler -> triggers 'wcpt_checkbox_change' on original $row, sets same state on sibling $cb 6289 $("body").on("_wcpt_checkbox_change", ".wcpt-row", function (e, state) { 6290 var $this = $(this), 6291 $original_row = wcpt_get_original_row($this), 6292 $table = $original_row.closest("table"), 6293 $sibling_rows = wcpt_get_sibling_rows($this), 6294 $sibling_cbs = $(".wcpt-cart-checkbox", $sibling_rows); 6295 6296 // unnecessary trigger 6297 if (!$sibling_cbs.length || !$sibling_cbs.not(":disabled").length) { 6298 return; 6299 } 6300 6301 // $row .data()-> state 6302 $original_row.data("wcpt_checked", state); 6303 6304 // $table .data()-> checked_rows 6305 var $table_checked_rows = $table.data("wcpt_checked_rows") 6306 ? $table.data("wcpt_checked_rows") 6307 : $(); 6308 6309 if (state) { 6310 $table_checked_rows = $table_checked_rows.add($original_row); 6311 } else { 6312 $table_checked_rows = $table_checked_rows.not($original_row); 6313 } 6314 6315 $table.data("wcpt_checked_rows", $table_checked_rows); 6316 6317 // publish event 6318 $original_row.trigger("wcpt_checkbox_change", state); 6319 }); 6320 6321 // -- 'wcpt_checkbox_change' handler -> set the same state on siblings rows' $cb and data on sibling rows 6322 $("body").on("wcpt_checkbox_change", ".wcpt-row", function (e, state) { 6323 var $this = $(this), 6324 $sibling_rows = wcpt_get_sibling_rows($this), 6325 $sibling_cbs = $(".wcpt-cart-checkbox", $sibling_rows); 6326 6327 $sibling_rows.data("wcpt_checked", state); 6328 $sibling_cbs.prop("checked", state); 6329 }); 6330 6331 // -- row html class 6332 $("body").on("wcpt_checkbox_change", ".wcpt-row", function (e, state) { 6333 var $this = $(this), 6334 $original_row = wcpt_get_original_row($this), 6335 $sibling_rows = wcpt_get_sibling_rows($this), 6336 html_class = "wcpt-row--checked"; 6337 6338 if ($original_row.data("wcpt_checked")) { 6339 $sibling_rows.addClass(html_class); 6340 } else { 6341 $sibling_rows.removeClass(html_class); 6342 } 6343 }); 6344 6345 // -- select variation 6346 $("body").on("select_variation", ".wcpt-row", function (e) { 6347 var $this = $(this), 6348 selected_variation = $this.data("wcpt_variation"), 6349 $cb = $this.find(".wcpt-cart-checkbox"); 6350 6351 // reset 6352 $cb.removeAttr("disabled"); 6353 $cb.removeClass("wcpt-disabled"); 6354 6355 if ( 6356 !selected_variation || 6357 $.isEmptyObject(selected_variation) || 6358 (!selected_variation.is_in_stock && !selected_variation.is_on_backorder) 6359 ) { 6360 $this.trigger("wcpt_checkbox_change", false); 6361 $cb.attr("disabled", true); 6362 $cb.addClass("wcpt-disabled"); 6363 } 6364 }); 6365 6366 // -- qty 6367 // -- -- '.qty' change handler -> trigger '_wcpt_checkbox_change' on $row 6368 setTimeout(function () { 6369 // too many 3rd party modules trigger $qty at page load 6370 $("body").on( 6371 "change", 6372 ".wcpt input.qty, .wcpt select.wcpt-qty-select", 6373 function (e) { 6374 var $this = $(this), 6375 $row = $this.closest(".wcpt-row"), 6376 $original_row = wcpt_get_original_row($row); 6377 6378 if ( 6379 $this.closest("form.cart").length || 6380 // don't permit auto-check when initial-val is min 6381 ($this.attr("data-wcpt-initial-value") === "min" && 6382 !$original_row.data("wcpt_checked")) 6383 ) { 6384 return; 6385 } 6386 6387 $original_row.trigger( 6388 "_wcpt_checkbox_change", 6389 !!parseFloat($this.val()), 6390 ); 6391 }, 6392 ); 6393 }, 1); 6394 6395 // -- -- raise qty to min or reduce to initial qty upon 'wcpt_checkbox_change' 6396 $("body").on("wcpt_checkbox_change", ".wcpt-row", function (e, state) { 6397 var $this = $(this), 6398 $sibling_rows = wcpt_get_sibling_rows($this), 6399 $sibling_qty = $("input.qty, select.wcpt-qty-select", $sibling_rows), 6400 $sibling_qty_wrappers = $(".wcpt-quantity", $sibling_rows); 6401 6402 $sibling_qty.each(function () { 6403 var $this = $(this), 6404 val = $this.val(), 6405 min = $this.attr("data-wcpt-min") 6406 ? parseFloat($this.attr("data-wcpt-min")) 6407 : $this.attr("min"), 6408 initial_qty = $this.attr("data-wcpt-initial-value"); 6409 6410 if (state) { 6411 if (!val || val === "0" || isNaN(val)) { 6412 val = min; 6413 } 6414 } else { 6415 if (initial_qty == "empty") { 6416 val = ""; 6417 } else if (initial_qty == "0") { 6418 val = 0; 6419 } else { 6420 val = min; 6421 } 6422 } 6423 6424 $this.val(val); 6425 });
6426 6427 limit_qty_controller($sibling_qty_wrappers); 6428 }); 6429 6430 // -- -- update total upon 'wcpt_checkbox_change' 6431 $("body").on("wcpt_checkbox_change", ".wcpt-row", function (e, state) { 6432 var $this = $(this), 6433 $sibling_rows = wcpt_get_sibling_rows($this); 6434 6435 update_row_total($sibling_rows); 6436 }); 6437 6438 // -- 'wcpt_checkbox_change' handler -> create / update / hide '.wcpt-cart-checkbox-trigger' button 6439 $("body").on("wcpt_checkbox_change", ".wcpt-row", wcpt_checkbox_trigger_init); 6440 6441 // Also refresh the global trigger after each layout (e.g. AJAX pagination) 6442 $("body").on("wcpt_layout", ".wcpt", function () { 6443 wcpt_checkbox_trigger_init(); 6444 }); 6445 6446 function wcpt_checkbox_trigger_init() { 6447 // global floating checkbox trigger (across all tables on the page) 6448 // setup / show trigger 6449 var $checkbox_trigger = $(".wcpt-cart-checkbox-trigger"); 6450 6451 if (!$checkbox_trigger.length) { 6452 var html = $("#tmpl-wcpt-cart-checkbox-trigger").html(); 6453 $checkbox_trigger = $(html).appendTo("body"); 6454 } else { 6455 $checkbox_trigger.removeClass("wcpt-hide"); 6456 } 6457 6458 // aggregate selection across ALL tables on the page, 6459 // using the per-container cross-page state on each $wcpt 6460 var totalQty = 0, 6461 totalCost = 0; 6462 6463 $(".wcpt").each(function () { 6464 var $container = $(this), 6465 table_id = 6466 typeof wcpt_util !== "undefined" 6467 ? wcpt_util.get_table_id($container) 6468 : "", 6469 tableState = $container.data("wcpt_all_checked") || {}; 6470 6471 if ( 6472 table_id && 6473 window.wcpt_cart_checked_state && 6474 window.wcpt_cart_checked_state[table_id] 6475 ) { 6476 tableState = window.wcpt_cart_checked_state[table_id]; 6477 } 6478 6479 if (!tableState || typeof tableState !== "object") { 6480 return; 6481 } 6482 6483 Object.keys(tableState).forEach(function (key) { 6484 var entry = tableState[key]; 6485 if (!entry) { 6486 return; 6487 } 6488 6489 var q = !isNaN(parseFloat(entry.qty)) ? parseFloat(entry.qty) : 1, 6490 t = !isNaN(parseFloat(entry.total)) ? parseFloat(entry.total) : 0; 6491 6492 totalQty = (totalQty * 1e12 + q * 1e12) / 1e12; 6493 totalCost = (totalCost * 1e12 + t * 1e12) / 1e12; 6494 }); 6495 }); 6496 6497 if (!totalQty) { 6498 $checkbox_trigger.hide(); 6499 return; 6500 } 6501 6502 $checkbox_trigger 6503 .data({ 6504 wcpt_qty: totalQty, 6505 wcpt_cost: totalCost, 6506 }) 6507 .find(".wcpt-total-selected") 6508 .text(totalQty) 6509 .end() 6510 .find(".wcpt-total-selected-cost") 6511 .text(format_price_figure(totalCost)); 6512 6513 $checkbox_trigger 6514 .trigger("wcpt_checkbox_trigger_updating") 6515 .show() 6516 .trigger("wcpt_checkbox_trigger_updated"); 6517 } 6518 6519 window.wcpt_checkbox_trigger_init = wcpt_checkbox_trigger_init; 6520 6521 // -- heading 6522 // -- -- check all $cb in table via $heading_cb '.wcpt-cart-checkbox-heading' 6523 $("body").on("click", ".wcpt-cart-checkbox-heading", function () { 6524 var $this = $(this), 6525 state = $this.prop("checked"), 6526 $container = $this.closest(".wcpt"),
6527 $table = wcpt_get_container_original_table($container), 6528 $rows = $(".wcpt-row", $table); 6529 6530 $rows.trigger("_wcpt_checkbox_change", state); 6531 $("wcpt-cart-checkbox--last-clicked", $table).removeClass( 6532 "wcpt-cart-checkbox--last-clicked", 6533 ); 6534 }); 6535 6536 // -- -- auto toggle heading checkbox 6537 $("body").on("wcpt_checkbox_change", ".wcpt-row", function (e, state) { 6538 // Debounce multiple calls 6539 clearTimeout(window.wcpt_cb_heading); 6540 window.wcpt_cb_heading = setTimeout(function () { 6541 $(".wcpt").each(function () { 6542 var $container = $(this), 6543 $heading_cb = $container.find(".wcpt-cart-checkbox-heading:visible"), 6544 $checkboxes = $container.find(".wcpt-cart-checkbox"), 6545 $available_cbs = $checkboxes.filter(":visible").not(":disabled"), 6546 $checked_cbs = $available_cbs.filter(":checked"), 6547 all_checked = 6548 $available_cbs.length && 6549 $checked_cbs.length === $available_cbs.length; 6550 6551 // Always update heading checkbox state 6552 $heading_cb.prop("checked", all_checked); 6553 }); 6554 }, 100); 6555 }); 6556 6557 // -- shift + click 6558 // -- -- record shift key 6559 $("body").on("keydown", function (e) { 6560 if (e.shiftKey) { 6561 wcpt_shiftKey = true; 6562 $("body").one("keyup", function (e) { 6563 wcpt_shiftKey = false; 6564 }); 6565 } 6566 }); 6567 6568 // -- -- act upon $cb shift key selected by user 6569 $("body").on("change", ".wcpt-cart-checkbox", function (e, wcpt_sync) { 6570 if (wcpt_sync) { 6571 return false; 6572 } 6573 6574 var $this = $(this), 6575 $table = $this.closest(".wcpt-table"), 6576 $cb = $(".wcpt-cart-checkbox", $table); 6577 (($last_clicked = $cb.filter(".wcpt-cart-checkbox--last-clicked")), 6578 (checked = $this.prop("checked"))); 6579 6580 $last_clicked.removeClass("wcpt-cart-checkbox--last-clicked"); 6581 $this.addClass("wcpt-cart-checkbox--last-clicked"); 6582 6583 if ( 6584 $last_clicked.length && 6585 typeof wcpt_shiftKey !== "undefined" && 6586 wcpt_shiftKey 6587 ) { 6588 var min = Math.min($cb.index($this), $cb.index($last_clicked)), 6589 max = Math.max($cb.index($this), $cb.index($last_clicked)); 6590 6591 $cb 6592 .filter(function () { 6593 var $this = $(this), 6594 index = $cb.index($this); 6595 6596 if ($this.prop("disabled")) { 6597 return false; 6598 } 6599 6600 if (index >= min && index <= max) { 6601 return true; 6602 } else { 6603 return false; 6604 } 6605 }) 6606 .prop("checked", checked) 6607 .trigger("change", true); 6608 } 6609 }); 6610 6611 // -- select / clear all 6612 $("body").on( 6613 "click", 6614 ".wcpt-add-selected__select-all, .wcpt-add-selected__clear-all", 6615 function (e) { 6616 var $this = $(this), 6617 isSelectAll = $this.hasClass("wcpt-add-selected__select-all"), 6618 $container = $this.closest(".wcpt"),
6619 $table = wcpt_get_container_original_table($container), 6620 $rows = $(".wcpt-row", $table); 6621 6622 $rows.trigger("_wcpt_checkbox_change", isSelectAll); 6623 6624 // If this is clear-all, also clear cross-page selection state for this table 6625 if (!isSelectAll && typeof wcpt_util !== "undefined") { 6626 var table_id = wcpt_util.get_table_id($container); 6627 if (table_id) { 6628 // delegate actual clearing to the persistence module 6629 $(document).trigger("wcpt_clear_all_checked_for_table", [ 6630 table_id, 6631 $container, 6632 ]); 6633 6634 // ensure the per-container mirror used by the global trigger is cleared 6635 $container.data("wcpt_all_checked", {}); 6636 6637 // immediately recompute / hide the global floating trigger if needed 6638 wcpt_checkbox_trigger_init(); 6639 } 6640 } 6641 $("wcpt-cart-checkbox--last-clicked", $table).removeClass( 6642 "wcpt-cart-checkbox--last-clicked", 6643 ); 6644 }, 6645 ); 6646 6647 // -- -- toggle the buttons 6648 $("body").on( 6649 "wcpt_checkbox_change", 6650 ".wcpt-row", 6651 update_table_add_selected_to_cart, 6652 ); 6653 6654 function update_table_add_selected_to_cart() { 6655 var _ = this; 6656 setTimeout(function () { 6657 var $this = $(_), 6658 $container = $this.closest(".wcpt"),
6659 $table = wcpt_get_container_original_table($container), 6660 // cross-page selection state for this table (from persistence module) 6661 tableState = $container.data("wcpt_all_checked") || {}; 6662 $add_checked = $(".wcpt-add-selected:visible", $container); 6663 6664 $add_checked.removeClass( 6665 "wcpt-add-selected--unselected wcpt-add-selected--single-item-selected", 6666 ); 6667 6668 var keys = Object.keys(tableState), 6669 totalQty = 0, 6670 totalCost = 0; 6671 6672 if (keys.length) { 6673 // derive total quantity and total cost across all pages from persisted state 6674 keys.forEach(function (k) { 6675 var entry = tableState[k]; 6676 if (!entry) { 6677 return; 6678 } 6679 var q = !isNaN(parseFloat(entry.qty)) ? parseFloat(entry.qty) : 1, 6680 t = !isNaN(parseFloat(entry.total)) ? parseFloat(entry.total) : 0; 6681 6682 totalQty = (totalQty * 1e12 + q * 1e12) / 1e12; 6683 totalCost = (totalCost * 1e12 + t * 1e12) / 1e12; 6684 }); 6685 6686 $(".wcpt-total-selected", $add_checked).text(totalQty); 6687 $(".wcpt-total-selected-cost .wcpt-amount", $add_checked).text( 6688 format_price_figure(totalCost), 6689 ); 6690 6691 if (totalQty == 1) { 6692 $add_checked.addClass("wcpt-add-selected--single-item-selected"); 6693 } 6694 } else { 6695 // nothing selected across any page 6696 $add_checked.addClass("wcpt-add-selected--unselected"); 6697 $(".wcpt-total-selected", $add_checked).text(0); 6698 $(".wcpt-total-selected-cost .wcpt-amount", $add_checked).text( 6699 format_price_figure(0), 6700 ); 6701 } 6702 }, 100); 6703 } 6704 6705 // price decimal 6706 function format_price_figure(price) { 6707 price = parseFloat(price); 6708 6709 // remove decimal if it is unnecessary ie .00 6710 if (price !== parseInt(price)) { 6711 price = parseFloat(price).toFixed(wcpt_params.price_decimals); 6712 } else { 6713 price = parseInt(price); 6714 } 6715 6716 // decimal separator 6717 price = (price + "").replace(".", wcpt_params.price_decimal_separator); 6718 6719 // thousands separator 6720 var decimal_split = price.split(wcpt_params.price_decimal_separator); 6721 ((non_decimal = decimal_split[0].toString()), (decimal = decimal_split[1])); 6722 6723 if (non_decimal.length > 3) { 6724 formatted_non_decimal = 6725 non_decimal.slice(0, -3) + 6726 wcpt_params.price_thousand_separator + 6727 non_decimal.slice(-3); 6728 price = 6729 formatted_non_decimal + 6730 (decimal ? wcpt_params.price_decimal_separator + decimal : ""); 6731 } 6732 6733 return price; 6734 } 6735 6736 function unformat_price_figure(price) { 6737 price = price.toString(); 6738 6739 // remove thousands separator 6740 price = price.replace(wcpt_params.price_thousand_separator, ""); 6741 6742 // replace decimal separator 6743 price = price.replace(wcpt_params.price_decimal_separator, "."); 6744 6745 return parseFloat(price); 6746 } 6747 6748 // follow wc settings for price display 6749 function format_price(num) { 6750 if (!num && num !== "0" && num !== 0) { 6751 return ""; 6752 } 6753 6754 return wcpt_params.price_format 6755 .replace("%1$s", wcpt_params.currency_symbol) 6756 .replace("%2$s", format_price_figure(num)); 6757 } 6758 6759 // -- add to cart 6760 $("body").on( 6761 "click", 6762 ".wcpt-cart-checkbox-trigger, .wcpt-cart-checkbox-trigger--local", 6763 wcpt_cart_checkbox, 6764 ); 6765 function wcpt_cart_checkbox() { 6766 var $this = $(this), 6767 products = {}, 6768 variations = {}, 6769 attributes = {}, 6770 addons = {}, 6771 measurement = {}, 6772 nyp = {}, // name your price 6773 $checked_rows = $(), 6774 $table = $(), 6775 tableContainersById = {}; 6776 6777 if ($this.hasClass("wcpt-cart-checkbox-trigger")) { 6778 // global 6779 var $container = $(".wcpt"); 6780 6781 $container.each(function () { 6782 var $c = $(this), 6783 table_id = 6784 typeof wcpt_util !== "undefined" 6785 ? wcpt_util.get_table_id($c) 6786 : false, 6787 $orig_table = wcpt_get_container_original_table($c); 6788 6789 if (table_id) { 6790 tableContainersById[table_id] = $c; 6791 } 6792 6793 $table = $table.add($orig_table); 6794 }); 6795 6796 $this.addClass("wcpt-hide"); 6797 } else { 6798 // local 6799 var $container = $this.closest(".wcpt"), 6800 table_id = 6801 typeof wcpt_util !== "undefined" 6802 ? wcpt_util.get_table_id($container) 6803 : false; 6804 6805 if (table_id) { 6806 tableContainersById[table_id] = $container; 6807 } 6808
6809 $table = wcpt_get_container_original_table($container); 6810 } 6811 6812 $table.each(function () { 6813 var $this = $(this); 6814 $table_checked_rows = $this.data("wcpt_checked_rows") 6815 ? $this.data("wcpt_checked_rows") 6816 : $(); 6817 6818 $checked_rows = $checked_rows.add($table_checked_rows); 6819 }); 6820 6821 $checked_rows.each(function () { 6822 var $row = $(this), 6823 product_id = $row.attr("data-wcpt-product-id"), 6824 variation_id = false, 6825 variation_attributes = false, 6826 $qty = $(".qty, .wcpt-qty-select", $row).first(), 6827 val = parseFloat($qty.length ? $qty.val() : 1); 6828 if (isNaN(val)) { 6829 val = 0; 6830 } 6831 6832 if (typeof products[product_id] === "undefined") { 6833 products[product_id] = val; 6834 } else { 6835 products[product_id] += val; // variation 6836 } 6837 6838 // variable 6839 if ($row.hasClass("wcpt-product-type-variable")) { 6840 var data = $row.data(); 6841 6842 if ( 6843 data.wcpt_variation_selected && 6844 data.wcpt_variation_available && 6845 data.wcpt_complete_match && 6846 data.wcpt_variation_id 6847 ) { 6848 variation_id = data.wcpt_variation_id; 6849 6850 if (data.wcpt_attributes) { 6851 variation_attributes = data.wcpt_attributes; 6852 } 6853 } 6854 6855 // variation 6856 } else if ($row.hasClass("wcpt-product-type-variation")) { 6857 variation_id = $row.attr("data-wcpt-variation-id"); 6858 variation_attributes = JSON.parse( 6859 $row.attr("data-wcpt-variation-attributes"), 6860 ); 6861 } 6862 6863 if (variation_id) { 6864 if (!variations[product_id]) { 6865 variations[product_id] = {}; 6866 } 6867 6868 if (!variations[product_id][variation_id]) { 6869 variations[product_id][variation_id] = val; 6870 } 6871 6872 if (variation_attributes) { 6873 attributes[variation_id] = variation_attributes; 6874 } 6875 } 6876 6877 // addons 6878 if ( 6879 $row.hasClass("wcpt-product-has-addons") && 6880 wcpt_row_has_inline_addons_form($row) 6881 ) { 6882 addons[product_id] = wcpt_get_addons($row); 6883 } 6884 6885 // measurement 6886 if ($row.hasClass("wcpt-product-has-measurement")) { 6887 measurement[product_id] = wcpt_get_measurement($row); 6888 } 6889 6890 // name your price 6891 if ($row.hasClass("wcpt-product-has-name-your-price")) { 6892 nyp[product_id] = wcpt_get_nyp($row); 6893 } 6894 }); 6895 6896 // Off-page selections from the checkbox persistence module 6897 if (window.wcpt_cart_checkbox_persist) { 6898 window.wcpt_cart_checkbox_persist.aggregateOffPageIntoPayload( 6899 tableContainersById, 6900 { 6901 products: products, 6902 variations: variations, 6903 attributes: attributes, 6904 }, 6905 ); 6906 window.wcpt_cart_checkbox_persist.clearTables(tableContainersById); 6907 } 6908 6909 // Uncheck all currently checked rows on the page before wcpt_cart, 6910 // otherwise totals / visual state may be inconsistent. 6911 $checked_rows.trigger("_wcpt_checkbox_change", false); 6912 6913 // When every selection was off-page, $checked_rows is empty so the 6914 // checkbox-change â update_table_add_selected_to_cart path never runs. 6915 // Refresh the local "Add selected" button(s) from cleared state. 6916 $.each(tableContainersById, function (id, $c) { 6917 if ($c && $c.length) { 6918 update_table_add_selected_to_cart.call($c.get(0)); 6919 } 6920 }); 6921 6922 var payload = { 6923 products: products, 6924 addons: addons, 6925 measurement: measurement, 6926 variations: variations, 6927 attributes: attributes, 6928 nyp: nyp, 6929 }; 6930 6931 // mark this cart request as originating from a checkbox-trigger bulk add 6932 var checkboxScope = $this.hasClass("wcpt-cart-checkbox-trigger") 6933 ? "global" 6934 : "local"; 6935 6936 wcpt_cart({ 6937 payload: payload, 6938 redirect: $this.attr("data-wcpt-redirect-url"), 6939 external_payload: { 6940 wcpt_checkbox_bulk: checkboxScope, 6941 }, 6942 }); 6943 } 6944 6945 // addons 6946 // True when addon fields are printed and visible in the row's cart form. 6947 // Select variation "attribute dropdowns" embeds a cart_form with only attributes 6948 // visible â hidden addon markup there must not block the product form modal. 6949 window.wcpt_row_has_inline_addons_form = function ($row) { 6950 return !!$( 6951 [ 6952 ".wcpt-add-to-cart-wrapper .prad-addons-wrapper", 6953 ".wcpt-add-to-cart-wrapper .wc-pao-addon", 6954 ".wcpt-add-to-cart-wrapper .wc-pao-addons-container", 6955 ".wcpt-add-to-cart-wrapper #product-addons-total", 6956 ".wcpt-add-to-cart-wrapper .wcpa_form_outer", 6957 ].join(", "), 6958 wcpt_get_sibling_rows($row), 6959 ).filter(":visible").length; 6960 }; 6961 6962 window.wcpt_get_addons = function ($row) { 6963 var $form = $( 6964 ".wcpt-add-to-cart-wrapper form", 6965 wcpt_get_sibling_rows($row), 6966 ), 6967 addons = {}; 6968 6969 // WooCommerce Custom Product Addons 6970 var $wcpa_fields = $form.find(".wcpa_form_outer"); 6971 if ($wcpa_fields.length) { 6972 var $fields = $wcpa_fields, 6973 $_form = $("<form>"); 6974 6975 $_form 6976 .append($fields.clone()) 6977 .find("select") 6978 .each(function () { 6979 // cloned select needs correct value 6980 var $this = $(this), 6981 name = $this.attr("name"), 6982 value = $fields.find('[name="' + name + '"]select').val(); 6983 $this.val(value); 6984 }); 6985 6986 $.each($_form.serializeArray(), function (i, field) { 6987 var field_name = field.name, 6988 suffix_index = field_name.indexOf("--wcpt"); 6989 6990 if (-1 !== suffix_index) { 6991 // must be checkbox or radio 6992 field_name = field_name.substring(0, suffix_index); 6993 6994 if (field.name.slice(-1) == "]") { 6995 // checkbox 6996 if (!addons[field_name]) { 6997 addons[field_name] = []; 6998 } 6999 addons[field_name].push(field.value); 7000 return; 7001 } else { 7002 // radio 7003 addons[field_name] = field.value; 7004 } 7005 } else { 7006 // other field types 7007 addons[field_name] = field.value; 7008 } 7009 }); 7010 7011 // official WooCommerce Product Addons 7012 } else { 7013 $.each($form.serializeArray(), function (i, field) { 7014 var field_name = field.name; 7015 7016 if (field_name.slice(-2) == "[]") { 7017 field_name = field_name.substring(0, field_name.length - 2); 7018 } 7019 7020 if (typeof addons[field_name] === "undefined") { 7021 addons[field_name] = field.value; // add prop 7022 } else { 7023 if (typeof addons[field_name] !== "object") { 7024 // make it an array 7025 addons[field_name] = [addons[field_name]]; 7026 } 7027 addons[field_name].push(field.value); // update prop 7028 } 7029 }); 7030 } 7031 7032 return addons; 7033 }; 7034 7035 // measurement 7036 window.wcpt_get_measurement = function ($row) { 7037 var $price_calculator = $( 7038 ".wcpt-add-to-cart-wrapper form #price_calculator", 7039 wcpt_get_sibling_rows($row), 7040 ), 7041 measurement = {}; 7042 7043 $("input", $price_calculator).each(function () { 7044 var $this = $(this); 7045 measurement[$this.attr("name")] = $this.val(); 7046 });
7047 7048 return measurement; 7049 }; 7050 7051 // name your price 7052 window.wcpt_get_nyp = function ($row) { 7053 var $nyp = get_nyp_input_element($row), 7054 val = 0; 7055 7056 if ($nyp.length) { 7057 val = $nyp.val(); 7058 } 7059 7060 return val; 7061 }; 7062 7063 function get_nyp_input_element($row) { 7064 return $(".wcpt-name-your-price--input", wcpt_get_sibling_rows($row)); 7065 } 7066 7067 $("body").on("keyup input", ".wcpt-search-filter-options", function (e) { 7068 var $this = $(this), 7069 val = $this.val().toLowerCase().trim(), 7070 $filter = $this.closest(".wcpt-filter"), 7071 $option = $(".wcpt-dropdown-option", $filter); 7072 7073 e.preventDefault(); 7074 7075 if (!val) { 7076 $option.show(); 7077 7078 $(".wcpt-filter-hierarchy-accordion--open", $filter).removeClass( 7079 "wcpt-filter-hierarchy-accordion--open", 7080 ); 7081 7082 $("input[type=radio], input[type=checkbox]", $option).each(function () { 7083 var $this = $(this); 7084 if ($this.is(":checked")) { 7085 $this 7086 .closest(".wcpt-dropdown-option.wcpt-filter-hierarchy-accordion") 7087 .addClass("wcpt-filter-hierarchy-accordion--open"); 7088 } 7089 }); 7090 return; 7091 } 7092 7093 $option.each(function () { 7094 var $this = $(this), 7095 label = $this.text().toLowerCase().trim(); 7096 if (label.indexOf(val) == -1) { 7097 $this.hide(); 7098 } else { 7099 $this.show(); 7100 $this 7101 .closest(".wcpt-dropdown-option.wcpt-filter-hierarchy-accordion") 7102 .addClass("wcpt-filter-hierarchy-accordion--open"); 7103 } 7104 }); 7105 }); 7106 7107 // global product search shortcode 7108 $("body").on( 7109 "change keydown keyup", 7110 ".wcpt-global-search__keyword-input", 7111 function () { 7112 var $this = $(this), 7113 $search = $this.closest(".wcpt-global-search"); 7114 7115 if ($this.val()) { 7116 $search.removeClass("wcpt-global-search--empty"); 7117 } else { 7118 $search.addClass("wcpt-global-search--empty"); 7119 } 7120 }, 7121 ); 7122 7123 $(".wcpt-global-search__keyword-input").trigger("change"); 7124 7125 // -- facade 7126 $("body").on("change", ".wcpt-global-search__category-selector", function () { 7127 var $this = $(this), 7128 value = $this.val(), 7129 $option = $('option[value="' + value + '"]', $this), 7130 text = $option.text().trim(), 7131 $facade = $this.siblings(".wcpt-global-search__category-selector-facade"); 7132 7133 $(".wcpt-global-search__category-selector-facade__text", $facade).text( 7134 text, 7135 ); 7136 }); 7137 7138 // -- redirect empty 7139 $("body").on("submit", ".wcpt-global-search__form", function (e) { 7140 var $this = $(this), 7141 keyword = $this.find(".wcpt-global-search__keyword-input").val(), 7142 $select = $this.find(".wcpt-global-search__category-selector"), 7143 clear_redirect_url = $this.attr("data-wcpt-clear-redirect-url"), 7144 clear_redirect = $this.attr("data-wcpt-clear-redirect"), 7145 redirect = $this.attr("data-wcpt-redirect"), 7146 category = $(".wcpt-global-search__category-selector", $this).val(), 7147 action = $this.attr("action"); 7148 7149 // select 'All' category 7150 if (!keyword) { 7151 if (clear_redirect !== "category") { 7152 $select.val("").change(); 7153 } 7154 7155 // redirect 7156 if (clear_redirect_url) { 7157 e.preventDefault(); 7158 window.location = clear_redirect_url; 7159 } 7160 } else if (redirect == "category" && category) { 7161 e.preventDefault(); 7162 window.location = wcpt_product_category_links[category] + "?s=" + keyword; 7163 } else if (redirect == "shop") { 7164 e.preventDefault(); 7165 var url = action + "?s=" + keyword; 7166 if (category) { 7167 url += "&wcpt_search_category=" + category; 7168 } 7169 window.location = url; 7170 } else if (redirect == "search") { 7171 e.preventDefault(); 7172 var url = action + "?s=" + keyword + "&post_type=product"; 7173 if (category) { 7174 url += "&wcpt_search_category=" + category; 7175 } 7176 window.location = url; 7177 } 7178 }); 7179 7180 // -- clear 7181 $("body").on("click", ".wcpt-global-search__clear", function () { 7182 var $this = $(this), 7183 $input = $this.siblings(".wcpt-global-search__keyword-input"), 7184 $form = $this.closest(".wcpt-global-search__form"); 7185 $input.val(""); 7186 $form.submit(); 7187 }); 7188 7189 // -- focus / blur 7190 $("body") 7191 .on("focus", ".wcpt-global-search__keyword-input", function () { 7192 var $this = $(this), 7193 $wrapper = $this.parent(); 7194 $wrapper.addClass("wcpt-global-search__keyword-input-wrapper--focus"); 7195 }) 7196 .on("blur", ".wcpt-global-search__keyword-input", function () { 7197 var $this = $(this), 7198 $wrapper = $this.parent(); 7199 $wrapper.removeClass("wcpt-global-search__keyword-input-wrapper--focus"); 7200 });
7201 7202 // cart 7203 7204 window.wcpt_cart = function (params) { 7205 // maybe add to pending requests 7206 if ( 7207 // there has been at least one cart call earlier 7208 window.wcpt_cart_call_id && 7209 // no results cache ie, no call is resolved yet 7210 (!window.wcpt_cart_result_cache || 7211 // the call id in result cache (ie last resolved call) is uneqal to current call id 7212 window.wcpt_cart_call_id != 7213 window.wcpt_cart_result_cache.payload.wcpt_cart_call_id) 7214 ) { 7215 !window.wcpt_cart_pending_req_params && 7216 (window.wcpt_cart_pending_req_params = {}); 7217 7218 // -- add this req to pending as well and return 7219 $.extend(true, window.wcpt_cart_pending_req_params, params); 7220 return; 7221 } 7222 7223 // begin / increment the cart call counter 7224 window.wcpt_cart_call_id = window.wcpt_cart_call_id 7225 ? window.wcpt_cart_call_id + 1 7226 : 1; 7227 7228 var _params = { 7229 payload: { 7230 wcpt_cart_call_id: window.wcpt_cart_call_id, 7231 }, 7232 before: false, 7233 always: false, 7234 redirect: false, 7235 external_payload: {}, 7236 }; 7237 params = $.extend({}, _params, params ? params : {}); 7238 7239 params.payload.wcpt_cart_call_id = window.wcpt_cart_call_id; 7240 params.payload.cart_widget_permitted = cart_widget_permitted(); 7241 7242 // view 7243 7244 $(".wcpt-cart-widget").addClass("wcpt-cart-widget--loading"); // cart widget 7245 7246 // product req data used by view 7247 var product_request = { 7248 products: params.payload.products 7249 ? $.extend({}, params.payload.products) 7250 : {}, 7251 variations: params.payload.variations 7252 ? $.extend({}, params.payload.variations) 7253 : {}, 7254 }; 7255 7256 if ( 7257 // add in variation modal request data so it may be used by the view 7258 typeof params.payload !== "undefined" && 7259 params.payload.variation_form && 7260 typeof params.external_payload !== "undefined" && 7261 params.external_payload["add-to-cart"] 7262 ) { 7263 var product_id = params.external_payload["add-to-cart"], 7264 variation_id = params.external_payload["variation_id"] 7265 ? params.external_payload["variation_id"] 7266 : "", 7267 quantity = parseFloat(params.external_payload["quantity"]); 7268 7269 product_request.products[product_id] = quantity; 7270 7271 if (variation_id) { 7272 product_request.variations[product_id] = {}; 7273 product_request.variations[product_id][variation_id] = quantity; 7274 } 7275 } 7276 7277 $(".wcpt-row:visible").each(function () { 7278 var $row = $(this), 7279 $sibling_rows = wcpt_get_sibling_rows($row), 7280 product_id = $row.attr("data-wcpt-product-id"), 7281 variation_id = $row.attr("data-wcpt-variation-id"), 7282 $button = $(cart_button_selector, $row), 7283 in_cart = parseFloat($row.attr("data-wcpt-in-cart")); 7284 7285 // before request, update view like - add loading badge, animate Remove el 7286 if (product_request.products) { 7287 $.each(product_request.products, function (id, qty) { 7288 if (id == product_id) { 7289 if ( 7290 // product is a variation, and not part of the cart request 7291 variation_id && 7292 product_request.variations && 7293 product_request.variations[product_id] && 7294 typeof product_request.variations[product_id][variation_id] == 7295 "undefined" 7296 ) { 7297 return; 7298 } 7299 7300 loading_badge_on_button($button); 7301 7302 if (0 === qty && in_cart) { 7303 // removing 7304 $row.addClass("wcpt-removing-product"); 7305 } else { 7306 // adding 7307 $row.addClass("wcpt-adding-product"); 7308 7309 var $qty = $("input.qty", $row), 7310 $wcpt_qty = $qty.not(".cart .qty"), 7311 initial = $wcpt_qty.attr("data-wcpt-initial-value"), 7312 min = parseFloat($wcpt_qty.attr("data-wcpt-min")), 7313 return_to_initial = $wcpt_qty.attr( 7314 "data-wcpt-return-to-initial", 7315 ); 7316 7317 if (return_to_initial) { 7318 $sibling_rows.trigger("_wcpt_checkbox_change", false); 7319 7320 if (initial == "min") { 7321 $qty.val(min); 7322 } else if (initial === "0") { 7323 $qty.val(0); 7324 } else if (initial === "empty") { 7325 $qty.val(""); 7326 } 7327 } 7328 7329 var $wrapper = $qty.closest(".wcpt-quantity"); 7330 if ($wrapper.length) { 7331 limit_qty_controller($wrapper); 7332 } 7333 7334 // dropdown 7335 var $qty = $("select.wcpt-qty-select", $row), 7336 $first = $qty.children("option:first-child"); 7337 7338 $qty.val($first.attr("value")); 7339 } 7340 7341 // total 7342 var $total = $(".wcpt-total", $sibling_rows), 7343 force_total_qty = qty; 7344 7345 if ( 7346 variation_id && 7347 product_request.variations && 7348 product_request.variations[product_id] && 7349 product_request.variations[product_id][variation_id] 7350 ) { 7351 force_total_qty = 7352 product_request.variations[product_id][variation_id]; 7353 } 7354 7355 // don't allow it to be reset by qty going to 0 7356 if ($total.length) { 7357 update_row_total($sibling_rows, force_total_qty); 7358 } 7359 } 7360 }); 7361 } 7362 });
7363 7364 var data = $.extend( 7365 {}, 7366 { 7367 wcpt_payload: params.payload, 7368 lang: wcpt_i18n.lang, 7369 }, 7370 params.external_payload ? params.external_payload : {}, 7371 ); 7372 7373 $("body").trigger("wcpt_cart_request", data); 7374 7375 // use cache 7376 if (params.payload.use_cache && window.wcpt_cart_result_cache) { 7377 window.wcpt_cart_result_cache.payload.wcpt_cart_call_id = 7378 window.wcpt_cart_call_id; 7379 $("body").trigger("wcpt_cart", window.wcpt_cart_result_cache); 7380 7381 // fetch fresh 7382 } else { 7383 $.post( 7384 wcpt_params.wc_ajax_url.replace("%%endpoint%%", "wcpt_cart"), 7385 data, 7386 function (result) { 7387 window.wcpt_cart_result_cache = $.extend({}, result, { 7388 success: true, 7389 notice: "", 7390 is_cache: true, 7391 }); 7392 $("body").trigger("wcpt_cart", result); 7393 }, 7394 ).always(function (result) { 7395 if (params.always) { 7396 params.always(result); 7397 } 7398 7399 // resolve any pending requests 7400 if (window.wcpt_cart_pending_req_params) { 7401 wcpt_cart(window.wcpt_cart_pending_req_params); 7402 window.wcpt_cart_pending_req_params = false; 7403 } 7404 7405 if (params.redirect) { 7406 if ( 7407 // redirect after user reads error 7408 !result.success && 7409 result.notice 7410 ) { 7411 $("body").on( 7412 "click touchstart", 7413 ".wcpt-notice-wrapper", 7414 function () { 7415 window.location = params.redirect; 7416 }, 7417 ); 7418 } else { 7419 // redirect immediately 7420 window.location = params.redirect; 7421 } 7422 } 7423 }); 7424 } 7425 }; 7426 7427 $("body").on("wcpt_cart", function (e, result) { 7428 // do not resolve if there are pending cart calls 7429 if (window.wcpt_cart_pending_req_params) { 7430 return; 7431 } 7432 7433 // cart widget 7434 if (result.payload.wcpt_cart_call_id === window.wcpt_cart_call_id) { 7435 // latest call resolved 7436 $(".wcpt-cart-widget ").removeClass("wcpt-cart-widget--loading"); 7437 } 7438 7439 // error 7440 if (!result.success && result.notice) { 7441 var $body = $("body"), 7442 $notice = $( 7443 '<div class="wcpt-notice-wrapper">' + result.notice + "</div>", 7444 ); 7445 $body.append($notice); 7446 $body.one("click", function () { 7447 $notice.remove(); 7448 }); 7449 } 7450 7451 if (result.cart_widget && cart_widget_permitted()) { 7452 var $body = $("body"), 7453 $old = $(".wcpt-cart-widget").not(".wcpt-cart-checkbox-trigger"), 7454 $new = $(result.cart_widget); 7455 7456 if ($new.hasClass("wcpt-hide")) { 7457 $body.removeClass("wcpt-cart-widget-visible"); 7458 } else { 7459 $body.addClass("wcpt-cart-widget-visible"); 7460 } 7461 7462 $body.append($new); 7463 $old.remove(); 7464 } 7465 7466 // If this cart response came from a checkbox-trigger bulk add-to-cart, 7467 // clear all selection state across tables (including persisted state) 7468 // so nothing remains checked on any page. 7469 if ( 7470 result && 7471 result.external_payload && 7472 result.external_payload.wcpt_checkbox_bulk 7473 ) { 7474 if (typeof wcpt_util !== "undefined") { 7475 $(".wcpt").each(function () { 7476 var $container = $(this), 7477 table_id = wcpt_util.get_table_id($container); 7478 7479 if (!table_id) { 7480 return; 7481 } 7482 7483 // Use the built-in API to clear persisted cross-page state 7484 $(document).trigger("wcpt_clear_all_checked_for_table", [ 7485 table_id, 7486 $container, 7487 ]); 7488 7489 // Uncheck any rows currently in the DOM for this table 7490 $container.find(".wcpt-row").each(function () { 7491 $(this).trigger("_wcpt_checkbox_change", false); 7492 }); 7493 }); 7494 } 7495 7496 // Hide the global floating trigger (if present) 7497 $(".wcpt-cart-checkbox-trigger").addClass("wcpt-hide").hide(); 7498 } 7499 7500 // added / removed 7501 var added = (removed = false); 7502 7503 if (result.payload.products && result.in_cart) { 7504 $.each(result.payload.products, function (product_id, product_qty) { 7505 var variation_id = (variation_qty = false); 7506 7507 if ( 7508 result.payload.variations && 7509 typeof result.payload.variations[product_id] !== "undefined" 7510 ) { 7511 variation_id = Object.keys(result.payload.variations[product_id])[0]; 7512 variation_qty = Object.values( 7513 result.payload.variations[product_id], 7514 )[0]; 7515 } 7516 7517 if (product_qty === "0") { 7518 // req. type: "remove" 7519 if ( 7520 $.isEmptyObject(result.in_cart) || 7521 !result.in_cart.length || 7522 !result.in_cart[product_id] || 7523 (variation_id && !result.in_cart[product_id][variation_id]) 7524 ) { 7525 removed = true; 7526 } 7527 } else { 7528 // req. type: "add" 7529 if ( 7530 !$.isEmptyObject(result.in_cart) && 7531 result.in_cart[product_id] && 7532 (!variation_id || result.in_cart[product_id][variation_id]) 7533 ) { 7534 added = true; 7535 } 7536 } 7537 }); 7538 } 7539 7540 if (result.payload.variation_form) { 7541 added = true; 7542 } 7543 7544 var in_cart_products = []; // variations and parent variable products at same level 7545 7546 if (result.in_cart) { 7547 $.each(result.in_cart, function (key, val) { 7548 // variable product 7549 if (typeof val === "object") { 7550 // related to parent variable product 7551 var qty = 0, 7552 total = 0; 7553 7554 // iterate over variations 7555 $.each(val, function (key2, val2) { 7556 var _total = result.in_cart_total[key][key2]; 7557
7558 // insert variations as separate entries 7559 in_cart_products.push({ 7560 id: key2, 7561 type: "variation", 7562 quantity: val2, 7563 total: _total, 7564 }); 7565 7566 // sum up variation quantities and totals (decimal fix) 7567 qty = (qty * 1000 + parseFloat(val2) * 1000) / 1000; 7568 total = (total * 1000 + parseFloat(_total) * 1000) / 1000; 7569 }); 7570 7571 // insert parent variable product 7572 in_cart_products.push({ 7573 id: key, 7574 type: "variable", 7575 quantity: qty, 7576 total: total, 7577 }); 7578 } else { 7579 // simple 7580 in_cart_products.push({ 7581 id: key, 7582 type: "simple", 7583 quantity: val, 7584 total: result.in_cart_total[key], 7585 }); 7586 } 7587 }); 7588 } 7589 7590 // view 7591 7592 var $rows = $(".wcpt-row"); 7593 7594 $rows.each(function () { 7595 var $row = $(this), 7596 type = $row.attr("data-wcpt-type"), 7597 product_id = $row.attr("data-wcpt-product-id"), 7598 variation_id = $row.attr("data-wcpt-variation-id"), 7599 id = type == "variation" ? variation_id : product_id, 7600 cart_item = false, 7601 min_call_id = $row.data("wcpt-min-cart-call-id"); 7602 7603 if (min_call_id > result.payload.wcpt_cart_call_id) { 7604 return; 7605 } 7606 7607 $.each(in_cart_products, function (key, item) { 7608 if ( 7609 // variation 7610 (variation_id && variation_id == item.id) || 7611 // regular product 7612 (!variation_id && product_id == item.id) 7613 ) { 7614 cart_item = item; 7615 return false; 7616 } 7617 }); 7618 7619 // wcpt initiated adding / removing process is complete 7620 $row.removeClass("wcpt-adding-product wcpt-removing-product"); 7621 7622 // update 'in cart' qty 7623 var cart_qty = cart_item ? cart_item.quantity : 0; 7624 $row.attr("data-wcpt-in-cart", cart_qty); 7625 $in_cart = $(".wcpt-in-cart", $row).text(cart_qty); 7626 $in_cart.each(function () { 7627 var $this = $(this), 7628 template = $this.attr("data-wcpt-template"), 7629 empty_template = $this.attr("data-wcpt-empty-template"); 7630 7631 if (cart_qty) { 7632 $this.text(template.replace("{n}", cart_qty)); 7633 } else { 7634 $this.text(empty_template); 7635 } 7636 }); 7637 7638 // update 'in cart' total 7639 var cart_total = cart_item ? cart_item.total : 0; 7640 $(".wcpt-total", $row).attr("data-wcpt-in-cart-total", cart_total); 7641 update_row_total($row); 7642 7643 // badge 7644 7645 if (!$row.hasClass("wcpt-adding-product")) { 7646 $button = $(cart_button_selector, $row); 7647 add_count_badge_to_button(cart_qty, $button); 7648 7649 if (!$button.hasClass("wcpt-out-of-stock")) { 7650 enable_button($button); 7651 } 7652 } 7653 7654 // remove 7655 7656 if (!$row.hasClass("wcpt-removing-product")) { 7657 var $remove = $(".wcpt-remove", $row); 7658 7659 // -- enable 7660 if (cart_item) { 7661 $remove.removeClass("wcpt-disabled"); 7662 7663 // -- disable 7664 } else { 7665 // $remove.addClass('wcpt-disabled').removeClass('wcpt-removing'); 7666 $remove.addClass("wcpt-disabled"); 7667 7668 // wc button 7669 $(".add_to_cart_button", $row) 7670 .removeClass("added") 7671 .next(".added_to_cart") 7672 .remove(); 7673 } 7674 } 7675 }); 7676 7677 if ( 7678 result.fragments && 7679 !result.payload.skip_cart_triggers && 7680 !result.is_cache 7681 ) { 7682 var trigger_lock = true, 7683 $body = $("body"), 7684 $button = false; 7685 7686 // fake button for single add / remove 7687 if ( 7688 result.payload && 7689 result.payload.products && 7690 Object.keys(result.payload.products).length === 1 7691 ) { 7692 $.each(result.payload.products, function (product_id, qty) { 7693 if (qty) { 7694 // added 7695 $button = $('<button data-product_id="' + product_id + '">'); 7696 } else { 7697 // removed 7698 $button = $('<a href="" data-product_id="' + product_id + '">'); 7699 } 7700 }); 7701 } 7702 7703 if (added || result.payload.force_refresh_cart) { 7704 $body.trigger("added_to_cart", [ 7705 result.fragments, 7706 result.cart_hash, 7707 $button, 7708 trigger_lock, 7709 ]); 7710 } 7711 7712 if (removed) { 7713 $body.trigger("removed_from_cart", [ 7714 result.fragments, 7715 result.cart_hash, 7716 $button, 7717 trigger_lock, 7718 ]); 7719 } 7720 } 7721 }); 7722 7723 $("body").on( 7724 "added_to_cart removed_from_cart", 7725 function (e, fragment, cart_hash, $button, trigger_lock) { 7726 if (!trigger_lock) { 7727 wcpt_cart({ 7728 payload: { skip_cart_triggers: true }, 7729 }); 7730 } 7731 }, 7732 ); 7733 7734 // PRO 7735 7736 $("body").on("click", ".wcpt-nav-redirect-option", function (e) { 7737 var $this = $(this), 7738 $link = $this.find("a.wcpt-nav-redirect-link"), 7739 url = $link.attr("href"); 7740 7741 if ( 7742 $(e.target).closest(".wcpt-filter-hierarchy-accordion__trigger").length 7743 ) { 7744 return; 7745 } 7746 7747 window.location.href = url; 7748 }); 7749 7750 // table heading offset based on nav header 7751 $("body").on("wcpt_layout", ".wcpt", function (e, data) { 7752 var $this = $(this), 7753 $freeze_nav_sidebar = $( 7754 ".wcpt-navigation.wcpt-left-sidebar.wcpt-sticky:visible", 7755 $this, 7756 ), 7757 $freeze_nav_header = $( 7758 ".wcpt-navigation.wcpt-header.wcpt-sticky:visible", 7759 $this, 7760 ), 7761 $freeze_table_heading = $( 7762 ".frzTbl-fixed-heading-wrapper-outer:visible", 7763 $this, 7764 ), 7765 sc_attrs = $this.data("wcpt_sc_attrs"), 7766 device = get_device(), 7767 offset = parseInt( 7768 sc_attrs[device + "_freeze_heading_offset"] 7769 ? sc_attrs[device + "_freeze_heading_offset"] 7770 : sc_attrs[device + "_scroll_offset"] 7771 ? sc_attrs[device + "_scroll_offset"] 7772 : 0, 7773 ); 7774 7775 $freeze_nav_sidebar.css("top", offset); 7776 7777 if (device == "laptop") { 7778 $freeze_nav_header.css("top", offset); 7779 7780 offset += parseInt($freeze_nav_header.outerHeight()); 7781 7782 $freeze_table_heading.css({ top: offset }); 7783 } else { 7784 offset += parseInt($freeze_nav_sidebar.outerHeight()); 7785 $freeze_nav_header.css({ top: offset }); 7786 7787 offset += $freeze_nav_header.outerHeight(); 7788 $freeze_table_heading.css({ top: offset }); 7789 } 7790 }); 7791 7792 // cart widget permission 7793 function cart_widget_permitted() { 7794 var url = window.location.href.split("?")[0], 7795 include_match = false, 7796 exclude_match = false, 7797 include_urls = false, 7798 exclude_urls = false, 7799 site_url = wcpt_params.site_url + "/"; 7800 7801 if (!wcpt_params.cart_widget_enabled_site_wide && !$(".wcpt").length) { 7802 return false; 7803 } 7804 7805 if (wcpt_params.cart_widget_include_urls.trim()) { 7806 include_urls = wcpt_params.cart_widget_include_urls 7807 .trim() 7808 .replace(/\r\n/g, "\n") 7809 .split("\n"); 7810 } 7811 7812 if (wcpt_params.cart_widget_exclude_urls.trim()) { 7813 exclude_urls = wcpt_params.cart_widget_exclude_urls 7814 .trim() 7815 .replace(/\r\n/g, "\n") 7816 .split("\n"); 7817 } 7818 7819 if (include_urls.length) { 7820 include_match = false; 7821 $.each(include_urls, function (i, path) { 7822 path = path.trim(); 7823 7824 if (path.indexOf(site_url) === 0) { 7825 path = path.substring(site_url.length, path.length); 7826 } 7827 7828 if (path.trim() == "/") { 7829 if (url === site_url) { 7830 include_match = true; 7831 } 7832 return; 7833 } 7834 7835 if (path.trim() == "/*") { 7836 include_match = true; 7837 return; 7838 } 7839 7840 path = path.replace(/(^\s*\/)|(\/\s*$)/g, ""); // ensure no slash at start or end 7841 if ("*" === path.substr(-1)) { 7842 var remove = 1; 7843 if ("/*" === path.substr(-2)) { 7844 remove = 2; 7845 } 7846 if ( 7847 -1 !== 7848 url.indexOf( 7849 site_url + path.substring(0, path.length - remove) + "/", 7850 ) 7851 ) { 7852 include_match = true; 7853 } 7854 } else { 7855 path += "/"; 7856 if (url == site_url + path) { 7857 include_match = true; 7858 } 7859 } 7860 }); 7861 7862 if (!include_match) { 7863 return false; 7864 } 7865 } 7866 7867 if (exclude_urls.length) { 7868 exclude_match = false; 7869 7870 $.each(exclude_urls, function (i, path) { 7871 path = path.trim(); 7872 7873 if (path.indexOf(site_url) === 0) { 7874 path = path.substring(site_url.length, path.length); 7875 } 7876 7877 if (path.trim() == "/") { 7878 if (url === site_url) { 7879 exclude_match = true; 7880 } 7881 return; 7882 } 7883 7884 if (path.trim() == "/*") { 7885 exclude_match = true; 7886 return; 7887 } 7888 7889 path = path.replace(/(^\s*\/)|(\/\s*$)/g, ""); // ensure no slash at start or end 7890 if ("*" === path.substr(-1)) { 7891 var remove = 1; 7892 if ("/*" === path.substr(-2)) { 7893 remove = 2; 7894 } 7895 7896 if ( 7897 -1 !== 7898 url.indexOf( 7899 site_url + path.substring(0, path.length - remove) + "/", 7900 ) 7901 ) { 7902 exclude_match = true; 7903 } 7904 } else { 7905 path += "/"; 7906 if (url == site_url + path) { 7907 exclude_match = true; 7908 } 7909 } 7910 }); 7911 7912 if (exclude_match) { 7913 return false; 7914 } 7915 } 7916 7917 return true; 7918 } 7919 7920 // cart widget close x 7921 $("body").on("click", ".wcpt-cart-checkbox-trigger__close", function (e) { 7922 e.stopPropagation(); 7923 var $cart_widget = $(this).closest(".wcpt-cart-checkbox-trigger"); 7924 7925 if (typeof wcpt_util !== "undefined") { 7926 $(".wcpt").each(function () { 7927 var $container = $(this), 7928 table_id = wcpt_util.get_table_id($container); 7929 7930 if (!table_id) { 7931 return; 7932 } 7933 7934 $(document).trigger("wcpt_clear_all_checked_for_table", [ 7935 table_id, 7936 $container, 7937 ]); 7938 7939 $container.find(".wcpt-row").each(function () { 7940 $(this).trigger("_wcpt_checkbox_change", false); 7941 }); 7942 }); 7943 } 7944 7945 wcpt_checkbox_trigger_init(); 7946 $cart_widget.addClass("wcpt-hide").hide(); 7947 }); 7948 7949 // multirange 7950 7951 // -- input[type=number] change 7952 $("body").on( 7953 "input change", 7954 ".wcpt-range-input-min, .wcpt-range-input-max", 7955 function () { 7956 var $this = $(this), 7957 $container = $this.closest(".wcpt-range-options-wrapper"), 7958 $min = $container.find(".wcpt-range-input-min"), 7959 min_val = parseFloat($min.val()), 7960 $max = $container.find(".wcpt-range-input-max"), 7961 max_val = parseFloat($max.val()); 7962 7963 $range = $container.find(".wcpt-range-slider.original"); 7964 7965 if (!$range.length) { 7966 return; 7967 } 7968 7969 // max step fix 7970 var actual_max = parseFloat($this.attr("data-wcpt-actual-max")), 7971 val = parseFloat($this.val()); 7972 7973 if (!isNaN(actual_max) && !isNaN(val) && val > actual_max) { 7974 $this.val(actual_max); 7975 } 7976 7977 // handle cleared min/max inputs 7978 if (isNaN(min_val)) { 7979 min_val = parseFloat($range.attr("min")); 7980 $min.val(min_val); 7981 } 7982 7983 if (isNaN(max_val)) { 7984 max_val = parseFloat($range.attr("max")); 7985 $max.val(max_val); 7986 } 7987 7988 $range.val(min_val + "," + max_val); 7989 }, 7990 ); 7991 7992 // -- input[type=range] change 7993 $("body").on("input change", ".wcpt-range-slider", function () { 7994 var $this = $(this), 7995 $container = $this.closest(".wcpt-range-options-wrapper"), 7996 $min = $(".wcpt-range-input-min", $container), 7997 $max = $(".wcpt-range-input-max", $container), 7998 $range_original = $container.find(".wcpt-range-slider.original"), 7999 $range_ghost = $container.find(".wcpt-range-slider.ghost"), 8000 min = parseFloat($range_original.prop("valueLow")), 8001 max = parseFloat($range_original.prop("valueHigh")), 8002 permitted_max = parseFloat($this.attr("max")), 8003 step = parseFloat($this.attr("step")); 8004 8005 // range input max fix 8006 // if( max + step > permitted_max ){ 8007 // max = permitted_max; // sending this to input[type="number"] 8008 // } 8009 8010 $min.val(min); 8011 $max.val(max); 8012 }); 8013 8014 // switch elements based on selected variation 8015 $("body").on( 8016 "select_variation", 8017 ".wcpt-product-type-variable", 8018 function (e, data) { 8019 var $row = get_product_rows($(this)), 8020 $items = $( 8021 ".wcpt-variation-description__item, .wcpt-variation-content", 8022 $row, 8023 ); 8024 8025 $items.hide(); 8026 8027 if ($row.data("wcpt_variation_selected")) { 8028 var variation_id = $row.data("wcpt_variation_id"); 8029 $items.filter("[data-wcpt-variation-id=" + variation_id + "]").show(); 8030 } 8031 }, 8032 ); 8033 8034 // name your price 8035 $("body").on( 8036 "select_variation", 8037 ".wcpt-product-type-variable", 8038 function (e, data) { 8039 var $row = get_product_rows($(this)), 8040 $input = $(".wcpt-name-your-price--input", $row), 8041 initial_value_field = $input.attr("data-wcpt-nyp-initial-value-field"), 8042 input = false, 8043 min = false, 8044 max = false, 8045 suggested = false; 8046 8047 // if price has no variable switch, always stays "$min - $max" then just replace it completely with "from $_min" 8048 // if it has variable switch 8049 // then hide it whenever selected variation has nyp 8050 8051 $row.removeClass("wcpt-product-has-name-your-price"); 8052 8053 if ($row.data("wcpt_variation_selected")) { 8054 // variation selected 8055 var variation = $row.data("wcpt_variation"); 8056 8057 if (variation.is_nyp) { 8058 input = true; 8059 min = variation.minimum_price; 8060 max = variation.maximum_price; 8061 suggested = variation.suggested_price; 8062 8063 $row.addClass("wcpt-product-has-name-your-price"); 8064 } 8065 } else { 8066 // variation not selected 8067 input = false; 8068 } 8069 8070 // no nyp input field 8071 if (!input) { 8072 $input.parent().addClass("wcpt-hide"); 8073 $row 8074 .find("wcpt-price.wcpt-variable-switch") 8075 .removeClass("wcpt-hide--name-your-price"); 8076 8077 // found nyp input field 8078 } else { 8079 $input.parent().removeClass("wcpt-hide"); 8080 8081 if (min) { 8082 $input.attr({ 8083 min: min, 8084 "data-wcpt-nyp-minimum-price": min, 8085 }); 8086 } else { 8087 $input.attr({ 8088 min: 0, 8089 "data-wcpt-nyp-minimum-price": 0, 8090 }); 8091 } 8092 8093 if (max) { 8094 $input.attr("max", max); 8095 8096 $input.attr({ 8097 max: max, 8098 "data-wcpt-nyp-maximum-price": max, 8099 }); 8100 } else { 8101 $input.attr({ 8102 max: "", 8103 "data-wcpt-nyp-maximum-price": "", 8104 }); 8105 } 8106 8107 if (initial_value_field && !$input.val()) { 8108 var value = variation[initial_value_field + "_price"] 8109 ? variation[initial_value_field + "_price"] 8110 : ""; 8111 $input.val(value); 8112 } 8113 8114 $row 8115 .find("wcpt-price.wcpt-variable-switch") 8116 .addClass("wcpt-hide--name-your-price"); 8117 } 8118 8119 if (!min) { 8120 $row.find(".wcpt-name-your-price--minimum").addClass("wcpt-hide"); 8121 } else { 8122 $row.find(".wcpt-name-your-price--minimum").removeClass("wcpt-hide");
8123 $row.find(".wcpt-name-your-price--minimum .wcpt-amount").text(min); 8124 } 8125 8126 if (!max) { 8127 $row.find(".wcpt-name-your-price--maximum").addClass("wcpt-hide"); 8128 } else { 8129 $row.find(".wcpt-name-your-price--maximum").removeClass("wcpt-hide"); 8130 $row.find(".wcpt-name-your-price--maximum .wcpt-amount").text(max); 8131 } 8132 8133 if (!suggested) { 8134 $row.find(".wcpt-name-your-price--suggested").addClass("wcpt-hide"); 8135 } else { 8136 $row.find(".wcpt-name-your-price--suggested").removeClass("wcpt-hide"); 8137 $row 8138 .find(".wcpt-name-your-price--suggested .wcpt-amount") 8139 .text(suggested); 8140 } 8141 8142 $input.change(); 8143 }, 8144 ); 8145 8146 // -- checkbox 8147 $("body").on("wcpt_checkbox_change", ".wcpt-row", function (e, checked) { 8148 var $this = $(this), 8149 $rows = wcpt_get_sibling_rows($this.closest(".wcpt-row")), 8150 $nyp = get_nyp_input_element($rows); 8151 8152 if (checked) { 8153 nyp_validate($nyp); 8154 } else if (!$nyp.val()) { 8155 nyp_hide_error($nyp); 8156 } 8157 }); 8158 8159 // -- validate 8160 function nyp_validate($nyp) { 8161 var $nyp_wrapper = $nyp.parent(), 8162 nyp_val = $nyp.val(), 8163 nyp_min = $nyp.attr("data-wcpt-nyp-minimum-price"), 8164 nyp_max = $nyp.attr("data-wcpt-nyp-maximum-price"), 8165 message_template = "", 8166 $error = "", 8167 $row = $nyp.closest(".wcpt-row"), 8168 checked = $row.data("wcpt_checked"); 8169 8170 if ( 8171 (nyp_val && nyp_min && parseFloat(nyp_val) < parseFloat(nyp_min)) || 8172 (checked && !nyp_val && nyp_min) 8173 ) { 8174 $nyp_wrapper.addClass( 8175 "wcpt-name-your-price-wrapper--input-error wcpt-name-your-price-wrapper--input-error--min-price", 8176 ); 8177 8178 $error = $( 8179 ".wcpt-name-your-price-input-error-message--min-price", 8180 $nyp_wrapper, 8181 ); 8182 message_template = $error.attr("data-wcpt-error-message-template"); 8183 $error.text( 8184 message_template.replace("[min]", format_price_figure(nyp_min)), 8185 ); 8186 } else if ( 8187 nyp_val && 8188 nyp_max && 8189 parseFloat(nyp_val) > parseFloat(nyp_max) 8190 ) { 8191 $nyp_wrapper.addClass( 8192 "wcpt-name-your-price-wrapper--input-error wcpt-name-your-price-wrapper--input-error--max-price", 8193 ); 8194 8195 $error = $( 8196 ".wcpt-name-your-price-input-error-message--max-price", 8197 $nyp_wrapper, 8198 ); 8199 message_template = $error.attr("data-wcpt-error-message-template"); 8200 $error.text( 8201 message_template.replace("[max]", format_price_figure(nyp_max)), 8202 ); 8203 } else { 8204 $nyp_wrapper.removeClass( 8205 "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", 8206 ); 8207 } 8208 } 8209 8210 // -- hide validation errors 8211 function nyp_hide_error($nyp) { 8212 $nyp 8213 .parent() 8214 .removeClass( 8215 "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", 8216 ); 8217 } 8218 8219 // trigger nav feedback from range slider and min max 8220 $("body").on("change", ".wcpt-range-options-wrapper input", function () { 8221 var $this = $(this), 8222 $filter = $this.closest(".wcpt-filter"); 8223 8224 $filter.find("input[type=radio]").prop("checked", false); 8225 nav_filter_feedback($this.closest(".wcpt-navigation")); 8226 }); 8227 8228 // refresh / block table 8229 // -- lock 8230 $("body").on("wcpt_cart_request", function (e, data) { 8231 if ( 8232 data.wcpt_payload.skip_cart_triggers || 8233 data.wcpt_payload.use_cache || 8234 data.is_cache 8235 ) { 8236 return; 8237 } 8238 8239 $(".wcpt").each(function () { 8240 var $container = $(this), 8241 sc_attrs = JSON.parse($container.attr("data-wcpt-sc-attrs")); 8242 8243 if (sc_attrs.refresh_table) { 8244 $container.addClass("wcpt-loading wcpt-refreshing"); 8245 } else if (sc_attrs.block_table) { 8246 $container.addClass("wcpt-loading"); 8247 } 8248 }); 8249 }); 8250 // -- refresh 8251 $("body").on("wcpt_cart", function (e, result) { 8252 if ( 8253 result.payload.skip_cart_triggers || 8254 result.payload.use_cache || 8255 result.is_cache 8256 ) { 8257 return; 8258 } 8259 8260 $(".wcpt").each(function () { 8261 var $container = $(this), 8262 sc_attrs = JSON.parse($container.attr("data-wcpt-sc-attrs")), 8263 table_id = $container.attr("data-wcpt-table-id"); 8264 8265 if (sc_attrs.refresh_table) { 8266 $.each(window.wcpt_cache.data, function (key, val) { 8267 if ( 8268 key.indexOf("?" + table_id + "_") !== -1 || 8269 key.indexOf("&" + table_id + "_") !== -1 8270 ) { 8271 delete window.wcpt_cache.data[key]; 8272 } 8273 }); 8274 8275 attempt_ajax($container, "", true, "refresh_table"); 8276 } else if (sc_attrs.block_table) { 8277 $container.removeClass("wcpt-loading"); 8278 } 8279 });
8280 }); 8281 8282 // switching variation should update Button element's product link 8283 $("body").on( 8284 "select_variation", 8285 ".wcpt-product-type-variable", 8286 function (e, data) { 8287 var $row = get_product_rows($(this)), 8288 $link_button = $( 8289 ".wcpt-button[data-wcpt-link-code='product_link']", 8290 $row, 8291 ); 8292 8293 if ($link_button.length) { 8294 var original_link = $link_button.attr("data-wcpt-product-link"), 8295 new_link = ""; 8296 8297 if (!original_link) { 8298 original_link = $link_button.attr("href"); 8299 $link_button.attr("data-wcpt-product-link", original_link); 8300 } 8301 8302 if (data.variation_id) { 8303 new_link = original_link + "?"; 8304 $.each(data.attributes, function (key, value) { 8305 new_link = new_link + key + "=" + value + "&"; 8306 }); 8307 new_link = new_link + "variation_id=" + data.variation_id; 8308 $link_button.attr("href", new_link); 8309 } else { 8310 $link_button.attr("href", original_link); 8311 } 8312 } 8313 }, 8314 ); 8315 8316 // berocket 8317 $(document).on("berocket_ajax_filtering_end", function (e) { 8318 $(".wcpt").each(function () { 8319 after_every_load($(this)); 8320 }); 8321 }); 8322 8323 // waveplayer 8324 8325 // -- holds the detached waveplayer instances 8326 window.$wcpt_waveplayer_preserve = $(); 8327 8328 //-- avoid losing functionality on cloned players during freeze table 8329 $("body").on("wcpt_layout", ".wcpt", function () { 8330 var $this = $(this); 8331 8332 if (typeof WavePlayer !== "undefined") { 8333 WavePlayer.loadInstances(); 8334 8335 if (!$wcpt_waveplayer_preserve.length) { 8336 return; 8337 } 8338 8339 var $replace = get_matching_waveplayer_elm($this); 8340 8341 if ($replace.length) { 8342 $replace.replaceWith($wcpt_waveplayer_preserve); 8343 $wcpt_waveplayer_preserve 8344 .find(".wvpl-waveform canvas") 8345 .each(function () { 8346 var $this = $(this); 8347 $this.width($this.attr("data-wcpt-last-width")); 8348 $this.height($this.attr("data-wcpt-last-height")); 8349 }); 8350 8351 waveplayer_active_ui_feedback.call($wcpt_waveplayer_preserve.get(0)); 8352 8353 $wcpt_waveplayer_preserve = $(); 8354 8355 WavePlayer.redrawAllInstances(); 8356 } 8357 } 8358 }); 8359 8360 // -- check for and return waveplayer element in container that needs to be replaced 8361 function get_matching_waveplayer_elm($container) { 8362 if (WavePlayer.persistentTrack && WavePlayer.instances.length) { 8363 var instance = false, 8364 track_index = false; 8365 8366 $.each(WavePlayer.instances, function (_instance_index, _instance) { 8367 var $_instances = $(_instance.node); 8368 if ( 8369 $_instances.is(":visible") && 8370 $_instances.parent().hasClass("wcpt-waveplayer-container") 8371 ) { 8372 var $row = $_instances.closest(".wcpt-row"); 8373 8374 if ($row.length) { 8375 $.each(_instance.tracks, function (_track_index, _track) { 8376 if ( 8377 _track.file === WavePlayer.persistentTrack.file && 8378 _track.product_id === WavePlayer.persistentTrack.product_id 8379 ) { 8380 instance = _instance; 8381 track_index = _track_index; 8382 } 8383 }); 8384 } 8385 } 8386 }); 8387 8388 if (instance) { 8389 return $(instance.node).parent(".wcpt-waveplayer-container"); 8390 } 8391 } 8392 8393 return $(); 8394 } 8395 8396 // -- add to cart 8397 $("body").on("added_to_cart", function (e, fragment, cart_hash, $button) { 8398 if (typeof WavePlayer === "undefined" || !$button) { 8399 return; 8400 } 8401 8402 var product_id = $button.attr("data-product_id"); 8403 if (typeof WavePlayer.updateTrackCartStatus !== "undefined") { 8404 WavePlayer.updateTrackCartStatus(product_id, "add"); 8405 } 8406 8407 $(".wvpl-cart[data-product_id=" + product_id + "]") 8408 .attr("title", WavePlayer.__("Add to cart", "waveplayer")) 8409 .attr("data-event", "goToCart") 8410 .attr("data-callback", "goToCart") 8411 .removeClass("wvpl-add_to_cart") 8412 .addClass("wvpl-in_cart") 8413 .removeClass("wvpl-spin"); 8414 }); 8415 8416 // -- row color 8417 $("body").on( 8418 "click", 8419 ".wcpt-row .wcpt-waveplayer-container", 8420 waveplayer_active_ui_feedback, 8421 ); 8422 8423 // -- active feedback 8424 function waveplayer_active_ui_feedback() { 8425 var $this = $(this), 8426 active_row_background_color = $this.attr( 8427 "data-wcpt-waveplayer-active-row-background-color", 8428 ), 8429 active_row_outline_color = $this.attr( 8430 "data-wcpt-waveplayer-active-row-outline-color", 8431 ) 8432 ? $this.attr("data-wcpt-waveplayer-active-row-outline-color") 8433 : "#4198de", 8434 active_row_outline_width = $this.attr( 8435 "data-wcpt-waveplayer-active-row-outline-width", 8436 ) 8437 ? parseFloat( 8438 $this.attr("data-wcpt-waveplayer-active-row-outline-width"), 8439 ) + "px" 8440 : "1px", 8441 $rows = wcpt_get_sibling_rows($this.closest(".wcpt-row")); 8442 8443 $rows.addClass("wcpt-waveplayer-active").css({ 8444 background: active_row_background_color 8445 ? active_row_background_color 8446 : "", 8447 outline: active_row_outline_width + " solid " + active_row_outline_color, 8448 });
8449 8450 $(".wcpt-row").not($rows).removeClass("wcpt-waveplayer-active").css({ 8451 background: "", 8452 outline: "", 8453 }); 8454 } 8455 8456 // 3rd party tab and accordion compatibility 8457 8458 // -- elementor accordion, tab & toggle 8459 $("body").on("click", ".elementor-tab-title", function () { 8460 var $this = $(this), 8461 $container = $this.closest(".elementor-widget-container"), 8462 $wcpt = $container.find(".wcpt"); 8463 8464 $wcpt.trigger("wcpt_layout", { source: "elementor__tab" }); 8465 }); 8466 8467 // -- divi 8468 // -- -- tab 8469 $("ul.et_pb_tabs_controls > li").on("click", function () { 8470 var $this = $(this), 8471 index = $this.index(), 8472 $controls = $this.closest(".et_pb_tabs_controls"), 8473 $tabs = $controls.siblings(".et_pb_all_tabs"), 8474 $container = $tabs.children().eq(index), 8475 $wcpt = $container.find(".wcpt"); 8476 8477 setTimeout(function () { 8478 $wcpt.trigger("wcpt_layout", { source: "divi__tab" }); 8479 }, 700); 8480 }); 8481 // -- -- accordion 8482 $(".et_pb_toggle_title").on("click", function () { 8483 var $this = $(this), 8484 $content = $this.next(".et_pb_toggle_content"), 8485 $wcpt = $content.find(".wcpt"); 8486 8487 setTimeout(function () { 8488 $wcpt.trigger("wcpt_layout", { source: "divi__tab" }); 8489 }, 700); 8490 }); 8491 8492 // -- beaver builder 8493 $("body").on("click", ".fl-accordion-button, .fl-tabs-label", function () { 8494 var $this = $(this), 8495 $container = $this.closest(".fl-accordion-item, .fl-tabs-panel"), 8496 $wcpt = $container.find(".wcpt"); 8497 8498 $wcpt.trigger("wcpt_layout", { source: "beaver_builder__tab" }); 8499 }); 8500 8501 // -- shortcode ultimate 8502 // -- -- tab 8503 $("body").on("click", ".su-tabs-nav span", function () { 8504 var $this = $(this), 8505 index = $this.index(), 8506 $nav = $this.closest(".su-tabs-nav"), 8507 $panes = $nav.siblings(".su-tabs-panes"), 8508 $container = $panes.children().eq(index), 8509 $wcpt = $container.find(".wcpt"); 8510 8511 $wcpt.trigger("wcpt_layout", { source: "shortcode_ultimate__tab" }); 8512 }); 8513 // -- -- accordion 8514 $("body").on("click", ".su-spoiler-title", function () { 8515 var $this = $(this), 8516 $container = $this.closest(".su-spoiler"), 8517 $wcpt = $container.find(".wcpt"); 8518 8519 $wcpt.trigger("wcpt_layout", { 8520 source: "shortcode_ultimate__accordion", 8521 }); 8522 }); 8523 8524 // -- wp bakery visual composer 8525 // -- -- tab 8526 $("body").on("click", ".vce-classic-tabs-tab", function () { 8527 var $this = $(this), 8528 index = $this.index(), 8529 $nav = $this.closest(".vce-classic-tabs-container"), 8530 $panes = $nav.siblings(".vce-classic-tabs-panels-container"), 8531 $container = $panes 8532 .children(".vce-classic-tabs-panels") 8533 .children() 8534 .eq(index), 8535 $wcpt = $container.find(".wcpt"); 8536 8537 setTimeout(function () { 8538 $wcpt.trigger("wcpt_layout", { source: "visual_composer__tab" }); 8539 }, 1); 8540 }); 8541 // -- -- accordion (1) 8542 $("body").on("click", ".vce-classic-accordion-panel-heading", function () { 8543 var $this = $(this), 8544 $container = $this.closest(".vce-classic-accordion-panel"), 8545 $wcpt = $container.find(".wcpt"); 8546 setTimeout(function () { 8547 $wcpt.trigger("wcpt_layout", { 8548 source: "visual_composer__accordion", 8549 }); 8550 }, 1); 8551 }); 8552 // -- -- accordion (2) 8553 $("body").on("click", ".vc_tta-panel-heading", function () { 8554 var $this = $(this), 8555 $container = $this.closest(".vc_tta-panel"), 8556 $wcpt = $container.find(".wcpt"); 8557 setTimeout(function () { 8558 $wcpt.trigger("wcpt_layout", { 8559 source: "visual_composer__accordion", 8560 }); 8561 }, 1); 8562 }); 8563 8564 // -- responsive accordion and collapse 8565 $("body").on("click", ".wpsm_panel-heading", function () { 8566 var $this = $(this), 8567 $container = $this.closest(".wpsm_panel"), 8568 $content = $container.find(".wpsm_panel-collapse"); 8569 8570 $(".wcpt", $content).trigger("wcpt_layout", { 8571 source: "rac__accordion", 8572 }); 8573 }); 8574 8575 // -- king composer 8576 $("body").on("click", ".kc_accordion_header", function () { 8577 var $this = $(this), 8578 $container = $this.closest(".kc_accordion_section"), 8579 $content = $container.find(".kc_accordion_content"); 8580 8581 $(".wcpt", $content).trigger("wcpt_layout", { 8582 source: "king_composer__accordion", 8583 });
8584 }); 8585 8586 // -- helpie faq 8587 $("body").on("mouseup touchend", ".accordion__header", function () { 8588 var $this = $(this), 8589 $container = $this.closest(".accordion__item"), 8590 $content = $container.find(".accordion__body"); 8591 8592 clearTimeout(window.wcpt_helpie_timeout); 8593 window.wcpt_helpie_timeout = setTimeout(function () { 8594 $(".wcpt:visible", $content).trigger("wcpt_layout", { 8595 source: "helpie_faq__accordion", 8596 }); 8597 }, 200); 8598 }); 8599 8600 // -- tabs (wpshopmart) 8601 $("body").on("click", ".wpsm_nav li", function () { 8602 var $this = $(this), 8603 index = $this.index(), 8604 $nav = $this.closest(".wpsm_nav"), 8605 $panes = $nav.siblings(".tab-content"), 8606 $container = $panes.children().eq(index), 8607 $wcpt = $container.find(".wcpt"); 8608 8609 $wcpt.trigger("wcpt_layout", { source: "tabs_wpshopmart__tab" }); 8610 }); 8611 8612 // -- tabby responsive tabs 8613 $("body").on("click", ".tabtitle", function () { 8614 var $this = $(this), 8615 $panel = $this.siblings(".tabcontent"); 8616 8617 $(".wcpt", $panel).trigger("wcpt_layout", { source: "tabby_tab" }); 8618 }); 8619 8620 $("body").on("click", ".responsive-tabs__list__item", function () { 8621 var $this = $(this), 8622 index = $this.index(), 8623 $container = $this.closest(".responsive-tabs"), 8624 $panel = $(".tabcontent", $container).eq(index); 8625 8626 $(".wcpt", $panel).trigger("wcpt_layout", { source: "tabby_tab" }); 8627 }); 8628 8629 // -- kadence blocks 8630 $("body").on("click", ".kt-title-item", function () { 8631 var $this = $(this), 8632 $tab_container = $this.closest(".kt-tabs-wrap"); 8633 8634 setTimeout(function () { 8635 $tab_container.find(".wcpt:visible").trigger("wcpt_layout"); 8636 }, 100); 8637 }); 8638 8639 // sonaar 8640 // -- audio player play / pause 8641 $("body").on("click", ".wcpt-player--sonaar", function () { 8642 var $this = $(this), 8643 playlist_id = $this.attr("data-wcpt-sonaar-playlist-id"); 8644 8645 if (typeof IRON == "undefined" || !playlist_id) { 8646 return; 8647 } 8648 8649 $(".wcpt-player--sonaar").not($this).removeClass("wcpt-player--playing"); 8650 8651 // pause 8652 if ( 8653 IRON.sonaar.player.isPlaying && 8654 IRON.sonaar.player.playlistID == playlist_id 8655 ) { 8656 IRON.sonaar.player.pause(); 8657 $this.removeClass("wcpt-player--playing wcpt-media-loaded"); 8658 } else { 8659 $this.addClass("wcpt-player--playing wcpt-media-loaded"); 8660 8661 IRON.sonaar.player.setPlayer({ 8662 id: playlist_id, 8663 autoplay: true, 8664 soundwave: true, 8665 }); 8666 } 8667 }); 8668 8669 // -- auto set play / pause status after every load 8670 function sonaar_player_auto_status() { 8671 if (typeof IRON == "undefined") { 8672 return; 8673 } 8674 8675 var playlist_id = IRON.sonaar.player.playlistID; 8676 8677 if (playlist_id) { 8678 $( 8679 '.wcpt-player--sonaar[data-wcpt-sonaar-playlist-id="' + 8680 playlist_id + 8681 '"]', 8682 ).addClass("wcpt-player--playing wcpt-media-loaded"); 8683 } 8684 } 8685 8686 // TI Wishlist integration 8687 8688 if (typeof wcpt_ti_wishlist_ids !== "undefined") { 8689 // update counter widget 8690 setTimeout(function () { 8691 $(".wishlist_products_counter_number").text(wcpt_ti_wishlist_ids.length); 8692 if (wcpt_ti_wishlist_ids.length) { 8693 $(".wishlist_products_counter").addClass( 8694 "wishlist-counter-with-products", 8695 ); 8696 } else { 8697 $(".wishlist_products_counter").removeClass( 8698 "wishlist-counter-with-products", 8699 ); 8700 } 8701 }, 100); 8702 8703 // wishlist button 8704 $("body").on("click", ".wcpt-wishlist", function (e) { 8705 var $this = $(this), 8706 $row = $this.closest(".wcpt-row"), 8707 $wcpt = $this.closest(".wcpt"), 8708 product_type = $row.attr("data-wcpt-type"), 8709 product_id = $row.attr("data-wcpt-product-id"), 8710 variation_id = $row.attr("data-wcpt-variation-id") 8711 ? $row.attr("data-wcpt-variation-id") 8712 : $row.data("wcpt_variation_id") 8713 ? $row.data("wcpt_variation_id") 8714 : false, 8715 variable_permitted = $this.attr("data-wcpt-variable-permitted"); 8716 8717 // variable product must have variation selected 8718 if (product_type == "variable" && !variable_permitted && !variation_id) { 8719 alert("Please select some options first"); 8720 return; 8721 } 8722 8723 var list_id = variation_id ? variation_id : product_id; 8724 8725 $this.addClass("wcpt-loading"); 8726 8727 var data = { 8728 tinv_wishlist_id: false, 8729 tinv_wishlist_name: false, 8730 product_type: $row.attr("data-wcpt-type"), 8731 product_id: $row.attr("data-wcpt-product-id"), 8732 product_variation: variation_id, 8733 redirect: false, 8734 }; 8735 8736 if (variation_id) { 8737 var attributes = 8738 data.product_type === "variable" 8739 ? $row.data("wcpt_attributes") 8740 : JSON.parse($row.attr("data-wcpt-variation-attributes")); 8741 8742 $.extend(data, { 8743 form: $.extend( 8744 { 8745 quantity: 1, 8746 product_id: product_id, 8747 variation_id: variation_id, 8748 }, 8749 attributes, 8750 ), 8751 }); 8752 } 8753 8754 // add 8755 if (-1 === wcpt_ti_wishlist_ids.indexOf(list_id)) { 8756 $.ajax({
8757 url: wcpt_params.ajax_url, 8758 method: "POST", 8759 beforeSend: function () { 8760 // variation added from variable product, refresh page to show it 8761 if ( 8762 product_type === "variable" && 8763 $wcpt.data("wcpt_sc_attrs").ti_wishlist 8764 ) { 8765 $wcpt.addClass("wcpt-loading"); 8766 } 8767 }, 8768 data: $.extend({}, { product_action: "addto" }, data), 8769 }).done(function (response) { 8770 // variation added from variable product, refresh page to show it 8771 if ( 8772 product_type === "variable" && 8773 $wcpt.data("wcpt_sc_attrs").ti_wishlist 8774 ) { 8775 window.location.reload(); 8776 } 8777 8778 wcpt_ti_wishlist_update_row_view($row); 8779 8780 wcpt_ti_wishlist_growler({ 8781 name: $this.attr("data-wcpt-product-name"), 8782 view_wishlist_label: $this.attr("data-wcpt-view-wishlist-label"), 8783 item_added_label: $this.attr("data-wcpt-item-added-label"), 8784 icon: $this.attr("data-wcpt-icon"), 8785 url: $this.attr("data-wcpt-custom-url"), 8786 duration_seconds: $this.attr("data-wcpt-duration-seconds"), 8787 }); 8788 8789 // update counter widget 8790 if (response && typeof response.wishlists_data !== "undefined") { 8791 $(".wishlist_products_counter_number").text( 8792 response.wishlists_data.counter, 8793 ); 8794 if (response.wishlists_data.counter) { 8795 $(".wishlist_products_counter").addClass( 8796 "wishlist-counter-with-products", 8797 ); 8798 } else { 8799 $(".wishlist_products_counter").removeClass( 8800 "wishlist-counter-with-products", 8801 ); 8802 } 8803 } 8804 }); 8805 8806 // add to maintained list as well 8807 wcpt_ti_wishlist_ids.push(list_id); 8808 8809 // update UI 8810 $this.addClass("wcpt-active"); 8811 8812 // remove 8813 } else { 8814 $.ajax({
8815 url: wcpt_params.ajax_url, 8816 method: "POST", 8817 data: $.extend({}, { product_action: "remove" }, data), 8818 }).done(function (response) { 8819 // update counter widget 8820 if (response && typeof response.wishlists_data !== "undefined") { 8821 $(".wishlist_products_counter_number").text( 8822 response.wishlists_data.counter, 8823 ); 8824 if (response.wishlists_data.counter) { 8825 $(".wishlist_products_counter").addClass( 8826 "wishlist-counter-with-products", 8827 ); 8828 } else { 8829 $(".wishlist_products_counter").removeClass( 8830 "wishlist-counter-with-products", 8831 ); 8832 } 8833 } 8834 }); 8835 8836 // remove from maintained list as well 8837 var index = wcpt_ti_wishlist_ids.indexOf(list_id); 8838 if (index > -1) { 8839 wcpt_ti_wishlist_ids.splice(index, 1); 8840 } 8841 8842 wcpt_ti_wishlist_update_row_view($row); 8843 8844 // update UI 8845 // $this.removeClass('wcpt-active'); 8846 8847 // remove product row from wishlist table 8848 if ($wcpt.data("wcpt_sc_attrs").ti_wishlist) { 8849 var $remove_row = $row; 8850 8851 // don't remove variable product if it was being used to remove a variation only 8852 if (variation_id) { 8853 $remove_row = $wcpt.find( 8854 '.wcpt-product-type-variation[data-wcpt-variation-id="' + 8855 variation_id + 8856 '"]', 8857 ); 8858 } 8859 8860 $remove_row.addClass("wcpt-wishlist-removing-row"); 8861 setTimeout(function () { 8862 $remove_row.remove(); 8863 }, 500); 8864 } 8865 } 8866 }); 8867 8868 function wcpt_ti_wishlist_growler(data) { 8869 var template = $("#wcpt-ti-wishlist-growler-template").html(), 8870 $growler = $(template.replace("{n}", '"' + data.name + '"')), 8871 reveal_class = "wcpt-ti-wishlist-growler--revealed", 8872 duration_ms = data.duration_seconds * 1000; 8873 8874 $growler.attr("data-wcpt-icon", data.icon); 8875 $(".wcpt-ti-wishlist-growler").remove(); // remove previous 8876 8877 if (data.item_added_label) {
8878 $(".wcpt-ti-wishlist-growler__label--item-added", $growler).text( 8879 data.item_added_label.replace("{n}", '"' + data.name + '"'), 8880 ); 8881 } 8882 8883 if (data.view_wishlist_label) { 8884 $(".wcpt-ti-wishlist-growler__label--view-wishlist", $growler).text( 8885 data.view_wishlist_label.replace("{n}", '"' + data.name + '"'), 8886 ); 8887 } 8888 8889 if (data.url) { 8890 $growler.attr("href", data.url); 8891 } else if (wcpt_ti_wishlist_url) { 8892 $growler.attr("href", wcpt_ti_wishlist_url); 8893 } 8894 8895 $("body").append($growler); 8896 8897 setTimeout(function () { 8898 $growler.addClass(reveal_class); 8899 }, 100); 8900 8901 setTimeout(function () { 8902 $growler.removeClass(reveal_class); 8903 }, duration_ms); 8904 8905 setTimeout(function () { 8906 $growler.remove(); 8907 }, duration_ms + 500); 8908 } 8909 8910 $("body").on("wcpt_after_every_load", ".wcpt", function () { 8911 var $this__container = $(this), 8912 $visible_rows = wcpt_util 8913 .get_uninit_rows($this__container) 8914 .filter(":visible"); 8915 8916 // variable products in wishlist were definitely added without variation selected 8917 if (wcpt_util.get_sc_attrs($this__container).ti_wishlist) { 8918 $visible_rows 8919 .filter(".wcpt-product-type-variable") 8920 .trigger("select_variation", { 8921 variation_id: false, 8922 complete_match: false, 8923 attributes: false, 8924 variation: false, 8925 variation_found: false, 8926 variation_selected: false, 8927 variation_available: false, 8928 }); 8929 } 8930 8931 $visible_rows.each(function () { 8932 var $this__container = $(this); 8933 wcpt_ti_wishlist_update_row_view($this__container); 8934 }); 8935 }); 8936 8937 $("body").on("select_variation", ".wcpt-row", function () { 8938 var $this = $(this); 8939 wcpt_ti_wishlist_update_row_view($this); 8940 }); 8941 8942 function wcpt_ti_wishlist_update_row_view($row) { 8943 var product_type = $row.attr("data-wcpt-type"), 8944 product_id = $row.attr("data-wcpt-product-id"), 8945 variation_id = $row.data("wcpt_variation_id") 8946 ? $row.data("wcpt_variation_id") 8947 : $row.attr("data-wcpt-variation-id") 8948 ? $row.attr("data-wcpt-variation-id") 8949 : false, 8950 $buttons = $(".wcpt-wishlist", wcpt_get_sibling_rows($row)), 8951 variable_permitted = $buttons.attr("data-wcpt-variable-permitted"); 8952 8953 if (product_type === "variable" && !variable_permitted && !variation_id) { 8954 $buttons.removeClass("wcpt-active").addClass("wcpt-disabled"); 8955 8956 return; 8957 } 8958 8959 $buttons.removeClass("wcpt-disabled wcpt-loading"); 8960 8961 var list_id = variation_id ? variation_id : product_id; 8962 8963 if (-1 === wcpt_ti_wishlist_ids.indexOf(list_id + "")) { 8964 $buttons.removeClass("wcpt-active"); 8965 } else { 8966 $buttons.addClass("wcpt-active"); 8967 } 8968 } 8969 } 8970 8971 // WooCommerce Wholesale Prices 8972 8973 // -- select_variation handler to switch vals in wcpt_wholesale 8974 $("body").on("select_variation", ".wcpt-row", function () { 8975 var $this = $(this), 8976 variation = $this.data("wcpt_variation"), 8977 $wholesale_shortcode = $(".wcpt-wholesale", $this); 8978 8979 $wholesale_shortcode.each(function () { 8980 var $this = $(this); 8981 8982 // set the view: default or variation 8983 $this.removeClass( 8984 "wcpt-wholesale--variation-view-enabled wcpt-wholesale--default-view-enabled", 8985 ); 8986 8987 if (variation) { 8988 $this.addClass("wcpt-wholesale--variation-view-enabled"); 8989 } else { 8990 $this.addClass("wcpt-wholesale--default-view-enabled"); 8991 } 8992 8993 // wholesale table 8994 if ($this.hasClass("wcpt-wholesale--wholesale-table")) { 8995 var variation_html = "-"; 8996 8997 if (variation && variation.wholesale_price) { 8998 var table_html_match = 8999 variation.price_html.match(/(<table.+table)>/s); 9000 if (table_html_match) { 9001 variation_html = table_html_match[0]; 9002 } 9003 } 9004 9005 $this.html(variation_html); 9006 } 9007 9008 // wholesale price 9009 if ($this.hasClass("wcpt-wholesale--wholesale-price")) { 9010 var variation_text = "-"; 9011 9012 if (variation) { 9013 var wholesale_price = ""; 9014 if (variation.wholesale_price) { 9015 wholesale_price = variation.wholesale_price; 9016 } 9017 variation_text = format_price(wholesale_price); 9018 } 9019 9020 $(".wcpt-wholesale__variation-view", $this).text(variation_text); 9021 } 9022 9023 // original price 9024 if ($this.hasClass("wcpt-wholesale--original-price")) { 9025 var variation_text = "-"; 9026 9027 if (variation) { 9028 var original_price = ""; 9029 if (variation.wholesale_price) { 9030 original_price = variation.original_display_price; // cannot use display_price here because it is set to wholesale_price by WCPT 9031 } else { 9032 original_price = variation.display_price; 9033 } 9034 variation_text = format_price(original_price); 9035 } 9036 9037 $(".wcpt-wholesale__variation-view", $this).text(variation_text); 9038 } 9039 9040 // wholesale label 9041 if ($this.hasClass("wcpt-wholesale--wholesale-label")) { 9042 $this.removeClass( 9043 "wcpt-wholesale--variation-is-on-wholesale-view-enabled wcpt-wholesale--variation-is-not-on-wholesale-view-enabled", 9044 ); 9045 if (variation) { 9046 if (variation.wholesale_price) { 9047 $this.addClass( 9048 "wcpt-wholesale--variation-is-on-wholesale-view-enabled", 9049 ); 9050 } else { 9051 $this.addClass( 9052 "wcpt-wholesale--variation-is-not-on-wholesale-view-enabled", 9053 ); 9054 } 9055 } 9056 } 9057 });
9058 }); 9059 9060 // Variation Swatches 9061 $("body").on( 9062 "change", 9063 ".wcpt-row .variations_form .variation_id", 9064 function () { 9065 var $this = $(this), 9066 $form = $this.closest(".variations_form"); 9067 9068 if ($form.hasClass("wvs-loaded")) { 9069 get_select_variation_from_cart_form($form); 9070 } 9071 }, 9072 ); 9073 9074 // init tables 9075 var $wcpt = $(".wcpt"); 9076 if ($wcpt.length) { 9077 $wcpt.each(function () { 9078 after_every_load($(this)); 9079 }); 9080 } 9081 9082 // lazy load 9083 lazy_load_start(); 9084 9085 // init cart widget 9086 var cart_init_required = false; 9087 9088 if ( 9089 document.cookie.indexOf("woocommerce_items_in_cart") !== -1 || 9090 (typeof wcpt_cart_result_cache !== "undefined" && 9091 wcpt_cart_result_cache.cart_quantity) 9092 ) { 9093 cart_init_required = true; 9094 } 9095 9096 if (cart_init_required) { 9097 wcpt_cart({ 9098 payload: { skip_cart_triggers: true }, 9099 }); 9100 } 9101 9102 $(window).on("pageshow", function (e) { 9103 // trigger cart events 9104 if (e.originalEvent.persisted) { 9105 wcpt_cart({ 9106 payload: { skip_cart_triggers: true }, 9107 }); 9108 } 9109 9110 // reset select variation to default 9111 $(".wcpt-select-variation-dropdown").each(function () { 9112 var $this = $(this), 9113 $row = $this.closest(".wcpt-row"), 9114 variation_id = $row.data("wcpt_variation_id"); 9115 9116 if (variation_id) { 9117 $this.val(variation_id); 9118 } else { 9119 $this.val(""); 9120 } 9121 }); 9122 }); 9123}); 9124 9125// module: child row 9126(function ($) { 9127 /** 9128 * Accordion mode variable. Set to true to allow only one child row open at a time. 9129 */ 9130 var accordion_mode = true; // set this variable as needed 9131 var child_row_instance_counter = 0; 9132 9133 function getChildRowContainerSelector($container) { 9134 var instanceId = $container.attr("data-wcpt-child-row-instance-id"); 9135 9136 if (!instanceId) { 9137 child_row_instance_counter += 1; 9138 instanceId = "wcpt-child-row-" + child_row_instance_counter; 9139 $container.attr("data-wcpt-child-row-instance-id", instanceId); 9140 } 9141 9142 return '.wcpt[data-wcpt-child-row-instance-id="' + instanceId + '"]'; 9143 } 9144 9145 /** 9146 * Register event handlers for child row logic 9147 */ 9148 $("body") 9149 // Initialize all child row DOM logic after table load 9150 .on("wcpt_after_every_load", ".wcpt", initChildRows) 9151 // Each child row: register parent/child data binding and set background 9152 .on("wcpt_init_child_row", ".wcpt-child-row", registerParentChild) 9153 .on("wcpt_init_child_row", ".wcpt-child-row", setChildRowBg) 9154 // Click: toggle child rows by explicit control 9155 .on("click", ".wcpt-child-row-toggle", toggleChildRow) 9156 // Click: anywhere in designated rows, except on interactive elements 9157 .on("click", ".wcpt-has-child-row--click-anywhere", toggleChildRowAnywhere) 9158 // Hover state handlers (parent/child highlighting) 9159 .on("mouseenter", ".wcpt-has-child-row", parentRowMouseenter) 9160 .on("mouseleave", ".wcpt-has-child-row", parentRowMouseleave) 9161 .on("mouseenter", ".wcpt-child-row", childRowMouseenter) 9162 .on("mouseleave", ".wcpt-child-row", childRowMouseleave); 9163 9164 // In pages with multiple tables, some instances can be rendered before this 9165 // module attaches wcpt_after_every_load listeners. Initialize existing tables. 9166 $(function () { 9167 $(".wcpt").each(function () { 9168 initChildRows.call(this); 9169 }); 9170 }); 9171 9172 /** 9173 * Init all child rows in the current .wcpt table, 9174 * triggers event to register proper properties 9175 */ 9176 function initChildRows() { 9177 var $container = $(this); 9178 9179 ensureParentChildBindings($container); 9180 9181 $(".wcpt-child-row:not(.wcpt-child-row--init)", $container).each( 9182 function () { 9183 var $row = $(this); 9184 $row.trigger("wcpt_init_child_row").addClass("wcpt-child-row--init"); 9185 }, 9186 ); 9187 } 9188 9189 function ensureParentChildBindings($container) { 9190 $container.find(".wcpt-child-row").each(function () { 9191 var $child = $(this); 9192 var $parent = $child.prev(".wcpt-row"); 9193 9194 if (!$parent.length) return; 9195 9196 if (!$child.data("wcpt_parent_row")) { 9197 $child.data("wcpt_parent_row", $parent); 9198 } 9199 9200 if (!$parent.data("wcpt_child_row")) { 9201 $parent.data("wcpt_child_row", $child); 9202 } 9203 });
9204 } 9205 9206 /** 9207 * Binds parent <-> child row references for easier later access 9208 */ 9209 function registerParentChild() { 9210 var $child = $(this), 9211 $parent = $child.prev(); 9212 9213 $child.data("wcpt_parent_row", $parent); 9214 $parent.data("wcpt_child_row", $child); 9215 } 9216 9217 /** 9218 * Sets background color of a child row to match its parent's first cell (or whole row) 9219 */ 9220 function setChildRowBg() { 9221 var $child = $(this), 9222 $parent = $child.data("wcpt_parent_row"); 9223 9224 // fallback safely in case parent not found 9225 if (!$parent || !$parent.length) return; 9226 9227 var parentBg = $parent.css("background-color") || "", 9228 firstCellBg = $("> td:first-child", $parent).css("background-color"), 9229 chosenBg = 9230 firstCellBg && firstCellBg !== "rgba(0, 0, 0, 0)" 9231 ? firstCellBg 9232 : parentBg; 9233 9234 $("> td", $child).css("background-color", chosenBg); 9235 } 9236 9237 /** 9238 * Ensures all cells in table have stable width when child row opened (for visual stability) 9239 */ 9240 function applyColumnWidthStyles($container) { 9241 var $firstOpenRow = $container 9242 .find( 9243 ".wcpt-row:has(td.wcpt-child-row-toggle:not(.wcpt-child-row-toggle--closed))", 9244 ) 9245 .first(); 9246 var styleHtml = '<style class="wcpt-child-row-width">'; 9247 var containerSelector = getChildRowContainerSelector($container); 9248 if ($firstOpenRow.length) { 9249 $firstOpenRow.children(".wcpt-cell").each(function (index) { 9250 var width = $(this).outerWidth(); 9251 // Scope styles to a unique table instance on the page. 9252 styleHtml += 9253 containerSelector + 9254 " .wcpt-row > .wcpt-cell:nth-child(" + 9255 (index + 1) + 9256 ") { width: " + 9257 width + 9258 "px !important; }"; 9259 }); 9260 styleHtml += "</style>"; 9261 $container.find("style.wcpt-child-row-width").remove(); 9262 $container.append(styleHtml); 9263 } 9264 } 9265 9266 /** 9267 * Main click handler: toggles child row open/closed for a given row/cell or header cell. 9268 * Supports accordion_mode: if true, only one child row stays open at a time (unless opening all). 9269 */ 9270 function toggleChildRow(e) { 9271 var $clicked = $(this), 9272 $container = $clicked.closest(".wcpt"); 9273 9274 // Reconcile bindings if this table was rendered before child-row init hooks. 9275 ensureParentChildBindings($container); 9276 9277 // Detect if this is a table heading (bulk open/close) 9278 var isHeader = !$clicked.is("td"); 9279 9280 if (isHeader) { 9281 // Header toggle always continues as normal: opens/closes all 9282 toggleAllChildRows( 9283 $container, 9284 !$clicked.hasClass("wcpt-child-row-toggle--closed"), 9285 ); 9286 updateRowVisibilityMarks($container); 9287 handleColumnWidths( 9288 $container, 9289 !$clicked.hasClass("wcpt-child-row-toggle--closed"), 9290 ); 9291 $clicked.toggleClass("wcpt-child-row-toggle--closed"); 9292 return; 9293 } 9294 9295 var wasClosed = $clicked.hasClass("wcpt-child-row-toggle--closed"); 9296 9297 // If accordion mode is on, and we're opening a row (not closing), close any other open rows first (excluding this one) 9298 // if (accordion_mode && wasClosed) { 9299 if (wasClosed) { 9300 // Find all open toggles except the one being clicked 9301 $container 9302 .find("td.wcpt-child-row-toggle:not(.wcpt-child-row-toggle--closed)") 9303 .each(function () { 9304 var $otherTd = $(this); 9305 if (!$otherTd.is($clicked)) { 9306 // Only close if not this one 9307 var $otherRow = $otherTd.closest(".wcpt-row"); 9308 var $otherChildRow = $otherRow.data("wcpt_child_row"); 9309 $otherTd.addClass("wcpt-child-row-toggle--closed"); 9310 if ($otherChildRow && $otherChildRow.length) { 9311 var $otherWrapper = $otherChildRow.find( 9312 ".wcpt-child-row__wrapper", 9313 ); 9314 $otherWrapper.slideUp(); 9315 } 9316 } 9317 }); 9318 } 9319 9320 // Now toggle clicked row as usual 9321 $clicked.toggleClass("wcpt-child-row-toggle--closed"); 9322 var isClosedNow = $clicked.hasClass("wcpt-child-row-toggle--closed"); 9323 9324 // Handle column width stabilization 9325 handleColumnWidths($container, !isClosedNow); 9326 9327 // Single row toggle logic 9328 toggleSingleChildRow($clicked, $container, isClosedNow); 9329 9330 updateRowVisibilityMarks($container); 9331 } 9332 9333 /** 9334 * Ensures all columns keep their current width (optional: only when opening). 9335 */ 9336 function handleColumnWidths($container, openingOne) { 9337 var $openRows = $container.find( 9338 ".wcpt-row:has(td.wcpt-child-row-toggle:not(.wcpt-child-row-toggle--closed))", 9339 ); 9340 var $style = $container.find("style.wcpt-child-row-width"); 9341 var containerSelector = getChildRowContainerSelector($container); 9342 9343 if (openingOne && $openRows.length) { 9344 // Lock cell widths according to the first row with open toggle 9345 var $row = $openRows.first(); 9346 var styleHtml = '<style class="wcpt-child-row-width">'; 9347 $row.children(".wcpt-cell").each(function (i) { 9348 styleHtml += 9349 containerSelector + 9350 " .wcpt-row > .wcpt-cell:nth-child(" + 9351 (i + 1) + 9352 ") { width: " + 9353 $(this).outerWidth() + 9354 "px !important; }"; 9355 }); 9356 styleHtml += "</style>"; 9357 $style.remove(); 9358 $container.append(styleHtml); 9359 } else { 9360 // Only remove width style if ALL now closed 9361 var allClosed = 9362 $container.find("td.wcpt-child-row-toggle").length === 9363 $container.find( 9364 "td.wcpt-child-row-toggle.wcpt-child-row-toggle--closed", 9365 ).length; 9366 if (allClosed) { 9367 $style.remove(); 9368 } else { 9369 applyColumnWidthStyles($container); // fallback to routine (already defined) 9370 } 9371 } 9372 } 9373 9374 /** 9375 * Toggle a single row's child row visibility and header icon state. 9376 */ 9377 function toggleSingleChildRow($clicked, $container, isClosedNow) { 9378 var $row = $clicked.closest(".wcpt-row"); 9379 var $childRow = $row.data("wcpt_child_row"); 9380 9381 if ( 9382 (!$childRow || !$childRow.length) && 9383 $row.next().hasClass("wcpt-child-row") 9384 ) { 9385 $childRow = $row.next(); 9386 $row.data("wcpt_child_row", $childRow); 9387 $childRow.data("wcpt_parent_row", $row); 9388 } 9389 9390 if ($childRow && $childRow.length) { 9391 var $wrapper = $childRow.find(".wcpt-child-row__wrapper"); 9392 if ($row.hasClass("wcpt-has-child-row--visible")) { 9393 $wrapper.slideUp(); 9394 } else { 9395 $wrapper.slideDown(); 9396 } 9397 } 9398 9399 // Sync header toggle icon: open header icon if ANY row is open, else close header icon 9400 var anyOpen = 9401 $container.find( 9402 "td.wcpt-child-row-toggle:not(.wcpt-child-row-toggle--closed)", 9403 ).length > 0;
9404 var $headerToggles = $container.find("th.wcpt-child-row-toggle"); 9405 if (anyOpen) { 9406 $headerToggles.removeClass("wcpt-child-row-toggle--closed"); 9407 } else { 9408 $headerToggles.addClass("wcpt-child-row-toggle--closed"); 9409 } 9410 } 9411 9412 /** 9413 * Toggle all child rows via the header toggle cell. 9414 * Support "close all" even if accordion_mode is enabled. 9415 */ 9416 function toggleAllChildRows($container, closing) { 9417 var $tdToggles = $container.find("td.wcpt-child-row-toggle"); 9418 $tdToggles.each(function () { 9419 var $td = $(this); 9420 var $row = $td.closest(".wcpt-row"); 9421 var $childRow = $row.data("wcpt_child_row"); 9422 var $wrapper = $childRow 9423 ? $childRow.find(".wcpt-child-row__wrapper") 9424 : $(); 9425 if (closing) { 9426 if (!$td.hasClass("wcpt-child-row-toggle--closed")) { 9427 $td.addClass("wcpt-child-row-toggle--closed"); 9428 if ($childRow && $wrapper.length) $wrapper.slideUp(); 9429 } 9430 } else { 9431 if ($td.hasClass("wcpt-child-row-toggle--closed")) { 9432 $td.removeClass("wcpt-child-row-toggle--closed"); 9433 if ($childRow && $wrapper.length) $wrapper.slideDown(); 9434 } 9435 } 9436 }); 9437 // If opening bulk, re-apply width stabilization 9438 if (!closing) { 9439 applyColumnWidthStyles($container); 9440 } else { 9441 // Always remove width stabilization on close all 9442 var $style = $container.find("style.wcpt-child-row-width"); 9443 $style.remove(); 9444 } 9445 } 9446 9447 /** 9448 * Add or remove .wcpt-has-child-row--visible on rows according to open toggle state (for CSS effects). 9449 */ 9450 function updateRowVisibilityMarks($container) { 9451 $container.find("td.wcpt-child-row-toggle").each(function () { 9452 var $td = $(this); 9453 var $row = $td.closest(".wcpt-row"); 9454 // Use a short delay to let animations run and class state stabilize 9455 setTimeout(function () { 9456 if ($td.hasClass("wcpt-child-row-toggle--closed")) { 9457 $row.removeClass("wcpt-has-child-row--visible"); 9458 } else { 9459 $row.addClass("wcpt-has-child-row--visible"); 9460 } 9461 }, 100); 9462 }); 9463 } 9464 9465 /** 9466 * Handler: clicking anywhere in "click-anywhere" parent row will toggle child, 9467 * but not if clicking on interactive/child row controls or links. 9468 */ 9469 function toggleChildRowAnywhere(e) { 9470 var $row = $(this).closest(".wcpt-row"); 9471 var ignoreSelectors = [ 9472 ".wcpt-child-row-toggle", 9473 "a", 9474 ".wcpt-tooltip", 9475 "input", 9476 "button", 9477 ".wcpt-button", 9478 ".wcpt-link", 9479 ".wcpt-quantity", 9480 ].join(", "); 9481 if (!$(e.target).closest(ignoreSelectors).length) { 9482 $(".wcpt-child-row-toggle", $row).click(); 9483 } 9484 } 9485 9486 /** Highlighting logic for hover grouping (parent and child row highlight effects) **/ 9487 function parentRowMouseenter() { 9488 var $parent = $(this), 9489 $child = $parent.data("wcpt_child_row"); 9490 if ($child && $child.length) { 9491 $child.addClass("wcpt-parent-row-hovered"); 9492 } 9493 } 9494 9495 function parentRowMouseleave() { 9496 var $parent = $(this), 9497 $child = $parent.data("wcpt_child_row"); 9498 if ($child && $child.length) { 9499 $child.removeClass("wcpt-parent-row-hovered"); 9500 } 9501 } 9502 9503 function childRowMouseenter() { 9504 var $child = $(this), 9505 $parent = $child.data("wcpt_parent_row"); 9506 if ($parent && $parent.length) { 9507 $parent.addClass("wcpt-child-row-hovered"); 9508 } 9509 } 9510 9511 function childRowMouseleave() { 9512 var $child = $(this), 9513 $parent = $child.data("wcpt_parent_row"); 9514 if ($parent && $parent.length) { 9515 $parent.removeClass("wcpt-child-row-hovered"); 9516 } 9517 } 9518})(jQuery); 9519 9520// module: instant search 9521(function ($) { 9522 $("body").on("wcpt_after_every_load", ".wcpt", init_instant_search); 9523 9524 // -- add instant search html class on $search and stop 'enter' key handler 9525 function init_instant_search() { 9526 var $container = $(this), 9527 $search = $(".wcpt-search-wrapper", $container); 9528 9529 if ( 9530 wcpt_util.get_sc_attrs($container).instant_search && 9531 !$search.hasClass("wcpt-instant-search") 9532 ) { 9533 $search.addClass("wcpt-instant-search"); 9534 $(".wcpt-instant-search .wcpt-search-input", $container).on( 9535 "keydown", 9536 function (e) { 9537 if (e.keyCode === 13 || e.which === 13) { 9538 e.stopPropagation(); 9539 } 9540 }, 9541 ); 9542 } else { 9543 $search.removeClass("wcpt-instant-search"); 9544 } 9545 } 9546 9547 function get_instant_search_words(val) {
9548 return val.split(/\s+/).filter(Boolean); 9549 } 9550 9551 // Per-column text cache; each search word must match at least one column. 9552 function get_instant_search_cell_texts($row) { 9553 var texts = $row.data("wcpt_instant_search_cell_texts"); 9554 if (!texts) { 9555 texts = []; 9556 $row.children(".wcpt-cell").each(function () { 9557 texts.push($(this).text().toLowerCase().trim()); 9558 }); 9559 if (!texts.length) { 9560 texts = [$row.text().toLowerCase().trim()]; 9561 } 9562 $row.data("wcpt_instant_search_cell_texts", texts); 9563 } 9564 return texts; 9565 } 9566 9567 function row_matches_instant_search($row, val) { 9568 var words = get_instant_search_words(val), 9569 cellTexts = get_instant_search_cell_texts($row), 9570 w, 9571 c, 9572 wordMatch; 9573 9574 for (w = 0; w < words.length; w++) { 9575 wordMatch = false; 9576 for (c = 0; c < cellTexts.length; c++) { 9577 if (cellTexts[c].indexOf(words[w]) !== -1) { 9578 wordMatch = true; 9579 break; 9580 } 9581 } 9582 if (!wordMatch) { 9583 return false; 9584 } 9585 } 9586 return true; 9587 } 9588 9589 // -- search logic 9590 $("body").on( 9591 "keyup input", 9592 ".wcpt-instant-search .wcpt-search-input", 9593 function (e) { 9594 var $this = $(this), 9595 val = $this.val().toLowerCase().trim(), 9596 $wcpt = $this.closest(".wcpt"), 9597 $table = $(".wcpt-table:visible:not(.frzTbl-clone-table)", $wcpt), 9598 $rows = $(".wcpt-row", $table); 9599 9600 if (!val) { 9601 $rows.removeClass("wcpt-row--instant-search-hidden"); 9602 } else { 9603 $rows.each(function () { 9604 var $this = $(this), 9605 match = row_matches_instant_search($this, val); 9606 9607 if (match) { 9608 $this.removeClass("wcpt-row--instant-search-hidden"); 9609 } else { 9610 $this.addClass("wcpt-row--instant-search-hidden"); 9611 } 9612 9613 // child row -- reveal both if either has match 9614 if ($this.hasClass("wcpt-child-row")) { 9615 $parent_row = $this.data("wcpt_parent_row"); 9616 9617 if ( 9618 !$this.hasClass("wcpt-row--instant-search-hidden") || 9619 !$parent_row.hasClass("wcpt-row--instant-search-hidden") 9620 ) { 9621 $this 9622 .add($parent_row) 9623 .removeClass("wcpt-row--instant-search-hidden"); 9624 } 9625 } 9626 }); 9627 9628 // show category group headings if it has search results 9629 $rows.filter(".wcpt-row--category-heading").each(function () { 9630 var $this = $(this), 9631 cat_slug = $this.attr("data-wcpt-group-by-category-slug"); 9632 9633 if ( 9634 $rows.filter( 9635 '[data-wcpt-group-by-category-slug="' + cat_slug + '"]:visible', 9636 ).length 9637 ) { 9638 $this.removeClass("wcpt-row--instant-search-hidden"); 9639 } 9640 }); 9641 } 9642 9643 wcpt_util.assign_even_odd_row_classes($table); 9644 }, 9645 ); 9646})(jQuery); 9647 9648// module: instant sort 9649(function ($) { 9650 function get_instant_sort_params_for_heading( 9651 $sorting_icons, 9652 all_sort_params, 9653 ) { 9654 var current_sort_params = {}; 9655 9656 if (!$sorting_icons || !$sorting_icons.length || !all_sort_params) { 9657 return current_sort_params; 9658 } 9659 9660 // Preferred: match by element id class (wcpt-{id}) 9661 $.each(all_sort_params.column_heading || {}, function (id, params) { 9662 if ($sorting_icons.hasClass("wcpt-" + id)) { 9663 current_sort_params = $.extend({}, params); 9664 return false; 9665 } 9666 }); 9667 9668 if (!$.isEmptyObject(current_sort_params) && current_sort_params.orderby) { 9669 return current_sort_params; 9670 } 9671 9672 // Fallback: match by data attributes on the sorting icons. 9673 // Needed when heading.sorting ids were regenerated and no longer match 9674 // the keys stored in data-wcpt-instant-sort-params. 9675 var orderby = $sorting_icons.attr("data-wcpt-orderby") || "", 9676 meta_key = ( 9677 $sorting_icons.attr("data-wcpt-meta-key") || "" 9678 ).toLowerCase(), 9679 orderby_attribute = ( 9680 $sorting_icons.attr("data-wcpt-orderby-attribute") || "" 9681 ).toLowerCase(), 9682 orderby_taxonomy = ( 9683 $sorting_icons.attr("data-wcpt-orderby-taxonomy") || "" 9684 ).toLowerCase(); 9685 9686 if (!orderby) { 9687 return {}; 9688 } 9689 9690 $.each(all_sort_params.column_heading || {}, function (id, params) { 9691 if (!params || params.orderby !== orderby) { 9692 return; 9693 } 9694 9695 if ( 9696 -1 !== $.inArray(orderby, ["meta_value", "meta_value_num"]) && 9697 String(params.meta_key || "") 9698 .toLowerCase() 9699 .trim() !== meta_key 9700 ) { 9701 return; 9702 } 9703 9704 if ( 9705 -1 !== $.inArray(orderby, ["attribute", "attribute_num"]) && 9706 String(params.orderby_attribute || "") 9707 .toLowerCase() 9708 .trim() !== orderby_attribute 9709 ) { 9710 return; 9711 } 9712 9713 if ( 9714 orderby === "taxonomy" && 9715 String(params.orderby_taxonomy || "") 9716 .toLowerCase() 9717 .trim() !== orderby_taxonomy 9718 ) { 9719 return; 9720 } 9721 9722 current_sort_params = $.extend({}, params); 9723 return false; 9724 });
9725 9726 return current_sort_params; 9727 } 9728 9729 function get_instant_sort_heading_icons($wcpt, id, option_sort_params) { 9730 var $sorting_icons = $(".wcpt-" + id, $wcpt); 9731 9732 if ($sorting_icons.length) { 9733 return $sorting_icons; 9734 } 9735 9736 // Fallback when icon class id doesn't match the params key 9737 return $(".wcpt-sorting-icons", $wcpt).filter(function () { 9738 var $icons = $(this), 9739 orderby = $icons.attr("data-wcpt-orderby") || "", 9740 meta_key = ($icons.attr("data-wcpt-meta-key") || "").toLowerCase(), 9741 orderby_attribute = ( 9742 $icons.attr("data-wcpt-orderby-attribute") || "" 9743 ).toLowerCase(), 9744 orderby_taxonomy = ( 9745 $icons.attr("data-wcpt-orderby-taxonomy") || "" 9746 ).toLowerCase(); 9747 9748 if (!option_sort_params || orderby !== option_sort_params.orderby) { 9749 return false; 9750 } 9751 9752 if ( 9753 -1 !== $.inArray(orderby, ["meta_value", "meta_value_num"]) && 9754 String(option_sort_params.meta_key || "") 9755 .toLowerCase() 9756 .trim() !== meta_key 9757 ) { 9758 return false; 9759 } 9760 9761 if ( 9762 -1 !== $.inArray(orderby, ["attribute", "attribute_num"]) && 9763 String(option_sort_params.orderby_attribute || "") 9764 .toLowerCase() 9765 .trim() !== orderby_attribute 9766 ) { 9767 return false; 9768 } 9769 9770 if ( 9771 orderby === "taxonomy" && 9772 String(option_sort_params.orderby_taxonomy || "") 9773 .toLowerCase() 9774 .trim() !== orderby_taxonomy 9775 ) { 9776 return false; 9777 } 9778 9779 return true; 9780 }); 9781 } 9782 9783 // -- init 9784 $("body").on("wcpt_after_every_load", ".wcpt", init_instant_sort); 9785 9786 function init_instant_sort() { 9787 var $this = $(this), 9788 sc_attrs = $this.data("wcpt_sc_attrs"); 9789 9790 if (sc_attrs.instant_sort) { 9791 // attach data 9792 var sort_data = []; 9793 $( 9794 ".wcpt-row:not(.wcpt-child-row):not(.wcpt-row--category-heading)", 9795 $this, 9796 ).each(function () { 9797 var $this = $(this), 9798 id = $this.attr("data-wcpt-product-id"), 9799 variation_id = $this.attr("data-wcpt-variation-id"), 9800 product_sort_data = $.extend( 9801 {}, 9802 { 9803 id: id, 9804 variation_id: variation_id, 9805 }, 9806 JSON.parse($this.attr("data-wcpt-instant-sort-props")), 9807 ); 9808 sort_data.push(product_sort_data); 9809 }); 9810 9811 $this.data("wcpt_sort_data", sort_data); 9812 9813 // replace handlers 9814 wcpt_util.run_once_on_container( 9815 $this, 9816 "wcpt_instant_sort_init", 9817 function ($container) { 9818 // -- column heading sort icons handler 9819 9820 // -- -- remove previous handlers and the init html class on container 9821 $container.off( 9822 "click.wcpt_sort_by_column_headings", 9823 ".wcpt-heading.wcpt-sortable", 9824 ); 9825 9826 // -- -- attach new handlers 9827 $container.on( 9828 "click.wcpt_instant_sort", 9829 ".wcpt-heading.wcpt-sortable", 9830 function (e) { 9831 var $this = $(this), 9832 $sorting_icons = $(".wcpt-sorting-icons", $this), 9833 new_order = $sorting_icons.hasClass("wcpt-sorting-asc") 9834 ? "desc" 9835 : "asc", 9836 $wcpt = $this.closest(".wcpt"), 9837 all_sort_params = JSON.parse( 9838 $wcpt.attr("data-wcpt-instant-sort-params"), 9839 ), 9840 current_sort_params = get_instant_sort_params_for_heading( 9841 $sorting_icons, 9842 all_sort_params, 9843 ); 9844 9845 // If heading icons aren't linked to instant-sort params (id mismatch), 9846 // fall back to the regular AJAX column-heading sort. 9847 if ( 9848 $.isEmptyObject(current_sort_params) || 9849 !current_sort_params.orderby 9850 ) { 9851 return window.wcpt_column_heading_sort_handler.call(this, e); 9852 } 9853 9854 $.extend(current_sort_params, { order: new_order }); 9855 9856 wcpt_instant_sort(current_sort_params, $wcpt); 9857 9858 instant_sort_ui_feedback(current_sort_params, $wcpt); 9859 }, 9860 ); 9861 9862 // -- 'Sort By' dropdown handler 9863 $container.on( 9864 "change.wcpt_instant_sort", 9865 '[data-wcpt-filter="sort_by"] input[type="radio"]', 9866 function (e) { 9867 e.stopPropagation(); // block access to wcpt regular sort handler 9868 9869 var $this = $(this), 9870 $option = $this.closest(".wcpt-option, .wcpt-dropdown-option"), // @TODO include dropdown option 9871 $wcpt = $this.closest(".wcpt"), 9872 all_sort_params = JSON.parse( 9873 $wcpt.attr("data-wcpt-instant-sort-params"), 9874 ), 9875 index = $option.index(), 9876 current_sort_params = all_sort_params.dropdown[index]; 9877 9878 wcpt_instant_sort(current_sort_params, $wcpt); 9879 9880 instant_sort_ui_feedback(current_sort_params, $wcpt); 9881 }, 9882 ); 9883 }, 9884 ); 9885 } 9886 } 9887 9888 function instant_sort_ui_feedback(current_sort_params, $wcpt) { 9889 var all_sort_params = JSON.parse( 9890 $wcpt.attr("data-wcpt-instant-sort-params"), 9891 ); 9892 9893 if ( 9894 -1 !== $.inArray(current_sort_params.orderby, ["rating", "price-desc"]) 9895 ) { 9896 current_sort_params.order = "desc"; 9897 } 9898 9899 if (current_sort_params.orderby == "price-desc") { 9900 current_sort_params.orderby = "price"; 9901 } 9902 9903 // 'Sort By' dropdown (or row) 9904 $.each( 9905 all_sort_params["dropdown"], 9906 function (dropdown_option_index, option_sort_params) { 9907 if ( 9908 -1 !== $.inArray(option_sort_params.orderby, ["rating", "price-desc"]) 9909 ) { 9910 option_sort_params.order = "desc"; 9911 } 9912 9913 if (option_sort_params.orderby == "price-desc") { 9914 option_sort_params.orderby = "price"; 9915 } 9916 9917 if ( 9918 current_sort_params.orderby !== option_sort_params.orderby || 9919 (current_sort_params.order && 9920 current_sort_params.order.toLowerCase() !== 9921 option_sort_params.order.toLowerCase()) || 9922 (current_sort_params.orderby == "meta_key" && 9923 current_sort_params.meta_key && 9924 current_sort_params.meta_key.toLowerCase() !== 9925 option_sort_params.meta_key.toLowerCase()) || 9926 (-1 !== 9927 $.inArray(current_sort_params.orderby, [ 9928 "attribute", 9929 "attribute_num", 9930 ]) && 9931 current_sort_params.orderby_attribute && 9932 current_sort_params.orderby_attribute.toLowerCase() !== 9933 option_sort_params.orderby_attribute.toLowerCase()) || 9934 (current_sort_params.orderby == "taxonomy" && 9935 current_sort_params.orderby_taxonomy && 9936 current_sort_params.orderby_taxonomy.toLowerCase() !== 9937 option_sort_params.orderby_taxonomy.toLowerCase()) 9938 ) { 9939 return; 9940 } 9941 9942 var $dropdown = $('[data-wcpt-filter="sort_by"]', $wcpt), // might be row 9943 $selected_input = $dropdown.find("input").eq(dropdown_option_index), 9944 $selected_option = $selected_input.closest( 9945 ".wcpt-dropdown-option, .wcpt-option", 9946 ), 9947 $heading_label = $(".wcpt-dropdown-label", $dropdown); 9948 9949 $heading_label.text($("span", $selected_option).text()); 9950 $selected_input.prop("checked", true); 9951 $selected_option 9952 .addClass("wcpt-active") 9953 .siblings() 9954 .removeClass("wcpt-active"); 9955 9956 $dropdown.removeClass("wcpt-open"); 9957 }, 9958 ); 9959 9960 // 'Sorting' column heading icons 9961 $.each( 9962 all_sort_params["column_heading"], 9963 function (id, option_sort_params) { 9964 if ( 9965 current_sort_params.orderby !== option_sort_params.orderby || 9966 ($.inArray(current_sort_params.orderby, [ 9967 "meta_value", 9968 "meta_value_num", 9969 ]) && 9970 current_sort_params.meta_key && 9971 current_sort_params.meta_key.toLowerCase() !== 9972 option_sort_params.meta_key.toLowerCase()) || 9973 (-1 !== 9974 $.inArray(current_sort_params.orderby, [ 9975 "attribute", 9976 "attribute_num", 9977 ]) && 9978 current_sort_params.orderby_attribute && 9979 current_sort_params.orderby_attribute.toLowerCase() !== 9980 option_sort_params.orderby_attribute.toLowerCase()) || 9981 (current_sort_params.orderby == "taxonomy" && 9982 current_sort_params.orderby_taxonomy && 9983 current_sort_params.orderby_taxonomy.toLowerCase() !== 9984 option_sort_params.orderby_taxonomy.toLowerCase()) 9985 ) { 9986 return; 9987 } 9988 9989 var $sorting_icons = get_instant_sort_heading_icons( 9990 $wcpt, 9991 id, 9992 option_sort_params, 9993 ), 9994 new_order = current_sort_params.order.toLowerCase(); 9995 9996 if (!$sorting_icons.length) { 9997 return; 9998 } 9999 10000 // UI feedback 10001 $(".wcpt-sorting-icons", $wcpt).removeClass( 10002 "wcpt-sorting-asc wcpt-sorting-desc", 10003 ); 10004 $(".wcpt-sorting-icon", $wcpt).removeClass("wcpt-active wcpt-inactive"); 10005 10006 if (new_order == "asc") { 10007 $sorting_icons 10008 .addClass("wcpt-sorting-asc") 10009 .removeClass("wcpt-sorting-desc"); 10010 } else { 10011 $sorting_icons 10012 .addClass("wcpt-sorting-desc") 10013 .removeClass("wcpt-sorting-asc"); 10014 } 10015 10016 $(".wcpt-sorting-" + new_order + "-icon", $sorting_icons).addClass( 10017 "wcpt-active", 10018 ); 10019 }, 10020 ); 10021 } 10022 10023 window.wcpt_instant_sort = function (params, $wcpt) { 10024 var sort_data = $wcpt.data("wcpt_sort_data"); 10025 10026 if (!params.order) { 10027 params.order = "asc"; 10028 } 10029 10030 params.order = params.order.toLowerCase(); 10031 10032 if (-1 !== $.inArray(params.orderby, ["price-desc", "rating"])) { 10033 params.order = "desc"; 10034 } 10035 10036 switch (params.orderby) { 10037 case "title": 10038 sort_data.sort(function (a, b) { 10039 return params.order == "asc" 10040 ? a.title.localeCompare(b.title) 10041 : b.title.localeCompare(a.title); 10042 }); 10043 10044 break; 10045 10046 case "sku": // as text 10047 sort_data.sort(function (a, b) { 10048 return params.order == "asc" 10049 ? a.sku.localeCompare(b.sku) 10050 : b.sku.localeCompare(a.sku); 10051 }); 10052 10053 break; 10054 10055 case "sku_num": 10056 sort_data.sort(function (a, b) { 10057 var a_sku_num = isNaN(parseFloat(a.sku)) ? 0 : parseFloat(a.sku), 10058 b_sku_num = isNaN(parseFloat(b.sku)) ? 0 : parseFloat(b.sku); 10059 10060 return params.order == "asc" 10061 ? a_sku_num - b_sku_num 10062 : b_sku_num - a_sku_num; 10063 }); 10064 10065 break; 10066 10067 case "menu_order": 10068 sort_data.sort(function (a, b) { 10069 return params.order == "asc" 10070 ? a.menu_order - b.menu_order 10071 : b.menu_order - a.menu_order; 10072 }); 10073 10074 break; 10075 10076 case "price": 10077 case "price-desc": 10078 sort_data.sort(function (a, b) { 10079 var a_price = a.price, 10080 b_price = b.price; 10081 10082 if (params.order == "asc" && a.min_price) { 10083 a_price = a.min_price; 10084 } 10085 10086 if (params.order == "desc" && a.max_price) { 10087 a_price = a.max_price; 10088 } 10089 10090 if (params.order == "asc" && b.min_price) { 10091 b_price = b.min_price; 10092 } 10093 10094 if (params.order == "desc" && b.max_price) { 10095 b_price = b.max_price; 10096 } 10097 10098 return params.order == "asc" ? a_price - b_price : b_price - a_price; 10099 }); 10100 10101 break; 10102 10103 case "meta_value": // as text 10104 sort_data.sort(function (a, b) { 10105 var key = "meta_value__" + params.meta_key, 10106 a_meta = a[key] == null ? "" : String(a[key]),
10107 b_meta = b[key] == null ? "" : String(b[key]); 10108 10109 return params.order == "asc" 10110 ? a_meta.localeCompare(b_meta) 10111 : b_meta.localeCompare(a_meta); 10112 }); 10113 10114 break; 10115 10116 case "meta_value_num": 10117 sort_data.sort(function (a, b) { 10118 var key = "meta_value__" + params.meta_key, 10119 a_meta = a[key], 10120 b_meta = b[key]; 10121 10122 var a_meta_num = isNaN(parseFloat(a_meta)) ? 0 : parseFloat(a_meta), 10123 b_meta_num = isNaN(parseFloat(b_meta)) ? 0 : parseFloat(b_meta); 10124 10125 return params.order == "asc" 10126 ? a_meta_num - b_meta_num 10127 : b_meta_num - a_meta_num; 10128 }); 10129 10130 break; 10131 10132 case "attribute": // as text 10133 sort_data.sort(function (a, b) { 10134 var key = "attribute__" + params.orderby_attribute, 10135 a_val = a[key] == null ? "" : String(a[key]), 10136 b_val = b[key] == null ? "" : String(b[key]); 10137 10138 return params.order == "asc" 10139 ? a_val.localeCompare(b_val) 10140 : b_val.localeCompare(a_val); 10141 }); 10142 10143 break; 10144 10145 case "attribute_num": // as number 10146 sort_data.sort(function (a, b) { 10147 var key = "attribute__" + params.orderby_attribute, 10148 a_val = a[key], 10149 b_val = b[key]; 10150 10151 var a_val_num = isNaN(parseFloat(a_val)) ? 0 : parseFloat(a_val), 10152 b_val_num = isNaN(parseFloat(b_val)) ? 0 : parseFloat(b_val); 10153 10154 return params.order == "asc" 10155 ? a_val_num - b_val_num 10156 : b_val_num - a_val_num; 10157 }); 10158 10159 break; 10160 10161 case "taxonomy": // as text 10162 sort_data.sort(function (a, b) { 10163 var key = "taxonomy__" + params.orderby_taxonomy, 10164 a_val = a[key] == null ? "" : String(a[key]), 10165 b_val = b[key] == null ? "" : String(b[key]); 10166 10167 return params.order == "asc" 10168 ? a_val.localeCompare(b_val) 10169 : b_val.localeCompare(a_val); 10170 }); 10171 10172 break; 10173 10174 case "brand": // product_brand taxonomy (same prop key as taxonomy sort) 10175 sort_data.sort(function (a, b) { 10176 var key = "taxonomy__product_brand", 10177 a_val = a[key] || "", 10178 b_val = b[key] || ""; 10179 10180 return params.order == "asc" 10181 ? a_val.localeCompare(b_val) 10182 : b_val.localeCompare(a_val); 10183 }); 10184 10185 break; 10186 10187 case "taxonomy_num": // as number 10188 sort_data.sort(function (a, b) { 10189 var a_val = a["taxonomy__" + params.orderby_taxonomy], 10190 b_val = b["taxonomy__" + params.orderby_taxonomy]; 10191 10192 var a_val_num = isNaN(parseFloat(a_val)) ? 0 : parseFloat(a_val), 10193 b_val_num = isNaN(parseFloat(b_val)) ? 0 : parseFloat(b_val); 10194 10195 return params.order == "asc" 10196 ? a_val_num - b_val_num 10197 : b_val_num - a_val_num; 10198 }); 10199 10200 break; 10201 10202 case "category": 10203 sort_data.sort(function (a, b) { 10204 return params.order == "asc" 10205 ? a.category.localeCompare(b.category) 10206 : b.category.localeCompare(a.category); 10207 }); 10208 10209 case "date": 10210 case "popularity": 10211 case "id": 10212 case "rating": 10213 sort_data.sort(function (a, b) { 10214 return params.order == "asc" 10215 ? a[params.orderby] - b[params.orderby] 10216 : b[params.orderby] - a[params.orderby]; 10217 }); 10218 10219 break; 10220 10221 default: 10222 break; 10223 } 10224 10225 // render 10226 $.each(sort_data, function (index, product_sort_data) { 10227 var $row = false; 10228 if (product_sort_data.variation_id) { 10229 // variation 10230 $row = $wcpt.find( 10231 '.wcpt-row[data-wcpt-variation-id="' + 10232 product_sort_data.variation_id + 10233 '"]', 10234 ); 10235 } else { 10236 // other 10237 $row = $wcpt.find( 10238 '.wcpt-row[data-wcpt-product-id="' + product_sort_data.id + '"]', 10239 ); 10240 } 10241 10242 $row.each(function () { 10243 var $this = $(this), 10244 $tbody = $this.closest("tbody"); 10245 $this.detach().appendTo($tbody); 10246 }); 10247 });
10248 10249 // -- group by category 10250 if ( 10251 wcpt_util.get_sc_attrs($wcpt)[ 10252 wcpt_util.get_device() + "_group_by_category" 10253 ] 10254 ) { 10255 $( 10256 $(".wcpt-row:not(.wcpt-row--category-heading)", $wcpt).get().reverse(), 10257 ).each(function () { 10258 var $this = $(this), 10259 cat_slug = $this.attr("data-wcpt-group-by-category-slug"), 10260 $category_heading = $this.siblings( 10261 '.wcpt-row--category-heading[data-wcpt-group-by-category-slug="' + 10262 cat_slug + 10263 '"]', 10264 ); 10265 $this.detach().insertAfter($category_heading); 10266 }); 10267 } 10268 10269 wcpt_util.assign_even_odd_row_classes($(".wcpt-table", $wcpt)); 10270 }; 10271})(jQuery); 10272 10273// module: download csv 10274(function ($) { 10275 $("body").on("click", ".wcpt-csv-download", function () { 10276 var $this = $(this), 10277 file_name = $this.attr("data-wcpt-file-name"), 10278 include_all_products = $this.attr("data-wcpt-csv-include-all-products"), 10279 $container = $this.closest(".wcpt"), 10280 encrypted_query_vars = $container.attr("data-wcpt-encrypted-query-vars"), 10281 encrypted_user_filters = $container.attr( 10282 "data-wcpt-encrypted-user-filters", 10283 ), 10284 encrypted_columns = $this.attr("data-wcpt-csv-encrypted-columns"), 10285 table_id = $this.closest(".wcpt").attr("data-wcpt-table-id"); 10286 10287 if ($this.hasClass("wcpt-disabled")) { 10288 return; 10289 } 10290 10291 $.ajax({
10292 url: wcpt_params.wc_ajax_url.replace("%%endpoint%%", "wcpt_get_csv"), 10293 method: "POST", // @todo: change to GET 10294 beforeSend: function () { 10295 $this.addClass("wcpt-disabled wcpt-loading"); 10296 }, 10297 data: { 10298 wcpt_csv_include_all_products: include_all_products, 10299 wcpt_csv_encrypted_query_vars: encrypted_query_vars, 10300 wcpt_csv_encrypted_user_filters: encrypted_user_filters, 10301 wcpt_csv_encrypted_columns: encrypted_columns, 10302 wcpt_csv_table_id: table_id, 10303 }, 10304 }).done(function (json_data) { 10305 $this.removeClass("wcpt-disabled wcpt-loading"); 10306 build_csv_and_download(json_data, file_name); 10307 }); 10308 }); 10309 10310 function build_csv_and_download(json_data, file_name) { 10311 var csv = ""; 10312 $.each(json_data, function (_, row) { 10313 $.each(row, function (_, val) { 10314 csv += val + ","; 10315 }); 10316 csv = csv.slice(0, -1) + "\n"; 10317 }); 10318 10319 var $pseudo_link = $("<a>", { 10320 href: "data:Application/octet-stream," + encodeURIComponent(csv), 10321 download: file_name + ".csv", 10322 }); 10323 10324 $pseudo_link.appendTo("body").get(0).click(); 10325 $pseudo_link.remove(); 10326 } 10327})(jQuery); 10328 10329// module: reveal sidebar 10330(function ($) { 10331 function get_reveal_sidebar_parts($from) { 10332 var $container = $from.closest(".wcpt"), 10333 $wrapper = $container.find(".wcpt-navigation-wrapper").first(), 10334 $button = $wrapper.find(".wcpt-header .wcpt-reveal-sidebar"), 10335 $sidebar = $wrapper.children(".wcpt-left-sidebar"); 10336 10337 return { 10338 $container: $container, 10339 $wrapper: $wrapper, 10340 $button: $button, 10341 $sidebar: $sidebar, 10342 }; 10343 } 10344 10345 function get_sidebar_filter_counts($sidebar) { 10346 var used = 0, 10347 total = 0; 10348 10349 if (!$sidebar || !$sidebar.length) { 10350 return { used: 0, total: 0 }; 10351 } 10352 10353 $sidebar.find(".wcpt-filter").each(function () { 10354 var $filter = $(this), 10355 filter = $filter.attr("data-wcpt-filter"), 10356 $checked = $filter.find("input[type=checkbox]:checked"), 10357 $selected = $filter.find("input[type=radio]:checked"), 10358 $min = $filter.find(".wcpt-range-input-min"), 10359 $max = $filter.find(".wcpt-range-input-max"), 10360 is_used = false; 10361 10362 if (filter === "sort_by" || filter === "results_per_page") { 10363 return; 10364 } 10365 10366 total += 1; 10367 10368 if ($checked.length) { 10369 is_used = true; 10370 } else if ($selected.length && $selected.attr("value")) { 10371 is_used = true; 10372 } else if ( 10373 $min.length && 10374 ($min.val() != $min.attr("min") || $max.val() != $max.attr("max")) 10375 ) { 10376 is_used = true; 10377 } 10378 10379 if (is_used) { 10380 used += 1; 10381 } 10382 }); 10383 10384 $sidebar.find(".wcpt-search").each(function () { 10385 var $search = $(this), 10386 keyword = ($search.find(".wcpt-search-input").val() || "").trim(); 10387 10388 total += 1; 10389 10390 if (keyword || $search.hasClass("wcpt-active")) { 10391 used += 1; 10392 } 10393 }); 10394 10395 return { used: used, total: total }; 10396 } 10397 10398 function update_reveal_sidebar_count($from) { 10399 var parts = get_reveal_sidebar_parts($from), 10400 $button = parts.$button, 10401 $sidebar = parts.$sidebar, 10402 $count = $button.find(".wcpt-active-count--reveal-sidebar"), 10403 counts; 10404 10405 if (!$button.length || !$count.length) { 10406 return; 10407 } 10408 10409 counts = get_sidebar_filter_counts($sidebar); 10410 $count 10411 .text(counts.total) 10412 .toggleClass("wcpt-active-count--zero", !counts.used); 10413 } 10414 10415 function save_reveal_sidebar_scroll($from) { 10416 var parts = get_reveal_sidebar_parts($from), 10417 $container = parts.$container, 10418 $sidebar = parts.$sidebar, 10419 sidebar, 10420 scrollTop, 10421 anchorEl, 10422 $filter, 10423 active; 10424 10425 if (!$container.length || !$sidebar.length || !$sidebar[0]) { 10426 return; 10427 } 10428 10429 sidebar = $sidebar[0]; 10430 scrollTop = sidebar.scrollTop; 10431 $container.data("wcpt-reveal-sidebar-scroll", scrollTop); 10432 10433 // Prefer the focused control; otherwise the first filter intersecting 10434 // the current viewport â used to re-align after content height shifts. 10435 active = document.activeElement; 10436 if (active && sidebar.contains(active)) { 10437 anchorEl = active.closest(".wcpt-filter, .wcpt-search"); 10438 } 10439 10440 if (!anchorEl) { 10441 $sidebar.find(".wcpt-filter, .wcpt-search").each(function () { 10442 if (this.offsetTop + this.offsetHeight > scrollTop) { 10443 anchorEl = this; 10444 return false; 10445 } 10446 }); 10447 } 10448 10449 if (!anchorEl) { 10450 $container.removeData("wcpt-reveal-sidebar-scroll-anchor"); 10451 return; 10452 } 10453 10454 $filter = $(anchorEl).closest(".wcpt-filter"); 10455 $container.data("wcpt-reveal-sidebar-scroll-anchor", { 10456 filter: $filter.attr("data-wcpt-filter") || "", 10457 taxonomy: $filter.attr("data-wcpt-taxonomy") || "", 10458 metaKey: 10459 $filter.attr("data-wcpt-meta-key") || 10460 $filter.attr("data-wcpt-meta_key") || 10461 "", 10462 isSearch: $(anchorEl).closest(".wcpt-search").length > 0, 10463 viewOffset: anchorEl.offsetTop - scrollTop, 10464 });
10465 } 10466 10467 function find_reveal_sidebar_scroll_anchor($sidebar, anchor) { 10468 var $match = $(); 10469 10470 if (!anchor) { 10471 return $match; 10472 } 10473 10474 if (anchor.isSearch) { 10475 return $sidebar.find(".wcpt-search").first(); 10476 } 10477 10478 if (!anchor.filter) { 10479 return $match; 10480 } 10481 10482 $match = $sidebar.find( 10483 '.wcpt-filter[data-wcpt-filter="' + anchor.filter + '"]', 10484 ); 10485 10486 if (anchor.taxonomy) { 10487 $match = $match.filter('[data-wcpt-taxonomy="' + anchor.taxonomy + '"]'); 10488 } 10489 10490 if (anchor.metaKey) { 10491 $match = $match.filter(function () { 10492 var $this = $(this); 10493 return ( 10494 $this.attr("data-wcpt-meta-key") === anchor.metaKey || 10495 $this.attr("data-wcpt-meta_key") === anchor.metaKey 10496 ); 10497 }); 10498 } 10499 10500 return $match.first(); 10501 } 10502 10503 function restore_reveal_sidebar_scroll($from) { 10504 var parts = get_reveal_sidebar_parts($from), 10505 $container = parts.$container, 10506 $sidebar = parts.$sidebar, 10507 scroll = $container.data("wcpt-reveal-sidebar-scroll"), 10508 anchor = $container.data("wcpt-reveal-sidebar-scroll-anchor"), 10509 $anchor, 10510 sidebar; 10511 10512 if (!$sidebar.length || !$sidebar[0] || typeof scroll === "undefined") { 10513 return; 10514 } 10515 10516 sidebar = $sidebar[0]; 10517 $anchor = find_reveal_sidebar_scroll_anchor($sidebar, anchor); 10518 10519 if ($anchor.length) { 10520 sidebar.scrollTop = Math.max(0, $anchor[0].offsetTop - anchor.viewOffset); 10521 } else { 10522 sidebar.scrollTop = scroll; 10523 } 10524 } 10525 10526 // Re-apply after layout settles â early restore can get clamped while the 10527 // panel's scrollHeight is still shrinking/growing, which drifts scroll up. 10528 function schedule_reveal_sidebar_scroll_restore($from) { 10529 var $container = $from.closest(".wcpt"); 10530 10531 $container.data("wcpt-reveal-sidebar-scroll-pending", true); 10532 restore_reveal_sidebar_scroll($container); 10533 10534 requestAnimationFrame(function () { 10535 restore_reveal_sidebar_scroll($container); 10536 requestAnimationFrame(function () { 10537 restore_reveal_sidebar_scroll($container); 10538 $container.removeData("wcpt-reveal-sidebar-scroll-pending"); 10539 }); 10540 }); 10541 } 10542 10543 function close_reveal_sidebar($from) { 10544 var parts = $from && $from.length ? get_reveal_sidebar_parts($from) : null, 10545 $wrappers = parts 10546 ? parts.$wrapper 10547 : $(".wcpt-navigation-wrapper--sidebar-open"); 10548 10549 $wrappers.each(function () { 10550 var $wrapper = $(this), 10551 $container = $wrapper.closest(".wcpt"); 10552 10553 $wrapper 10554 .removeClass("wcpt-navigation-wrapper--sidebar-open") 10555 .find("> .wcpt-left-sidebar") 10556 .attr("aria-hidden", "true"); 10557 $wrapper 10558 .find(".wcpt-header .wcpt-reveal-sidebar") 10559 .attr("aria-expanded", "false"); 10560 $container.data("wcpt-reveal-sidebar-open", false); 10561 }); 10562 10563 $(".wcpt-reveal-sidebar-backdrop").remove(); 10564 if (!$(".wcpt-navigation-wrapper--sidebar-open").length) { 10565 $("body").removeClass("wcpt-reveal-sidebar-on"); 10566 } 10567 } 10568 10569 function open_reveal_sidebar($from, skip_animation) { 10570 var parts = get_reveal_sidebar_parts($from), 10571 $container = parts.$container, 10572 $wrapper = parts.$wrapper, 10573 $button = parts.$button, 10574 $sidebar = parts.$sidebar; 10575 10576 if ( 10577 !$sidebar.length || 10578 !$button.length || 10579 $button.hasClass("wcpt-reveal-sidebar--unavailable") 10580 ) { 10581 return; 10582 } 10583 10584 // When restoring after a table refresh, open instantly (no slide-in) 10585 if (skip_animation) { 10586 $sidebar.addClass("wcpt-reveal-sidebar--no-anim"); 10587 } 10588 10589 $wrapper.addClass("wcpt-navigation-wrapper--sidebar-open"); 10590 $button.attr("aria-expanded", "true"); 10591 $sidebar.attr("aria-hidden", "false"); 10592 $container.data("wcpt-reveal-sidebar-open", true); 10593 10594 $("body").addClass("wcpt-reveal-sidebar-on"); 10595 if (!$(".wcpt-reveal-sidebar-backdrop").length) { 10596 $("body").append('<div class="wcpt-reveal-sidebar-backdrop"></div>'); 10597 } 10598 10599 if (skip_animation) { 10600 // force reflow so the "open" state applies without transition, then 10601 // restore transitions for subsequent interactions 10602 $sidebar[0].offsetHeight; 10603 $sidebar.removeClass("wcpt-reveal-sidebar--no-anim"); 10604 // HTML replace resets scroll; put the panel back where the user was 10605 schedule_reveal_sidebar_scroll_restore($container); 10606 } 10607 } 10608 10609 function init_reveal_sidebar() { 10610 var $container = $(this), 10611 parts = get_reveal_sidebar_parts($container), 10612 $wrapper = parts.$wrapper, 10613 $button = parts.$button, 10614 $sidebar = parts.$sidebar; 10615 10616 if (!$button.length) { 10617 $wrapper.removeClass( 10618 "wcpt-navigation-wrapper--reveal-sidebar wcpt-navigation-wrapper--sidebar-open", 10619 ); 10620 return; 10621 } 10622 10623 if (!$sidebar.length) { 10624 $button.addClass("wcpt-reveal-sidebar--unavailable"); 10625 return; 10626 } 10627 10628 var table_id = $container.attr("data-wcpt-table-id"), 10629 sidebar_id = "wcpt-reveal-sidebar-" + table_id; 10630 10631 $wrapper.addClass("wcpt-navigation-wrapper--reveal-sidebar"); 10632 $button.removeClass("wcpt-reveal-sidebar--unavailable").attr({ 10633 "aria-controls": sidebar_id, 10634 "aria-expanded": "false", 10635 });
10636 $sidebar.attr({ 10637 id: sidebar_id, 10638 "aria-hidden": "true", 10639 }); 10640 10641 update_reveal_sidebar_count($container); 10642 10643 // Keep sidebar open across filter refreshes (without re-animating) 10644 if ($container.data("wcpt-reveal-sidebar-open")) { 10645 open_reveal_sidebar($container, true); 10646 } 10647 } 10648 10649 $("body") 10650 .on("wcpt_before_ajax_container_replace", function (e, data) { 10651 if (data && data.$container && data.$container.length) { 10652 save_reveal_sidebar_scroll(data.$container); 10653 } 10654 }) 10655 .on("wcpt_layout", ".wcpt", function () { 10656 var $container = $(this); 10657 if ( 10658 $container.data("wcpt-reveal-sidebar-open") && 10659 $container.data("wcpt-reveal-sidebar-scroll-pending") 10660 ) { 10661 restore_reveal_sidebar_scroll($container); 10662 } 10663 }) 10664 .on("wcpt_after_every_load", ".wcpt", init_reveal_sidebar) 10665 .on("click", ".wcpt-header .wcpt-reveal-sidebar", function (e) { 10666 e.preventDefault(); 10667 open_reveal_sidebar($(this)); 10668 }) 10669 .on("click", ".wcpt-reveal-sidebar-backdrop", function () { 10670 close_reveal_sidebar($(".wcpt-navigation-wrapper--sidebar-open").first()); 10671 }) 10672 .on( 10673 "change input", 10674 ".wcpt-left-sidebar .wcpt-filter, .wcpt-left-sidebar .wcpt-search", 10675 function () { 10676 update_reveal_sidebar_count($(this)); 10677 }, 10678 ) 10679 .on("keydown", function (e) { 10680 if (e.key === "Escape") { 10681 close_reveal_sidebar(); 10682 } 10683 }); 10684 10685 $(function () { 10686 $(".wcpt").each(function () { 10687 init_reveal_sidebar.call(this); 10688 }); 10689 }); 10690})(jQuery); 10691 10692// module: infinite scroll 10693(function ($) { 10694 // controller 10695 // -- after_every_load handler - attach observer once 10696 $("body").on("wcpt_after_every_load", ".wcpt", function () { 10697 var $this__container = $(this), 10698 sc_attrs = wcpt_util.get_sc_attrs($this__container), 10699 $infinite_scroll_dots = $(".wcpt-infinite-scroll-dots", $this__container); 10700 10701 if (!$infinite_scroll_dots.length) { 10702 return; 10703 } 10704 10705 // intersection observer for load trigger 10706 wcpt_util.run_once_on_container( 10707 $this__container, 10708 "wcpt_infinite_scroll_init", 10709 function ($container) { 10710 const intersectionObserver = new IntersectionObserver( 10711 (entries) => { 10712 if (entries[0].intersectionRatio <= 0) return; 10713 10714 if ( 10715 !$container.hasClass( 10716 "wcpt-loading wcpt-infinite-scroll-loading-results", 10717 ) 10718 ) { 10719 $container.trigger("wcpt_infinite_scroll"); 10720 } 10721 }, 10722 { rootMargin: "100px" }, 10723 ); 10724 // start observing 10725 intersectionObserver.observe($infinite_scroll_dots[0]); 10726 $this__container.data( 10727 "wcpt-infinite-scroll-intersection-observer", 10728 intersectionObserver, 10729 ); 10730 }, 10731 ); 10732 10733 // fresh dots 10734 var intersection_observer = $this__container.data( 10735 "wcpt-infinite-scroll-intersection-observer", 10736 ); 10737 intersection_observer.observe($infinite_scroll_dots[0]); 10738 }); 10739 10740 // -- a separate trigger 'wcpt_infinite_scroll' that inits infinite scroll on $wcpt 10741 $("body").on("wcpt_infinite_scroll", ".wcpt", function () { 10742 var $this__container = $(this); 10743 append_next_page($this__container); 10744 }); 10745 10746 // -- helper: handler 10747 append_next_page = ($container) => { 10748 var table_id = wcpt_util.get_table_id($container), 10749 page_number = wcpt_util.get_current_page_number($container), 10750 new_query = `${table_id}_paged=${page_number + 1}`, 10751 append_new_query = true, 10752 purpose = "infinite_scroll", 10753 query = window.wcpt_build_ajax_query_string( 10754 $container, 10755 new_query, 10756 append_new_query, 10757 purpose, 10758 ); 10759 10760 window.wcpt_fetch_markup_and_apply_callback( 10761 $container, 10762 query, 10763 purpose, 10764 ajax_success__infinite_scroll, 10765 ); 10766 $container.removeClass("wcpt-loading"); 10767 $container.addClass("wcpt-infinite-scroll-loading-results"); 10768 }; 10769 10770 // ajax success handler 10771 function ajax_success__infinite_scroll(response, $container) { 10772 var $response = $(response), 10773 $new_container = $response.filter(".wcpt").first(); 10774 10775 if (!$new_container.length) { 10776 $new_container = $response.find(".wcpt").first(); 10777 } 10778 if (!$new_container.length) { 10779 $new_container = $response; 10780 } 10781 10782 var $new_rows = $(".wcpt-row", $new_container); 10783 $(".wcpt-table > tbody", $container).append($new_rows); 10784 10785 if ( 10786 typeof wcpt_util !== "undefined" && 10787 wcpt_util.merge_variable_switch_cf_from_response 10788 ) { 10789 wcpt_util.merge_variable_switch_cf_from_response( 10790 response, 10791 wcpt_util.get_table_id($container), 10792 ); 10793 } 10794 10795 if ( 10796 typeof wcpt_util !== "undefined" && 10797 wcpt_util.merge_variable_switch_templates_from_response 10798 ) { 10799 wcpt_util.merge_variable_switch_templates_from_response( 10800 $new_container, 10801 $container, 10802 ); 10803 } 10804 10805 $container.attr( 10806 "data-wcpt-query-string", 10807 $new_container.attr("data-wcpt-query-string"), 10808 ); 10809 $container.removeClass("wcpt-infinite-scroll-loading-results"); 10810 10811 // remove infinit scroll dots if results exhausted 10812 if (!$new_container.find(".wcpt-infinite-scroll-dots").length) { 10813 $container.find(".wcpt-infinite-scroll-dots").hide(); 10814 } 10815 10816 $("body").trigger("wcpt_infinite_scroll_after_load", { 10817 $container: $container, 10818 });
10819 } 10820})(jQuery); 10821 10822// module: freeze table 10823(function ($) { 10824 // @TODO - add init command here as well 10825 10826 // destroy previous instance before replacing the table 10827 $("body").on("wcpt_before_ajax_container_replace", (ev, data) => { 10828 var $freeze_table = wcpt_util.get_freeze_table(data.$container); 10829 if ($freeze_table) { 10830 $freeze_table.freezeTable("destroy"); 10831 } 10832 }); 10833 10834 $("body") 10835 .on("wcpt-nav-modal-on", function () { 10836 $(".frzTbl-table").freezeTable("pause"); 10837 }) 10838 .on("wcpt-nav-modal-off", function () { 10839 $(".frzTbl-table").freezeTable("unpause"); 10840 }); 10841})(jQuery); 10842 10843// module: form mode 10844(function ($) { 10845 // redirect to shop with correct table id in url params 10846 $("body").on("wcpt_before_ajax_query", function (e, data) { 10847 var sc_attrs = wcpt_util.get_sc_attrs(data.$wcpt), 10848 purpose = data.purpose, 10849 table_id = wcpt_util.get_table_id(data.$wcpt); 10850 10851 if (purpose == "filter" && sc_attrs.form_mode) { 10852 if ( 10853 sc_attrs.dynamic_filters_lazy_load || 10854 sc_attrs.dynamic_hide_filters || 10855 sc_attrs.dynamic_recount 10856 ) { 10857 var $apply = data.$wcpt.find(".wcpt-apply"); 10858 if ($apply.length && !$apply.hasClass("wcpt-apply--clicked")) { 10859 return; 10860 } 10861 } 10862 10863 // replace table id in query string 10864 data.query = data.query 10865 .split(table_id + "_") 10866 .join(wcpt_params.shop_table_id + "_"); 10867 10868 // don't let lang param repeat, lang=fr,fr error 10869 // var i = wcpt_params.shop_url.indexOf("?lang="); 10870 // if (i !== -1 && query.indexOf("lang=") !== -1) { 10871 // wcpt_params.shop_url = wcpt_params.shop_url.substring(0, i); 10872 // } 10873 10874 // add query string to shop url 10875 data.query = 10876 wcpt_params.shop_url + 10877 (wcpt_params.shop_url.indexOf("?") == -1 10878 ? data.query 10879 : "&" + data.query.slice(1)); 10880 10881 // hide form on submit 10882 if (sc_attrs.hide_form_on_submit) { 10883 data.query += "&hide_form=" + table_id; 10884 } 10885 10886 // disable device requirement 10887 data.query += "&" + wcpt_params.shop_table_id + "_device="; 10888 10889 // redirect to shop with correct table id in url params 10890 window.location = data.query; 10891 10892 // disable proceeding with ajax query 10893 data.proceed = false; 10894 } 10895 }); 10896})(jQuery); 10897 10898// module permission 10899// -- check if disabled 10900wcpt_is_module_disabled = function (module, $container) { 10901 var disabled_modules = $container.data("wcpt-disabled-modules"); 10902 if (!disabled_modules) { 10903 return false; 10904 } 10905 10906 var disabled = false; 10907 jQuery.each(disabled_modules, function (key, val) { 10908 if (val.module == module) { 10909 disabled = true; 10910 } 10911 }); 10912 10913 return disabled; 10914}; 10915 10916// -- disable 10917wcpt_disable_module = function (module, $container, source) { 10918 var disabled_modules = $container.data("wcpt-disabled-modules"); 10919 if (!disabled_modules) { 10920 disabled_modules = []; 10921 } 10922 10923 disabled_modules.push({ 10924 module: module, 10925 source: source, 10926 }); 10927 10928 $container.data("wcpt-disabled-modules", disabled_modules); 10929}; 10930 10931// -- permit 10932wcpt_permit_module = function (module, $container, source) { 10933 var disabled_modules = $container.data("wcpt-disabled-modules"); 10934 if (!disabled_modules) { 10935 disabled_modules = []; 10936 } 10937 10938 var i = disabled_modules.length; 10939 while (i) { 10940 --i; 10941 if ( 10942 disabled_modules[i].module == module && 10943 disabled_modules[i].source == source 10944 ) { 10945 disabled_modules.splice(i, 1); 10946 } 10947 } 10948 10949 $container.data("wcpt-disabled-modules", disabled_modules); 10950}; 10951 10952// util functions 10953(function ($) { 10954 window.wcpt_util = { 10955 get_table_id: ($wcpt) => { 10956 return $wcpt.attr("data-wcpt-table-id"); 10957 }, 10958 10959 get_current_page_number: ($wcpt) => { 10960 var params = wcpt_util.get_table_parsed_query($wcpt), 10961 table_id = wcpt_util.get_table_id($wcpt), 10962 sc_attrs = wcpt_util.get_sc_attrs($wcpt), 10963 current_page = params[`${table_id}_paged`] 10964 ? params[`${table_id}_paged`] 10965 : 1; 10966 current_page = parseInt(current_page, 10); 10967 10968 // Archive tables: fall back to the URL when PHP missed the page query var 10969 // (common on the WooCommerce shop page, which paginates via `page` not `paged`). 10970 if (current_page <= 1 && sc_attrs._archive) { 10971 if (wcpt_params.permalink_structure) { 10972 var regex = new RegExp( 10973 "\\/" + wcpt_params.pagination_slug + "\\/(\\d+)", 10974 ); 10975 var match = window.location.pathname.match(regex); 10976 if (match && match[1]) { 10977 current_page = parseInt(match[1], 10); 10978 } 10979 } else { 10980 var url_params = new URLSearchParams(window.location.search); 10981 if (url_params.has(wcpt_params.pagination_slug)) { 10982 current_page = parseInt( 10983 url_params.get(wcpt_params.pagination_slug), 10984 10, 10985 ); 10986 } 10987 } 10988 } 10989 10990 return current_page; 10991 }, 10992 10993 get_table_parsed_query: ($wcpt) => { 10994 var query_string = $wcpt.attr("data-wcpt-query-string"), 10995 _index = query_string.indexOf("?"), 10996 params = 10997 _index == -1 10998 ? {}
10999 : wcpt_util.parse_query_string(query_string.slice(_index + 1)); 11000 11001 return params; 11002 }, 11003 11004 parse_query_string: (query) => { 11005 // if first character is ? then remove it 11006 if (query.charAt(0) === "?") { 11007 query = query.slice(1); 11008 } 11009 11010 var vars = query.split("&"); 11011 var query_string = {}; 11012 for (var i = 0; i < vars.length; i++) { 11013 var pair = vars[i].split("="), 11014 key = decodeURIComponent(pair[0]), 11015 value = decodeURIComponent(pair[1]); 11016 11017 // If first entry with this name 11018 if (typeof query_string[key] === "undefined") { 11019 query_string[key] = value; 11020 11021 // If second entry with this name 11022 } else if (typeof query_string[key] === "string") { 11023 var arr = [query_string[key], value]; 11024 query_string[key] = arr; 11025 11026 // If third or later entry with this name 11027 } else { 11028 query_string[key].push(value); 11029 } 11030 } 11031 return query_string; 11032 }, 11033 11034 get_sc_attrs: ($container, refresh_cache) => { 11035 if (!refresh_cache) { 11036 if ($container.data("wcpt_sc_attrs")) { 11037 return $container.data("wcpt_sc_attrs"); 11038 } else { 11039 return {}; 11040 } 11041 } 11042 11043 var sc_attrs = {}; 11044 var sc_attrs_string = $container.attr("data-wcpt-sc-attrs"); 11045 if (sc_attrs_string && -1 == $.inArray(sc_attrs_string, ["[]", "{}"])) { 11046 sc_attrs = JSON.parse(sc_attrs_string); 11047 } 11048 $container.data("wcpt_sc_attrs", sc_attrs); // access sc attrs from here 11049 11050 return sc_attrs; 11051 }, 11052 11053 get_device: () => { 11054 // device 11055 var device = "laptop"; // default 11056 11057 if ($(window).width() <= wcpt_params.breakpoints.phone) { 11058 device = "phone"; 11059 } else if ($(window).width() <= wcpt_params.breakpoints.tablet) { 11060 device = "tablet"; 11061 } 11062 11063 return device; 11064 }, 11065 11066 get_uninit_rows: ($container, refresh_cache) => { 11067 if (!refresh_cache) { 11068 return $container.data("wcpt_new_rows"); 11069 } 11070 11071 var $new_rows = $container.find(".wcpt-row:not(.wcpt-row--init)"); 11072 $container.data("wcpt_new_rows", $new_rows); // access sc attrs from here 11073 11074 return $new_rows; 11075 }, 11076 11077 run_once_on_container: ($container, init_data_flag, callback) => { 11078 if (!$container.data(init_data_flag)) { 11079 callback($container); 11080 $container.data(init_data_flag, true); 11081 } 11082 }, 11083 11084 // legacy 11085 do_once_on_container: ($container, init_data_flag, callback) => { 11086 wcpt_util.run_once_on_container($container, init_data_flag, callback); 11087 }, 11088 11089 get_freeze_table: ($container) => { 11090 var $freeze_table = false; 11091 $container.find(".frzTbl-table").each(function () { 11092 var $this = $(this); 11093 if ($this.data("freezeTable")) { 11094 $freeze_table = $this; 11095 } 11096 }); 11097 return $freeze_table; 11098 }, 11099 11100 assign_even_odd_row_classes: ($table) => { 11101 var $rows = $( 11102 ".wcpt-row:not(.wcpt-child-row, .wcpt-row--category-heading):visible", 11103 $table, 11104 ), 11105 $child_rows = $(".wcpt-child-row", $table); 11106 11107 $rows.each(function () { 11108 var $this = $(this); 11109 11110 if ($rows.index($this) % 2) { 11111 $this.addClass("wcpt-even").removeClass("wcpt-odd"); 11112 } else { 11113 $this.addClass("wcpt-odd").removeClass("wcpt-even"); 11114 } 11115 }); 11116 11117 $child_rows.each(function () { 11118 var $this = $(this), 11119 $parent_row = $this.data("wcpt_parent_row"); 11120 11121 if ($parent_row.hasClass("wcpt-even")) { 11122 $this.removeClass("wcpt-odd").addClass("wcpt-even"); 11123 } else { 11124 // odd 11125 $this.removeClass("wcpt-even").addClass("wcpt-odd"); 11126 } 11127 }); 11128 }, 11129 11130 update_url: (query, $container) => { 11131 if (typeof window.history === "undefined") { 11132 return; 11133 } 11134 11135 // Get current URL and params 11136 const current_url = new URL(window.location.href); 11137 const new_params = new URLSearchParams(query); 11138 11139 // Start fresh with current non-table params 11140 const final_params = new URLSearchParams(); 11141 11142 // Copy over non-table related params from current URL
11143 current_url.searchParams.forEach((val, key) => { 11144 // Skip table-specific params (those with numeric prefix) 11145 if (!/^\d+_\w+/.test(key)) { 11146 // Only add if not in new_params 11147 if (!new_params.has(key)) { 11148 final_params.append(key, val); 11149 } 11150 } 11151 }); 11152 11153 // Add new params, filtering out unnecessary ones 11154 new_params.forEach((val, key) => { 11155 // Skip device params 11156 if (key.slice(-7) === "_device") { 11157 return; 11158 } 11159 11160 // Skip paged=1 params 11161 if (key.slice(-6) === "_paged" && val === "1") { 11162 return; 11163 } 11164 11165 // Add param (will override any existing) 11166 final_params.append(key, val); 11167 }); 11168 11169 // update pagination on archive pages 11170 const table_id = wcpt_util.get_table_id($container); 11171 const sc_attrs = wcpt_util.get_sc_attrs($container); 11172 11173 if (sc_attrs._archive) { 11174 // Archive page pagination (uses WP core pagination) 11175 const current_page = wcpt_util.get_current_page_number($container); 11176 11177 // if permalinks are like ?paged=2 then do that 11178 if (!wcpt_params.permalink_structure) { 11179 if (current_page > 1) { 11180 final_params.set("paged", current_page); 11181 } else { 11182 final_params.delete("paged"); 11183 } 11184 } else { 11185 if (current_page <= 1) { 11186 // Remove pagination from URL 11187 const regex = new RegExp( 11188 "\\/(?:page|" + wcpt_params.pagination_slug + ")\\/\\d+", 11189 ); 11190 current_url.pathname = current_url.pathname.replace(regex, ""); 11191 } else { 11192 // Check if URL already has /{pagination_slug}/ in it 11193 if ( 11194 current_url.pathname.includes( 11195 "/" + wcpt_params.pagination_slug + "/", 11196 ) 11197 ) { 11198 // Replace existing page number 11199 const regex = new RegExp( 11200 "\\/" + wcpt_params.pagination_slug + "\\/\\d+", 11201 ); 11202 current_url.pathname = current_url.pathname.replace( 11203 regex, 11204 "/" + wcpt_params.pagination_slug + "/" + current_page, 11205 ); 11206 } else { 11207 // Add /{pagination_slug}/N to end of URL before query params 11208 current_url.pathname = 11209 current_url.pathname.replace(/\/$/, "") + 11210 "/" + 11211 wcpt_params.pagination_slug + 11212 "/" + 11213 current_page; 11214 } 11215 } 11216 } 11217 11218 final_params.delete(`${table_id}_paged`); 11219 } 11220 11221 // Update URL 11222 current_url.search = final_params.toString(); 11223 11224 var data = { 11225 url: current_url.href, 11226 $container: $container, 11227 proceed: true, 11228 }; 11229 11230 $("body").trigger("wcpt_update_url", data); 11231 11232 if (data.proceed) { 11233 window.history.replaceState({}, "", data.url); 11234 } 11235 }, 11236 11237 remove_param_from_url: (param) => { 11238 if ( 11239 param && 11240 typeof window.history !== "undefined" && 11241 window.location.search 11242 ) { 11243 var url_object = new URL(window.location); 11244 url_object.searchParams.delete(param); 11245 history.replaceState({}, "", url_object.href); 11246 } 11247 }, 11248 11249 get_encrypted_attrs: ($container) => { 11250 const encrypted_attrs = {}; 11251 $container.each(function () { 11252 $.each(this.attributes, function () { 11253 if (this.specified && this.name.startsWith("data-wcpt-encrypted-")) { 11254 encrypted_attrs[this.name] = this.value; 11255 } 11256 }); 11257 }); 11258 return encrypted_attrs; 11259 }, 11260 11261 infinite_scroll_enabled: ($container) => { 11262 var sc_attrs = wcpt_util.get_sc_attrs($container); 11263 var device = wcpt_util.get_device($container); 11264 return sc_attrs[device + "_infinite_scroll"]; 11265 }, 11266 11267 merge_variable_switch_cf_from_response: (response, table_id) => { 11268 if (!response || !table_id) { 11269 return; 11270 } 11271 11272 var varName = "wcpt_" + table_id + "_variable_switch_cf", 11273 prefix = "var " + varName + " = ", 11274 text = typeof response === "string" ? response : "", 11275 idx = text.indexOf(prefix), 11276 jsonStr, 11277 newData; 11278 11279 if (idx === -1) { 11280 return; 11281 } 11282 11283 jsonStr = text.substring(idx + prefix.length).trim(); 11284 11285 if (jsonStr.charAt(jsonStr.length - 1) === ";") { 11286 jsonStr = jsonStr.slice(0, -1).trim(); 11287 } 11288 11289 idx = jsonStr.indexOf("</script>"); 11290 if (idx !== -1) { 11291 jsonStr = jsonStr.substring(0, idx).trim(); 11292 if (jsonStr.charAt(jsonStr.length - 1) === ";") { 11293 jsonStr = jsonStr.slice(0, -1).trim(); 11294 } 11295 } 11296
11297 try { 11298 newData = JSON.parse(jsonStr); 11299 } catch (e) { 11300 return; 11301 } 11302 11303 if (!newData || typeof newData !== "object") { 11304 return; 11305 } 11306 11307 if (typeof window[varName] === "undefined") { 11308 window[varName] = newData; 11309 return; 11310 } 11311 11312 $.each(newData, function (element_id, product_vals) { 11313 if ( 11314 typeof window[varName][element_id] === "undefined" || 11315 window[varName][element_id] === null 11316 ) { 11317 window[varName][element_id] = product_vals; 11318 } else if (product_vals && typeof product_vals === "object") { 11319 $.extend(window[varName][element_id], product_vals); 11320 } 11321 }); 11322 }, 11323 11324 // Merge price / availability <script type="text/template"> nodes from an 11325 // AJAX response into the live table. Needed for paginate-only swaps and 11326 // infinite scroll, which only append rows and would otherwise leave 11327 // variable-switch price resets without a template (blank prices). 11328 merge_variable_switch_templates_from_response: ($source, $container) => { 11329 if (!$source || !$source.length || !$container || !$container.length) { 11330 return; 11331 } 11332 11333 var $target_scope = $container.find(".wcpt-nav-main").first(); 11334 if (!$target_scope.length) { 11335 $target_scope = $container; 11336 } 11337 11338 $source 11339 .find( 11340 "script[data-wcpt-price-type], script[data-wcpt-availability-message]", 11341 ) 11342 .addBack( 11343 "script[data-wcpt-price-type], script[data-wcpt-availability-message]", 11344 ) 11345 .each(function () { 11346 var $tpl = $(this), 11347 element_id = $tpl.attr("data-wcpt-element-id"), 11348 price_type = $tpl.attr("data-wcpt-price-type"), 11349 availability_message = $tpl.attr("data-wcpt-availability-message"), 11350 selector; 11351 11352 if (!element_id) { 11353 return; 11354 } 11355 11356 if (price_type) { 11357 selector = 11358 'script[data-wcpt-element-id="' + 11359 element_id + 11360 '"][data-wcpt-price-type="' + 11361 price_type + 11362 '"]'; 11363 } else if (availability_message) { 11364 selector = 11365 'script[data-wcpt-element-id="' + 11366 element_id + 11367 '"][data-wcpt-availability-message="' + 11368 availability_message + 11369 '"]'; 11370 } else { 11371 return; 11372 } 11373 11374 if (!$container.find(selector).length) { 11375 $target_scope.append($tpl.clone()); 11376 } 11377 }); 11378 }, 11379 11380 format_price_figure: (price) => { 11381 price = parseFloat(price); 11382 11383 // remove decimal if it is unnecessary ie .00 11384 if (price !== parseInt(price)) { 11385 price = parseFloat(price).toFixed(wcpt_params.price_decimals); 11386 } else { 11387 price = parseInt(price); 11388 } 11389 11390 // decimal separator 11391 price = (price + "").replace(".", wcpt_params.price_decimal_separator); 11392 11393 // thousands separator 11394 var decimal_split = price.split(wcpt_params.price_decimal_separator); 11395 ((non_decimal = decimal_split[0].toString()), 11396 (decimal = decimal_split[1])); 11397 11398 if (non_decimal.length > 3) { 11399 formatted_non_decimal = 11400 non_decimal.slice(0, -3) + 11401 wcpt_params.price_thousand_separator + 11402 non_decimal.slice(-3); 11403 price = 11404 formatted_non_decimal + 11405 (decimal ? wcpt_params.price_decimal_separator + decimal : ""); 11406 } 11407 11408 return price; 11409 }, 11410 11411 reload_table: ($container) => { 11412 var $wcpt = $container.hasClass("wcpt") 11413 ? $container 11414 : $container.closest(".wcpt"); 11415 if (!$wcpt.length) return; 11416 var table_id = $wcpt.attr("data-wcpt-table-id"); 11417 // Clear cache for this table so reload fetches fresh data 11418 if (window.wcpt_cache && window.wcpt_cache.data) { 11419 $.each(window.wcpt_cache.data, function (key) { 11420 if ( 11421 key.indexOf("?" + table_id + "_") !== -1 || 11422 key.indexOf("&" + table_id + "_") !== -1 11423 ) { 11424 delete window.wcpt_cache.data[key]; 11425 } 11426 }); 11427 } 11428 // Reload with all current parameters (same as refresh_table flow) 11429 window.wcpt_attempt_ajax($wcpt, "", true, "refresh_table"); 11430 }, 11431 }; 11432})(jQuery); 11433 11434// Create a new event called wcptPressing 11435(function ($) { 11436 const holdDelay = 300; 11437 const moveThreshold = 10; 11438 const holdingInterval = 100;
11439 const activeHolds = new Map(); 11440 let currentMouseTarget = null; // Track the element where mousedown started 11441 11442 // Helper function to handle hold start 11443 function startHold(element, holdData) { 11444 const holdTimer = setTimeout(() => { 11445 const hold = activeHolds.get(holdData.target); 11446 if (hold) { 11447 element.trigger("wcptTouchHoldStart"); 11448 hold.hasStarted = true; 11449 11450 const holdingTimer = setInterval(() => { 11451 if (hold.hasStarted) { 11452 element.trigger("wcptPressing"); 11453 } 11454 }, holdingInterval); 11455 hold.holdingTimer = holdingTimer; 11456 } 11457 }, holdDelay); 11458 11459 activeHolds.set(holdData.target, { 11460 element, 11461 holdTimer, 11462 holdingTimer: null, 11463 startY: holdData.startY, 11464 hasStarted: false, 11465 }); 11466 } 11467 11468 // Helper function to handle hold end 11469 function endHold(target) { 11470 const hold = activeHolds.get(target); 11471 if (hold) { 11472 clearTimeout(hold.holdTimer); 11473 clearInterval(hold.holdingTimer); 11474 if (hold.hasStarted) { 11475 hold.element.trigger("wcptTouchHoldStop"); 11476 } 11477 activeHolds.delete(target); 11478 } 11479 } 11480 11481 // Touch Events 11482 $(document).on("touchstart", function (e) { 11483 const element = $(e.target); 11484 startHold(element, { 11485 target: e.target, 11486 startY: e.originalEvent.touches[0].clientY, 11487 }); 11488 }); 11489 11490 $(document).on("touchmove", function (e) { 11491 const touch = e.originalEvent.touches[0]; 11492 const hold = activeHolds.get(e.target); 11493 11494 if (hold) { 11495 const moveY = Math.abs(touch.clientY - hold.startY); 11496 if (moveY > moveThreshold) { 11497 endHold(e.target); 11498 } 11499 } 11500 }); 11501 11502 $(document).on("touchend touchcancel", function (e) { 11503 endHold(e.target); 11504 }); 11505 11506 // Mouse Events 11507 $(document).on("mousedown", function (e) { 11508 currentMouseTarget = e.target; // Store the initial target 11509 const element = $(currentMouseTarget); 11510 startHold(element, { 11511 target: currentMouseTarget, 11512 startY: e.clientY, 11513 }); 11514 }); 11515 11516 $(document).on("mousemove", function (e) { 11517 if (currentMouseTarget) { 11518 // Only check if we have an active mousedown 11519 const hold = activeHolds.get(currentMouseTarget); 11520 if (hold) { 11521 const moveY = Math.abs(e.clientY - hold.startY); 11522 if (moveY > moveThreshold) { 11523 endHold(currentMouseTarget); 11524 currentMouseTarget = null; 11525 } 11526 } 11527 } 11528 }); 11529 11530 $(document).on("mouseup", function (e) { 11531 if (currentMouseTarget) { 11532 endHold(currentMouseTarget); 11533 currentMouseTarget = null; 11534 } 11535 }); 11536 11537 // Handle cases where mouse leaves the document 11538 $(document).on("mouseleave", function (e) { 11539 if (currentMouseTarget) { 11540 endHold(currentMouseTarget); 11541 currentMouseTarget = null; 11542 } 11543 }); 11544})(jQuery); 11545 11546// woocommerce mini-cart block integration with MutationObserver 11547(function ($) { 11548 // Set up MutationObserver to watch for changes to data-cart-totals 11549 const miniCartElement = document.querySelector(".wc-block-mini-cart"); 11550 11551 if (miniCartElement) { 11552 // Keep track of the previous cart totals to detect actual changes 11553 let previousCartTotals = miniCartElement.getAttribute("data-cart-totals"); 11554 11555 // Create a new observer 11556 const observer = new MutationObserver(function (mutations) { 11557 mutations.forEach(function (mutation) { 11558 if ( 11559 mutation.type === "attributes" && 11560 mutation.attributeName === "data-cart-totals" 11561 ) { 11562 const newCartTotals = 11563 miniCartElement.getAttribute("data-cart-totals"); 11564 11565 // Only run wcpt_cart if the data-cart-totals value has changed 11566 if (newCartTotals !== previousCartTotals) { 11567 previousCartTotals = newCartTotals; 11568 if (typeof wcpt_cart === "function") { 11569 wcpt_cart(); 11570 } 11571 } 11572 } 11573 }); 11574 }); 11575 11576 // Start observing the mini cart element for attribute changes 11577 observer.observe(miniCartElement, { 11578 attributes: true, 11579 attributeFilter: ["data-cart-totals"], // Only watch the data-cart-totals attribute 11580 });
11581 } 11582 11583 function refreshCartStore() { 11584 if (window.wp && window.wp.data) { 11585 // Option 1: Invalidate the cart store's resolution cache and force a refresh 11586 const cartStore = window.wp.data.dispatch("wc/store/cart"); 11587 if (cartStore) { 11588 // First invalidate any cached data 11589 if (cartStore.invalidateResolutionForStore) { 11590 cartStore.invalidateResolutionForStore(); 11591 } 11592 11593 // Force a refresh by requesting cart data again 11594 window.wp.data.select("wc/store/cart").getCartData(true); // true forces bypass of cache 11595 11596 return true; 11597 } 11598 11599 // Option 2: Alternative approach using resolveSelect if the above doesn't work 11600 const resolver = 11601 window.wp.data && 11602 window.wp.data.controls && 11603 window.wp.data.controls.resolveSelect; 11604 if (resolver) { 11605 resolver("wc/store/cart", "getCartData", [true]); // Force refresh 11606 return true; 11607 } 11608 } 11609 } 11610 11611 $(document).on("wcpt_cart", function (e, data) { 11612 const wp = window.wp; 11613 if (!wp || !wp.data) return; 11614 11615 const cartStore = wp.data.select("wc/store/cart"); 11616 if (!cartStore || !cartStore.getCartData) return; 11617 11618 const cartData = cartStore.getCartData(); 11619 if (!cartData || !cartData.items) return; 11620 11621 const totalQuantity = cartData.items.reduce((total, item) => { 11622 return total + (item.quantity || 0); 11623 }, 0); 11624 11625 if (data.cart_quantity != totalQuantity) { 11626 refreshCartStore(); 11627 } 11628 }); 11629 11630 // window.wcptRefreshMiniCart = refreshMiniCart; 11631})(jQuery); 11632 11633// WP MediaElementJS audio player playing HTML class for WCPT tables 11634(function ($) { 11635 // Handle audio player initialization after WCPT content loads 11636 $("body").on("wcpt_after_every_load", ".wcpt", function () { 11637 var $new_rows = wcpt_util.get_uninit_rows($(this), true); 11638 11639 // Find all audio players in the newly loaded rows 11640 var $mejs_players = $new_rows.find(".wcpt-audio-player .mejs-container"); 11641 // Initialize each audio player 11642 initMejsPlayers($mejs_players); 11643 }); 11644 11645 // Helper function to initialize audio players 11646 function initMejsPlayers($mejs_players) { 11647 $mejs_players.each(function () { 11648 var $player = $(this); 11649 var playingClass = "wcpt-mejs-playing"; 11650 11651 // Get the native media element 11652 var mediaElement = $player.find("audio")[0]; 11653 11654 // For existing MediaElement.js instances 11655 if (mediaElement.player) { 11656 attachMediaEvents(mediaElement, $player, playingClass); 11657 } 11658 // For players that haven't been initialized yet 11659 else { 11660 // Wait for MediaElement to initialize 11661 $(document).on("MEJSready", function (e, mejsObj) { 11662 // Check if this is our audio element 11663 if (mejsObj && mejsObj.media === mediaElement) { 11664 attachMediaEvents(mediaElement, $player, playingClass); 11665 } 11666 }); 11667 11668 // Fallback to native media events 11669 attachMediaEvents(mediaElement, $player, playingClass); 11670 } 11671 }); 11672 } 11673 11674 // Helper function to attach media event handlers 11675 function attachMediaEvents(mediaElement, $player, playingClass) { 11676 if (!mediaElement || !$player.length) return; 11677 11678 // Add event listeners 11679 mediaElement.addEventListener("play", handlePlay); 11680 mediaElement.addEventListener("pause", handlePause); 11681 mediaElement.addEventListener("ended", handleEnded); 11682 11683 // Named event handlers so we can remove them if needed 11684 function handlePlay() { 11685 $player.addClass(playingClass); 11686 } 11687 11688 function handlePause() { 11689 $player.removeClass(playingClass); 11690 } 11691 11692 function handleEnded() { 11693 $player.removeClass(playingClass); 11694 } 11695 } 11696})(jQuery); 11697 11698// module: apply / reset navigation buttons 11699(function ($) { 11700 // apply button 11701 // -- trigger change 11702 $("body").on("click", ".wcpt-apply", function () { 11703 var $this = $(this); 11704 $this.addClass("wcpt-apply--clicked"); 11705 $(this).trigger("change"); 11706 }); 11707 // -- stop navigation if change came from anything else 11708 $("body").on("wcpt_after_navigation_change", ".wcpt", function (e, data) { 11709 var $container = $(this), 11710 sc_attrs = wcpt_util.get_sc_attrs($container), 11711 $apply = $(".wcpt-apply", $container); 11712 11713 if (!$apply.length) { 11714 return; 11715 } 11716 11717 // don't interrup: 11718 // -- form mode with adaptive filters 11719 if ( 11720 sc_attrs.form_mode && 11721 (sc_attrs.dynamic_filters_lazy_load || 11722 sc_attrs.dynamic_hide_filters || 11723 sc_attrs.dynamic_recount) 11724 ) { 11725 return; 11726 } 11727 // -- inputs marked for immediate filter 11728 if ( 11729 $(data.event.target).closest('[data-wcpt-nav-apply-immediately="true"]') 11730 .length 11731 ) { 11732 return; 11733 } 11734
11735 if (!$container.find(".wcpt-apply--clicked").length) { 11736 data.proceed = false; 11737 } 11738 }); 11739})(jQuery); 11740 11741// module: dynamic filters 11742(function ($) { 11743 // filters lazy load 11744 $("body").on("wcpt_after_every_load", ".wcpt", function () { 11745 var $container = $(this), 11746 sc_attrs = wcpt_util.get_sc_attrs($container), 11747 last_ajax_purpose = $container.data("wcpt_last_ajax_purpose"); 11748 11749 // check for shortcode attrs 11750 if ( 11751 last_ajax_purpose === "paginate" || 11752 !sc_attrs.dynamic_filters_lazy_load || 11753 (!sc_attrs.dynamic_recount && !sc_attrs.dynamic_hide_filters) 11754 ) { 11755 return; 11756 } 11757 11758 var encrypted_cache = $container.attr( 11759 "data-wcpt-encrypted-dynamic-filters-lazy-load-cache", 11760 ); 11761 11762 $.ajax({ 11763 url: wcpt_params.wc_ajax_url.replace( 11764 "%%endpoint%%", 11765 "wcpt__dynamic_filter__lazy_load", 11766 ), 11767 method: "POST", 11768 data: { 11769 wcpt_dynamic_filter_encrypted_cache: encrypted_cache, 11770 }, 11771 success: function (result) { 11772 var result = JSON.parse(result); 11773 11774 // append style and script 11775 $(result.style + result.script).appendTo($container); 11776 11777 var $filters = $(".wcpt-filter", $container); 11778 11779 var target_filters = [ 11780 "category", 11781 "attribute", 11782 "availability", 11783 "on_sale", 11784 "taxonomy", 11785 ]; 11786 11787 if ($container.data("wcpt_sc_attrs").dynamic_recount) { 11788 $filters.each(function () { 11789 var $filter = $(this), 11790 filter_type = $filter.attr("data-wcpt-filter"); 11791 if (!target_filters.includes(filter_type)) return true; 11792 11793 var taxonomy = false; 11794 if (["taxonomy", "attribute"].includes(filter_type)) { 11795 taxonomy = $filter.attr("data-wcpt-taxonomy"); 11796 } 11797 11798 result.options.forEach(function (option) { 11799 if (option.filter !== filter_type) return; 11800 if (taxonomy && option.taxonomy !== taxonomy) return; 11801 11802 var $option = $( 11803 '[data-wcpt-value="' + option.value + '"]', 11804 $filter, 11805 ); 11806 if (!$option.length) return; 11807 11808 var count = 11809 '<span class="wcpt-count">(' + option.count + ")</span>", 11810 $label = $( 11811 'label[data-wcpt-value="' + option.value + '"]', 11812 $filter, 11813 ), 11814 $icon = $label.children(".wcpt-icon"), 11815 $prev_count = $(".wcpt-count", $label); 11816 11817 $prev_count.remove(); // in case duplicate taxonomy filter is printed 11818 11819 if ($icon.length) { 11820 $icon.before(count); 11821 } else { 11822 $label.append(count); 11823 } 11824 }); 11825 }); 11826 } 11827 11828 $filters.removeClass("wcpt--dynamic-filters--loading-filter"); 11829 }, 11830 }); 11831 }); 11832})(jQuery); 11833 11834// module: connect with archive search 11835(function ($) { 11836 $("body").on("wcpt_before_ajax_query", function (e, data) { 11837 var $archive_search = data.$wcpt.find( 11838 ".wcpt-search--connect-with-archive input[type='search']", 11839 ); 11840 11841 if ($archive_search.length) { 11842 var current_keyword = $archive_search.val().trim(); 11843 var previous_keyword = wcpt_util.get_sc_attrs(data.$wcpt)._search; 11844 11845 if (current_keyword !== previous_keyword) { 11846 // redirect to archive search 11847 window.location.href = 11848 wcpt_params.shop_url + "?s=" + current_keyword + "&post_type=product"; 11849 data.proceed = false; 11850 } else { 11851 var search_param_name = $archive_search.attr("name"); 11852 var regex = new RegExp(search_param_name + "=[^&]*"); 11853 data.query = data.query.replace(regex, ""); 11854 } 11855 } 11856 }); 11857})(jQuery); 11858 11859// module: column stacking 11860(function ($) { 11861 // Remove all stacking markers and stacked values before reapplying 11862 function reset_column_stacking($container) { 11863 $container.find(".wcpt-cell-val--stacked").remove(); 11864 $container 11865 .find(".wcpt-cell--stacking,.wcpt-stack--hide") 11866 .removeClass("wcpt-cell--stacking wcpt-stack--hide"); 11867 } 11868 11869 function get_stacking_params($container) { 11870 var device = wcpt_util.get_device(); 11871 var last_device = $container.data("wcpt-stacking-last-device"); 11872 11873 if (last_device === device) { 11874 return false; 11875 } 11876 $container.data("wcpt-stacking-last-device", device); 11877 11878 var sc_attrs = wcpt_util.get_sc_attrs($container); 11879 var stacking_params = sc_attrs[device + "_stack_columns"]; 11880 return stacking_params 11881 ? stacking_params.split("|").map(function (name) { 11882 return name.trim(); 11883 }) 11884 : []; 11885 } 11886 11887 function stack_columns($container, stacking_params) { 11888 reset_column_stacking($container); 11889 if (!stacking_params.length) return; 11890 11891 // Get the name of the first column, so we can skip hiding its heading 11892 var $headingRow = $container.find(".wcpt-heading-row"); 11893 var $headingThs = $headingRow.children("th.wcpt-heading"); 11894 var firstColName = ( 11895 $headingThs.eq(0).attr("data-wcpt-column-name") || "" 11896 ).trim(); 11897 11898 // Hide the heading if it's a stacking column, except for the first column 11899 $headingThs.each(function (i) { 11900 var $th = $(this); 11901 var colName = ($th.attr("data-wcpt-column-name") || "").trim();
11902 // Do not hide heading if it's the first column, even if stacking includes it 11903 if (colName === firstColName) { 11904 return; // skip hiding the first heading 11905 } 11906 if (stacking_params.indexOf(colName) !== -1) { 11907 $th.addClass("wcpt-heading--stacking wcpt-stack--hide"); 11908 } 11909 }); 11910 11911 // For each row, merge stacked columns into previous column 11912 $container.find(".wcpt-row").each(function () { 11913 var $row = $(this); 11914 var $cells = $row.children("td.wcpt-cell"); 11915 // If stacking on the first column, just skip: don't stack or hide it 11916 for (var idx = $cells.length - 1; idx > 0; idx--) { 11917 var $cell = $($cells[idx]); 11918 var $prevCell = $($cells[idx - 1]); 11919 var colName = ($cell.attr("data-wcpt-column-name") || "").trim(); 11920 11921 // Do not apply stacking if this is the first column (idx==0 is never reached by this loop) 11922 if (colName === firstColName) { 11923 continue; // skip stacking and hiding for first column 11924 } 11925 11926 if (stacking_params.indexOf(colName) !== -1) { 11927 $cell.addClass("wcpt-cell--stacking wcpt-stack--hide"); 11928 // Clone contents to move 11929 var $val = $cell.children(".wcpt-cell-val").first(); 11930 if ($val.length && $prevCell.length) { 11931 // Create new stacked container and append the content (prevent duplicate IDs, avoid events) 11932 var $stackedDiv = $( 11933 "<div class='wcpt-cell-val wcpt-cell-val--stacked'></div>", 11934 ); 11935 $stackedDiv.append($val.contents().clone(true, true)); 11936 // Append to previous cell's .wcpt-cell-val 11937 var $prevVal = $prevCell.children(".wcpt-cell-val").first(); 11938 if ($prevVal.length) { 11939 $prevVal.append($stackedDiv); 11940 } 11941 } 11942 } 11943 } 11944 }); 11945 } 11946 11947 // On layout called, apply stacking 11948 $(document).on("wcpt_layout", ".wcpt", function (e, data) { 11949 var $wcpt = $(this); 11950 var stacking_params = get_stacking_params($wcpt); 11951 if (!stacking_params) return; 11952 stack_columns($wcpt, stacking_params); 11953 }); 11954})(jQuery); 11955 11956//module: list layout 11957(function ($) { 11958 $(document).on("wcpt_layout", ".wcpt", function () { 11959 var $container = $(this); 11960 11961 if (!$container.hasClass("wcpt-list-view")) { 11962 return; 11963 } 11964 11965 var table_id = wcpt_util.get_table_id($container), 11966 $table = $container.find(".wcpt-table"), 11967 $first_row = $table.find( 11968 "tbody tr:not(.wcpt-row--category-heading):first", 11969 ), 11970 css = ""; 11971 11972 if (!$table.length) { 11973 return; 11974 } 11975 11976 // remove existing css first 11977 $container.find("style.wcpt-list-view-column-widths").remove(); 11978 11979 // revert list to tabel mode to get dimensions 11980 $container.addClass("wcpt-list-view--calc"); 11981 11982 // get widths and vertical/horizontal alignment from first row cells 11983 var total_width = $first_row.outerWidth(); 11984 $first_row.find("td").each(function (index) { 11985 var width = ($(this).outerWidth() / total_width) * 100; 11986 var verticalAlign = $(this).css("vertical-align") || "middle"; 11987 var textAlign = $(this).css("text-align"); 11988 11989 var alignCSS = ""; 11990 // vertical alignment 11991 if (verticalAlign === "top") { 11992 alignCSS = "justify-content: flex-start;"; 11993 } else if (verticalAlign === "bottom") { 11994 alignCSS = "justify-content: flex-end;"; 11995 } else { 11996 alignCSS = "justify-content: center;"; 11997 } 11998 11999 // // horizontal alignment 12000 // if (textAlign === "left") { 12001 // alignCSS += " justify-content: flex-start;"; 12002 // } else if (textAlign === "right") { 12003 // alignCSS += " justify-content: flex-end;"; 12004 // } else if (textAlign === "center") { 12005 // alignCSS += " justify-content: center;"; 12006 // } 12007 12008 css += 12009 "#wcpt-" + 12010 table_id + 12011 ".wcpt-list-view :is(.wcpt-row, .wcpt-heading-row):not(.wcpt-row--category-heading) :is(.wcpt-cell, .wcpt-heading):nth-child(" + 12012 (index + 1) + 12013 ") { width: " + 12014 width.toFixed(2) + 12015 "% !important; " + 12016 alignCSS + 12017 " }\n"; 12018 });
12019 12020 // get border spacing between rows 12021 var border_spacing = parseInt($table.css("border-spacing").split(" ")[1]); 12022 css += 12023 "#wcpt-" + 12024 table_id + 12025 ".wcpt-list-view :is(.wcpt-row:not(:last-child), .wcpt-heading-row) { margin-bottom: " + 12026 border_spacing + 12027 "px; }\n"; 12028 12029 // add new css 12030 var $style = $('<style class="wcpt-list-view-column-widths"></style>'); 12031 $container.append($style); 12032 $style.html(css); 12033 12034 $container.removeClass("wcpt-list-view--calc"); 12035 }); 12036})(jQuery); 12037 12038// module: input height & width match with add to cart button 12039(function ($) { 12040 // range submit button height match 12041 $("body").on("wcpt_after_every_load", ".wcpt", function () { 12042 const $container = $(this), 12043 $range_submit_button = $container.find( 12044 ".wcpt-range-submit-button, .wcpt-date-picker-submit-button", 12045 ); 12046 12047 $range_submit_button.each(function () { 12048 const $button = $(this); 12049 const height = $button.siblings(".wcpt-range-input-min").outerHeight(); 12050 $button.outerHeight(height); 12051 }); 12052 }); 12053 12054 // quantity element size match with cart button 12055 $("body").on("wcpt_layout", ".wcpt", function () { 12056 const $container = $(this), 12057 $row = $container.find(".wcpt-row"); 12058 12059 $row.each(function () { 12060 const $row = $(this), 12061 $quantity_elements = $row.find(".wcpt-quantity"), 12062 $cart_button_elements = $row.find( 12063 ".wcpt-button[data-wcpt-link-code*='cart']", 12064 ); 12065 12066 if ($quantity_elements.length && $cart_button_elements.length) { 12067 $quantity_elements.each(function () { 12068 const $quantity = $(this); 12069 if ($quantity.hasClass("wcpt-cart-button-height-match")) { 12070 const height = $cart_button_elements.outerHeight(); 12071 $quantity.outerHeight(height); 12072 $quantity.find("select").outerHeight(height); 12073 } 12074 if ($quantity.hasClass("wcpt-cart-button-width-match")) { 12075 const width = $cart_button_elements.outerWidth(); 12076 $quantity 12077 .outerWidth(width) 12078 .css("box-sizing", "border-box !important"); 12079 $quantity.find("select").outerWidth(width); 12080 } 12081 }); 12082 } 12083 }); 12084 }); 12085})(jQuery); 12086 12087// module: search option above filter menu 12088(function ($) { 12089 $("body").on("wcpt_layout", ".wcpt", function () { 12090 var $this = $(this), 12091 $nav_dropdown = $(".wcpt-dropdown.wcpt-filter", $this); 12092 12093 $nav_dropdown.each(function () { 12094 var $this = $(this), 12095 $menu = $(".wcpt-dropdown-menu", $this); 12096 12097 if ( 12098 $this.hasClass("wcpt-filter--search-filter-options-enabled") && 12099 !$(".wcpt-search-filter-options", $this).length 12100 ) { 12101 var placeholder = $this.attr( 12102 "data-wcpt-search-filter-options-placeholder", 12103 ); 12104 12105 $menu.prepend( 12106 '<input type="search" class="wcpt-search-filter-options" placeholder="' + 12107 placeholder + 12108 '" />', 12109 ); 12110 12111 $menu.css("max-height", "none"); 12112 12113 $(".wcpt-search-filter-options", $menu) 12114 .nextAll() 12115 .wrapAll( 12116 $( 12117 '<div class="wcpt-search-filter-option-set" style="max-height: ' + 12118 $menu.css("max-height") + 12119 ';"></div>', 12120 ), 12121 ); 12122 } 12123 12124 // Always set max-height for option set 12125 var $optionSet = $(".wcpt-search-filter-option-set", $menu); 12126 if ($optionSet.length) { 12127 $menu.css("max-height", ""); 12128 $optionSet.css("max-height", $menu.css("max-height")); 12129 $menu.css("max-height", "none"); 12130 } 12131 }); 12132 }); 12133})(jQuery); 12134 12135// module: auto select products - add to cart checkbox 12136(function ($) { 12137 $("body").on("wcpt_after_every_load", ".wcpt", function () { 12138 const $container = $(this), 12139 sc_attrs = wcpt_util.get_sc_attrs($container); 12140 12141 if (!sc_attrs.auto_select_products) { 12142 return; 12143 } 12144 12145 const $checkbox = $container.find(".wcpt-cart-checkbox"); 12146 12147 $checkbox.prop("checked", true).trigger("change"); 12148 }); 12149})(jQuery); 12150 12151// module: pagination 12152(function ($) { 12153 // infinite scroll remove pagination archive slug & table param 12154 $("body").on("wcpt_update_url", function (e, data) { 12155 var $container = data.$container, 12156 table_id = wcpt_util.get_table_id($container), 12157 sc_attrs = wcpt_util.get_sc_attrs($container), 12158 page_param_key = table_id + "_paged", 12159 pagination_slug = wcpt_params.pagination_slug; 12160 12161 if (!wcpt_util.infinite_scroll_enabled($container)) { 12162 return; 12163 } 12164 12165 // Get current URL 12166 var current_url = new URL(data.url); 12167 var pathname = current_url.pathname; 12168 var search = current_url.search; 12169 12170 // Remove any pagination slug from URL 12171 if (sc_attrs && sc_attrs._archive) { 12172 // Remove WP core pagination slug from pathname 12173 pathname = pathname.replace( 12174 new RegExp("/" + pagination_slug + "/\\d+/?"), 12175 "/", 12176 ); 12177 12178 // Remove WP core pagination param from search 12179 search = search.replace( 12180 new RegExp("[?&]" + pagination_slug + "=\\d+"), 12181 "", 12182 ); 12183 } 12184 12185 // Remove custom pagination from search 12186 search = search.replace(new RegExp("[?&]" + page_param_key + "=\\d+"), ""); 12187 12188 data.url = pathname + search; 12189 }); 12190 12191 // reset pagination when ajax is disabled 12192 $("body").on("wcpt_before_nav_redirect", function (e, data) { 12193 if (data.purpose !== "paginate") { 12194 // If not paginating, need to reset to page 1 12195 var $container = data.$container, 12196 table_id = wcpt_util.get_table_id($container), 12197 sc_attrs = wcpt_util.get_sc_attrs($container), 12198 page_param_key = table_id + "_paged", 12199 pagination_slug = wcpt_params.pagination_slug; 12200 12201 // Remove any pagination slug from base_url 12202 if (sc_attrs._archive) { 12203 // Remove WP core pagination slug 12204 data.base_url = data.base_url.replace( 12205 new RegExp("/" + pagination_slug + "/\\d+/?"), 12206 "/", 12207 ); 12208 // Remove WP core pagination param 12209 data.query = data.query.replace( 12210 new RegExp("[?&]" + pagination_slug + "=\\d+"), 12211 "", 12212 ); 12213 } 12214 12215 // Remove custom pagination 12216 data.query = data.query.replace( 12217 new RegExp("[?&]" + page_param_key + "=\\d+"), 12218 "", 12219 ); 12220 } 12221 }); 12222 12223 // apply pagination - ajax disabled / enabled / archive 12224 $("body").on( 12225 "click",
12226 ".wcpt-pagination .page-numbers:not(.dots):not(.current)", 12227 function (e) { 12228 e.preventDefault(); 12229 var $this = $(this), 12230 $container = $this.closest(".wcpt"), 12231 table_id = wcpt_util.get_table_id($container), 12232 page_param_key = table_id + "_paged", 12233 sc_attrs = wcpt_util.get_sc_attrs($container), 12234 url = $this.attr("href"), 12235 pagination_slug = wcpt_params.pagination_slug, // 'page' or 'paged' 12236 page = 1, // Default to page 1 12237 query = "", 12238 append = true; 12239 12240 if (sc_attrs._archive) { 12241 if (sc_attrs.disable_ajax) { 12242 window.location.href = url; 12243 return; 12244 } 12245 12246 // Archive page pagination (uses WP core pagination) unless table delivered over AJAX 12247 if (wcpt_params.permalink_structure) { 12248 // Pretty permalinks: /shop/page/2/ 12249 var regex = new RegExp("/" + pagination_slug + "/(\\d+)"); 12250 var match = url.match(regex); 12251 if (match && match[1]) { 12252 page = parseInt(match[1], 10); 12253 } 12254 } else { 12255 // Plain permalinks: /?paged=2 12256 var index = url.indexOf("?"); 12257 var params = 12258 index === -1 12259 ? false 12260 : wcpt_util.parse_query_string(url.slice(index + 1)); 12261 if (params && params[pagination_slug]) { 12262 page = parseInt(params[pagination_slug], 10); 12263 } 12264 } 12265 } 12266 12267 if (!sc_attrs._archive || page == 1) { 12268 // Standard table pagination - uses custom query var like /?123_paged=2 12269 var index = url.indexOf("?"); 12270 var params = 12271 index === -1 12272 ? false 12273 : wcpt_util.parse_query_string(url.slice(index + 1)); 12274 12275 if (params && params[page_param_key]) { 12276 page = parseInt(params[page_param_key], 10); 12277 } 12278 } 12279 query = page_param_key + "=" + page; 12280 12281 window.wcpt_attempt_ajax($container, query, append, "paginate"); 12282 }, 12283 ); 12284})(jQuery); 12285 12286// module: persistent add-to-cart checkbox state across pages 12287(function ($) { 12288 // --------------------------------------------------------------------------- 12289 // state 12290 // --------------------------------------------------------------------------- 12291 12292 var memoryState = {}; 12293 var pendingRestoreState = {}; 12294 var suppressVariationPersistMutations = false; 12295 var isRestoring = false; 12296 12297 function getState() { 12298 return memoryState; 12299 } 12300 12301 function publishState() { 12302 window.wcpt_cart_checked_state = memoryState; 12303 } 12304 12305 // --------------------------------------------------------------------------- 12306 // helpers 12307 // --------------------------------------------------------------------------- 12308 12309 var QTY_SELECTOR = "input.qty, select.wcpt-qty-select"; 12310 12311 function getPersistedQtyInput($rowElements) { 12312 var $wcptQty = $rowElements 12313 .find(".wcpt-quantity input.qty, .wcpt-quantity select.wcpt-qty-select") 12314 .first(); 12315 12316 if ($wcptQty.length) { 12317 return $wcptQty; 12318 } 12319 12320 var $formQty = $rowElements.find(".variations_form input.qty").first(); 12321 if ($formQty.length) { 12322 return $formQty; 12323 } 12324 12325 return $rowElements.find(QTY_SELECTOR).first(); 12326 } 12327 12328 function rowKey($row) { 12329 return typeof window.wcpt_get_row_key === "function" 12330 ? window.wcpt_get_row_key($row) || "" 12331 : ""; 12332 } 12333 12334 function getRowElements($row) { 12335 if (typeof window.wcpt_get_sibling_rows === "function") { 12336 var $siblings = window.wcpt_get_sibling_rows($row); 12337 if ($siblings && $siblings.length) { 12338 return $siblings; 12339 } 12340 } 12341 return $row; 12342 } 12343 12344 function getPrimaryRow($row) { 12345 if (typeof window.wcpt_get_original_row === "function") { 12346 var $original = window.wcpt_get_original_row($row); 12347 if ($original && $original.length) { 12348 return $original; 12349 } 12350 } 12351 if ($row.closest("table").hasClass("frzTbl-clone-table")) { 12352 return $(); 12353 } 12354 return $row; 12355 } 12356 12357 function resolvePrimaryRow($row) { 12358 var $primary = getPrimaryRow($row); 12359 return $primary.length ? $primary : $row; 12360 } 12361 12362 function isVariableRow($row) { 12363 return ( 12364 $row.hasClass("wcpt-product-type-variable") || 12365 $row.attr("data-wcpt-type") == "variable" 12366 ); 12367 } 12368 12369 function getTableContext($row) { 12370 var $primaryRow = resolvePrimaryRow($row), 12371 $container = $primaryRow.closest(".wcpt"); 12372 12373 if (!$container.length || typeof wcpt_util === "undefined") { 12374 return null; 12375 } 12376 12377 var table_id = wcpt_util.get_table_id($container); 12378 if (!table_id) { 12379 return null; 12380 } 12381 12382 return { $row: $primaryRow, $container: $container, table_id: table_id }; 12383 } 12384 12385 function variationIdsMatch(a, b) { 12386 return a && b && String(a) === String(b); 12387 } 12388 12389 function releaseRestoreSuppress() { 12390 queueMicrotask(function () { 12391 suppressVariationPersistMutations = false; 12392 });
12393 } 12394 12395 function getVariationRowTarget($row) { 12396 var $target = isVariableRow($row) 12397 ? $row 12398 : $row.find(".wcpt-product-type-variable").first(); 12399 12400 return $target.length ? $target : $row; 12401 } 12402 12403 function findVariationInForm($form, variationId) { 12404 var variations = parseJsonAttr($form, "data-product_variations"), 12405 found = null; 12406 12407 $.each(variations || [], function (index, value) { 12408 if (variationIdsMatch(value.variation_id, variationId)) { 12409 found = value; 12410 return false; 12411 } 12412 }); 12413 12414 return found; 12415 } 12416 12417 function readFormAttributes($form) { 12418 var attributes = {}; 12419 12420 $form.find(".variations select").each(function () { 12421 var $select = $(this), 12422 name = $select.attr("name"); 12423 12424 if (name) { 12425 attributes[name] = $select.val(); 12426 } 12427 }); 12428 12429 return attributes; 12430 } 12431 12432 function isVariationPurchasable(variation) { 12433 return !!( 12434 variation && 12435 variation.is_purchasable && 12436 variation.is_in_stock && 12437 variation.variation_is_visible 12438 ); 12439 } 12440 12441 function eachVariationAttributeSelect($rows, fn) { 12442 $rows.find(".wcpt-select-variation-attribute-term").each(function () { 12443 var $select = $(this), 12444 name = $select.attr("data-wcpt-attribute"); 12445 12446 if (name) { 12447 fn($select, name); 12448 } 12449 }); 12450 12451 $rows.find(".variations_form .variations select").each(function () { 12452 var $select = $(this), 12453 name = $select.attr("name"); 12454 12455 if (name) { 12456 fn($select, name); 12457 } 12458 }); 12459 } 12460 12461 function buildSelectVariationMeta(meta, $form, variationId) { 12462 var variation = findVariationInForm($form, variationId); 12463 12464 if (variation) { 12465 meta.variation = variation; 12466 } 12467 12468 meta.attributes = readFormAttributes($form); 12469 meta.variation_id = String(variationId); 12470 meta.variation_found = true; 12471 meta.variation_selected = true; 12472 meta.variation_available = isVariationPurchasable(meta.variation); 12473 12474 return meta; 12475 } 12476 12477 function triggerSelectVariation($target, meta) { 12478 $target.trigger("select_variation", meta); 12479 } 12480 12481 function whenCartFormVariationReady( 12482 $primaryRow, 12483 $form, 12484 variationId, 12485 onReady, 12486 ) { 12487 var finished = false; 12488 12489 function finish() { 12490 if (finished) { 12491 return; 12492 } 12493 finished = true; 12494 $primaryRow.off(".wcptPersistRestore"); 12495 $form.off(".wcptPersistRestore"); 12496 onReady(); 12497 } 12498 12499 function isResolved() { 12500 return variationIdsMatch( 12501 $form.find("input.variation_id").val(), 12502 variationId, 12503 ); 12504 } 12505 12506 $primaryRow.on("select_variation.wcptPersistRestore", function (e, data) { 12507 if (variationIdsMatch(data && data.variation_id, variationId)) { 12508 finish(); 12509 } 12510 }); 12511 12512 $form.on( 12513 "woocommerce_variation_has_changed.wcptPersistRestore", 12514 function () { 12515 if (isResolved()) { 12516 finish(); 12517 } 12518 }, 12519 ); 12520 12521 $form.trigger("check_variations"); 12522 12523 if (isResolved()) { 12524 finish(); 12525 } 12526 } 12527 12528 function parseJsonAttr($el, attr) { 12529 var raw = $el.attr(attr); 12530 if (!raw) { 12531 return null; 12532 } 12533 try { 12534 return JSON.parse(raw); 12535 } catch (e) { 12536 return null; 12537 } 12538 } 12539 12540 function getRowVariationAttributes($row) { 12541 if ($row.hasClass("wcpt-product-type-variation")) { 12542 return parseJsonAttr($row, "data-wcpt-variation-attributes"); 12543 } 12544 12545 var attributes = $row.data("wcpt_attributes"); 12546 attributes = 12547 attributes && !$.isEmptyObject(attributes) 12548 ? $.extend({}, attributes) 12549 : {}; 12550 12551 eachVariationAttributeSelect($row, function ($select, name) { 12552 var val = $select.val(); 12553 if (val) { 12554 attributes[name] = val; 12555 } 12556 }); 12557 12558 return $.isEmptyObject(attributes) ? null : attributes; 12559 } 12560 12561 function findPersistedEntry( 12562 tableState, 12563 productId, 12564 variationId, 12565 variableProduct, 12566 ) { 12567 if (!tableState || !productId) { 12568 return null; 12569 } 12570 12571 if (variationId) { 12572 var variationKey = "v:" + String(variationId); 12573 if (tableState[variationKey]) { 12574 return { key: variationKey, entry: tableState[variationKey] }; 12575 } 12576 } 12577 12578 var foundKey = null; 12579 var foundEntry = null; 12580 12581 $.each(tableState, function (stateKey, entry) { 12582 if ( 12583 foundEntry || 12584 !entry || 12585 String(entry.product_id) !== String(productId) 12586 ) { 12587 return; 12588 } 12589 12590 if (variationId) { 12591 if (variationIdsMatch(entry.variation_id, variationId)) { 12592 foundKey = stateKey; 12593 foundEntry = entry; 12594 } 12595 } else if (variableProduct) { 12596 if (entry.variation_id) { 12597 foundKey = stateKey; 12598 foundEntry = entry; 12599 } 12600 } else if (!entry.variation_id) { 12601 foundKey = stateKey; 12602 foundEntry = entry; 12603 } 12604 });
12605 12606 return foundEntry ? { key: foundKey, entry: foundEntry } : null; 12607 } 12608 12609 function resolveRestoreKey(match, productId) { 12610 if (match.key) { 12611 return match.key; 12612 } 12613 if (match.entry.variation_id) { 12614 return "v:" + String(match.entry.variation_id); 12615 } 12616 if (productId) { 12617 return "p:" + String(productId); 12618 } 12619 return ""; 12620 } 12621 12622 function findRestoreMatch(tableState, $row) { 12623 var productId = $row.attr("data-wcpt-product-id"), 12624 variationId = 12625 $row.attr("data-wcpt-variation-id") || $row.data("wcpt_variation_id"); 12626 12627 if (isVariableRow($row) && productId) { 12628 return findPersistedEntry(tableState, productId, false, true); 12629 } 12630 12631 var match = findPersistedEntry(tableState, productId, variationId); 12632 if (!match) { 12633 var key = rowKey($row); 12634 if (key && tableState[key]) { 12635 match = { key: key, entry: tableState[key] }; 12636 } 12637 } 12638 return match; 12639 } 12640 12641 // --------------------------------------------------------------------------- 12642 // entry build / apply 12643 // --------------------------------------------------------------------------- 12644 12645 function buildPersistedEntry($row) { 12646 var $rowElements = getRowElements($row), 12647 $qty = getPersistedQtyInput($rowElements), 12648 qtyVal = $qty.length ? parseFloat($qty.val()) : 1, 12649 unitPrice = parseFloat($row.attr("data-wcpt-price")); 12650 12651 if (isNaN(qtyVal) || qtyVal <= 0) { 12652 qtyVal = 1; 12653 } 12654 12655 if (isVariableRow($row)) { 12656 var variationData = $row.data("wcpt_variation"); 12657 if (variationData && !isNaN(parseFloat(variationData.display_price))) { 12658 unitPrice = parseFloat(variationData.display_price); 12659 } 12660 } 12661 12662 if (isNaN(unitPrice)) { 12663 unitPrice = 0; 12664 } 12665 12666 return { 12667 product_id: $row.attr("data-wcpt-product-id"), 12668 variation_id: String( 12669 $row.attr("data-wcpt-variation-id") || 12670 $row.data("wcpt_variation_id") || 12671 "", 12672 ), 12673 attributes: getRowVariationAttributes($row), 12674 qty: qtyVal, 12675 total: (unitPrice * qtyVal * 1e12) / 1e12, 12676 }; 12677 } 12678 12679 function applyPersistedQty($rowElements, qtyVal) { 12680 if (isNaN(qtyVal) || qtyVal <= 0) { 12681 return; 12682 } 12683 12684 var $targets = $rowElements 12685 .find(".wcpt-quantity input.qty, .wcpt-quantity select.wcpt-qty-select") 12686 .add($rowElements.find(".variations_form input.qty")); 12687 12688 if (!$targets.length) { 12689 $targets = $rowElements.find(QTY_SELECTOR); 12690 } 12691 12692 $targets.each(function () { 12693 var $qtyInput = $(this); 12694 $qtyInput.val(qtyVal); 12695 if (!$qtyInput.closest("form.cart").length) { 12696 $qtyInput.trigger("change"); 12697 } 12698 }); 12699 } 12700 12701 function applyPersistedQtyWhenReady($rowElements, qtyVal, onDone) { 12702 var finished = false; 12703 12704 function complete() { 12705 if (finished) { 12706 return; 12707 } 12708 finished = true; 12709 if (onDone) { 12710 onDone(); 12711 } 12712 } 12713 12714 function setQty() { 12715 applyPersistedQty($rowElements, qtyVal); 12716 } 12717 12718 if (isNaN(qtyVal) || qtyVal <= 0) { 12719 complete(); 12720 return; 12721 } 12722 12723 var $form = $rowElements.find(".variations_form").first(); 12724 if (!$form.length) { 12725 setQty(); 12726 complete(); 12727 return; 12728 } 12729 12730 // WC cart form re-renders qty on show_variation â apply after that settles 12731 $form.one( 12732 "show_variation.wcptPersistQty found_variation.wcptPersistQty", 12733 function () { 12734 setQty(); 12735 complete(); 12736 }, 12737 ); 12738 12739 queueMicrotask(setQty); 12740 12741 requestAnimationFrame(function () { 12742 setQty(); 12743 complete(); 12744 }); 12745 } 12746 12747 function applyPersistedCheckboxAndQty($row, entry) { 12748 if (!$row || !entry) { 12749 return; 12750 } 12751 12752 var $primaryRow = resolvePrimaryRow($row), 12753 $rowElements = getRowElements($primaryRow), 12754 qtyVal = parseFloat(entry.qty); 12755 12756 function checkAndApply() { 12757 var $enabledCb = $rowElements 12758 .find(".wcpt-cart-checkbox") 12759 .not(":disabled") 12760 .first(); 12761 12762 if (!$enabledCb.length) { 12763 return false; 12764 } 12765 12766 isRestoring = true; 12767 $primaryRow.trigger("_wcpt_checkbox_change", true); 12768 12769 if (!isNaN(qtyVal) && qtyVal > 0) { 12770 applyPersistedQtyWhenReady($rowElements, qtyVal, function () { 12771 isRestoring = false; 12772 }); 12773 } else { 12774 isRestoring = false; 12775 } 12776 12777 return true; 12778 } 12779
12780 if (checkAndApply()) { 12781 return; 12782 } 12783 12784 $primaryRow.one("select_variation.wcptPersistCheckbox", function (e, data) { 12785 if (data && data.variation_selected && data.variation_id) { 12786 checkAndApply(); 12787 } 12788 }); 12789 } 12790 12791 // --------------------------------------------------------------------------- 12792 // state mutations 12793 // --------------------------------------------------------------------------- 12794 12795 function syncContainerMirror($container, table_id) { 12796 $container.data( 12797 "wcpt_all_checked", 12798 memoryState[table_id] ? memoryState[table_id] : {}, 12799 ); 12800 } 12801 12802 function refreshFloatingTrigger() { 12803 if (typeof window.wcpt_checkbox_trigger_init === "function") { 12804 window.wcpt_checkbox_trigger_init(); 12805 } 12806 } 12807 12808 function commitTableState(table_id, $container) { 12809 if ( 12810 memoryState[table_id] && 12811 Object.keys(memoryState[table_id]).length === 0 12812 ) { 12813 delete memoryState[table_id]; 12814 } 12815 if ($container && $container.length) { 12816 syncContainerMirror($container, table_id); 12817 } 12818 publishState(); 12819 refreshFloatingTrigger(); 12820 } 12821 12822 function flushVisibleRows($container) { 12823 if (!$container || !$container.length || typeof wcpt_util === "undefined") { 12824 return; 12825 } 12826 12827 var flushedVariableProducts = {}; 12828 12829 $container.find(".wcpt-row").each(function () { 12830 var $primaryRow = getPrimaryRow($(this)); 12831 12832 if (!$primaryRow.length || !$primaryRow.data("wcpt_checked")) { 12833 return; 12834 } 12835 12836 if ( 12837 isVariableRow($primaryRow) && 12838 !$primaryRow.data("wcpt_variation_id") 12839 ) { 12840 return; 12841 } 12842 12843 var productId = $primaryRow.attr("data-wcpt-product-id"); 12844 if (productId && isVariableRow($primaryRow)) { 12845 if (flushedVariableProducts[productId]) { 12846 return; 12847 } 12848 flushedVariableProducts[productId] = true; 12849 } 12850 12851 saveRow($primaryRow); 12852 }); 12853 } 12854 12855 function pruneUncheckedVisibleEntries($container) { 12856 if (!$container || !$container.length || typeof wcpt_util === "undefined") { 12857 return; 12858 } 12859 12860 var table_id = wcpt_util.get_table_id($container); 12861 if (!table_id || !memoryState[table_id]) { 12862 return; 12863 } 12864 12865 var visibleProductIds = {}, 12866 checkedProductIds = {}; 12867 12868 $container.find(".wcpt-row").each(function () { 12869 var $primaryRow = getPrimaryRow($(this)); 12870 if (!$primaryRow.length) { 12871 return; 12872 } 12873 12874 var productId = $primaryRow.attr("data-wcpt-product-id"); 12875 if (!productId) { 12876 return; 12877 } 12878 12879 visibleProductIds[String(productId)] = true; 12880 12881 if ($primaryRow.data("wcpt_checked")) { 12882 checkedProductIds[String(productId)] = true; 12883 } 12884 }); 12885 12886 var tableState = memoryState[table_id], 12887 removed = false; 12888 12889 $.each(tableState, function (key, entry) { 12890 if (!entry || !entry.product_id) { 12891 return; 12892 } 12893 12894 var productId = String(entry.product_id); 12895 if (!visibleProductIds[productId] || checkedProductIds[productId]) { 12896 return; 12897 } 12898 12899 delete tableState[key]; 12900 removed = true; 12901 }); 12902 12903 if (removed) { 12904 commitTableState(table_id, $container); 12905 } 12906 } 12907 12908 function finalizeTablePage($container) { 12909 queueMicrotask(function () { 12910 if ($container && $container.length) { 12911 // pruneUncheckedVisibleEntries($container); 12912 12913 if (typeof wcpt_util !== "undefined") { 12914 var table_id = wcpt_util.get_table_id($container); 12915 if (table_id) { 12916 syncContainerMirror($container, table_id); 12917 } 12918 } 12919 } 12920 12921 releaseRestoreSuppress(); 12922 refreshFloatingTrigger(); 12923 }); 12924 } 12925 12926 function saveRow($row) { 12927 var ctx = getTableContext($row); 12928 if (!ctx) { 12929 return; 12930 } 12931 12932 $row = ctx.$row; 12933 12934 if (!$row.data("wcpt_checked")) { 12935 return; 12936 } 12937 12938 var key = rowKey($row); 12939 if (!key) { 12940 return; 12941 } 12942 12943 if (isVariableRow($row) && !$row.data("wcpt_variation_id")) { 12944 return; 12945 } 12946 12947 if (!memoryState[ctx.table_id]) { 12948 memoryState[ctx.table_id] = {}; 12949 } 12950 12951 if (isVariableRow($row)) { 12952 removePersistedEntriesForProduct( 12953 ctx.table_id, 12954 $row.attr("data-wcpt-product-id"), 12955 $row.data("wcpt_variation_id"), 12956 ctx.$container, 12957 true, 12958 ); 12959 } 12960 12961 memoryState[ctx.table_id][key] = buildPersistedEntry($row); 12962 commitTableState(ctx.table_id, ctx.$container); 12963 } 12964 12965 function removePersistedEntriesForProduct( 12966 table_id, 12967 productId, 12968 exceptVariationId, 12969 $container, 12970 skipPublish, 12971 ) { 12972 if (!table_id || !productId || !memoryState[table_id]) { 12973 return false; 12974 } 12975 12976 var tableState = memoryState[table_id], 12977 removed = false; 12978 12979 $.each(tableState, function (stateKey, entry) { 12980 if (!entry || String(entry.product_id) !== String(productId)) { 12981 return; 12982 } 12983 12984 if ( 12985 exceptVariationId && 12986 entry.variation_id && 12987 String(entry.variation_id) === String(exceptVariationId) 12988 ) { 12989 return; 12990 } 12991 12992 delete tableState[stateKey]; 12993 removed = true; 12994 });
12995 12996 if (!removed) { 12997 return false; 12998 } 12999 13000 if (!skipPublish) { 13001 commitTableState(table_id, $container); 13002 } 13003 return true; 13004 } 13005 13006 function removeRow($row) { 13007 if (isRestoring) { 13008 return; 13009 } 13010 13011 var ctx = getTableContext($row); 13012 if (!ctx || !memoryState[ctx.table_id]) { 13013 return; 13014 } 13015 13016 var key = rowKey(ctx.$row); 13017 if (!key || !memoryState[ctx.table_id][key]) { 13018 return; 13019 } 13020 13021 delete memoryState[ctx.table_id][key]; 13022 commitTableState(ctx.table_id, ctx.$container); 13023 } 13024 13025 function hasPersistedVariableProduct(table_id, productId) { 13026 if (!table_id || !productId) { 13027 return false; 13028 } 13029 13030 var state = pendingRestoreState[table_id] || memoryState[table_id] || null; 13031 13032 return !!findPersistedEntry(state, productId, false, true); 13033 } 13034 13035 function clearTable(table_id, $container) { 13036 if (memoryState[table_id]) { 13037 delete memoryState[table_id]; 13038 publishState(); 13039 } 13040 13041 if ($container && $container.length) { 13042 $container.data("wcpt_all_checked", {}); 13043 } 13044 refreshFloatingTrigger(); 13045 } 13046 13047 // --------------------------------------------------------------------------- 13048 // restore 13049 // --------------------------------------------------------------------------- 13050 13051 function getVariationMetaFromEntry($row, entry) { 13052 var $rows = getRowElements($row), 13053 variationId = String(entry.variation_id), 13054 meta = { 13055 variation_id: variationId, 13056 complete_match: true, 13057 attributes: entry.attributes ? $.extend({}, entry.attributes) : {}, 13058 variation: false, 13059 variation_found: true, 13060 variation_selected: true, 13061 variation_available: true, 13062 }, 13063 $option = $rows 13064 .find( 13065 '.wcpt-select-variation-dropdown option[value="' + variationId + '"]', 13066 ) 13067 .first(), 13068 $radioWrap = $option.length 13069 ? $() 13070 : $rows 13071 .find('.wcpt-variation-radio[value="' + variationId + '"]') 13072 .first() 13073 .closest(".wcpt-select-variation"), 13074 $source = $option.length ? $option : $radioWrap, 13075 $form = $rows.find(".variations_form").first(); 13076 13077 if ($source.length) { 13078 var attrs = parseJsonAttr($source, "data-wcpt-attributes"), 13079 variation = parseJsonAttr($source, "data-wcpt-variation"); 13080 13081 if (attrs) { 13082 meta.attributes = attrs; 13083 } 13084 if (variation) { 13085 meta.variation = variation; 13086 } 13087 meta.complete_match = $source.hasClass("wcpt-complete_match"); 13088 } 13089 13090 if ($form.length && ($.isEmptyObject(meta.attributes) || !meta.variation)) { 13091 var variation = findVariationInForm($form, variationId); 13092 13093 if (variation) { 13094 if ($.isEmptyObject(meta.attributes) && variation.attributes) { 13095 meta.attributes = $.extend({}, variation.attributes); 13096 } 13097 if (!meta.variation) { 13098 meta.variation = variation; 13099 } 13100 } 13101 } 13102 13103 return meta; 13104 } 13105 13106 function applyAttributeSelects($row, attributes) { 13107 if (!attributes) { 13108 return; 13109 } 13110 13111 eachVariationAttributeSelect( 13112 getRowElements($row), 13113 function ($select, name) { 13114 if (typeof attributes[name] !== "undefined") { 13115 $select.val(attributes[name]); 13116 } 13117 }, 13118 ); 13119 } 13120 13121 function rowNeedsPrepWait($row) { 13122 if (!isVariableRow($row)) { 13123 return false; 13124 } 13125 13126 return !!getRowElements($row).find( 13127 ".variations_form, .wcpt-select-variation-dropdown, .wcpt-variation-radio", 13128 ).length; 13129 } 13130 13131 function whenRowPrepComplete($row, callback) { 13132 var $primaryRow = resolvePrimaryRow($row); 13133 13134 if ($primaryRow.data("wcpt_prep_variation_complete")) { 13135 callback(); 13136 return; 13137 } 13138 13139 $primaryRow.one("wcpt_row_prep_variation_complete", callback); 13140 } 13141 13142 function selectPersistedVariation($row, entry, callback) { 13143 if (!entry || !entry.variation_id) { 13144 if (callback) { 13145 callback(); 13146 } 13147 return; 13148 } 13149 13150 var $primaryRow = resolvePrimaryRow($row), 13151 $rows = getRowElements($primaryRow), 13152 variationId = String(entry.variation_id), 13153 meta = getVariationMetaFromEntry($primaryRow, entry), 13154 $form = $rows.find(".variations_form").first(), 13155 $dropdown = $rows.find(".wcpt-select-variation-dropdown"), 13156 $radio = $rows.find('.wcpt-variation-radio[value="' + variationId + '"]'), 13157 $attrSelects = $rows.find(".wcpt-select-variation-attribute-term"), 13158 $target = getVariationRowTarget($primaryRow); 13159 13160 isRestoring = true; 13161 applyAttributeSelects($primaryRow, meta.attributes); 13162 13163 function finishVariationRestore() { 13164 isRestoring = false; 13165 if (callback) { 13166 callback(); 13167 } 13168 } 13169 13170 function triggerControl($control, setup) { 13171 setup(); 13172 $control.trigger("change"); 13173 finishVariationRestore(); 13174 } 13175 13176 if ($dropdown.length) { 13177 triggerControl($dropdown, function () { 13178 $dropdown.val(variationId); 13179 }); 13180 return; 13181 } 13182 13183 if ($radio.length) { 13184 triggerControl($radio, function () { 13185 $radio.prop("checked", true); 13186 }); 13187 return; 13188 } 13189 13190 if ($form.length) { 13191 whenCartFormVariationReady($primaryRow, $form, variationId, function () { 13192 triggerSelectVariation( 13193 $target, 13194 buildSelectVariationMeta(meta, $form, variationId), 13195 ); 13196 finishVariationRestore(); 13197 }); 13198 return; 13199 } 13200 13201 if ($attrSelects.length) { 13202 $target.one( 13203 "select_variation.wcptPersistRestore", 13204 finishVariationRestore, 13205 ); 13206 $attrSelects.trigger("change"); 13207 return; 13208 } 13209 13210 triggerSelectVariation($target, meta); 13211 finishVariationRestore(); 13212 } 13213 13214 function restorePersistedRow($row, entry, onComplete) { 13215 function runRestore() { 13216 if (isVariableRow($row) && entry.variation_id) { 13217 selectPersistedVariation($row, entry, onComplete); 13218 return; 13219 } 13220 13221 applyPersistedCheckboxAndQty($row, entry); 13222 if (onComplete) { 13223 onComplete(); 13224 } 13225 } 13226 13227 if (rowNeedsPrepWait($row)) { 13228 whenRowPrepComplete($row, runRestore); 13229 return; 13230 } 13231 13232 runRestore(); 13233 } 13234 13235 function restoreTablePage($container) { 13236 if (typeof wcpt_util === "undefined") { 13237 releaseRestoreSuppress(); 13238 return; 13239 } 13240 13241 var table_id = wcpt_util.get_table_id($container); 13242 if (!table_id) { 13243 releaseRestoreSuppress(); 13244 return; 13245 } 13246 13247 var tableState = 13248 pendingRestoreState[table_id] || 13249 (memoryState[table_id] 13250 ? $.extend(true, {}, memoryState[table_id]) 13251 : null); 13252
13253 delete pendingRestoreState[table_id]; 13254 13255 if (!tableState || $.isEmptyObject(tableState)) { 13256 finalizeTablePage($container); 13257 return; 13258 } 13259 13260 if (!memoryState[table_id]) { 13261 memoryState[table_id] = $.extend(true, {}, tableState); 13262 } 13263 13264 publishState(); 13265 13266 var restoredKeys = {}, 13267 restoredVariableProducts = {}, 13268 pendingRestores = 0; 13269 13270 function finishRestoreCycle() { 13271 pendingRestores--; 13272 if (pendingRestores <= 0) { 13273 finalizeTablePage($container); 13274 } 13275 } 13276 13277 $container.find(".wcpt-row").each(function () { 13278 var $primaryRow = getPrimaryRow($(this)); 13279 if (!$primaryRow.length) { 13280 return; 13281 } 13282 13283 var productId = $primaryRow.attr("data-wcpt-product-id"), 13284 match, 13285 restoreKey; 13286 13287 if (isVariableRow($primaryRow) && productId) { 13288 if (restoredVariableProducts[productId]) { 13289 return; 13290 } 13291 match = findPersistedEntry(tableState, productId, false, true); 13292 if (!match) { 13293 return; 13294 } 13295 restoredVariableProducts[productId] = true; 13296 restoreKey = "p:" + String(productId); 13297 } else { 13298 match = findRestoreMatch(tableState, $primaryRow); 13299 if (!match) { 13300 return; 13301 } 13302 restoreKey = resolveRestoreKey(match, productId); 13303 } 13304 13305 if (!restoreKey || restoredKeys[restoreKey]) { 13306 return; 13307 } 13308 13309 restoredKeys[restoreKey] = true; 13310 pendingRestores++; 13311 restorePersistedRow($primaryRow, match.entry, finishRestoreCycle); 13312 }); 13313 13314 if (pendingRestores <= 0) { 13315 finalizeTablePage($container); 13316 } 13317 } 13318 13319 function onVariationSelected($row, data) { 13320 var ctx = getTableContext($row); 13321 if (!ctx) { 13322 return; 13323 } 13324 13325 var productId = ctx.$row.attr("data-wcpt-product-id"); 13326 if (!productId) { 13327 return; 13328 } 13329 13330 var variationId = 13331 data && data.variation_id ? String(data.variation_id) : "", 13332 isSelected = !!(data && data.variation_selected && variationId); 13333 13334 if (isSelected) { 13335 var match = memoryState[ctx.table_id] 13336 ? findPersistedEntry(memoryState[ctx.table_id], productId, variationId) 13337 : null; 13338 13339 if (match) { 13340 applyPersistedCheckboxAndQty(ctx.$row, match.entry); 13341 return; 13342 } 13343 } 13344 13345 if (isRestoring || suppressVariationPersistMutations) { 13346 return; 13347 } 13348 13349 if (!isSelected) { 13350 removePersistedEntriesForProduct( 13351 ctx.table_id, 13352 productId, 13353 false, 13354 ctx.$container, 13355 ); 13356 return; 13357 } 13358 13359 removePersistedEntriesForProduct( 13360 ctx.table_id, 13361 productId, 13362 variationId, 13363 ctx.$container, 13364 ); 13365 13366 if (ctx.$row.data("wcpt_checked")) { 13367 saveRow(ctx.$row); 13368 } 13369 } 13370 13371 // --------------------------------------------------------------------------- 13372 // cart payload integration 13373 // --------------------------------------------------------------------------- 13374 13375 function aggregateOffPageIntoPayload(tableContainersById, payload) { 13376 if (typeof wcpt_util === "undefined" || !tableContainersById || !payload) { 13377 return; 13378 } 13379 13380 var products = payload.products || {}, 13381 variations = payload.variations || {}, 13382 attributes = payload.attributes || {}; 13383 13384 try { 13385 $.each(tableContainersById, function (table_id, $container) { 13386 if (!memoryState[table_id]) { 13387 return; 13388 } 13389 13390 $.each(memoryState[table_id], function (key, entry) { 13391 if (!entry || !entry.product_id) { 13392 return; 13393 } 13394 13395 var product_id = String(entry.product_id), 13396 variation_id = entry.variation_id 13397 ? String(entry.variation_id) 13398 : false, 13399 qty = parseFloat(entry.qty); 13400 13401 if (isNaN(qty) || qty <= 0) { 13402 qty = 1; 13403 } 13404 13405 var $candidate_rows = $container.find( 13406 '.wcpt-row[data-wcpt-product-id="' + product_id + '"]', 13407 ); 13408 13409 if (variation_id) { 13410 $candidate_rows = $candidate_rows.filter(function () { 13411 var $r = $(this); 13412 return ( 13413 $r.attr("data-wcpt-variation-id") === variation_id || 13414 String($r.data("wcpt_variation_id")) === variation_id 13415 ); 13416 }); 13417 } 13418 13419 var has_local_checked = $candidate_rows.filter(function () { 13420 return !!$(this).data("wcpt_checked"); 13421 }).length; 13422 13423 if (has_local_checked) { 13424 return; 13425 } 13426 13427 if (typeof products[product_id] === "undefined") { 13428 products[product_id] = qty; 13429 } else { 13430 products[product_id] += qty; 13431 } 13432 13433 if (variation_id) { 13434 if (!variations[product_id]) { 13435 variations[product_id] = {}; 13436 } 13437 if (!variations[product_id][variation_id]) { 13438 variations[product_id][variation_id] = qty; 13439 } else { 13440 variations[product_id][variation_id] += qty; 13441 } 13442 13443 if (entry.attributes) { 13444 attributes[variation_id] = entry.attributes; 13445 } 13446 } 13447 }); 13448 }); 13449 } catch (e) { 13450 // ignore aggregation failures 13451 } 13452 13453 payload.products = products; 13454 payload.variations = variations; 13455 payload.attributes = attributes; 13456 } 13457 13458 function clearTables(tableContainersById) { 13459 if (typeof wcpt_util === "undefined" || !tableContainersById) { 13460 return; 13461 } 13462 13463 $.each(tableContainersById, function (table_id, $container) { 13464 if (!table_id || !$container || !$container.length) { 13465 return; 13466 } 13467 clearTable(table_id, $container); 13468 });
13469 } 13470 13471 // --------------------------------------------------------------------------- 13472 // event bindings 13473 // --------------------------------------------------------------------------- 13474 13475 // save on check 13476 $("body").on("wcpt_checkbox_change", ".wcpt-row", function (e, state) { 13477 if (isRestoring || suppressVariationPersistMutations || !state) { 13478 return; 13479 } 13480 saveRow($(this)); 13481 }); 13482 13483 $("body").on("change", ".wcpt-cart-checkbox", function () { 13484 if (isRestoring || suppressVariationPersistMutations) { 13485 return; 13486 } 13487 var $cb = $(this); 13488 if (!$cb.prop("checked")) { 13489 removeRow($cb.closest(".wcpt-row")); 13490 } 13491 }); 13492 13493 // qty -> 0 removes persisted row (not wcpt_checkbox_change false â prep resets fire that too) 13494 $("body").on( 13495 "change", 13496 ".wcpt input.qty, .wcpt select.wcpt-qty-select", 13497 function () { 13498 if (isRestoring || suppressVariationPersistMutations) { 13499 return; 13500 } 13501 13502 var $this = $(this); 13503 if ($this.closest("form.cart").length) { 13504 return; 13505 } 13506 13507 var val = parseFloat($this.val()); 13508 if (isNaN(val) || val <= 0) { 13509 var $row = $this.closest(".wcpt-row"); 13510 if (typeof window.wcpt_get_original_row === "function") { 13511 $row = window.wcpt_get_original_row($row); 13512 } 13513 removeRow($row); 13514 return; 13515 } 13516 13517 var $row = $this.closest(".wcpt-row"); 13518 if (typeof window.wcpt_get_original_row === "function") { 13519 $row = window.wcpt_get_original_row($row); 13520 } 13521 13522 if (isVariableRow($row) && !$row.data("wcpt_variation_id")) { 13523 return; 13524 } 13525 13526 if ($row.data("wcpt_checked")) { 13527 saveRow($row); 13528 } 13529 }, 13530 ); 13531 13532 // flush checked rows before DOM is replaced (fast pagination) 13533 $("body").on("wcpt_before_ajax_container_replace", function (e, data) { 13534 if (!data || !data.$container) { 13535 return; 13536 } 13537 flushVisibleRows(data.$container); 13538 }); 13539 13540 // snapshot before prep_variation_options can clear persisted rows via qty reset 13541 $("body").on("wcpt_before_every_load", ".wcpt", function () { 13542 if (typeof wcpt_util === "undefined") { 13543 return; 13544 } 13545 13546 var table_id = wcpt_util.get_table_id($(this)); 13547 if (table_id && memoryState[table_id]) { 13548 pendingRestoreState[table_id] = $.extend(true, {}, memoryState[table_id]); 13549 } 13550 13551 suppressVariationPersistMutations = true; 13552 }); 13553 13554 $("body").on( 13555 "select_variation", 13556 ".wcpt-row.wcpt-product-type-variable", 13557 function (e, data) { 13558 var $row = $(this); 13559 queueMicrotask(function () { 13560 onVariationSelected($row, data); 13561 }); 13562 }, 13563 ); 13564 13565 // run after freeze-table layout so sibling rows exist for restore 13566 $("body").on("wcpt_layout", ".wcpt", function (e, data) { 13567 if (!data || data.source !== "after_every_load") { 13568 return; 13569 } 13570 13571 restoreTablePage($(this)); 13572 }); 13573 13574 $(document).on( 13575 "wcpt_clear_all_checked_for_table", 13576 function (e, table_id, $container) { 13577 clearTable(table_id, $container); 13578 }, 13579 ); 13580 13581 // --------------------------------------------------------------------------- 13582 // public API 13583 // --------------------------------------------------------------------------- 13584 13585 window.wcpt_cart_checkbox_persist = { 13586 getState: getState, 13587 hasPersistedVariableProduct: hasPersistedVariableProduct, 13588 aggregateOffPageIntoPayload: aggregateOffPageIntoPayload, 13589 clearTables: clearTables, 13590 }; 13591 13592 publishState(); 13593})(jQuery); 13594 13595// module: product compare 13596(function ($) { 13597 // ---- Compare State "API" ---- 13598 13599 // Separate cookie for WCPT native compare logic 13600 var WCPT_COMPARE_COOKIE = "wcpt_compare_products"; 13601 13602 // Get products in compare (array of strings) 13603 function getCompareIDs() { 13604 var val = getCookie(WCPT_COMPARE_COOKIE); 13605 if (!val || val === "null") return []; 13606 return val 13607 .split(",") 13608 .map(function (v) { 13609 return v.trim(); 13610 }) 13611 .filter(function (v) { 13612 return !!v; 13613 }); 13614 } 13615
13616 // Set products in compare (array of strings) 13617 function setCompareIDs(ids) { 13618 setCookie(WCPT_COMPARE_COOKIE, ids.join(","), 30); // 30 day expiry for native compare 13619 $(document).trigger("wcpt_compare_changed", [ids]); // Broadcast state change 13620 } 13621 13622 // Add/remove by id (API helpers) 13623 function addToCompare(id) { 13624 var ids = getCompareIDs(); 13625 id = String(id); 13626 if (ids.indexOf(id) === -1) { 13627 ids.push(id); 13628 setCompareIDs(ids); 13629 } 13630 } 13631 function removeFromCompare(id) { 13632 var ids = getCompareIDs(); 13633 id = String(id); 13634 var idx = ids.indexOf(id); 13635 if (idx !== -1) { 13636 ids.splice(idx, 1); 13637 setCompareIDs(ids); 13638 } 13639 } 13640 function isInCompare(id) { 13641 var ids = getCompareIDs(); 13642 return ids.indexOf(String(id)) !== -1; 13643 } 13644 function clearCompare() { 13645 setCompareIDs([]); 13646 } 13647 13648 // Expose public API 13649 window.wcptCompare = { 13650 getIDs: getCompareIDs, 13651 setIDs: setCompareIDs, 13652 add: addToCompare, 13653 remove: removeFromCompare, 13654 isIn: isInCompare, 13655 clear: clearCompare, 13656 }; 13657 13658 // --- Cookie utils 13659 function getCookie(name) { 13660 var nameEQ = name + "="; 13661 var ca = document.cookie.split(";"); 13662 for (var i = 0; i < ca.length; i++) { 13663 var c = ca[i].trim(); 13664 if (c.indexOf(nameEQ) === 0) 13665 return decodeURIComponent(c.substring(nameEQ.length)); 13666 } 13667 return ""; 13668 } 13669 13670 function setCookie(name, val, days) { 13671 var d = new Date(); 13672 d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000); 13673 var expires = "expires=" + d.toUTCString(); 13674 document.cookie = 13675 name + "=" + encodeURIComponent(val) + "; " + expires + "; path=/"; 13676 } 13677 13678 // --- UI STATE REFRESH --- 13679 13680 // Helper function to get product/variation ID from row 13681 function getProductIdFromRow($row) { 13682 // First check for variation_id (using .data() for variable products with selected variation) 13683 var variationId = $row.data("wcpt_variation_id"); 13684 if (variationId) { 13685 return String(variationId); 13686 } 13687 // Then check for data-wcpt-variation-id attribute (for variation products) 13688 var variationIdAttr = $row.attr("data-wcpt-variation-id"); 13689 if (variationIdAttr) { 13690 return String(variationIdAttr); 13691 } 13692 // Finally fall back to product ID 13693 var productId = $row.attr("data-wcpt-product-id"); 13694 if (productId) { 13695 return String(productId); 13696 } 13697 return ""; 13698 } 13699 13700 function updateCompareButtonStates(forceIDs) { 13701 // Optionally accept an override ids (for API parity/event-coalescing) 13702 var ids = forceIDs || getCompareIDs(); 13703 13704 $(".wcpt-compare-button").each(function () { 13705 var $btn = $(this); 13706 var $row = $btn.closest(".wcpt-row"); 13707 var id = getProductIdFromRow($row); 13708 var $count = $btn.find(".wcpt-compare-button__count"); 13709 var $input = $btn.find("input[type='checkbox']"); 13710 if (!id) return; 13711 13712 // Update button's data attribute to match current row state 13713 $btn.attr("data-wcpt-product-id", id); 13714 13715 var inCompare = ids.indexOf(id) !== -1; 13716 if (inCompare) { 13717 $btn.removeClass("wcpt-not-in-compare").addClass("wcpt-in-compare"); 13718 $input.prop("checked", true); 13719 } else { 13720 $btn.removeClass("wcpt-in-compare").addClass("wcpt-not-in-compare"); 13721 $input.prop("checked", false); 13722 } 13723 if ($count.length) { 13724 $count.text("(" + ids.length + ")"); 13725 } 13726 }); 13727 } 13728 13729 // ---- WCOSC SYNC API (completely separate from native) 13730 // When possible, keep woosc integration as *optionally* in sync for visuals 13731 function updateWooscButtonStatesSyncWithWCPT() { 13732 if (typeof woosc_vars == "undefined") return; 13733 // Set the WOOSC cookie value to our (WCPT) compare IDs 13734 var woosc_cookie_name = "woosc_products_" + woosc_vars.user_id; 13735 var wcpt_ids = getCompareIDs(); 13736 setCookie(woosc_cookie_name, wcpt_ids.join(","), 30); 13737 13738 // Use wcpt_ids for button state sync 13739 $(".woosc-btn").each(function () { 13740 var $btn = $(this); 13741 var id = $btn.attr("data-id"); 13742 if (wcpt_ids.indexOf(String(id)) !== -1) { 13743 $btn.addClass("woosc-btn-added woosc-added"); 13744 } else { 13745 $btn.removeClass("woosc-btn-added woosc-added"); 13746 } 13747 });
13748 } 13749 13750 // ---- UI/State Observers ---- 13751 13752 // Initial render/init: get compare state from cookie and update UI 13753 function initCompareUI() { 13754 updateCompareButtonStates(); 13755 updateWooscButtonStatesSyncWithWCPT(); 13756 } 13757 13758 $(function () { 13759 initCompareUI(); 13760 }); 13761 13762 // Also update on ajax table reloads 13763 $(document).on("wcpt_after_every_load", function () { 13764 updateCompareButtonStates(); 13765 updateWooscButtonStatesSyncWithWCPT(); 13766 }); 13767 13768 // Any time our compare state changes (API), refresh UI 13769 $(document).on("wcpt_compare_changed", function (e, ids) { 13770 if (!ids) ids = getCompareIDs(); 13771 updateCompareButtonStates(ids); 13772 updateWooscButtonStatesSyncWithWCPT(); 13773 // ^ Only for visual update, but NOT to modify woosc cookie. 13774 }); 13775 13776 // ---- Button Event Listeners ---- 13777 13778 // Main click handler: toggle state, update UI/state, NO interference with woosc 13779 $(document).on("click", ".wcpt-compare-button", function (e) { 13780 // If the event is from a checkbox (or woosc-integration), skip here 13781 if ( 13782 $(e.target).hasClass("wcpt-compare-button__external-link") || 13783 $(e.target).closest(".wcpt-compare-button__external-link").length || 13784 $(e.target).hasClass("woosc-btn") || 13785 $(e.target).closest(".woosc-btn").length || 13786 $(e.target).hasClass("wcpt-compare-button__icon") 13787 ) { 13788 return; 13789 } 13790 13791 var $btn = $(this); 13792 var $row = $btn.closest(".wcpt-row"); 13793 var id = getProductIdFromRow($row); 13794 if (!id) return; 13795 13796 // Update button's data attribute to match current row state 13797 $btn.attr("data-wcpt-product-id", id); 13798 13799 if (isInCompare(id)) { 13800 removeFromCompare(id); 13801 } else { 13802 addToCompare(id); 13803 setTimeout(function () { 13804 $(".woosc-btn.woosc-added").first().trigger("click"); 13805 }, 100); 13806 } 13807 // UI updates after wcpt_compare_changed event 13808 }); 13809 13810 // push changes from woosc to wcpt 13811 $(document).on("woosc_change_count", function (e, count) { 13812 var woosc_cookie_name = "woosc_products_" + woosc_vars.user_id; 13813 var woosc_compare_ids = getCookie(woosc_cookie_name) || []; 13814 13815 setCompareIDs( 13816 typeof woosc_compare_ids === "string" 13817 ? woosc_compare_ids.split(",") 13818 : woosc_compare_ids, 13819 ); 13820 updateCompareButtonStates(); 13821 }); 13822 13823 // Listen for select_variation event to update button ID and initialize state 13824 $(document).on("select_variation", ".wcpt-row", function (e, data) { 13825 var $row = $(this); 13826 var $btn = $row.find(".wcpt-compare-button"); 13827 13828 if (!$btn.length) return; 13829 13830 // Get the current ID from row (will prioritize variation_id if available) 13831 var id = getProductIdFromRow($row); 13832 13833 if (id) { 13834 // Update button's data attribute 13835 $btn.attr("data-wcpt-product-id", id); 13836 13837 // Initialize button state with current compare state 13838 var ids = getCompareIDs(); 13839 var inCompare = ids.indexOf(id) !== -1; 13840 var $count = $btn.find(".wcpt-compare-button__count"); 13841 var $input = $btn.find("input[type='checkbox']"); 13842 13843 if (inCompare) { 13844 $btn.removeClass("wcpt-not-in-compare").addClass("wcpt-in-compare"); 13845 $input.prop("checked", true); 13846 } else { 13847 $btn.removeClass("wcpt-in-compare").addClass("wcpt-not-in-compare"); 13848 $input.prop("checked", false); 13849 } 13850 13851 if ($count.length) { 13852 $count.text("(" + ids.length + ")"); 13853 } 13854 } 13855 }); 13856})(jQuery); 13857 13858// module: product quick view 13859(function ($) { 13860 $(document).on("click", ".wcpt-quick-view-button", function (e) { 13861 if ($(e.target).closest(".woosq-btn").length) { 13862 return; 13863 } 13864 13865 var $btn = $(this); 13866 $btn.find(".woosq-btn").click(); 13867 }); 13868})(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.