1"use strict"; 2 3; 4/* global wppopups_settings,*/ 5 6(function ($) { 7 'use strict'; 8 9 var WPPopups = { 10 popups: {}, 11 byBind: { 12 'class': [], 13 'href': [] 14 }, 15 16 /** 17 * Start the engine. 18 * 19 * @since 2.0.0 20 */ 21 init: function init() { 22 // Document ready 23 $(document).ready(WPPopups.ready); // Page load 24 25 $(window).on('load', WPPopups.load); // Document ready 26 27 $(document).on('wppopupsAjaxReady', WPPopups.ajaxStart); 28 WPPopups.bindUIActions(); 29 }, 30 31 /** 32 * Document ready. 33 * 34 * @since 2.0.0 35 */ 36 ready: function ready() { 37 $(document).trigger('wppopupsBeforeReady'); 38 if ($(".spu-box").length == 0) return false; 39 var data = { 40 action: 'wppopups_rules', 41 pid: wppopups_vars.pid, 42 referrer: document.referrer, 43 url: document.documentURI, 44 query_string: document.location.search, 45 is_category: wppopups_vars.is_category, 46 is_archive: wppopups_vars.is_archive, 47 is_singular: wppopups_vars.is_singular, 48 is_preview: wppopups_vars.is_preview, 49 is_search: wppopups_vars.is_search, 50 is_front_page: wppopups_vars.is_front_page, 51 is_blog_page: wppopups_vars.is_blog_page, 52 woo_is_shop: wppopups_vars.woo_is_shop, 53 woo_is_order_received: wppopups_vars.woo_is_order_received, 54 woo_is_product_category: wppopups_vars.woo_is_product_category, 55 woo_is_product_tag: wppopups_vars.woo_is_product_tag, 56 woo_is_product: wppopups_vars.woo_is_product, 57 woo_is_cart: wppopups_vars.woo_is_cart, 58 woo_is_checkout: wppopups_vars.woo_is_checkout, 59 woo_is_account_page: wppopups_vars.woo_is_account_page, 60 popups: [] 61 }; // save all popups rules into data 62 63 $(".spu-box").each(function () { 64 var $popup = $(this), 65 $id = $popup.data('id'), 66 $need_ajax = $popup.data('need_ajax'), 67 $rules = WPPopups.settings($popup, 'rules'), 68 $global_rules = WPPopups.settings($popup, 'global_rules'); // if old popup it's enabled id will be empty and break things 69 70 if ($id && $need_ajax) { 71 data.popups.push({ 72 id: $id, 73 rules: JSON.stringify($rules), 74 global_rules: JSON.stringify($global_rules), 75 parent: $popup.data('parent') 76 }); 77 } 78 }); 79 var ajax = { 80 url: wppopups_vars.ajax_url, 81 data: window.wp.hooks.applyFilters('wppopups_pre_ready_data', data), 82 cache: false, 83 type: 'POST', 84 dataType: 'json', 85 timeout: 30000, 86 success: function success(r) { 87 if (r.success) { 88 r.success.forEach(function (id) { 89 WPPopups.removePopup(id); 90 }); 91 } 92 93 $(document).trigger('wppopupsAjaxReady'); 94 }, 95 error: function error(r) { 96 console.log(r); 97 } 98 }; 99 100 if (data.popups.length) { 101 $.ajax(ajax); 102 } 103 104 WPPopups.initVideoSources(); 105 WPPopups.resetPopupsContent(); 106 WPPopups.hashTriggerPopups(); // init popups that doesn't need ajax 107 108 $(".spu-box").each(function () { 109 var $popup = $(this), 110 $need_ajax = $popup.data('need_ajax'); 111 112 if ($need_ajax) { 113 $popup.hide(); 114 return; 115 } 116 117 WPPopups.initPopup($popup); 118 }); 119 WPPopups.loadValidation(); 120 WPPopups.initSocialShortcodes(); 121 $(document).trigger('wppopupsReady'); 122 }, 123 124 /** 125 * Page load. 126 * 127 * @since 2.0.0 128 */ 129 load: function load() { 130 // center again once it's loaded 131 $(".spu-box").each(function () { 132 var $popup = $(this); 133 WPPopups.centerPopup($popup); 134 }); 135 }, 136 ajaxStart: function ajaxStart() { 137 $(".spu-box").each(function () { 138 var $popup = $(this), 139 $need_ajax = $popup.data('need_ajax'); 140 141 if (!$need_ajax) { 142 return; 143 } 144 145 WPPopups.initPopup($popup); 146 }); 147 $(document).trigger('wppopupsAjaxStarted'); 148 }, 149 150 /** 151 * Configure each popup 152 */ 153 initPopup: function initPopup($popup) { 154 var $id = $popup.data('id'); // Save popup for later 155 156 WPPopups.popups[$id] = $popup; // all triggers added by users 157 158 WPPopups.bindPopupTriggers($popup); // click triggers 159 160 WPPopups.bindClickTriggers($popup); // Forms inside popups 161 162 WPPopups.formSubmissions($popup); // Forms Email Marketing 163 //WPPopups.formOptinSubmissions($popup); 164 // Events bindings 165 166 $(window).resize(function () { 167 WPPopups.checkPopupSize($popup); 168 WPPopups.centerPopup($popup, true); 169 }); // This hook will be used by third parties 170 171 window.wp.hooks.doAction('wppopups_loop', $popup); 172 window.wp.hooks.addAction('wppopups_show_popup', 'wppopups', WPPopups.centerPopup, 99); // hide it 173 174 $popup.hide(); 175 }, 176 // --------------------------------------------------------------------// 177 // Triggers 178 // --------------------------------------------------------------------// 179 180 /** 181 * Trigger popup after N seconds 182 * @param value 183 * @param $popup 184 */ 185 triggerBySeconds: function triggerBySeconds(value, $popup) { 186 // if not value provided abort 187 if (value === '') { 188 return; 189 } 190 191 if ($popup.timer) { 192 clearTimeout($popup.timer); 193 } 194 195 $popup.timer = window.setTimeout(function () { 196 if (WPPopups.checkConvertion($popup)) { 197 WPPopups.showPopup($popup); 198 } 199 }, parseInt(value, 10) * 1000); 200 }, 201 202 /** 203 * Trigger if scroll % amount of screen 204 * @param value 205 * @param $popup 206 */ 207 triggerByPercentage: function triggerByPercentage(value, $popup) { 208 // if not value provided abort 209 if (value === '') { 210 return; 211 } 212 213 var $settings = WPPopups.settings($popup, 'settings'); // use custom namespace bind for off later 214 215 $(window).on('scroll.per' + $popup.data('id'), function () { 216 if ($popup.timer) { 217 clearTimeout($popup.timer); 218 } 219 220 $popup.timer = window.setTimeout(function () { 221 var triggerHeight = parseInt(value, 10) / 100 * $(document).height(); 222 var scrollY = $(window).scrollTop(), 223 trigger = scrollY + $(window).height() >= triggerHeight; // show box when criteria for this box is matched 224 225 if (trigger && WPPopups.checkConvertion($popup)) { 226 // remove listen event if box shouldn't be hidden again. TODO: add auto hide in trigger settings 227 if (!$settings.auto_hide) { 228 $(window).off('scroll.per' + $popup.data('id')); 229 } 230 231 WPPopups.showPopup($popup); 232 } else if ($settings.auto_hide) { 233 WPPopups.hidePopup($popup, false, 10, false); 234 } 235 }, 100); 236 }); 237 }, 238 239 /** 240 * Bind TRigger after user scrolled X pixels 241 * @param value 242 * @param $popup 243 */ 244 triggerByPixels: function triggerByPixels(value, $popup) { 245 // if not pixels provided abort 246 if (value === '') { 247 return; 248 } 249 250 var $settings = WPPopups.settings($popup, 'settings'); 251 $(window).on('scroll.pix' + $popup.data('id'), function () { 252 if ($popup.timer) { 253 clearTimeout($popup.timer); 254 } 255 256 $popup.timer = window.setTimeout(function () { 257 var scrollY = $(window).scrollTop(), 258 trigger = scrollY >= parseInt(value, 10); //triggerSeconds equals to the number field really 259 // show box when criteria for this box is matched 260 261 if (trigger && WPPopups.checkConvertion($popup)) { 262 // remove listen event if box shouldn't be hidden again 263 if (!$settings.auto_hide) { 264 $(window).off('scroll.pix' + $popup.data('id')); 265 } 266 267 WPPopups.showPopup($popup); 268 } else if ($settings.auto_hide) { 269 WPPopups.hidePopup($popup, false, 10, false); 270 } 271 }, 100); 272 }); 273 }, 274 triggerByClass: function triggerByClass(value, $popup) { 275 // if not class provided abort 276 if (value === '') { 277 return; 278 } // add . to class attribute if missing 279 280 281 value = value.charAt(0) != '.' ? '.' + value : value; 282 WPPopups.setBindActions(value, 'class'); // Only add the trigger if not cookies are set for popup 283 284 if (WPPopups.hasCookies($popup)) { 285 return; 286 } 287 288 $(document.body).on('click', value, function (e) { 289 e.preventDefault(); 290 WPPopups.showPopup($popup); 291 }); 292 }, 293 // --------------------------------------------------------------------// 294 // Binds 295 // --------------------------------------------------------------------// 296 297 /** 298 * Bind document triggers for popups 299 * @param $popup 300 */ 301 bindClickTriggers: function bindClickTriggers($popup) { 302 var id = $popup.data('id'); 303 WPPopups.setBindActions('.spu-open-' + id, 'class'); 304 WPPopups.setBindActions('#spu-' + id, 'href'); // Only add the trigger if not cookies are set for popup 305 306 if (WPPopups.hasCookies($popup)) { 307 return; 308 } // add link listener for this box 309 310 311 $(document.body).on('click', 'a[href="#spu-' + id + '"], .spu-open-' + id, function (e) { 312 e.preventDefault(); 313 WPPopups.showPopup($popup, true); 314 }); 315 $('a[href="#spu-' + id + '"], .spu-open-' + id).css('cursor', 'pointer').addClass('spu-clickable'); 316 }, 317 318 /** 319 * Bind triggers set in popup settings 320 * @param $popup 321 */ 322 bindPopupTriggers: function bindPopupTriggers($popup) { 323 if (window.wp.hooks.applyFilters('wppopups_disable_triggers', false, $popup)) return; 324 var $triggers = WPPopups.settings($popup, 'triggers'); 325 326 if (Object.keys($triggers).length) { 327 for (var i in $triggers) { 328 var func = 'triggerBy' + $triggers[i].trigger.charAt(0).toUpperCase() + $triggers[i].trigger.substr(1), 329 settings = WPPopups.settings($popup, 'settings'); 330 331 if (!WPPopups.hasCookies($popup) || wppopups_vars.is_preview || func == 'triggerByClass' || wppopups_vars.is_admin && settings.test_mode == '1') { 332 // only try if func really exist , may be premium one 333 if (typeof WPPopups[func] == 'function') { 334 WPPopups[func]($triggers[i].value, $popup); 335 } // hook for premium 336 337 338 window.wp.hooks.doAction('wppopups_trigger_popup', func, $triggers[i].value, $popup); 339 } 340 } 341 } 342 }, 343 344 /** 345 * Element bindings. 346 * 347 * @since 2.0.0 348 */ 349 bindUIActions: function bindUIActions() { 350 // Custom links inside popups conversion 351 $(document).on('click', '.spu-box a:not(".spu-close-popup, .spu-box .flp_wrapper a, .spu-box .spu-not-close, .spu-box .spu-not-close a, .spu-box .spu-gdpr a")', function () { 352 // hide the popup and track conversion 353 WPPopups.hidePopup($(this), true); 354 }); // Close and convert button, similar to above 355 356 $(document).on('click', '.spu-box .spu-close-convert,.spu-box .spu-close-convert a', function (e) { 357 e.preventDefault(); // hide the popup and track conversion 358 359 WPPopups.hidePopup($(this), true); 360 }); //close popup 361 362 $(document).on('click', '.spu-box .spu-close-popup', function (e) { 363 e.preventDefault(); // hide box, no conversion 364 365 WPPopups.hidePopup($(this), false); 366 }); //close with esc 367 368 $(document).on('keyup', function (e) { 369 if (e.keyCode == 27) { 370 WPPopups.togglePopups(false, false); 371 } 372 }); // close by clicking outside of popup 373 // get event by device or browser 374 375 var ua = navigator.userAgent, 376 event = ua.match(/iPad/i) || ua.match(/iPhone/i) ? "touchstart" : "click"; 377 $('body').on(event, function (ev) { 378 var $target = $(ev.target); // TODO: 379 // for some reason ninja form not working, added this dirty workaround 380 // $target.is('.spu-box *, .spu-box') return false, same for .has .parents, etc 381 // so no popup closing when form is clicked 382 383 if ($target.is('input.nf-element')) return; // verify if some bindAction is present 384 385 if (WPPopups.checkBindActions($target)) return; 386 if (window.wp.hooks.applyFilters('wppopups_disable_click_out', false, $target)) return; // test that event is user triggered and not programatically, 387 // and that it is not fired within the box 388 389 if (ev.originalEvent !== undefined && !$target.is('.spu-box *, .spu-box')) { 390 WPPopups.togglePopups(false, false); 391 } 392 }); 393 }, 394 395 /** 396 * Return setting from popup 397 * @param popup 398 * @param key 399 * @returns {*} 400 */ 401 settings: function settings(popup, key) { 402 var settings = {}; 403 404 if (popup.data('settings')) { 405 settings = popup.data('settings'); 406 } 407 408 if (key) { 409 if (typeof settings[key] == 'undefined') return {}; 410 return settings[key]; 411 } 412 413 return settings; 414 }, 415 416 /** 417 * Wrapper to execute toggle box on all popups 418 * @param elements 419 * @param show 420 * @param conversion 421 */ 422 togglePopups: function togglePopups(show, conversion) { 423 $('.spu-box').each(function () { 424 if (show) { 425 WPPopups.showPopup($(this)); 426 } else { 427 // premium version may disable advanced closing methods which use tooglePopups 428 if (window.wp.hooks.applyFilters('wppopups_allow_togglePopups', true, $(this))) { 429 WPPopups.hidePopup($(this), conversion); 430 } 431 } 432 }); 433 }, 434 435 /** 436 * Show given popup 437 * @param popup 438 * @param forse true/false 439 * @returns {boolean} 440 */ 441 showPopup: function showPopup(popup) { 442 var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
443 var $popup = WPPopups.getPopup(popup), 444 $id = $popup.data('id'), 445 $bg = $('#spu-bg-' + $id); // don't do anything if box is undergoing an animation or already visible 446 447 if ($popup.is(":animated") || $popup.is(":visible")) { 448 return false; 449 } // don't do anything if popup has any cookie created 450 451 452 if (!force && WPPopups.hasCookies($popup)) { 453 return false; 454 } // Hook before the popup it's shown 455 456 457 window.wp.hooks.doAction('wppopups_before_show_popup', $popup); // show container and the popup 458 459 $popup.parent('.wppopups-whole').show(); // Overlay Blur 460 461 WPPopups.toggleBlur($popup, true); 462 WPPopups.centerPopup($popup); // I want to stop supporting social shortcodes, let's see how many complain 463 464 /* setTimeout(function(){ 465 WPPopups.centerShortcodes($popup); 466 },1500);*/ 467 // Add scroll if needed 468 469 WPPopups.checkPopupSize($popup); // show box 470 471 var animation = WPPopups.settings($popup, 'animation'); 472 473 switch (animation.animation) { 474 case 'fade': 475 $popup.hide().fadeIn('slow'); 476 break; 477 478 case 'slide': 479 $popup.hide().slideDown('slow'); 480 break; 481 482 default: 483 $popup.show(); 484 break; 485 } //background only if not bottom/top bars 486 487 488 if (!$popup.hasClass('spu-position-top-bar') && !$popup.hasClass('spu-position-bottom-bar')) { 489 if (animation.animation === 'disable') { 490 $bg.show(); 491 } else { 492 $bg.fadeIn(); 493 } 494 } // Hook for show popup, used for example for premium animations 495 496 497 window.wp.hooks.doAction('wppopups_show_popup', $popup); // show videos if any 498 499 WPPopups.toggleVideoSources($popup, true); // trigger opening 500 501 $popup.trigger('wppopups.popup_opened', [$id]); 502 }, 503 504 /** 505 * Hide given popup 506 * @param popup 507 * @param conversion 508 * @param time 509 * @returns {boolean} 510 */ 511 hidePopup: function hidePopup(popup, conversion) { 512 var time = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; 513 var cookie = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true; 514 var $popup = WPPopups.getPopup(popup), 515 $id = $popup.data('id'), 516 $bg = $('#spu-bg-' + $id); // don't do anything if box is undergoing an animation or already visible 517 518 if ($popup.is(":animated") || $popup.is(":hidden") || window.wp.hooks.applyFilters('wppopups_cancel_hide', false, $popup, conversion)) { 519 return false; 520 } // save cookie 521 522 523 if (conversion === true) { 524 WPPopups.popupConverted($popup, cookie); 525 } 526 527 var settings = WPPopups.settings($popup, 'settings'); 528 529 if (settings.close_on_conversion == '1' && conversion || !conversion) { 530 setTimeout(function () { 531 var animation = WPPopups.settings($popup, 'animation'); // Hide with animation 532 533 switch (animation.animation) { 534 case 'slide': 535 $popup.slideUp('slow'); 536 $bg.fadeOut(); 537 break; 538 539 case 'disable': 540 $popup.hide(); 541 $bg.hide(); 542 break; 543 544 default: 545 $popup.removeClass('spu-animation-animated'); 546 $popup.fadeOut('slow'); 547 $bg.fadeOut(); 548 break; 549 } // Hook for show popup, used for example for premium animations 550 551 552 window.wp.hooks.doAction('wppopups_hide_popup', $popup); // hide videos if any 553 554 WPPopups.toggleVideoSources($popup, false); 555 }, parseInt(time, 10)); 556 } // Overlay Blur 557 558 559 WPPopups.toggleBlur($popup, false); // save cookie 560 561 WPPopups.popupClosed($popup, cookie); 562 }, 563 centerPopup: function centerPopup(popup, animate) { 564 if (!popup.hasClass('spu-position-centered') && !popup.hasClass('spu-position-float-right') && !popup.hasClass('spu-position-float-left')) { 565 return; 566 } 567 568 if (popup.is(':animated')) { 569 return; 570 } 571 572 var ww = $(window).width(), 573 wh = $(window).height(), 574 pw = popup.outerWidth(), 575 ph = popup.outerHeight(); // To center the box 576 577 var boxLeft = ww / 2 - pw / 2, 578 boxTop = wh / 2 - ph / 2; 579 var css = { 580 top: boxTop 581 }; 582 583 if (popup.hasClass('spu-position-centered')) { 584 css.left = boxLeft; 585 } 586 587 if (animate) { 588 popup.animate(css, { 589 queue: !1, 590 duration: 200 591 }); 592 } else { 593 popup.css(css); 594 } 595 }, 596 597 /** 598 * Removes the popup from DOM 599 * @param $popup 600 */ 601 removePopup: function removePopup(popup) { 602 var $popup = WPPopups.getPopup(popup), 603 $id = $popup.data('id'), 604 $bg = $('#spu-bg-' + $id), 605 $css = $('#spu-css-' + $id); 606 $popup.remove(); 607 $bg.remove(); 608 $css.remove(); 609 }, 610 611 /** 612 * Track popup conversion (save cookie) 613 * @param $popup 614 */ 615 popupConverted: function popupConverted($popup) { 616 var cookie = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; 617 618 if (cookie) { 619 // set cookie 620 var settings = WPPopups.settings($popup, 'settings'), 621 duration = parseFloat(settings.conversion_cookie_duration), 622 _cookie = settings.conversion_cookie_name, 623 type = settings.conversion_cookie_type; 624 625 if (duration > 0) { 626 WPPopups.createCookie(_cookie, true, duration, type); 627 } 628 } 629 630 $popup.trigger('wppopups.popup_converted', [$popup.data('id')]); 631 }, 632 633 /** 634 * Track popup closing (save cookie) 635 * @param $popup 636 */ 637 popupClosed: function popupClosed($popup) { 638 var cookie = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; 639 640 if (cookie) { 641 // set cookie 642 var settings = WPPopups.settings($popup, 'settings'), 643 duration = parseFloat(settings.closing_cookie_duration), 644 _cookie2 = settings.closing_cookie_name, 645 type = settings.closing_cookie_type; 646 647 if (duration > 0) { 648 WPPopups.createCookie(_cookie2, true, duration, type); 649 } 650 } 651 652 $popup.trigger('wppopups.popup_closed', [$popup.data('id')]); 653 }, 654 655 /** 656 * Check if the popup has any cookies created. 657 * @param $popup 658 */ 659 hasCookies: function hasCookies($popup) { 660 var $settings = WPPopups.settings($popup, 'settings'), 661 conversion_name = $settings.conversion_cookie_name, 662 closing_name = $settings.closing_cookie_name; 663 var hasCookies = false;
664 665 if (!(wppopups_vars.is_admin && $settings.test_mode == '1') && (WPPopups.getCookie(conversion_name) || WPPopups.getCookie(closing_name))) { 666 hasCookies = true; 667 } 668 669 return hasCookies; 670 }, 671 672 /** 673 * Apply Overlay Blur 674 * @param $popup 675 * @param show true/false 676 * @return 677 */ 678 toggleBlur: function toggleBlur($popup, show) { 679 // Overlay Blur 680 var colors = WPPopups.settings($popup, 'colors'), 681 $id = $popup.data('id'), 682 $box_css = $('#spu-css-' + $id); 683 684 if (colors.show_overlay == 'yes-blur') { 685 if (show) { 686 var blur_px = colors.overlay_blur ? colors.overlay_blur : 2, 687 blur_css = '/* ini:blur */body > *:not(.wppopups-whole) { -webkit-filter: blur(' + blur_px + 'px); -moz-filter: blur(' + blur_px + 'px); -o-filter: blur(' + blur_px + 'px); -ms-filter: blur(' + blur_px + 'px); filter: blur(' + blur_px + 'px); }/* fin:blur */'; 688 $box_css.append(blur_css); 689 } else { 690 var css = $box_css.text(), 691 ini = css.indexOf('/* ini:blur */'), 692 pro = css.substring(ini); 693 $box_css.text(css.replace(pro, '')); 694 } 695 } 696 }, 697 698 /** 699 * Check all iframes and set source to empty on page load 700 */ 701 initVideoSources: function initVideoSources() { 702 var iframe = $('.spu-box iframe'); 703 704 if (iframe && iframe.length) { 705 iframe.each(function () { 706 $(this).attr('spusrc', $(this).attr('src')); 707 $(this).attr('src', 'https://#'); 708 }); 709 } 710 }, 711 712 /** 713 * Toggle a popup iframe source 714 * @param popup 715 * @param show 716 */ 717 toggleVideoSources: function toggleVideoSources(popup, show) { 718 var iframe = popup.find('iframe'); 719 720 if (iframe && iframe.length) { 721 iframe.each(function () { 722 if (show) { 723 if ($(this).attr('spusrc')) { 724 $(this).attr('src', $(this).attr('spusrc')); 725 $(this).css("width", ""); 726 $(this).css("height", ""); 727 } 728 } else { 729 // destroy videos so they stop playing 730 $(this).attr('src', 'https://#'); 731 } 732 }); 733 } 734 }, 735 736 /** 737 * remove paddings and margins from first and last items inside popups 738 * so it looks even with the given padding 739 */ 740 resetPopupsContent: function resetPopupsContent() { 741 $(".spu-content").children().first().not('.spu-fields-container').css({ 742 "margin-top": 0, 743 "padding-top": 0 744 }).end().last().not('.spu-fields-container').css({ 745 'margin-bottom': 0, 746 'padding-bottom': 0 747 }); 748 }, 749 750 /** 751 * Trigger popup opening if anyone use #spu-open-123 in url 752 */ 753 hashTriggerPopups: function hashTriggerPopups() { 754 // shows the box when hash refers to a box 755 if (window.location.hash && window.location.hash.length > 0) { 756 var hash = window.location.hash, 757 form_key = hash.substring(1).indexOf('spu-form'), 758 link_key = hash.substring(1).indexOf('spu-open'), 759 form_popup = form_key !== -1, 760 link_popup = link_key !== -1; 761 if (form_popup) var i = form_key + 10;else if (link_popup) var i = link_key + 10; 762 763 if ((link_popup || form_popup) && $('#spu-' + hash.substring(i)).is('.spu-box')) { 764 var popup = $('#spu-' + hash.substring(i)); // if triggered by a form add a close button if don't exist already one 765 766 if (form_popup && popup.find('.spu-close-popup').length < 2) { 767 popup.find('.spu-content').append('<p style="text-align: center"><button class="btn btn-primary spu-close-popup">Close</button></p>'); 768 } // unbind regular triggers and set conversion 769 770 771 if (form_popup) { 772 $(window).off('scroll.per' + popup.data('id')); 773 $(window).off('scroll.pix' + popup.data('id')); 774 WPPopups.popupConverted(popup); 775 } 776 777 setTimeout(function () { 778 WPPopups.showPopup(popup, true); 779 }, 200); 780 } 781 } 782 }, 783 784 /** 785 * Handle form submissions in two ways. Adding ajax most known forms callbacks 786 * and adding #spu-form to action for regular forms that will open on page load 787 * @param $popup 788 */ 789 formSubmissions: function formSubmissions($popup) { 790 var popup_id = $popup.data('id'); 791 $popup.find('form').each(function () { 792 var box_form = $(this), 793 action = box_form.attr('action') || ''; 794 box_form.attr('action', action + '#spu-form-' + popup_id); // only run if not our form 795 796 if (box_form.hasClass('spu-optin-form')) { 797 return; 798 } // If url is external, we need to track popup before submission 799 800 801 if (WPPopups.isExternal(action)) { 802 box_form.on('submit', function () { 803 WPPopups.hidePopup($popup, true); 804 }); 805 } 806 }); // Ninja forms don't use form elements O_o 807 808 $(document).on('nfFormSubmitResponse', function () { 809 $popup.trigger('wppopups.form_submitted', [popup_id]); 810 WPPopups.hidePopup($popup, true, 2000); 811 }); // Contact form 7 812 813 $popup.on('wpcf7mailsent', function () { 814 $popup.trigger('wppopups.form_submitted', [popup_id]); 815 WPPopups.hidePopup($popup, true, 2000); 816 }); // Gravity forms (ajax mode) 817 818 $(document).on('gform_post_render', function (jqXHR, form_id) { 819 var $form = $('#gform_ajax_frame_' + form_id); 820 821 if (!$form.is('.spu-box *')) { 822 return; 823 } 824 825 var contents = $form.contents().find('*').html(), 826 form_content = $form.contents().find('#gform_wrapper_' + form_id), 827 is_confirmation = $form.contents().find('#gform_confirmation_wrapper_' + form_id).length > 0, 828 is_redirect = contents.indexOf('gformRedirect(){') >= 0; 829 830 if (is_confirmation || is_redirect) { 831 $popup.trigger('wppopups.form_submitted', [popup_id]); 832 WPPopups.hidePopup($popup, true, 2000); 833 } 834 }); // Caldera Forms (ajax mode) 835 836 $(document).on('cf.submission', function () { 837 $popup.trigger('wppopups.form_submitted', [popup_id]); 838 WPPopups.hidePopup($popup, true, 2000); 839 }); // WPForms (ajax mode) 840 841 $(document).on('wpformsAjaxSubmitSuccess', function () { 842 $popup.trigger('wppopups.form_submitted', [popup_id]); 843 WPPopups.hidePopup($popup, true, 2000); 844 }); // Yikes form 845 846 $(document).on('yikes_mailchimp_ajax_submission', function (e, response) { 847 if (response.success) { 848 $popup.trigger('wppopups.form_submitted', [popup_id]); 849 WPPopups.hidePopup($popup, true, 2000); 850 } 851 }); 852 }, 853 854 /** 855 * Check if url is external 856 * @param url 857 * @returns {boolean} 858 */ 859 isExternal: function isExternal(url) { 860 if (url.length === 0) { 861 return false;
862 } 863 864 var domain = function domain(url) { 865 return url.replace('http://', '').replace('https://', '').split('/')[0]; 866 }; 867 868 if (domain(url) == '') { 869 return false; 870 } 871 872 return domain(location.href) !== domain(url); 873 }, 874 875 /** 876 * Forms to Email Marketing 877 * @param $popup 878 */ 879 formOptinSubmissions: function formOptinSubmissions($popup) { 880 var popup_id = $popup.data('id'), 881 $form = $popup.find('.wppopups-form'), 882 $submit_btn = $form.find('.wppopups-submit-button'); 883 WPPopups.toggleButton($submit_btn, true); 884 var data = $form.serializeArray(); 885 data.push({ 886 name: 'action', 887 value: 'wppopups_optin_submit' 888 }); 889 data.push({ 890 name: 'id', 891 value: popup_id 892 }); 893 var ajax = { 894 url: wppopups_vars.ajax_url, 895 data: data, 896 cache: false, 897 type: 'POST', 898 dataType: 'json', 899 timeout: 30000, 900 success: function success(r) { 901 console.log(r); 902 903 if (r.success) { 904 // redirect if set in settings 905 if (r.data[1] && r.data[1].optin_redirect) { 906 $popup.trigger('wppopups.optin_on_redirect', [popup_id]); 907 var redirect = r.data[1].optin_redirect; 908 909 if (r.data[1].pass_lead_data && r.data[1].pass_lead_data == 1) { 910 var qstring = []; 911 var sep = redirect.match(/[\?]/g) ? '&' : '?'; 912 913 for (var key in data) { 914 if (data[key]['value'] == '') continue; 915 qstring.push(encodeURIComponent(data[key]['name']) + '=' + encodeURIComponent(data[key]['value'])); 916 } 917 918 redirect = redirect + sep + qstring.join('&'); 919 } 920 921 WPPopups.hidePopup($popup, true); 922 window.location.href = redirect; 923 return; 924 } // clear errors 925 926 927 $popup.find('.optin-errors').html('').hide(); // success message 928 929 if (r.data[0] && r.data[0].optin_success) { 930 WPPopups.popupConverted($popup); 931 $popup.find('.spu-content').html(r.data[0].optin_success); 932 WPPopups.centerPopup($popup, true); 933 934 if (r.data[0].optin_success_seconds && parseInt(r.data[0].optin_success_seconds) > 0) { 935 var seconds = parseInt(r.data[0].optin_success_seconds, 10) * 1000; 936 WPPopups.hidePopup($popup, false, seconds); 937 } 938 } else { 939 WPPopups.hidePopup($popup, true); 940 } 941 } else { 942 $popup.find('.optin-errors').html(r.data).fadeIn(); 943 } 944 945 $popup.trigger('wppopups.optin_form_submitted', [popup_id, r]); 946 WPPopups.toggleButton($submit_btn, false); 947 }, 948 error: function error(r) { 949 console.log(r); 950 WPPopups.toggleButton($submit_btn, false); 951 } 952 }; 953 $.ajax(ajax); 954 }, 955 loadValidation: function loadValidation() { 956 // Only load if jQuery validation library exists. 957 if (typeof $.fn.validate !== 'undefined') { 958 // Prepend URL field contents with http:// if user input doesn't contain a schema. 959 $('.wppopups-validate input[type=url]').change(function () { 960 var url = $(this).val(); 961 962 if (!url) { 963 return false; 964 } 965 966 if (url.substr(0, 7) !== 'http://' && url.substr(0, 8) !== 'https://') { 967 $(this).val('http://' + url); 968 } 969 }); 970 $.validator.messages.required = wppopups_vars.val_required; 971 $.validator.messages.url = wppopups_vars.val_url; 972 $.validator.messages.email = wppopups_vars.val_email; 973 $.validator.messages.number = wppopups_vars.val_number; // Validate email addresses. 974 975 $.validator.methods.email = function (value, element) { 976 return this.optional(element) || /^[a-z0-9.!#$%&'*+\/=?^_`{|}~-]+@((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}$/i.test(value); 977 }; // Validate checkbox choice limit. 978 979 980 $.validator.addMethod('check-limit', function (value, element) { 981 var $ul = $(element).closest('ul'), 982 $checked = $ul.find('input[type="checkbox"]:checked'), 983 choiceLimit = parseInt($ul.attr('data-choice-limit') || 0, 10); 984 985 if (0 === choiceLimit) { 986 return true; 987 } 988 989 return $checked.length <= choiceLimit; 990 }, function (params, element) { 991 var choiceLimit = parseInt($(element).closest('ul').attr('data-choice-limit') || 0, 10); 992 return wppopups_vars.val_checklimit.replace('{#}', choiceLimit); 993 }); // Finally load jQuery Validation library for our forms. 994 995 $('.wppopups-validate').each(function () { 996 var form = $(this), 997 popupID = form.data('popupid'), 998 properties; // TODO: cleanup this BC with wppopups_validate. 999 1000 if (typeof window['wppopups_' + popupID] !== 'undefined' && window['wppopups_' + popupID].hasOwnProperty('val
1000idate')) { 1001 properties = window['wppopups_' + popupID].validate; 1002 } else if (typeof wppopups_validate !== 'undefined') { 1003 properties = wppopups_validate; 1004 } else { 1005 properties = { 1006 errorClass: 'wppopups-error', 1007 validClass: 'wppopups-valid', 1008 errorPlacement: function errorPlacement(error, element) { 1009 if ('radio' === element.attr('type') || 'checkbox' === element.attr('type')) { 1010 if (element.hasClass('wppopups-likert-scale-option')) { 1011 if (element.closest('table').hasClass('single-row')) { 1012 element.closest('table').after(error); 1013 } else { 1014 element.closest('tr').find('th').append(error); 1015 } 1016 } else if (element.hasClass('wppopups-net-promoter-score-option')) { 1017 element.closest('table').after(error); 1018 } else { 1019 element.closest('.wppopups-field-checkbox').find('label.wppopups-error').remove(); 1020 element.parent().parent().parent().append(error); 1021 } 1022 } else if (element.is('select') && element.attr('class').match(/date-month|date-day|date-year/)) { 1023 if (0 === element.parent().find('label.wppopups-error:visible').length) { 1024 element.parent().find('select:last').after(error); 1025 } 1026 } else if (element.hasClass('wppopups-smart-phone-field')) { 1027 element.parent().after(error); 1028 } else if (element.hasClass('wppopups-validation-group-member')) { 1029 element.closest('.wppopups-field').append(error); 1030 } else { 1031 error.insertAfter(element); 1032 } 1033 }, 1034 highlight: function highlight(element, errorClass, validClass) { 1035 var $element = $(element), 1036 $field = $element.closest('.wppopups-field'), 1037 inputName = $element.attr('name'); 1038 1039 if ('radio' === $element.attr('type') || 'checkbox' === $element.attr('type')) { 1040 $field.find('input[name="' + inputName + '"]').addClass(errorClass).removeClass(validClass); 1041 } else { 1042 $element.addClass(errorClass).removeClass(validClass); 1043 } 1044 1045 $field.addClass('wppopups-has-error'); 1046 }, 1047 unhighlight: function unhighlight(element, errorClass, validClass) { 1048 var $element = $(element), 1049 $field = $element.closest('.wppopups-field'), 1050 inputName = $element.attr('name'); 1051 1052 if ('radio' === $element.attr('type') || 'checkbox' === $element.attr('type')) { 1053 $field.find('input[name="' + inputName + '"]').addClass(validClass).removeClass(errorClass); 1054 } else { 1055 $element.addClass(validClass).removeClass(errorClass); 1056 } 1057 1058 $field.removeClass('wppopups-has-error'); 1059 }, 1060 submitHandler: function submitHandler(form) { 1061 var $popup = $('#spu-' + popupID); 1062 WPPopups.formOptinSubmissions($popup); 1063 },
1064 invalidHandler: function invalidHandler(event, validator) {//if ( typeof validator.errorList[0] !== 'undefined' ) { 1065 // app.scrollToError( $( validator.errorList[0].element ) ); 1066 //} 1067 }, 1068 onkeyup: function onkeyup(element, event) { 1069 // This code is copied from JQuery Validate 'onkeyup' method with only one change: 'wppopups-novalidate-onkeyup' class check. 1070 var excludedKeys = [16, 17, 18, 20, 35, 36, 37, 38, 39, 40, 45, 144, 225]; 1071 1072 if ($(element).hasClass('wppopups-novalidate-onkeyup')) { 1073 return; // Disable onkeyup validation for some elements (e.g. remote calls). 1074 } 1075 1076 if (9 === event.which && '' === this.elementValue(element) || $.inArray(event.keyCode, excludedKeys) !== -1) { 1077 return; 1078 } else if (element.name in this.submitted || element.name in this.invalid) { 1079 this.element(element); 1080 } 1081 }, 1082 onfocusout: function onfocusout(element) { 1083 // This code is copied from JQuery Validate 'onfocusout' method with only one change: 'wppopups-novalidate-onkeyup' class check. 1084 var validate = false; 1085 1086 if ($(element).hasClass('wppopups-novalidate-onkeyup') && !element.value) { 1087 validate = true; // Empty value error handling for elements with onkeyup validation disabled. 1088 } 1089 1090 if (!this.checkable(element) && (element.name in this.submitted || !this.optional(element))) { 1091 validate = true; 1092 } 1093 1094 if (validate) { 1095 this.element(element); 1096 } 1097 }, 1098 onclick: function onclick(element) { 1099 var validate = false, 1100 type = (element || {}).type, 1101 $el = $(element); 1102 1103 if (['checkbox', 'radio'].indexOf(type) > -1) { 1104 if ($el.hasClass('wppopups-likert-scale-option')) { 1105 $el = $el.closest('tr'); 1106 } else { 1107 $el = $el.closest('.wppopups-field'); 1108 } 1109 1110 $el.find('label.wppopups-error').remove(); 1111 validate = true; 1112 } 1113 1114 if (validate) { 1115 this.element(element); 1116 } 1117 } 1118 }; 1119 } 1120 1121 form.validate(properties); 1122 }); 1123 } 1124 }, 1125 1126 /** 1127 * Helper func to toggle optin button 1128 * @param $submit_btn 1129 * @param disable 1130 */ 1131 toggleButton: function toggleButton($submit_btn, disable) { 1132 var oldText = $submit_btn.data('submit-text'), 1133 altText = $submit_btn.data('alt-text'); 1134 1135 if (disable) { 1136 // Display processing text. 1137 if (altText) { 1138 $submit_btn.text(altText); 1139 } 1140 1141 $submit_btn.addClass('spu-sending'); 1142 $submit_btn.attr('disabled', 'disabled'); 1143 } else { 1144 if (oldText) { 1145 $submit_btn.text(oldText); 1146 } 1147 1148 $submit_btn.removeClass('spu-sending'); 1149 $submit_btn.removeAttr('disabled'); 1150 } 1151 }, 1152 1153 /** 1154 * Check if popup it's bigger than windows to add scroll bars 1155 * @param $popup 1156 */ 1157 checkPopupSize: function checkPopupSize($popup) { 1158 // add 150 to give some margins on top/bottom 1159 if ($popup.outerHeight() + 150 >= $(window).height()) { 1160 $popup.css('overflow', 'auto').addClass('spu-scrollable'); 1161 $popup.css('max-height', $(window).height()); 1162 } else { 1163 $popup.css('overflow', 'initial').removeClass('spu-scrollable'); 1164 $popup.css('max-height', ''); 1165 } 1166 }, 1167 initSocialShortcodes: function initSocialShortcodes() { 1168 // if facebook being used on any popup 1169 if (wppopups_vars.facebook) { 1170 var SPUfb = false, 1171 FbTimer = setInterval(function () { 1172 if (typeof FB !== 'undefined' && !SPUfb) { 1173 try { 1174 FB.Event.subscribe('edge.create', function (href, html_element) { 1175 var $popup = $(html_element).parents('.spu-box'); 1176 1177 if ($popup.length) { 1178 WPPopups.hidePopup($popup, true); 1179 } 1180 }); 1181 } catch (ex) { 1182 console.log(ex); 1183 } 1184 1185 SPUfb = true; 1186 clearInterval(FbTimer); 1187 } 1188 }, 1000); 1189 } 1190 1191 if (wppopups_vars.twitter) { 1192 if (typeof twttr !== 'undefined') { 1193 try { 1194 twttr.ready(function (twttr) { 1195 twttr.events.bind('tweet', WPPopups.twitterCB); 1196 twttr.events.bind('follow', WPPopups.twitterCB); 1197 }); 1198 } catch (ex) {} 1199 } 1200 } 1201 1202 if (wppopups_vars.google) { 1203 if (typeof twttr !== 'undefined') { 1204 try { 1205 twttr.ready(function (twttr) { 1206 twttr.events.bind('tweet', WPPopups.twitterCB); 1207 twttr.events.bind('follow', WPPopups.twitterCB); 1208 }); 1209 } catch (ex) {} 1210 } 1211 } 1212 }, 1213 googleCB: function googleCB(a) { 1214 if ("on" == a.state) { 1215 var box_id = $('.spu-gogl').data('box-id'); 1216 1217 if (box_id) { 1218 WPPopups.hidePopup(box_id, true); 1219 } 1220 } 1221 }, 1222 closeGoogle: function closeGoogle(a) { 1223 if ("confirm" == a.type) { 1224 var box_id = $('.spu-gogl').data('box-id'); 1225 1226 if (box_id) { 1227 WPPopups.hidePopup(box_id, true); 1228 } 1229 } 1230 }, 1231 1232 /** 1233 * Twitter callback function 1234 * @param intent_event 1235 */ 1236 twitterCB: function twitterCB(intent_event) { 1237 var $popup = $(intent_event.target).parents('.spu-box'); 1238 1239 if ($popup) { 1240 WPPopups.hidePopup($popup, true); 1241 } 1242 }, 1243 1244 /** 1245 * Set cookie container user UUID. 1246 * 1247 */ 1248 setUserIndentifier: function setUserIndentifier() { 1249 if (wppopups_settings.uuid_cookie && !WPPopups.getCookie('_wpfuuid')) { 1250 // Generate UUID - http://stackoverflow.com/a/873856/1489528 1251 var s = new Array(36), 1252 hexDigits = '0123456789abcdef', 1253 uuid; 1254 1255 for (var i = 0; i < 36; i++) { 1256 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); 1257 } 1258
1259 s[14] = "4"; 1260 s[19] = hexDigits.substr(s[19] & 0x3 | 0x8, 1); 1261 s[8] = s[13] = s[18] = s[23] = '-'; 1262 uuid = s.join(""); 1263 WPPopups.createCookie('_wpfuuid', uuid, 3999); 1264 } 1265 }, 1266 1267 /** 1268 * Set classes or href link in a global array 1269 * @param bind_name 1270 * @param bind_type 1271 */ 1272 setBindActions: function setBindActions() { 1273 var bind_name = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; 1274 var bind_type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'class'; 1275 1276 if (bind_name != '') { 1277 switch (bind_type) { 1278 case 'class': 1279 bind_name = bind_name.charAt(0) == '.' ? bind_name.substring(1) : bind_name; 1280 if ($.inArray(bind_name, WPPopups.byBind['class']) === -1) WPPopups.byBind['class'].push(bind_name); 1281 break; 1282 1283 case 'href': 1284 if ($.inArray(bind_name, WPPopups.byBind['href']) === -1) WPPopups.byBind['href'].push(bind_name); 1285 break; 1286 } 1287 1288 window.wp.hooks.doAction('wppopups_set_bindactions', bind_name, bind_type); 1289 } 1290 }, 1291 1292 /** 1293 * Check if exists any BindActions when happen a event 1294 * @param target 1295 */ 1296 checkBindActions: function checkBindActions(target) { 1297 var $classes = WPPopups.byBind['class']; 1298 1299 for (var i = 0; i < $classes.length; i++) { 1300 if ($classes[i]) { 1301 if (target.hasClass($classes[i]) || target.closest('.' + $classes[i]).length) { 1302 return true; 1303 } 1304 } 1305 } 1306 1307 var $href = WPPopups.byBind['href']; 1308 1309 for (var _i = 0; _i < $href.length; _i++) { 1310 if ($href[_i] && target.attr('href') == $href[_i]) { 1311 return true; 1312 } 1313 } 1314 1315 return false; 1316 }, 1317 1318 /** 1319 * Create cookie. 1320 * 1321 * @since 2.0.0 1322 */ 1323 createCookie: function createCookie(name, value) { 1324 var duration = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 999; 1325 var type = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'd'; 1326 var expires = ''; // If we have a duration value, set it in the expiry of the cookie. 1327 1328 if (duration && type) { 1329 // If -1 is our value, set a session based cookie instead of a persistent cookie. 1330 if ('-1' === duration) { 1331 expires = ''; 1332 } else { 1333 var cookie_time; 1334 1335 switch (type) { 1336 case 'm': 1337 cookie_time = 60 * 1000; 1338 break; 1339 1340 case 'h': 1341 cookie_time = 60 * 60 * 1000; 1342 break; 1343 1344 default: 1345 cookie_time = 24 * 60 * 60 * 1000; 1346 } 1347 1348 var date = new Date(); 1349 date.setTime(date.getTime() + parseInt(duration) * cookie_time); 1350 expires = '; expires=' + date.toGMTString(); 1351 } 1352 } else { 1353 expires = '; expires=Thu, 01 Jan 1970 00:00:01 GMT'; 1354 } // Write the cookie. 1355 1356 1357 document.cookie = name + '=' + value + expires + '; path=/'; 1358 }, 1359 1360 /** 1361 * Retrieve cookie. 1362 * 1363 * @since 2.0.0 1364 */ 1365 getCookie: function getCookie(name) { 1366 var nameEQ = name + '=', 1367 ca = document.cookie.split(';'); 1368 1369 for (var i = 0; i < ca.length; i++) { 1370 var c = ca[i]; 1371 1372 while (c.charAt(0) === ' ') { 1373 c = c.substring(1, c.length); 1374 } 1375 1376 if (c.indexOf(nameEQ) == 0) { 1377 return c.substring(nameEQ.length, c.length); 1378 } 1379 } 1380 1381 return null; 1382 }, 1383 1384 /** 1385 * Delete cookie. 1386 */ 1387 removeCookie: function removeCookie(name) { 1388 WPPopups.createCookie(name, '', -1); 1389 }, 1390 1391 /** 1392 * Check if it's a number. So (12) and ('12') will be good 1393 * @param n 1394 * @returns {boolean} 1395 */ 1396 isNumber: function isNumber(n) { 1397 return !isNaN(parseFloat(n)) && !isNaN(n - 0); 1398 }, 1399 1400 /** 1401 * Get a popup from a string, or selector or inner selector such as coming from event 1402 * @param popup 1403 */ 1404 getPopup: function getPopup(popup) { 1405 if (WPPopups.isNumber(popup)) { 1406 return $('#spu-' + parseInt(popup)); 1407 } 1408 1409 if (popup.is('.spu-box')) { 1410 return popup; 1411 } 1412 1413 return popup.closest('.spu-box'); 1414 }, 1415 1416 /**
1417 * Check if the popup has convertion rules and fire if needed 1418 * We need to runt his check on js because some popups fire when other convert in the same page load 1419 * Eg: Show exit popup A if popup B fails 1420 * @param $popup 1421 * @returns {boolean} 1422 */ 1423 checkConvertion: function checkConvertion($popup) { 1424 // if no class trigger as normal 1425 if (!$popup.hasClass('yes-convert') && !$popup.hasClass('no-convert')) return true; 1426 var $settings = WPPopups.settings($('#spu-' + $popup.data('convert-id')), 'settings'); 1427 var popup_converted = false; 1428 1429 if (WPPopups.getCookie($settings.conversion_cookie_name)) { 1430 popup_converted = true; 1431 } 1432 1433 if ($popup.hasClass('yes-convert')) { 1434 return popup_converted; 1435 } else { 1436 return !popup_converted; 1437 } 1438 }, 1439 isMobile: function isMobile() { 1440 var Android = navigator.userAgent.match(/Android/i), 1441 BlackBerry = navigator.userAgent.match(/BlackBerry/i), 1442 iOS = navigator.userAgent.match(/iPhone|iPad|iPod/i), 1443 Opera = navigator.userAgent.match(/Opera Mini/i), 1444 Windows = navigator.userAgent.match(/IEMobile/i) || navigator.userAgent.match(/WPDesktop/i); 1445 return Android || BlackBerry || iOS || Opera || Windows; 1446 } 1447 }; // Initialize. 1448 1449 WPPopups.init(); // Add to global scope. 1450 1451 window.wppopups = WPPopups; 1452})(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.