PageSourceSearch

https://spaplatform.com.au/js/script.js

js spaplatform.com.au collected 2026-10-06 13:14:30 UTC 92,698 bytes, 2,882 lines download raw bytes

1/**
2 * Global variables
3 */
4"use strict";
5
6var userAgent = navigator.userAgent.toLowerCase(),
7  initialDate = new Date(),
8
9  $document = $(document),
10  $window = $(window),
11  $html = $("html"),
12
13  isDesktop = $html.hasClass("desktop"),
14  isIE = userAgent.indexOf("msie") != -1 ? parseInt(userAgent.split("msie")[1]) : userAgent.indexOf("trident") != -1 ? 11 : userAgent.indexOf("edge") != -1 ? 12 : false,
15  isSafari = navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1,
16  isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),
17  isTouch = "ontouchstart" in window,
18  onloadCaptchaCallback,
19  detailsBlock = document.getElementsByClassName('block-with-details'),
20  plugins = {
21      bootstrapDateTimePicker: $("[data-time-picker]"),
22      bootstrapModalDialog: $('.modal'),
23      bootstrapModalNotification: $('.notification'),
24      bootstrapTabs: $(".tabs-custom-init"),
25      bootstrapTooltip: $("[data-toggle='tooltip']"),
26      buttonNina: $('.button-nina'),
27      campaignMonitor: $('.campaign-mailform'),
28      captcha: $('.recaptcha'),
29      checkbox: $("input[type='checkbox']"),
30      circleJPlayer: $('.jp-player-circle-init'),
31      circleProgress: $(".progress-bar-circle"),
32      countDown: $(".countdown"),
33      counter: $(".counter"),
34      customToggle: $("[data-custom-toggle]"),
35      customWaypoints: $('[data-custom-scroll-to]'),
36      d3Charts: $('.d3-chart'),
37      dateCountdown: $('.DateCountdown'),
38      facebookfeed: $(".facebook"),
39      facebookWidget: $('#fb-root'),
40      flickrfeed: $(".flickr"),
41      gradientMove: $('.gradient-move'),
42      instafeed: $(".instafeed"),
43      isotope: $(".isotope"),
44      jPlayer: $('.jp-jplayer'),
45      jPlayerInit: $('.jp-player-init'),
46      jPlayerVideo: $('.jp-video-init'),
47      lightGallery: $("[data-lightgallery='group']"),
48      lightGalleryItem: $("[data-lightgallery='item']"),
49      mailchimp: $('.mailchimp-mailform'),
50      mfp: $('[data-lightbox]').not('[data-lightbox="gallery"] [data-lightbox]'),
51      mfpGallery: $('[data-lightbox^="gallery"]'),
52      materialParallax: $(".material-parallax"),
53      owl: $(".owl-carousel"),
54      pageLoader: $(".page-loader"),
55      parallaxText: $('.parallax-text'),
56      pointerEvents: isIE < 11 ? "js/pointer-events.min.js" : false,
57      popover: $('[data-toggle="popover"]'),
58      productThumb: $(".product-thumbnails"),
59      progressLinear: $(".progress-linear"),
60      radio: $("input[type='radio']"),
61      rdGoogleMaps: $(".rd-google-map"),
62      rdInputLabel: $(".form-label"),
63      rdMailForm: $(".rd-mailform"),
64      rdNavbar: $(".rd-navbar"),
65      regula: $("[data-constraints]"),
66      scroller: $(".scroll-wrap"),
67      search: $(".rd-search"),
68      searchResults: $('.rd-search-results'),
69      selectFilter: $(".select-filter"),
70      slick: $('.slick-slider'),
71      statefulButton: $('.btn-stateful'),
72      stepper: $("input[type='number']"),
73      styleSwitcher: $('.style-switcher'),
74      swiper: $(".swiper-slider"),
75      twitterfeed: $(".twitter"),
76      typedjs: $('.typed-text-wrap'),
77      videBG: $('.bg-vide'),
78      videoGallery: $('#video-gallery'),
79      viewAnimate: $('.view-animate'),
80      layoutPanel: $('.layout-panel')
81  };
82
83
84/**
85 * Initialize All Scripts
86 */
87$document.ready(function () {
88  var isNoviBuilder = window.xMode;
89
90
91  /**
92   * getSwiperHeight
93   * @description  calculate the height of swiper slider basing on data attr
94   */
95  function getSwiperHeight(object, attr) {
96    var val = object.attr("data-" + attr),
97      dim;
98
99    if (!val) {
100      return undefined;
101    }
102
103    dim = val.match(/(px)|(%)|(vh)$/i);
104
105    if (dim.length) {
106      switch (dim[0]) {
107        case "px":
108          return parseFloat(val);
109        case "vh":
110          return $(window).height() * (parseFloat(val) / 100);
111        case "%":
112          return object.width() * (parseFloat(val) / 100);
113      }
114    } else {
115      return undefined;
116    }
117  }
118
119  /**
120   * toggleSwiperInnerVideos
121   * @description  toggle swiper videos on active slides
122   */
123  function toggleSwiperInnerVideos(swiper) {
124    var prevSlide = $(swiper.slides[swiper.previousIndex]),
125      nextSlide = $(swiper.slides[swiper.activeIndex]),
126      videos;
127
128    prevSlide.find("video").each(function () {
129      this.pause();
130    });
131
132    videos = nextSlide.find("video");
133    if (videos.length) {
134      videos.get(0).play();
135    }
136  }
137
138  /**
139   * toggleSwiperCaptionAnimation
140   * @description  toggle swiper animations on active slides
141   */
142  function toggleSwiperCaptionAnimation(swiper) {
143    var prevSlide = $(swiper.container),
144      nextSlide = $(swiper.slides[swiper.activeIndex]);
145
146    prevSlide
147      .find("[data-caption-animate]")
148      .each(function () {
149        var $this = $(this);
150        $this
151          .removeClass("animated")
152          .removeClass($this.attr("data-caption-animate"))
153          .addClass("not-animated");
154      });
155
156    nextSlide
157      .find("[data-caption-animate]")
158      .each(function () {
159        var $this = $(this),
160          delay = $this.attr("data-caption-delay");
161
162
163        if (!isNoviBuilder) {
164          setTimeout(function () {
165            $this
166              .removeClass("not-animated")
167              .addClass($this.attr("data-caption-animate"))
168              .addClass("animated");
169          }, delay ? parseInt(delay) : 0);
170        } else {
171          $this
172            .removeClass("not-animated")
173        }
174      });
175  }
176
177  /**
178   * initSwiperWaypoints
179   * @description  init waypoints on new slides
180   */
181  function initSwiperWaypoints(swiper) {
182    var prevSlide = $(swiper.container),
183      nextSlide = $(swiper.slides[swiper.activeIndex]);
184
185    prevSlide
186      .find('[data-custom-scroll-to]')
187      .each(function () {
188        var $this = $(this);
189        initCustomScrollTo($this);
190      });
191
192    nextSlide
193      .find('[data-custom-scroll-to]')
194      .each(function () {
195        var $this = $(this);
196        initCustomScrollTo($this);
197      });
198  }
199
200  /**
201   * initSwiperButtonsNina
202   * @description  toggle waypoints on active slides
203   */
204  function initSwiperButtonsNina(swiper) {
205    var prevSlide = $(swiper.container),
206      nextSlide = $(swiper.slides[swiper.activeIndex]);
207
208    prevSlide
209      .find('.button-nina')
210      .each(function () {
211        initNinaButtons(this);
212      });
213
214    nextSlide
215      .find('.button-nina')
216      .each(function () {
217        initNinaButtons(this);
218      });
219  }
220
221
222  /**
223   * makeParallax
224   * @description  create swiper parallax scrolling effect
225   */
226  function makeParallax(el, speed, wrapper, prevScroll) {
227    var scrollY = window.scrollY || window.pageYOffset;
228
229    if (prevScroll != scrollY) {
230      prevScroll = scrollY;
231      el.addClass('no-transition');
232      el[0].style['transform'] = 'translate3d(0,' + -scrollY * (1 - speed) + 'px,0)';
233      el.height();
234      el.removeClass('no-transition');
235
236      if (el.attr('data-fade') === 'true') {
237        var bound = el[0].getBoundingClientRect(),
238          offsetTop = bound.top * 2 + scrollY,
239          sceneHeight = wrapper.outerHeight(),
240          sceneDevider = wrapper.offset().top + sceneHeight / 2.0,
241          layerDevider = offsetTop + el.outerHeight() / 2.0,
242          pos = sceneHeight / 6.0,
243          opacity;
244        if (sceneDevider + pos > layerDevider && sceneDevider - pos < layerDevider) {
245          el[0].style["opacity"] = 1;
246        } else {
247          if (sceneDevider - pos < layerDevider) {
248            opacity = 1 + ((sceneDevider + pos - layerDevider) / sceneHeight / 3.0 * 5);
249          } else {
250            opacity = 1 - ((sceneDevider - pos - layerDevider) / sceneHeight / 3.0 * 5);
251          }
252          el[0].style["opacity"] = opacity < 0 ? 0 : opacity > 1 ? 1 : opacity.toFixed(2);
253        }
254      }
255    }
256
257    requestAnimationFrame(function () {
258      makeParallax(el, speed, wrapper, prevScroll);
259    });
260  }
261
262  /**
263   * initCustomScrollTo
264   * @description  init smooth anchor animations
265   */
266  function initCustomScrollTo(obj) {
267    var $this = $(obj);
268    if (!isNoviBuilder) {
269      $this.on('click', function (e) {
270        e.preventDefault();
271        $("body, html").stop().animate({
272          scrollTop: $($(this).attr('data-custom-scroll-to')).offset().top
273        }, 1000, function () {
274          $window.trigger("resize");
275        });
276      });
277    }
278  }
279
280
281  /**
282   * initOwlCarousel
283   * @description  Init owl carousel plugin
284   */
285  function initOwlCarousel(c) {
286    var aliaces = ["-", "-xs-", "-sm-", "-md-", "-lg-", "-xl-"],
287      values = [0, 480, 768, 992, 1200, 1600],
288      responsive = {},
289      j, k;
290
291    for (j = 0; j < values.length; j++) {
292      responsive[values[j]] = {};
293      for (k = j; k >= -1; k--) {
294        if (!responsive[values[j]]["items"] && c.attr("data" + aliaces[k] + "
294items")) {
295          responsive[values[j]]["items"] = k < 0 ? 1 : parseInt(c.attr("data" + aliaces[k] + "items"));
296        }
297        if (!responsive[values[j]]["stagePadding"] && responsive[values[j]]["stagePadding"] !== 0 && c.attr("data" + aliaces[k] + "stage-padding")) {
298          responsive[values[j]]["stagePadding"] = k < 0 ? 0 : parseInt(c.attr("data" + aliaces[k] + "stage-padding"));
299        }
300        if (!responsive[values[j]]["margin"] && responsive[values[j]]["margin"] !== 0 && c.attr("data" + aliaces[k] + "margin")) {
301          responsive[values[j]]["margin"] = k < 0 ? 30 : parseInt(c.attr("data" + aliaces[k] + "margin"));
302        }
303      }
304    }
305
306    // Enable custom pagination
307    if (c.attr('data-dots-custom')) {
308      c.on("initialized.owl.carousel", function (event) {
309        var carousel = $(event.currentTarget),
310          customPag = $(carousel.attr("data-dots-custom")),
311          active = 0;
312
313        if (carousel.attr('data-active')) {
314          active = parseInt(carousel.attr('data-active'));
315        }
316
317        carousel.trigger('to.owl.carousel', [active, 300, true]);
318        customPag.find("[data-owl-item='" + active + "']").addClass("active");
319
320        customPag.find("[data-owl-item]").on('click', function (e) {
321          e.preventDefault();
322          carousel.trigger('to.owl.carousel', [parseInt(this.getAttribute("data-owl-item")), 300, true]);
323        });
324
325        carousel.on("translate.owl.carousel", function (event) {
326          customPag.find(".active").removeClass("active");
327          customPag.find("[data-owl-item='" + event.item.index + "']").addClass("active")
328        });
329      });
330    }
331
332    c.on("initialized.owl.carousel", function () {
333      initNinaButtons($(this).find('button-nina'));
334    });
335
336    if (c.attr('data-nav-custom')) {
337      c.on("initialized.owl.carousel", function (event) {
338        var carousel = $(event.currentTarget),
339          customNav = $(carousel.attr("data-nav-custom"));
340
341        // Custom Navigation Events
342        customNav.find(".owl-arrow-next").click(function (e) {
343          e.preventDefault();
344          carousel.trigger('next.owl.carousel');
345        });
346        customNav.find(".owl-arrow-prev").click(function (e) {
347          e.preventDefault();
348          carousel.trigger('prev.owl.carousel');
349        });
350      });
351    }
352
353    if (c.attr('data-custom-nav')) {
354      c.on("initialized.owl.carousel", function (event) {
355        var carousel = $(event.currentTarget),
356          customNav = carousel.parent().find('.owl-carousel-widget-nav');
357
358        // Custom Navigation Events
359        customNav.find(".slick-next").click(function (e) {
360          e.preventDefault();
361          carousel.trigger('next.owl.carousel');
362        });
363        customNav.find(".slick-prev").click(function (e) {
364          e.preventDefault();
365          carousel.trigger('prev.owl.carousel');
366        });
367      });
368    }
369
370    c.owlCarousel({
371      autoplay: c.attr("data-autoplay") === "true",
372      loop: isNoviBuilder ? false : c.attr('data-loop') == 'true',
373      items: 1,
374      center: c.attr("data-center-mod") || false,
375      dotsContainer: c.attr("data-pagination-class") || false,
376      navContainer: c.attr("data-navigation-class") || false,
377      mouseDrag: isNoviBuilder ? false : c.attr("data-mouse-drag") !== "false",
378      nav: c.attr("data-nav") === "true" && !c.attr('data-nav-custom'),
379      dots: c.attr("data-dots") === "true",
380      dotsEach: c.attr("data-dots-each") ? parseInt(c.attr("data-dots-each")) : false,
381      animateIn: c.attr('data-animation-in') ? c.attr('data-animation-in') : false,
382      animateOut: c.attr('data-animation-out') ? c.attr('data-animation-out') : false,
383      responsive: responsive,
384      navText: $.parseJSON(c.attr("data-nav-text")) || [],
385      navClass: $.parseJSON(c.attr("data-nav-class")) || ['owl-prev', 'owl-next']
386    });
387  }
388
389  /**
390   * isScrolledIntoView
391   * @description  check the element whas been scrolled into the view
392   */
393  function isScrolledIntoView(elem) {
394    if (!isNoviBuilder) {
395      return elem.offset().top + elem.outerHeight() >= $window.scrollTop() && elem.offset().top <= $window.scrollTop() + $window.height();
396    }
397    else {
398      return true;
399    }
400  }
401
402  /**
403   * Parallax text
404   * @description  function for parallax text
405   */
406  function scrollText($this) {
407    var translate = (scrollTop - $($this).data('orig-offset')) / $window.height() * 35;
408    $($this).css({transform: 'translate3d(0,' + translate + '%' + ', 0)'});
409  }
410
411  /**
412   * initOnView
413   * @description  calls a function when element has been scrolled into the view
414   */
415  function lazyInit(element, func) {
416    var $win = jQuery(window);
417    $win.on('load scroll', function () {
418      if ((!element.hasClass('lazy-loaded') && (isScrolledIntoView(element)))) {
419        func.call();
420        element.addClass('lazy-loaded');
421      }
422    });
423  }
424
425  /**
426   * initNinaButtons
427   * @description  Make hover effect for nina buttons
428   */
429  function initNinaButtons(ninaButtons) {
430    for (var i = 0; i < ninaButtons.length; i++) {
431      var btn = ninaButtons[i],
432        origContent = btn.innerHTML.trim();
433
434      if (!origContent) {
435        continue;
436      }
437
438      var textNode = Array.prototype.slice.call(ninaButtons[i].childNodes).filter(function (node) {
439        return node.nodeType === 3;
440      }).pop();
441      if (textNode == null) {
442        continue;
443      }
444
445      var dummy = document.createElement('div');
446      btn.replaceChild(dummy, textNode);
447      dummy.outerHTML = textNode.textContent.split('').map(function (letter) {
448        return "<span>" + letter.trim() + "</span>";
449      }).join('');
450
451      Array.prototype.slice.call(btn.childNodes).forEach(function (el, count) {
452        el.style.transition = 'opacity .22s ' + 0.03 * count + 's,' + ' transform .22s ' + 0.03 * count + 's' + ', color .22s';
453      });
454
455      btn.innerHTML += "<span class='button-original-content'>" + origContent + "</span>";
456
457      var delay = 0.03 * (btn.childElementCount - 1);
458      // btn.getElementsByClassName('button-original-content')[0].style.transitionDelay = delay + 's';
459      btn.getElementsByClassName('button-original-content')[0].style.transition = 'background .22s, color .22s, transform .22s ' + delay + 's';
460
461      btn.addEventListener('mouseenter', function(e) {
462        e.stopPropagation();
463      });
464
465      btn.addEventListener('mouseleave', function(e) {
466        e.stopPropagation();
467      });
468    }
469  }
470
471  /**
472   * Live Search
473   * @description  create live search results
474   */
475  function liveSearch(options) {
476    options.live.removeClass('cleared').html();
477    options.current++;
478    options.spin.addClass('loading');
479
480    $.get(handler, {
481      s: decodeURI(options.term),
482      liveSearch: options.element.attr('data-search-live'),
483      dataType: "html",
484      liveCount: options.liveCount,
485      filter: options.filter,
486      template: options.template
487    }, function (data) {
488      options.processed++;
489      var live = options.live;
490      if (options.processed == options.current && !live.hasClass('cleared')) {
491        live.find('> #search-results').removeClass('active');
492        live.html(data);
493        setTimeout(function () {
494          live.find('> #search-results').addClass('active');
495        }, 50);
496      }
497      options.spin.parents('.rd-search').find('.input-group-addon').removeClass('loading');
498    })
499  }
500
501  /**
502   * attachFormValidator
503   * @description  attach form validation to elements
504   */
505  function attachFormValidator(elements) {
506    for (var i = 0; i < elements.length; i++) {
507      var o = $(elements[i]), v;
508      o.addClass("form-control-has-validation").after("<span class='form-validation'></span>");
509      v = o.parent().find(".form-validation");
510      if (v.is(":last-child")) {
511        o.addClass("form-control-last-child");
512      }
513    }
514
515    elements
516      .on('input change propertychange blur', function (e) {
517        var $this = $(this), results;
518
519        if (e.type != "blur") {
520          if (!$this.parent().hasClass("has-error")) {
521            return;
522          }
523        }
524
525        if ($this.parents('.rd-mailform').hasClass('success')) {
526          return;
527        }
528
529        if ((results = $this.regula('validate')).length) {
530          for (i = 0; i < results.length; i++) {
531            $this.siblings(".form-validation").text(results[i].message).parent().addClass("has-error")
532          }
533        } else {
534          $this.siblings(".form-validation").text("").parent().removeClass("has-error")
535        }
536      })
537      .regula('bind');
538
539    var regularConstraintsMessages = [
540      {
541        type: regula.Constraint.Required,
542        newMessage: "The text field is required."
543      },
544      {
545        type: regula.Constraint.Email,
546        newMessage: "The email is not a valid email."
547      },
548      {
549        type: regula.Constraint.Numeric,
550        newMessage: "Only numbers are required"
551      },
552      {
553        type: regula.Constraint.Selected,
554        newMessage: "Please choose an option."
555      }
556    ];
557
558
559    for (var i = 0; i < regularConstraintsMessages.length; i++) {
560      var regularConstraint = regularConstraintsMessages[i];
561
562      regula.override({
563        constraintType: regularConstraint.type,
564        defaultMessage: regularConstraint.newMessage
565      });
566    }
567  }
568
569  /**
570   * isValidated
571   * @description  check if all elemnts pass validation
572   */
573  function isValidated(elements, captcha) {
574    var results, errors = 0;
575
576    if (elements.length) {
577      for (j = 0; j < elements.length; j++) {
578
579        var $input = $(elements[j]);
580        if ((results = $input.regula('validate')).length) {
581          for (k = 0; k < results.length; k++) {
582            errors++;
583            $input.siblings(".form-validation").text(results[k].message).parent().addClass("has-error");
584          }
585        } else {
586          $input.siblings(".form-validation").text("").parent().removeClass("has-error")
587        }
588      }
589
590      if (captcha) {
591        if (captcha.length) {
592          return validateReCaptcha(captcha) && errors == 0
593        }
594      }
595
596      return errors == 0;
597    }
598    return true;
599  }
600
601  /**
602   * Init Bootstrap tooltip
603   * @description  calls a function when need to init bootstrap tooltips
604   */
605  function initBootstrapTooltip(tooltipPlacement) {
606    if (window.innerWidth < 599) {
607      plugins.bootstrapTooltip.tooltip('destroy');
608      plugins.bootstrapTooltip.tooltip({
609        placement: 'bottom'
610      });
611    } else {
612      plugins.bootstrapTooltip.tooltip('destroy');
613      plugins.bootstrapTooltip.tooltip({
614        placement: tooltipPlacement
615      });
616    }
617  }
618
619
620  /**
621   * layoutPanel
622   * @description  Enables layoutPanel
623   */
624  if (plugins.layoutPanel.length > 0) {
625    var altColorToggle = plugins.layoutPanel.find('.alt-color-toggle'),
626      pageHead = document.head,
627      defaultCss = $(pageHead).find('link[href="css/style.css"]');
628
629    var altColorLink = document.createElement( "link" );
630    altColorLink.href = "css/style-alt-colors.css";
631    altColorLink.type = "text/css";
632    altColorLink.rel = "stylesheet";
633
634
635    if (altColorToggle[0].checked) {
636      defaultCss.after(altColorLink);
637    }
638
639    altColorToggle.click(function (e) {
640      if (e.target.checked) {
641        defaultCss.after(altColorLink);
642      }else{
643        pageHead.removeChild(altColorLink);
644      }
645    });
646  } 
647  
648  /**
649   * Copyright Year
650   * @description  Evaluates correct copyright year
651   */
652  var o = $(".copyright-year");
653  if (o.length) {
654    o.text(initialDate.getFullYear());
655  }
656
657
658  /**
659   * Page loader
660   * @description Enables Page loader
661   */
662  if (plugins.pageLoader.length > 0) {
663    $window.on("load", function () {
664      var loader = setTimeout(function () {
665        // plugins.pageLoader.addClass("loaded");
666        plugins.pageLoader.fadeOut(500, function(){
667          $(this).remove();
668        });
669
670        $window.trigger("resize");
671      }, 1000);
672    });
673  }
674
675  /**
676   * validateReCaptcha
677   * @description  validate google reCaptcha
678   */
679  function validateReCaptcha(captcha) {
680    var $captchaToken = captcha.find('.g-recaptcha-response').val();
681
682    if ($captchaToken == '') {
683      captcha
684        .siblings('.form-validation')
685        .html('Please, prove that you are not robot.')
686        .addClass('active');
687      captcha
688        .closest('.form-group')
689        .addClass('has-error');
690
691      captcha.bind('propertychange', function () {
692        var $this = $(this),
693          $captchaToken = $this.find('.g-recaptcha-response').val();
694
695        if ($captchaToken != '') {
696          $this
697            .closest('.form-group')
698            .removeClass('has-error');
699          $this
700            .siblings('.form-validation')
701            .removeClass('active')
702            .html('');
703          $this.unbind('propertychange');
704        }
705      });
706
707      return false;
708    }
709
710    return true;
711  }
712
713  /**
714   * onloadCaptchaCallback
715   * @description  init google reCaptcha
716   */
717  onloadCaptchaCallback = function () {
718    for (i = 0; i < plugins.captcha.length; i++) {
719      var $capthcaItem = $(plugins.captcha[i]);
720
721      grecaptcha.render(
722        $capthcaItem.attr('id'),
723        {
724          sitekey: $capthcaItem.attr('data-sitekey'),
725          size: $capthcaItem.attr('data-size') ? $capthcaItem.attr('data-size') : 'normal',
726          theme: $capthcaItem.attr('data-theme') ? $capthcaItem.attr('data-theme') : 'light',
727          callback: function (e) {
728            $('.recaptcha').trigger('propertychange');
729          }
730        }
731      );
732      $capthcaItem.after("<span class='form-validation'></span>");
733    }
734  };
735
736  /**
737   * Google ReCaptcha
738   * @description Enables Google ReCaptcha
739   */
740  if (plugins.captcha.length) {
741    $.getScript("//www.google.com/recaptcha/api.js?onload=onloadCaptchaCallback&render=explicit&hl=en");
742  }
743
744  /**
745   * Is Mac os
746   * @description  add additional class on html if mac os.
747   */
748  if (navigator.platform.match(/(Mac)/i)) $html.addClass("mac-os");
749
750
751  /**
752   * Is Safari
753   * @description  add additional class on html if safari.
754   */
755  if (isSafari) $html.addClass("safari");
756
757  /**
758   * IE Polyfills
759   * @description  Adds some loosing functionality to IE browsers
760   */
761  if (isIE) {
762    if (isIE < 10) {
763      $html.addClass("lt-ie-10");
764    }
765
766    if (isIE < 11) {
767      if (plugins.pointerEvents) {
768        $.getScript(plugins.pointerEvents)
769          .done(function () {
770            $html.addClass("ie-10");
771            PointerEventsPolyfill.initialize({});
772          });
773      }
774    }
775
776    if (isIE === 11) {
777      $("html").addClass("ie-11");
778    }
779
780    if (isIE === 12) {
781      $("html").addClass("ie-edge");
782    }
783  }
784
785  /**
786   * Bootstrap Tooltips
787   * @description Activate Bootstrap Tooltips
788   */
789  if (plugins.bootstrapTooltip.length && !isNoviBuilder) {
790    var tooltipPlacement = plugins.bootstrapTooltip.attr('data-placement');
791    initBootstrapTooltip(tooltipPlacement);
792    $(window).on('resize orientationchange', function () {
793      initBootstrapTooltip(tooltipPlacement);
794    })
795  }
796
797  /**
798   * bootstrapModalDialog
799   * @description Stop video in bootstrapModalDialog
800   */
801  if (plugins.bootstrapModalDialog.length > 0) {
802    var i = 0;
803
804    for (i = 0; i < plugins.bootstrapModalDialog.length; i++) {
805      var modalItem = $(plugins.bootstrapModalDialog[i]);
806
807      modalItem.on('hidden.bs.modal', $.proxy(function () {
808        var activeModal = $(this),
809          rdVideoInside = activeModal.find('video'),
810          youTubeVideoInside = activeModal.find('iframe');
811
812        if (rdVideoInside.length) {
813          rdVideoInside[0].pause();
814        }
815
816        if (youTubeVideoInside.length) {
817          var videoUrl = youTubeVideoInside.attr('src');
818
819          youTubeVideoInside
820            .attr('src', '')
821            .attr('src', videoUrl);
822        }
823      }, modalItem))
824    }
825  }
826
827  /**
828   * JQuery mousewheel plugin
829   * @description  Enables jquery mousewheel plugin
830   */
831  if (plugins.scroller.length) {
832    var i;
833    for (i = 0; i < plugins.scroller.length; i++) {
834      var scrollerItem = $(plugins.scroller[i]);
835
836      scrollerItem.mCustomScrollbar({
837        theme: scrollerItem.attr('data-theme') ? scrollerItem.attr('data-theme') : 'minimal',
838        scrollInertia: 100,
839        scrollButtons: {enable: false}
840      });
841    }
842  }
843
844
845  /**
846   * RD Google Maps
847   * @description Enables RD Google Maps plugin
848   */
849  if (plugins.rdGoogleMaps.length) {
850    var i;
851
852    $.getScript("//maps.google.com/maps/api/js?key=AIzaSyAwH60q5rWrS8bXwpkZwZwhw9Bw0pqKTZM&sensor=false&libraries=geometry,places&v=3.7", function () {
853      var head = document.getElementsByTagName('head')[0],
854        insertBefore = head.insertBefore;
855
856      head.insertBefore = function (newElement, referenceElement) {
857        if (newElement.href && newElement.href.indexOf('//fonts.googleapis.com/css?family=Roboto') != -1 || newElement.innerHTML.indexOf('gm-style') != -1) {
858          return;
859        }
860        insertBefore.call(head, newElement, referenceElement);
861      };
862
863      for (i = 0; i < plugins.rdGoogleMaps.length; i++) {
864
865        var $googleMapItem = $(plugins.rdGoogleMaps[i]);
866
867        lazyInit($googleMapItem, $.proxy(function () {
868          var $this = $(this),
869            styles = $this.attr("data-styles");
870
871          $this.googleMap({
872            marker: {
873              basic: $this.data('marker'),
874              active: $this.data('marker-active')
875            },
876            styles: styles ? JSON.parse(styles) : [],
877            onInit: function (map) {
878              var inputAddress = $('#rd-google-map-address');
879
880
881              if (inputAddress.length) {
882                var input = inputAddress;
883                var geocoder = new google.maps.Geocoder();
884                var marker = new google.maps.Marker(
885                  {
886                    map: map,
887                    icon: $this.data('marker-url'),
888                  }
889                );
890
891                var autocomplete = new google.maps.places.Autocomplete(inputAddress[0]);
892                autocomplete.bindTo('bounds', map);
893                inputAddress.attr('placeholder', '');
894                inputAddress.on('change', function () {
895                  $("#rd-google-map-address-submit").trigger('click');
896                });
897                inputAddress.on('keydown', function (e) {
898                  if (e.keyCode == 13) {
899                    $("#rd-google-map-address-submit").trigger('click');
900                  }
901                });
902
903
904                $("#rd-google-map-address-submit").on('click', function (e) {
905                  e.preventDefault();
906                  var address = input.val();
907                  geocoder.geocode({'address': address}, function (results, status) {
908                    if (status == google.maps.GeocoderStatus.OK) {
909                      var latitude = results[0].geometry.location.lat();
910                      var longitude = results[0].geometry.location.lng();
911
912                      map.setCenter(new google.maps.LatLng(
913                        parseFloat(latitude),
914                        parseFloat(longitude)
915                      ));
916                      marker.setPosition(new google.maps.LatLng(
917                        parseFloat(latitude),
918                        parseFloat(longitude)
919                      ))
920                    }
921                  });
922                });
923              }
924            }
925          });
926        }, $googleMapItem));
927      }
928    });
929  }
930
931  /**
932   * Facebook widget
933   * @description  Enables official Facebook widget
934   */
935  if (plugins.facebookWidget.length) {
936    lazyInit(plugins.facebookWidget, function () {
937      (function (d, s, id) {
938        var js, fjs = d.getElementsByTagName(s)[0];
939        if (d.getElementById(id)) return;
940        js = d.createElement(s);
941        js.id = id;
942        js.src = "//connect.facebook.net/en_EN/sdk.js#xfbml=1&version=v2.5";
943        fjs.parentNode.insertBefore(js, fjs);
944      }(document, 'script', 'facebook-jssdk'));
945    });
946  }
947
948  /**
949   * Radio
950   * @description Add custom styling options for input[type="radio"]
951   */
952  if (plugins.radio.length) {
953    var i;
954    for (i = 0; i < plugins.radio.length; i++) {
955      var $this = $(plugins.radio[i]);
956      $this.addClass("radio-custom").after("<span class='radio-custom-dummy'></span>")
957    }
958  }
959
960  /**
961   * Checkbox
962   * @description Add custom styling options for input[type="checkbox"]
963   */
964  if (plugins.checkbox.length) {
965    var i;
966    for (i = 0; i < plugins.checkbox.length; i++) {
967      var $this = $(plugins.checkbox[i]);
968      $this.addClass("checkbox-custom").after("<span class='checkbox-custom-dummy'></span>")
969    }
970  }
971
972  /**
973   * Popovers
974   * @description Enables Popovers plugin
975   */
976  if (plugins.popover.length) {
977    if (window.innerWidth < 767) {
978      plugins.popover.attr('data-placement', 'bottom');
979      plugins.popover.popover();
980    }
981    else {
982      plugins.popover.popover();
983    }
984  }
985
986  /**
987   * Bootstrap Buttons
988   * @description  Enable Bootstrap Buttons plugin
989   */
990  if (plugins.statefulButton.length) {
991    $(plugins.statefulButton).on('click', function () {
992      var statefulButtonLoading = $(this).button('loading');
993
994      setTimeout(function () {
995        statefulButtonLoading.button('reset')
996      }, 2000);
997    })
998  }
999
1000  /**
1001   * UI To Top
1002   * @description Enables ToTop Button
1003   */
1004  if (isDesktop && !isNoviBuilder) {
1005    $().UItoTop({
1006      easingType: 'easeOutQuart',
1007      containerClass: 'ui-to-top'
1008    });
1009  }
1010
1011  /**
1012   * RD Navbar
1013   * @description Enables RD Navbar plugin
1014   */
1015  if (plugins.rdNavbar.length) {
1016    for (i = 0; i < plugins.rdNavbar.length; i++) {
1017      var $currentNavbar = $(plugins.rdNavbar[i]);
1018      $currentNavbar.RDNavbar({
1019        stickUpClone: ($currentNavbar.attr("data-stick-up-clone") && !isNoviBuilder) ? $currentNavbar.attr("data-stick-up-clone") === 'true' : false,
1020        responsive: {
1021          0: {
1022            stickUp: (!isNoviBuilder) ? $currentNavbar.attr("data-stick-up") === 'true' : false
1023          },
1024          768: {
1025            stickUp: (!isNoviBuilder) ? $currentNavbar.attr("data-sm-stick-up") === 'true' : false
1026          },
1027          992: {
1028            stickUp: (!isNoviBuilder) ? $currentNavbar.attr("data-md-stick-up") === 'true' : false
1029          },
1030          1200: {
1031            stickUp: (!isNoviBuilder) ? $currentNavbar.attr("data-lg-stick-up") === 'true' : false
1032          }
1033        },
1034        callbacks: {
1035          onStuck: function () {
1036            var navbarSearch = this.$element.find('.rd-search input');
1037
1038            if (navbarSearch) {
1039              navbarSearch.val('').trigger('propertychange');
1040            }
1041          },
1042          onUnstuck: function () {
1043            if (this.$clone === null)
1044              return;
1045
1046            var navbarSearch = this.$clone.find('.rd-search input');
1047
1048            if (navbarSearch) {
1049              navbarSearch.val('').trigger('propertychange');
1050              navbarSearch.blur();
1051            }
1052          },
1053          onDropdownOver: function(){
1054            return !isNoviBuilder;
1055          }
1056        }
1057      });
1058      if (plugins.rdNavbar.attr("data-body-class")) {
1059        document.body.className += ' ' + plugins.rdNavbar.attr("data-body-class");
1060      }
1061    }
1062  }
1063
1064
1065  /**
1066   * RD Search
1067   * @description Enables search
1068   */
1069  if (plugins.search.length || plugins.searchResults) {
1070    var handler = "bat/rd-search.php";
1071    var defaultTemplate = '<h5 class="search_title"><a target="_top" href="#{href}" class="search_link">#{title}</a></h5>' +
1072      '<p>...#{token}...</p>' +
1073      '<p class="match"><em>Terms matched: #{count} - URL: #{href}</em></p>';
1074    var defaultFilter = '*.html';
1075
1076    if (plugins.search.length) {
1077
1078      plugins.search = $('.' + plugins.search[0].className);
1079
1080      for (i = 0; i < plugins.search.length; i++) {
1081        var searchItem = $(plugins.search[i]),
1082          options = {
1083            element: searchItem,
1084            filter: (searchItem.attr('data-search-filter')) ? searchItem.attr('data-search-filter') : defaultFilter,
1085            template: (searchItem.attr('data-search-template')) ? searchItem.attr('data-search-template') : defaultTemplate,
1086            live: (searchItem.attr('data-search-live')) ? (searchItem.find('.' + searchItem.attr('data-search-live'))) : false,
1087            liveCount: (searchItem.attr('data-search-live-count')) ? parseInt(searchItem.attr('data-search-live')) : 4,
1088            current: 0, processed: 0, timer: {}
1089          };
1090
1091        if ($('.rd-navbar-search-toggle').length) {
1092          var toggle = $('.rd-navbar-search-toggle');
1093          toggle.on('click', function () {
1094            if (!($(this).hasClass('active'))) {
1095              searchItem.find('input').val('').trigger('propertychange');
1096            }
1097          });
1098        }
1099
1100        if (options.live) {
1101          options.clearHandler = false;
1102
1103          searchItem.find('input').on("keyup input propertychange", $.proxy(function () {
1104            var ctx = this;
1105
1106            this.term = this.element.find('input').val().trim();
1107            this.spin = this.element.find('.input-group-addon');
1108
1109            clearTimeout(ctx.timer);
1110
1111            if (ctx.term.length > 2) {
1112              ctx.timer = setTimeout(liveSearch(ctx), 200);
1113
1114              if (ctx.clearHandler == false) {
1115                ctx.clearHandler = true;
1116
1117                $("body").on("click", function (e) {
1118                  if ($(e.toElement).parents('.rd-search').length == 0) {
1119                    ctx.live.addClass('cleared').html('');
1120                  }
1121                })
1122              }
1123
1124            } else if (ctx.term.length == 0) {
1125              ctx.live.addClass('cleared').html('');
1126            }
1127          }, options, this));
1128        }
1129
1130        searchItem.submit($.proxy(function () {
1131          $('<input />').attr('type', 'hidden')
1132            .attr('name', "filter")
1133            .attr('value', this.filter)
1134            .appendTo(this.element);
1135          return true;
1136        }, options, this))
1137      }
1138    }
1139
1140    if (plugins.searchResults.length) {
1141      var regExp = /\?.*s=([^&]+)\&filter=([^&]+)/g;
1142      var match = regExp.exec(location.search);
1143
1144      if (match != null) {
1145        $.get(handler, {
1146          s: decodeURI(match[1]),
1147          dataType: "html",
1148          filter: match[2],
1149          template: defaultTemplate,
1150          live: ''
1151        }, function (data) {
1152          plugins.searchResults.html(data);
1153        })
1154      }
1155    }
1156  }
1157
1158
1159  /**
1160   * ViewPort Universal
1161   * @description Add class in viewport
1162   */
1163  if (plugins.viewAnimate.length) {
1164    var i;
1165    for (i = 0; i < plugins.viewAnimate.length; i++) {
1166      var $view = $(plugins.viewAnimate[i]).not('.active');
1167      $document.on("scroll", $.proxy(function () {
1168        if (isScrolledIntoView(this)) {
1169          this.addClass("active");
1170        }
1171      }, $view))
1172        .trigger("scroll");
1173    }
1174  }
1175
1176
1177  /**
1178   * Swiper 3.1.7
1179   * @description  Enable Swiper Slider
1180   */
1181  if (plugins.swiper.length) {
1182    var i;
1183    for (i = 0; i < plugins.swiper.length; i++) {
1184      var s = $(plugins.swiper[i]);
1185      var pag = s.find(".swiper-pagination"),
1186        next = s.find(".swiper-button-next"),
1187        prev = s.find(".swiper-button-prev"),
1188        bar = s.find(".swiper-scrollbar"),
1189        parallax = s.parents('.rd-parallax').length,
1190        swiperSlide = s.find(".swiper-slide"),
1191        autoplay = false;
1192
1193      for (j = 0; j < swiperSlide.length; j++) {
1194        var $this = $(swiperSlide[j]),
1195          url;
1196
1197        if (url = $this.attr("data-slide-bg")) {
1198          $this.css({
1199            "background-image": "url(" + url + ")",
1200            "background-size": "cover"
1201          })
1202        }
1203      }
1204
1205
1206      swiperSlide.end()
1207        .find("[data-caption-animate]")
1208        .addClass("not-animated")
1209        .end();
1210      
1211      var swiperOptions = {
1212          autoplay: isNoviBuilder ? null : s.attr('data-autoplay') ? s.attr('data-autoplay') === "false" ? undefined : s.attr('data-autoplay') : 5000,
1213          direction: s.attr('data-direction') ? s.attr('data-direction') : "horizontal",
1214          effect: s.attr('data-slide-effect') ? s.attr('data-slide-effect') : "slide",
1215          speed: s.attr('data-slide-speed') ? s.attr('data-slide-speed') : 600,
1216          autoHeight: s.attr('data-auto-height') ? s.attr('data-auto-height') === "true" : false,
1217          keyboardControl: s.attr('data-keyboard') === "true",
1218          mousewheelControl: s.attr('data-mousewheel') === "true",
1219          mousewheelReleaseOnEdges: s.attr('data-mousewheel-release') === "true",
1220          nextButton: next.length ? next.get(0) : (s.attr('data-custom-next') ? $(s.attr('data-custom-next')) : null),
1221          prevButton: prev.length ? prev.get(0) : (s.attr('data-custom-prev') ? $(s.attr('data-custom-prev')) : null),
1222          pagination: pag.length ? pag.get(0) : null,
1223          paginationClickable: pag.length ? pag.attr("data-clickable") !== "false" : false,
1224          paginationBulletRender: pag.length ? pag.attr("data-index-bullet") === "true" ? function (index, className) {
1225            return '<span class="' + className + '">' + (index + 1) + '</span>';
1226          } : null : null,
1227          scrollbar: bar.length ? bar.get(0) : null,
1228          scrollbarDraggable: bar.length ? bar.attr("data-draggable") !== "false" : true,
1229          scrollbarHide: bar.length ? bar.attr("data-draggable") === "false" : false,
1230          loop: isNoviBuilder ? false : s.attr('data-loop') !== "false",
1231          setWrapperSize: true,
1232          simulateTouch: s.attr('data-simulate-touch') && !isNoviBuilder ? s.attr('data-simulate-touch') === "true" : false,
1233          onTransitionStart: function (swiper) {
1234            toggleSwiperInnerVideos(swiper);
1235          },
1236          onTransitionEnd: function (swiper) {
1237            toggleSwiperCaptionAnimation(swiper);
1238          },
1239          onInit: function (swiper) {
1240            toggleSwiperInnerVideos(swiper);
1241            toggleSwiperCaptionAnimation(swiper);
1242            initSwiperButtonsNina(swiper);
1243            initSwiperWaypoints(swiper);
1244
1245            var slideButtons = swiper.slides.find('.button');
1246
1247            slideButtons.hover(function(){
1248              swiper.stopAutoplay();
1249            }, function(){
1250              swiper.startAutoplay();
1251            });
1252
1253            var swiperParalax = s.find(".swiper-parallax");
1254
1255            for (var k = 0; k < swiperParalax.length; k++) {
1256              var $this = $(swiperParalax[k]),
1257                speed;
1258
1259              if (parallax && !isIE && !isMobile) {
1260                if (speed = $this.attr("data-speed")) {
1261                  makeParallax($this, speed, s, false);
1262                }
1263              }
1264            }
1265            $(window).on('resize', function () {
1266              swiper.update(true);
1267            })
1268          }
1269      };
1270
1271      plugins.swiper[i] = s.swiper(swiperOptions);
1272
1273      $(window)
1274        .on("resize", function () {
1275          var mh = getSwiperHeight(s, "min-height"),
1276            h = getSwiperHeight(s, "height");
1277          if (h) {
1278            s.css("height", mh ? mh > h ? mh : h : h);
1279          }
1280        })
1281        .trigger("resize");
1282
1283
1284    }
1285  }
1286
1287
1288  /**
1289   * Owl carousel
1290   * @description Enables Owl carousel plugin
1291   */
1292  if (plugins.owl.length) {
1293    var i;
1294    for (i = 0; i < plugins.owl.length; i++) {
1295      var c = $(plugins.owl[i]);
1296      //skip owl in bootstrap tabs
1297      if (!c.parents('.tab-content').length) {
1298        initOwlCarousel(c);
1299      }
1300    }
1301  }
1302
1303  /**
1304   * RD Flickr Feed
1305   * @description Enables RD Flickr Feed plugin
1306   */
1307  if (plugins.flickrfeed.length > 0) {
1308    var i;
1309    for (i = 0; i < plugins.flickrfeed.length; i++) {
1310      var flickrfeedItem = $(plugins.flickrfeed[i]);
1311      flickrfeedItem.RDFlickr({
1312        callback: function () {
1313          var items = flickrfeedItem.find("[data-lightgallery]");
1314
1315          if (items.length) {
1316            for (var j = 0; j < items.length; j++) {
1317              var image = new Image();
1318              image.setAttribute('data-index', j);
1319              image.onload = function () {
1320                items[this.getAttribute('data-index')].setAttribute('data-size', this.naturalWidth + 'x' + this.naturalHeight);
1321              };
1322              image.src = items[j].getAttribute('href');
1323            }
1324          }
1325        }
1326      });
1327    }
1328  }
1329
1330  /**
1331   * RD Twitter Feed
1332   * @description Enables RD Twitter Feed plugin
1333   */
1334  if (plugins.twitterfeed.length > 0) {
1335    var i;
1336    for (i = 0; i < plugins.twitterfeed.length; i++) {
1337      var twitterfeedItem = plugins.twitterfeed[i];
1338      $(twitterfeedItem).RDTwitter();
1339    }
1340  }
1341
1342  /**
1343   * RD Facebook
1344   * @description Enables RD Facebook plugin
1345   */
1346  if (plugins.facebookfeed.length > 0) {
1347    var i;
1348    for (i = 0; i < plugins.facebookfeed.length; i++) {
1349      var facebookfeedItem = plugins.facebookfeed[i];
1350      $(facebookfeedItem).RDFacebookFeed({
1351        accessToken: '402518649933453|EvW-aO2Fmq3JWev0DS6okm3bPo0',
1352        callbacks: {
1353          postsLoaded: function () {
1354            $('.post').each(function () {
1355              var $this = $(this);
1356              var comment = $this.find('.post_comments');
1357              comment.find('[data-fb-comment]').each(function () {
1358                if (this.innerHTML.trim().length == 0){
1359                  $(this).remove();
1360                }
1361              });
1362              if (comment.find('[data-fb-comment]').length == 0){
1363                comment.remove();
1364              }
1365            })
1366          }
1367        }
1368      });
1369    }
1370  }
1371
1372  /**
1373   * Bootstrap tabs
1374   * @description Activate Bootstrap Tabs
1375   */
1376  if (plugins.bootstrapTabs.length) {
1377    var i;
1378    for (i = 0; i < plugins.bootstrapTabs.length; i++) {
1379      var bootstrapTabsItem = $(plugins.bootstrapTabs[i]);
1380
1381      //If have owl carousel inside tab - resize owl carousel on click
1382      if (bootstrapTabsItem.find('.owl-carousel').length) {
1383        // init first open tab
1384
1385        var carouselObj = bootstrapTabsItem.find('.tab-content .tab-pane.active .owl-carousel');
1386
1387        initOwlCarousel(carouselObj);
1388
1389        //init owl carousel on tab change
1390        bootstrapTabsItem.find('.nav-custom a').on('click', $.proxy(function () {
1391          var $this = $(this);
1392
1393          $this.find('.owl-carousel').trigger('destroy.owl.carousel').removeClass('owl-loaded');
1394          $this.find('.owl-carousel').find('.owl-stage-outer').children().unwrap();
1395
1396          setTimeout(function () {
1397            var carouselObj = $this.find('.tab-content .tab-pane.active .owl-carousel');
1398
1399            if (carouselObj.length) {
1400              for (var j = 0; j < carouselObj.length; j++) {
1401                var carouselItem = $(carouselObj[j]);
1402                initOwlCarousel(carouselItem);
1403              }
1404            }
1405
1406          }, isNoviBuilder ? 1500 : 300);
1407
1408        }, bootstrapTabsItem));
1409      }
1410
1411      //If have slick carousel inside tab - resize slick carousel on click
1412      if (bootstrapTabsItem.find('.slick-slider').length) {
1413        bootstrapTabsItem.find('.tabs-custom-list > li > a').on('click', $.proxy(function () {
1414          var $this = $(this);
1415          var setTimeOutTime = isNoviBuilder ? 1500 : 300;
1416
1417          setTimeout(function () {
1418            $this.find('.tab-content .tab-pane.active .slick-slider').slick('setPosition');
1419          }, setTimeOutTime);
1420        }, bootstrapTabsItem));
1421      }
1422    }
1423  }
1424
1425
1426  /**
1427   * RD Input Label
1428   * @description Enables RD Input Label Plugin
1429   */
1430  if (plugins.rdInputLabel.length) {
1431    plugins.rdInputLabel.RDInputLabel();
1432  }
1433
1434  /**
1435   * Regula
1436   * @description Enables Regula plugin
1437   */
1438  if (plugins.regula.length) {
1439    attachFormValidator(plugins.regula);
1440  }
1441
1442
1443  /**
1444   * MailChimp Ajax subscription
1445   */
1446  if (plugins.mailchimp.length) {
1447    $.each(plugins.mailchimp, function (index, form) {
1448      var $form = $(form),
1449        $email = $form.find('input[type=email]'),
1450        $output = $("#" + plugins.mailchimp.attr("data-form-output"));
1451
1452      // Required by MailChimp
1453      $form.attr('novalidate', 'true');
1454      $email.attr('name', 'EMAIL');
1455
1456      $form.submit(function (e) {
1457        var data = {},
1458          url = $form.attr('action').replace('/post?', '/post-json?').concat('&c=?'),
1459          dataArray = $form.serializeArray();
1460
1461        $.each(dataArray, function (index, item) {
1462          data[item.name] = item.value;
1463        });
1464
1465        $.ajax({
1466          data: data,
1467          url: url,
1468          dataType: 'jsonp',
1469          error: function (resp, text) {
1470            $output.html('Server error: ' + text);
1471
1472            setTimeout(function () {
1473              $output.removeClass("active");
1474            }, 4000);
1475          },
1476          success: function (resp) {
1477            $output.html(resp.msg).addClass('active');
1478
1479            setTimeout(function () {
1480              $output.removeClass("active");
1481            }, 6000);
1482          },
1483          beforeSend: function (data) {
1484            // Stop request if builder or inputs are invalide
1485            if (isNoviBuilder || !isValidated($form.find('[data-constraints]')))
1486              return false;
1487
1488            $output.html('Submitting...').addClass('active');
1489          }
1490        });
1491
1492        return false;
1493      });
1494    });
1495  }
1496
1497
1498  /**
1499   * Campaign Monitor ajax subscription
1500   */
1501  if (plugins.campaignMonitor.length) {
1502    $.each(plugins.campaignMonitor, function (index, form) {
1503      var $form = $(form),
1504        $output = $("#" + plugins.campaignMonitor.attr("data-form-output"));
1505
1506      $form.submit(function (e) {
1507        var data = {},
1508          url = $form.attr('action'),
1509          dataArray = $form.serializeArray();
1510
1511        $.each(dataArray, function (index, item) {
1512          data[item.name] = item.value;
1513        });
1514
1515        $.ajax({
1516          data: data,
1517          url: url,
1518          dataType: 'jsonp',
1519          error: function (resp, text) {
1520            $output.html('Server error: ' + text);
1521
1522            setTimeout(function () {
1523              $output.removeClass("active");
1524            }, 4000);
1525          },
1526          success: function (resp) {
1527            console.log(resp);
1528
1529            $output.html(resp.Message).addClass('active');
1530
1531            setTimeout(function () {
1532              $output.removeClass("active");
1533            }, 6000);
1534          },
1535          beforeSend: function (data) {
1536            // Stop request if builder or inputs are invalide
1537            if (isNoviBuilder || !isValidated($form.find('[data-constraints]')))
1538              return false;
1539
1540            $output.html('Submitting...').addClass('active');
1541          }
1542        });
1543
1544        return false;
1545      });
1546    });
1547  }
1548
1549
1550  /**
1551   * RD Mailform
1552   * @version      3.2.0
1553   */
1554  if (plugins.rdMailForm.length) {
1555    var i, j, k,
1556      msg = {
1557        'MF000': 'Successfully sent!',
1558        'MF001': 'Recipients are not set!',
1559        'MF002': 'Form will not work locally!',
1560        'MF003': 'Please, define email field in your form!',
1561        'MF004': 'Please, define type of your form!',
1562        'MF254': 'Something went wrong with PHPMailer!',
1563        'MF255': 'Aw, snap! Something went wrong.'
1564      };
1565
1566    for (i = 0; i < plugins.rdMailForm.length; i++) {
1567      var $form = $(plugins.rdMailForm[i]),
1568        formHasCaptcha = false;
1569
1570      $form.attr('novalidate', 'novalidate').ajaxForm({
1571        data: {
1572          "form-type": $form.attr("data-form-type") || "contact",
1573          "counter": i
1574        },
1575        beforeSubmit: function (arr, $form, options) {
1576          if (isNoviBuilder)
1577            return;
1578
1579          var form = $(plugins.rdMailForm[this.extraData.counter]),
1580            inputs = form.find("[data-constraints]"),
1581            output = $("#" + form.attr("data-form-output")),
1582            captcha = form.find('.recaptcha'),
1583            captchaFlag = true;
1584
1585          output.removeClass("active error success");
1586
1587          if (isValidated(inputs, captcha)) {
1588
1589            // veify reCaptcha
1590            if(captcha.length) {
1591              var captchaToken = captcha.find('.g-recaptcha-response').val(),
1592                captchaMsg = {
1593                  'CPT001': 'Please, setup you "site key" and "secret key" of reCaptcha',
1594                  'CPT002': 'Something wrong with google reCaptcha'
1595                };
1596
1597              formHasCaptcha = true;
1598
1599              $.ajax({
1600                method: "POST",
1601                url: "bat/reCaptcha.php",
1602                data: {'g-recaptcha-response': captchaToken},
1603                async: false
1604              })
1605                .done(function (responceCode) {
1606                  if (responceCode !== 'CPT000') {
1607                    if (output.hasClass("snackbars")) {
1608                      output.html('<p><span class="icon text-middle mdi mdi-check icon-xxs"></span><span>' + captchaMsg[responceCode] + '</span></p>')
1609
1610                      setTimeout(function () {
1611                        output.removeClass("active");
1612                      }, 3500);
1613
1614                      captchaFlag = false;
1615                    } else {
1616                      output.html(captchaMsg[responceCode]);
1617                    }
1618
1619                    output.addClass("active");
1620                  }
1621                });
1622            }
1623
1624            if(!captchaFlag) {
1625              return false;
1626            }
1627
1628            form.addClass('form-in-process');
1629
1630            if (output.hasClass("snackbars")) {
1631              output.html('<p><span class="icon text-middle fa fa-circle-o-notch fa-spin icon-xxs"></span><span>Sending</span></p>');
1632              output.addClass("active");
1633            }
1634          } else {
1635            return false;
1636          }
1637        },
1638        error: function (result) {
1639          if (isNoviBuilder)
1640            return;
1641
1642          var output = $("#" + $(plugins.rdMailForm[this.extraData.counter]).attr("data-form-output")),
1643            form = $(plugins.rdMailForm[this.extraData.counter]);
1644
1645          output.text(msg[result]);
1646          form.removeClass('form-in-process');
1647
1648          if(formHasCaptcha) {
1649            grecaptcha.reset();
1650          }
1651        },
1652        success: function (result) {
1653          if (isNoviBuilder)
1654            return;
1655
1656          var form = $(plugins.rdMailForm[this.extraData.counter]),
1657            output = $("#" + form.attr("data-form-output")),
1658            select = form.find('select');
1659
1660          form
1661            .addClass('success')
1662            .removeClass('form-in-process');
1663
1664          if(formHasCaptcha) {
1665            grecaptcha.reset();
1666          }
1667
1668          result = result.length === 5 ? result : 'MF255';
1669          output.text(msg[result]);
1670
1671          if (result === "MF000") {
1672            if (output.hasClass("snackbars")) {
1673              output.html('<p><span class="icon text-middle mdi mdi-check icon-xxs"></span><span>' + msg[result] + '</span></p>');
1674            } else {
1675              output.addClass("active success");
1676            }
1677          } else {
1678            if (output.hasClass("snackbars")) {
1679              output.html(' <p class="snackbars-left"><span class="icon icon-xxs mdi mdi-alert-outline text-middle"></span><span>' + msg[result] + '</span></p>');
1680            } else {
1681              output.addClass("active error");
1682            }
1683          }
1684
1685          form.clearForm();
1686
1687          if (select.length){
1688            select.select2("val", "");
1689          }
1690
1691          form.find('input, textarea').trigger('blur');
1692
1693          setTimeout(function () {
1694            output.removeClass("active error success");
1695            form.removeClass('success');
1696          }, 3500);
1697        }
1698      });
1699    }
1700  }
1701
1702
1703  /**
1704   * lightGallery
1705   * @description Enables lightGallery plugin
1706   */
1707  if (plugins.lightGallery.length && !isNoviBuilder) {
1708    plugins.lightGallery.lightGallery({
1709      thumbnail: true,
1710      download: false,
1711      actualSize: false,
1712      selector: "[data-lightgallery='group-item']"
1713    });
1714  }
1715
1716  if (plugins.lightGalleryItem.length && !isNoviBuilder) {
1717    plugins.lightGalleryItem.lightGallery({
1718      selector: "this",
1719      download: false,
1720      actualSize: false
1721    });
1722  }
1723
1724  if (plugins.videoGallery.length && !isNoviBuilder) {
1725    plugins.videoGallery.lightGallery();
1726  }
1727
1728  /**
1729   * Custom Toggles
1730   */
1731  if (plugins.customToggle.length) {
1732    for (i = 0; i < plugins.customToggle.length; i++) {
1733      var $this = $(plugins.customToggle[i]);
1734
1735      $this.on('click', $.proxy(function (event) {
1736        event.preventDefault();
1737        var $ctx = $(this);
1738        $($ctx.attr('data-custom-toggle')).add(this).toggleClass('active');
1739      }, $this));
1740
1741      if ($this.attr("data-custom-toggle-disable-on-blur") === "true") {
1742        $("body").on("click", $this, function (e) {
1743          if (e.target !== e.data[0]
1744            && $(e.data.attr('data-custom-toggle')).find($(e.target)).length
1745            && e.data.find($(e.target)).length == 0) {
1746            $(e.data.attr('data-custom-toggle')).add(e.data[0]).removeClass('active');
1747          }
1748        })
1749      }
1750    }
1751  }
1752
1753  /**
1754   * jQuery Count To
1755   * @description Enables Count To plugin
1756   */
1757  if (plugins.counter.length) {
1758    var i;
1759
1760    for (i = 0; i < plugins.counter.length; i++) {
1761      var $counterNotAnimated = $(plugins.counter[i]).not('.animated');
1762      $document
1763        .on("scroll", $.proxy(function () {
1764          var $this = this;
1765
1766          if ((!$this.hasClass("animated")) && (isScrolledIntoView($this))) {
1767            $this.countTo({
1768              refreshInterval: 40,
1769              from: 0,
1770              to: parseInt($this.text(), 10),
1771              speed: $this.attr("data-speed") || 1000,
1772              formatter: function (value, options) {
1773                value = value.toFixed(options.decimals);
1774                if (value > 10000) {
1775                  var newValue = "",
1776                    stringValue = value.toString();
1777
1778                  for (var k = stringValue.length; k >= 0; k -= 3) {
1779                    if (k <= 3) {
1780                      newValue = ' ' + stringValue.slice(0, k) + newValue;
1781                    } else {
1782                      newValue = ' ' + stringValue.slice(k - 3, k) + newValue;
1783                    }
1784                  }
1785
1786                  return newValue;
1787                } else {
1788
1789                  return value;
1790                }
1791              }
1792            });
1793            $this.addClass('animated');
1794          }
1795        }, $counterNotAnimated))
1796        .trigger("scroll");
1797    }
1798  }
1799
1800  /**
1801   * TimeCircles
1802   * @description  Enable TimeCircles plugin
1803   */
1804  if (plugins.dateCountdown.length) {
1805    var i;
1806    for (i = 0; i < plugins.dateCountdown.length; i++) {
1807      var dateCountdownItem = $(plugins.dateCountdown[i]),
1808        time = {
1809          "Days": {
1810            "text": "Days",
1811            "show": true,
1812            color: dateCountdownItem.attr("data-color") ? dateCountdownItem.attr("data-color") : "#f9f9f9"
1813          },
1814          "Hours": {
1815            "text": "Hours",
1816            "show": true,
1817            color: dateCountdownItem.attr("data-color") ? dateCountdownItem.attr("data-color") : "#f9f9f9"
1818          },
1819          "Minutes": {
1820            "text": "Minutes",
1821            "show": true,
1822            color: dateCountdownItem.attr("data-color") ? dateCountdownItem.attr("data-color") : "#f9f9f9"
1823          },
1824          "Seconds": {
1825            "text": "Seconds",
1826            "show": true,
1827            color: dateCountdownItem.attr("data-color") ? dateCountdownItem.attr("data-color") : "#f9f9f9"
1828          }
1829        };
1830
1831      dateCountdownItem.TimeCircles({
1832        color: dateCountdownItem.attr("data-color") ? dateCountdownItem.attr("data-color") : "rgba(247, 247, 247, 1)",
1833        animation: "smooth",
1834        bg_width: dateCountdownItem.attr("data-bg-width") ? dateCountdownItem.attr("data-bg-width") : 0.6,
1835        circle_bg_color: dateCountdownItem.attr("data-bg") ? dateCountdownItem.attr("data-bg") : "rgba(0, 0, 0, 1)",
1836        fg_width: dateCountdownItem.attr("data-width") ? dateCountdownItem.attr("data-width") : 0.03
1837      });
1838
1839      $(window).on('load resize orientationchange', function () {
1840        if (window.innerWidth < 479) {
1841          this.dateCountdownItem.TimeCircles({
1842            time: {
1843              "Days": {
1844                "text": "Days",
1845                "show": true,
1846                color: this.dateCountdownItem.attr("data-color") ? this.dateCountdownItem.attr("data-color") : "#f9f9f9"
1847              },
1848              "Hours": {
1849                "text": "Hours",
1850                "show": true,
1851                color: this.dateCountdownItem.attr("data-color") ? this.dateCountdownItem.attr("data-color") : "#f9f9f9"
1852              },
1853              "Minutes": {
1854                "text": "Minutes",
1855                "show": true,
1856                color: this.dateCountdownItem.attr("data-color") ? this.dateCountdownItem.attr("data-color") : "#f9f9f9"
1857              },
1858              Seconds: {
1859                "text": "Seconds",
1860                show: false,
1861                color: this.dateCountdownItem.attr("data-color") ? this.dateCountdownItem.attr("data-color") : "#f9f9f9"
1862              }
1863            }
1864          }).rebuild();
1865        } else if (window.innerWidth < 767) {
1866          this.dateCountdownItem.TimeCircles({
1867            time: {
1868              "Days": {
1869                "text": "Days",
1870                "show": true,
1871                color: this.dateCountdownItem.attr("data-color") ? this.dateCountdownItem.attr("data-color") : "#f9f9f9"
1872              },
1873              "Hours": {
1874                "text": "Hours",
1875                "show": true,
1876                color: this.dateCountdownItem.attr("data-color") ? this.dateCountdownItem.attr("data-color") : "#f9f9f9"
1877              },
1878              "Minutes": {
1879                "text": "Minutes",
1880                "show": true,
1881                color: this.dateCountdownItem.attr("data-color") ? this.dateCountdownItem.attr("data-color") : "#f9f9f9"
1882              },
1883              Seconds: {
1884                text: '',
1885                show: false,
1886                color: this.dateCountdownItem.attr("data-color") ? this.dateCountdownItem.attr("data-color") : "#f9f9f9"
1887              }
1888            }
1889          }).rebuild();
1890        } else {
1891          this.dateCountdownItem.TimeCircles({time: this.time}).rebuild();
1892        }
1893      }.bind({dateCountdownItem: dateCountdownItem, time: time}));
1894    }
1895  }
1896
1897  /**
1898   * Circle Progress
1899   * @description Enable Circle Progress plugin
1900   */
1901  if (plugins.circleProgress.length) {
1902    var i;
1903    for (i = 0; i < plugins.circleProgress.length; i++) {
1904      var circleProgressItem = $(plugins.circleProgress[i]);
1905      $document
1906        .on("scroll", $.proxy(function () {
1907          var $this = $(this);
1908
1909          if (!$this.hasClass('animated') && isScrolledIntoView($this)) {
1910
1911            var arrayGradients = $this.attr('data-gradient').split(",");
1912
1913            $this.circleProgress({
1914              value: $this.attr('data-value'),
1915              size: $this.attr('data-size') ? $this.attr('data-size') : 175,
1916              lineCap: $this.attr('data-line-cap') ? $this.attr('data-line-cap') : 'butt',
1917              fill: {gradient: arrayGradients, gradientAngle: Math.PI / 4},
1918              startAngle: -Math.PI / 4 * 2,
1919              emptyFill: $this.attr('data-empty-fill') ? $this.attr('data-empty-fill') : "rgb(245,245,245)",
1920              thickness: $this.attr('data-thickness') ? parseInt($this.attr('data-thickness')) : 10,
1921
1922            }).on('circle-animation-progress', function (event, progress, stepValue) {
1923              $(this).find('.radial').text(String(stepValue.toFixed(2)).replace('0.', '').replace('1.', '1'));
1924            });
1925            $this.addClass('animated');
1926          }
1927        }, circleProgressItem))
1928        .trigger("scroll");
1929    }
1930  }
1931
1932  /**
1933   * Linear Progress bar
1934   * @description  Enable progress bar
1935   */
1936  if (plugins.progressLinear.length) {
1937    for (i = 0; i < plugins.progressLinear.length; i++) {
1938      var progressBar = $(plugins.progressLinear[i]);
1939      $window
1940        .on("scroll load", $.proxy(function () {
1941          var bar = $(this);
1942          if (!bar.hasClass('animated-first') && isScrolledIntoView(bar)) {
1943            var end = parseInt($(this).find('.progress-value').text(), 10);
1944            bar.find('.progress-bar-linear').css({width: end + '%'});
1945            bar.find('.progress-value').countTo({
1946              refreshInterval: 40,
1947              from: 0,
1948              to: end,
1949              speed: 500
1950            });
1951            bar.addClass('animated-first');
1952          }
1953        }, progressBar));
1954    }
1955  }
1956
1957
1958  /**
1959   * RD Instafeed JS
1960   * @description Enables Instafeed JS
1961   */
1962  if (plugins.instafeed.length > 0) {
1963    var i;
1964    for (i = 0; i < plugins.instafeed.length; i++) {
1965      var instafeedItem = $(plugins.instafeed[i]);
1966      instafeedItem.RDInstafeed({
1967        accessToken: '5526956400.ba4c844.c832b2a554764bc8a1c66c39e99687d7',
1968        clientId: ' c832b2a554764bc8a1c66c39e99687d7',
1969        userId: '5526956400',
1970        showLog: false
1971      });
1972    }
1973  }
1974  
1975  /**
1976   * Enable parallax by mouse
1977   */
1978  var parallaxJs = document.getElementsByClassName('parallax-scene-js');
1979  if (parallaxJs && !isNoviBuilder) {
1980    for (var i = 0; i < parallaxJs.length; i++) {
1981      var scene = parallaxJs[i];
1982      new Parallax(scene);
1983    }
1984  }
1985
1986
1987  /**
1988   * jpFormatePlaylistObj
1989   * @description  format dynamic playlist object for jPlayer init
1990   */
1991  function jpFormatePlaylistObj(playlistHtml) {
1992    var playlistObj = [];
1993
1994    // Format object with audio
1995    for (var i = 0; i < playlistHtml.length; i++){
1996      var playlistItem = playlistHtml[i],
1997        itemData = $(playlistItem).data();
1998      playlistObj[i] = {};
1999
2000      for ( var key in itemData ){
2001        playlistObj[i][key.replace('jp', '').toLowerCase()] = itemData[key];
2002      }
2003    }
2004
2005    return playlistObj;
2006  }
2007
2008  /**
2009   * initJplayerBase
2010   * @description Base jPlayer init
2011   */
2012  function initJplayerBase(index, item, mediaObj) {
2013    return new jPlayerPlaylist({
2014      jPlayer: item.getElementsByClassName("jp-jplayer")[0],
2015      cssSelectorAncestor: ".jp-audio-" + index // Need too bee a selector not HTMLElement or Jq object, so we make it unique
2016    }, mediaObj, {
2017      playlistOptions: {
2018        enableRemoveControls: false
2019      },
2020      supplied: "ogv, m4v, oga, mp3",
2021      useStateClassSkin: true,
2022      volume: 0.4
2023    });
2024  }
2025
2026
2027  /**
2028   * Jp Audio player
2029   * @description  Custom jPlayer script
2030   */
2031
2032  if (plugins.jPlayerInit.length) {
2033    $html.addClass( 'ontouchstart' in window || 'onmsgesturechange' in window ? 'touch' : 'no-touch' );
2034
2035    $.each(plugins.jPlayerInit, function (index, item) {
2036      $(item).addClass('jp-audio-' + index);
2037
2038      var mediaObj = jpFormatePlaylistObj($(item).find('.jp-player-list .jp-player-list-item')),
2039        playerInstance = initJplayerBase(index, item, mediaObj);
2040
2041      if( $(item).data('jp-player-name') ){
2042        var customJpPlaylists = $('[data-jp-playlist-relative-to="' + $(item).data('jp-player-name') + '"]'),
2043          playlistItems = customJpPlaylists.find("[data-jp-playlist-item]");
2044
2045        // Toggle audio play on custom playlist play button click
2046        playlistItems.on('click', customJpPlaylists.data('jp-playlist-play-on'), function (e) {
2047          var mediaObj = jpFormatePlaylistObj(playlistItems),
2048            $clickedItem = $(e.delegateTarget);
2049
2050          if ( !JSON.stringify(playerInstance.playlist) === JSON.stringify(mediaObj) || !playerInstance.playlist.length ) {
2051            playerInstance.setPlaylist(mediaObj);
2052          }
2053
2054          if ( !$clickedItem.hasClass('playing') ) {
2055            playerInstance.pause();
2056
2057            if ( $clickedItem.hasClass('last-played') ) {
2058              playerInstance.play();
2059            } else{
2060              playerInstance.play(playlistItems.index($clickedItem));
2061            }
2062
2063            playlistItems.removeClass('playing last-played');
2064            $clickedItem.addClass('playing');
2065          } else {
2066            playlistItems.removeClass('playing last-played');
2067            $clickedItem.addClass('last-played');
2068            playerInstance.pause();
2069          }
2070
2071        });
2072
2073
2074        // Callback for custom playlist
2075        $(playerInstance.cssSelector.jPlayer).bind($.jPlayer.event.play, function(e) {
2076
2077          var toggleState = function (elemClass, index) {
2078            var activeIndex = playlistItems.index(playlistItems.filter(elemClass));
2079
2080            if ( activeIndex !== -1 ) {
2081              if ( playlistItems.eq(activeIndex + index).length !== 0 ) {
2082                playlistItems.eq(activeIndex)
2083                  .removeClass('play-next play-prev playing last-played')
2084                  .end()
2085                  .eq(activeIndex + index)
2086                  .addClass('playing');
2087              }
2088            }
2089          };
2090
2091          // Check if user select next or prev track
2092          toggleState('.play-next', +1);
2093          toggleState('.play-prev', -1);
2094
2095          var lastPlayed = playlistItems.filter('.last-played');
2096
2097          // If user just press pause and than play on same track
2098          if (lastPlayed.length){
2099            lastPlayed.addClass('playing').removeClass('last-played play-next');
2100          }
2101        });
2102
2103
2104        // Add temp marker of last played audio
2105        $(playerInstance.cssSelector.jPlayer).bind($.jPlayer.event.pause, function(e) {
2106          playlistItems.filter('.playing').addClass('last-played').removeClass('playing');
2107
2108          $(playerInstance.cssSelector.cssSelectorAncestor).addClass('jp-state-visible');
2109        });
2110
2111        // Add temp marker that user want to play next audio
2112        $(item).find('.jp-next')
2113          .on('click', function (e) {
2114            playlistItems.filter('.playing, .last-played').addClass('play-next');
2115          });
2116
2117        // Add temp marker that user want to play prev audio
2118        $(item).find('.jp-previous')
2119          .on('click', function (e) {
2120            playlistItems.filter('.playing, .last-played').addClass('play-prev');
2121          });
2122      }
2123    });
2124
2125  }
2126
2127  /**
2128   * Instance CirclePlayer
2129   *
2130   * CirclePlayer(jPlayerSelector, media, options)
2131   *   jPlayerSelector: String - The css selector of the jPlayer div.
2132   *   media: Object - The media object used in jPlayer("setMedia",media).
2133   *   options: Object - The jPlayer options.
2134   *
2135   * @description  Multiple instances must set the cssSelectorAncestor in the jPlayer options.
2136   */
2137
2138  if (plugins.circleJPlayer.length) {
2139    $.each(plugins.circleJPlayer, function (index, item) {
2140      $(item).find('.cp-jplayer').addClass('cp-jplayer-' + index);
2141      $(item).find('.cp-container').addClass('cp-container-' + index);
2142
2143      new CirclePlayer(".cp-jplayer-" + index,
2144        {
2145          oga: $(item).data('jp-oga'),
2146          m4a: $(item).data('jp-m4a'),
2147          mp3: $(item).data('jp-mp3')
2148        }, {
2149          cssSelectorAncestor: ".cp-container-" + index,
2150          supplied: "mp3, m4a",
2151          volume: 0.4
2152        });
2153    });
2154  }
2155
2156
2157  /**
2158   * Jp Video player
2159   * @description  Custom jPlayer video initialization
2160   */
2161
2162  if (plugins.jPlayerVideo.length) {
2163    $.each(plugins.jPlayerVideo, function (index, item) {
2164      var $item = $(item);
2165
2166      $item.find('.jp-video').addClass('jp-video-' + index);
2167
2168      new jPlayerPlaylist({
2169        jPlayer: item.getElementsByClassName("jp-jplayer")[0],
2170        cssSelectorAncestor: ".jp-video-" + index // Need too bee a selector not HTMLElement or Jq object, so we make it unique
2171      }, jpFormatePlaylistObj($(item).find('.jp-player-list .jp-player-list-item')), {
2172        playlistOptions: {
2173          enableRemoveControls: false
2174        },
2175        size: {
2176          width: "100%",
2177          height: "auto",
2178        },
2179        supplied: "webmv, ogv, m4v",
2180        useStateClassSkin: true,
2181        volume: 0.4
2182      });
2183
2184      $(item).find(".jp-jplayer").on('click', function (e) {
2185        var $this = $(this);
2186        if ($('.jp-video-' + index).hasClass('jp-state-playing')) {
2187          $this.jPlayer("pause");
2188        } else {
2189          $this.jPlayer("play");
2190        }
2191      });
2192
2193      var initialContainerWidth = $item.width();
2194      // this is the overall page container, so whatever is relevant to your page
2195
2196      $window.resize(function () {
2197        if ($item.width() !== initialContainerWidth) {
2198          // checks current container size against it's rendered size on every resize.
2199          initialContainerWidth = $item.width();
2200          $item.trigger('resize', $item);
2201          //pass off to resize listener for performance
2202        }
2203      });
2204    });
2205
2206    $window.on('resize', function (e) {
2207      $('.jp-video').each(function (index) {
2208        // find every instance of jplayer using a class in their default markup
2209        var $parentContainer = $(this).closest('.jp-video-init'),
2210          // finds jplayers closest parent element from the ones you give it (can chain as many as you want)
2211          containerWidth = $parentContainer.width(),
2212          //takes the closest elements width
2213          ARWidth = 1280,
2214          ARHeight = 720;
2215
2216        // Width and height figures used to calculate the aspect ratio (will not restrict your players to this size)
2217
2218        var aspectRatio = ARHeight / ARWidth;
2219
2220        var videoHeight = Math.round(aspectRatio * containerWidth);
2221        // calculates the appropriate height in rounded pixels using the aspect ratio
2222        $(this).find('.jp-jplayer').width(containerWidth).height(videoHeight);
2223        // and then apply the width and height!
2224      });
2225    })
2226      .trigger('resize');
2227  }
2228
2229  /**
2230   * Select2
2231   * @description Enables select2 plugin
2232   */
2233  if (plugins.selectFilter.length) {
2234    var i;
2235    for (i = 0; i < plugins.selectFilter.length; i++) {
2236      var select = $(plugins.selectFilter[i]);
2237
2238      select.select2({
2239        theme: "bootstrap",
2240        val: null
2241      }).next().addClass(select.attr("class").match(/(input-sm)|(input-lg)|($)/i).toString().replace(new RegExp(",", 'g'), " "));
2242    }
2243  }
2244
2245  /**
2246   * Button Nina
2247   * @description Handle button Nina animation effect
2248   */
2249  if (plugins.buttonNina.length && !isNoviBuilder) {
2250    initNinaButtons(plugins.buttonNina);
2251  }
2252 
2253  /**
2254   * Bootstrap Date time picker
2255   */
2256  if (plugins.bootstrapDateTimePicker.length) {
2257    var i;
2258    for (i = 0; i < plugins.bootstrapDateTimePicker.length; i++) {
2259      var $dateTimePicker = $(plugins.bootstrapDateTimePicker[i]);
2260      var options = {};
2261
2262      options['format'] = 'dddd DD MMMM YYYY - HH:mm';
2263      if ($dateTimePicker.attr("data-time-picker") == "date") {
2264        options['format'] = 'MM-DD-YYYY';
2265        options['minDate'] = new Date();
2266      } else if ($dateTimePicker.attr("data-time-picker") == "time") {
2267        options['format'] = 'HH:mm';
2268      }
2269
2270      options["time"] = ($dateTimePicker.attr("data-time-picker") != "date");
2271      options["date"] = ($dateTimePicker.attr("data-time-picker") != "time");
2272      options["shortTime"] = true;
2273
2274      $dateTimePicker.bootstrapMaterialDatePicker(options);
2275    }
2276  }
2277
2278  /**
2279   * Stepper
2280   * @description Enables Stepper Plugin
2281   */
2282  if (plugins.stepper.length) {
2283    plugins.stepper.stepper({
2284      labels: {
2285        up: "",
2286        down: ""
2287      }
2288    });
2289  }
2290
2291  /**
2292   * typedjs
2293   * @description Enables Stepper Plugin
2294   */
2295  if (plugins.typedjs.length && !isNoviBuilder) {
2296    // var e = ["tech", "cooking", "gardening", "personal", "design", "travel", "adventure", "crafts", "family", "team"];
2297    var e = ["Analysis", "Reporting", "Tracking", "Marking"];
2298    $('.typed-text').typed({
2299      strings: e,
2300      typeSpeed: 150,
2301      loop: !0,
2302      backDelay: 1500
2303    });
2304  }
2305
2306  /**
2307   *  Autoshow modal
2308   * */
2309  if (plugins.bootstrapModalDialog.length) {
2310    plugins.bootstrapModalDialog.each(function (index) {
2311      var $this = $(plugins.bootstrapModalDialog[index]);
2312      if ($this.attr('data-autoshow') === 'true') $this.modal('show');
2313    });
2314  }
2315  /**
2316   *  Notification modal
2317   * */
2318  if (plugins.bootstrapModalNotification.length) {
2319    $('body').css('overflow', 'visible');
2320    plugins.bootstrapModalNotification.on('shown.bs.modal', function () {
2321      $(this).addClass('notification-open');
2322    });
2323  }
2324
2325  /**
2326   * Parallax text
2327   * */
2328  if (plugins.parallaxText.length && !isNoviBuilder) {
2329    var scrollTop = $window.scrollTop();
2330
2331    plugins.parallaxText.each(function () {
2332      $(this).data('orig-offset', $(this).offset().top);
2333      scrollText($(this));
2334    });
2335
2336    $window.scroll(function () {
2337      scrollTop = $window.scrollTop();
2338      plugins.parallaxText.each(function () {
2339        scrollText($(this));
2340      });
2341    });
2342
2343    $window.on('resize', function () {
2344      scrollTop = $window.scrollTop();
2345      plugins.parallaxText.each(function () {
2346        $(this).data('orig-offset', $(this).offset().top);
2347        scrollText($(this));
2348      });
2349    });
2350  }
2351
2352  /**
2353   * Custom Waypoints
2354   */
2355  if (plugins.customWaypoints.length && !isNoviBuilder) {
2356    initCustomScrollTo(plugins.customWaypoints);
2357  }
2358
2359
2360  /**
2361   * jQuery Countdown
2362   * @description  Enable countdown plugin
2363   */
2364  if (plugins.countDown.length) {
2365    var i;
2366    for (i = 0; i < plugins.countDown.length; i++) {
2367
2368      var countDownItem = plugins.countDown[i],
2369        d = new Date(),
2370        time = countDownItem.getAttribute('data-time'),
2371        type = countDownItem.getAttribute('data-type'), // {until | since}
2372        format = countDownItem.getAttribute('data-format') ? countDownItem.getAttribute('data-format') : 'YYYY/MM/DD hh:mm:ss',
2373        expiryText = countDownItem.getAttribute('data-expiry-text') ? countDownItem.getAttribute('data-expiry-text') : 'Countdown finished',
2374        labels = countDownItem.getAttribute('data-labels') ? countDownItem.getAttribute('data-labels') : '',
2375        layout = countDownItem.getAttribute('data-layout') ? countDownItem.getAttribute('data-layout') : '{yn} {yl} {on} {ol} {dn} {dl} {hnn}{sep}{mnn}{sep}{snn}',
2376        settings = [];
2377
2378
2379      if (labels.length > 0) {
2380        settings['labels'] = JSON.parse(labels);
2381      }
2382
2383      d.setTime(Date.parse(time)).toLocaleString();
2384      settings[type] = d;
2385      settings['expiryText'] = expiryText;
2386      settings['format'] = format;
2387      settings['alwaysExpire'] = true;
2388      settings['padZeroes'] = true;
2389      settings['layout'] = layout;
2390      settings['onExpiry'] = function() {
2391        this.classList += ' countdown-expired';
2392        this.innerHtml = expiryText;
2393      };
2394
2395      $(countDownItem).countdown(settings);
2396    }
2397  }
2398
2399  /**
2400   * Style Switcher
2401   * @description  Enable style switcher
2402   */
2403  if (plugins.styleSwitcher.length) {
2404    for (i = 0; i < plugins.styleSwitcher.length; i++) {
2405      var $currentSwitcher = $(plugins.styleSwitcher[i]),
2406        $switcherContainer,
2407        $switcherSection = $($currentSwitcher.find('> .style-switcher-container > .section')[0]),
2408        $switcherPanel = $($currentSwitcher.find('> .style-switcher-panel-wrap .style-switcher-panel')[0]),
2409        $switcherToggle = $($currentSwitcher.find('.style-switcher-toggle')[0]);
2410
2411
2412      // Init section-reverse toggle
2413      if ($switcherToggle) {
2414        $switcherToggle.click((function ($switcherSection) {
2415          return function () {
2416            $switcherSection.toggleClass('section-reverse');
2417          }
2418        })($switcherSection));
2419      }
2420
2421      // If switchable container is custom element (not .section)
2422      if ($currentSwitcher.attr('data-container')) {
2423        $switcherContainer = $($currentSwitcher.find($currentSwitcher.attr('data-container')));
2424      } else {
2425        $switcherContainer = $switcherSection;
2426      }
2427
2428      // Find active switcher panel item
2429      var $activeButton = $($switcherPanel.find('li.active > .button')[0]);
2430      if (!$activeButton.length) {
2431        $activeButton = $($switcherPanel.find('li > .button')[0]);
2432      }
2433
2434      if (!$activeButton.length) continue;
2435
2436      // Add handler to style switcher controls
2437      $switcherPanel.find('li > .button').click((function ($switcherContainer, $activeButton) {
2438        var currentClassSet = '',
2439          currentButton = $activeButton,
2440          prevButton = $activeButton;
2441
2442        return function () {
2443          currentButton.parent().removeClass('active');
2444          prevButton = currentButton;
2445          currentButton = $(this);
2446          var newClassSet = currentButton.attr('data-customize-class');
2447          currentButton.parent().addClass('active');
2448
2449          $switcherContainer.removeClass(currentClassSet);
2450          $switcherContainer.addClass(newClassSet);
2451          currentClassSet = newClassSet;
2452
2453          if (prevButton != currentButton) {
2454            // $switcherSection.removeClass('section-reverse');
2455          }
2456        }
2457      })($switcherContainer, $activeButton));
2458
2459      $activeButton.click();
2460    }
2461  }
2462
2463
2464  /**
2465   * Background Video
2466   * @description  Enable Video plugin
2467   */
2468
2469  if (plugins.videBG.length) {
2470    var i;
2471    for (i =0; i < plugins.videBG.length; i++) {
2472      plugins.videBG.vide({
2473        mp4: 'video/video-bg',
2474        poster: 'video/video-bg-lg'
2475      },
2476        {
2477          loop: isNoviBuilder ? false : plugins.videBG.vide = 'true',
2478          autoplay: isNoviBuilder ? false : plugins.videBG.vide = 'true',
2479        });
2480    }
2481  }
2482
2483  /**
2484   * Material Parallax
2485   * @description Enables Material Parallax plugin
2486   */
2487  if (plugins.materialParallax.length) {
2488    var i;
2489
2490    if (!isNoviBuilder && !isIE && !isMobile) {
2491      plugins.materialParallax.parallax();
2492    } else {
2493      for (i = 0; i < plugins.materialParallax.length; i++) {
2494        var parallax = $(plugins.materialParallax[i]),
2495          imgPath = parallax.find("img").attr("src");
2496
2497        parallax.css({
2498          "background-image": 'url(' + imgPath + ')',
2499          "background-attachment": "fixed",
2500          "background-size": "cover"
2501        });
2502      }
2503    }
2504  }
2505
2506  /**
2507   * Slick carousel
2508   * @description  Enable Slick carousel plugin
2509   */
2510  if (plugins.slick.length) {
2511    var i;
2512    for (i = 0; i < plugins.slick.length; i++) {
2513      var $slickItem = $(plugins.slick[i]);
2514
2515      $slickItem.slick({
2516        slidesToScroll: parseInt($slickItem.attr('data-slide-to-scroll')) || 1,
2517        asNavFor: $slickItem.attr('data-for') || false,
2518        dots: $slickItem.attr("data-dots") == "true",
2519        infinite: isNoviBuilder ? false : $slickItem.attr("data-loop") == "true",
2520        focusOnSelect: true,
2521        arrows: $slickItem.attr("data-arrows") == "true",
2522        prevArrow: $slickItem.attr('data-custom-arrows') == 'true' ? $('.slick-prev[data-slick="' + $slickItem.attr('id') + '"]') : '',
2523        nextArrow: $slickItem.attr('data-custom-arrows') == 'true' ? $('.slick-next[data-slick="' + $slickItem.attr('id') + '"]') : '',
2524        swipe: $slickItem.attr("data-swipe") == "true",
2525        autoplay: $slickItem.attr("data-autoplay") == "true",
2526        vertical: $slickItem.attr("data-vertical") == "true",
2527        centerMode: $slickItem.attr("data-center-mode") == "true",
2528        centerPadding: $slickItem.attr("data-center-padding") ? $slickItem.attr("data-center-padding") : '0.50',
2529        mobileFirst: true,
2530        responsive: [
2531          {
2532            breakpoint: 0,
2533            settings: {
2534              slidesToShow: parseInt($slickItem.attr('data-items')) || 1,
2535            }
2536          },
2537          {
2538            breakpoint: 479,
2539            settings: {
2540              slidesToShow: parseInt($slickItem.attr('data-xs-items')) || 1,
2541            }
2542          },
2543          {
2544            breakpoint: 767,
2545            settings: {
2546              slidesToShow: parseInt($slickItem.attr('data-sm-items')) || 1,
2547            }
2548          },
2549          {
2550            breakpoint: 991,
2551            settings: {
2552              slidesToShow: parseInt($slickItem.attr('data-md-items')) || 1,
2553            }
2554          },
2555          {
2556            breakpoint: 1199,
2557            settings: {
2558              slidesToShow: parseInt($slickItem.attr('data-lg-items')) || 1,
2559            }
2560          }
2561        ]
2562      })
2563        .on('afterChange', function (event, slick, currentSlide, nextSlide) {
2564          var $this = $(this),
2565            childCarousel = $this.attr('data-child');
2566
2567          if (childCarousel) {
2568            $(childCarousel + ' .slick-slide').removeClass('slick-current');
2569            $(childCarousel + ' .slick-slide').eq(currentSlide).addClass('slick-current');
2570          }
2571        });
2572    }
2573  }
2574
2575  if (plugins.productThumb.length) {
2576    var i;
2577    for (i = 0; i < plugins.productThumb.length; i++) {
2578      var thumbnails = $(plugins.productThumb[i]);
2579      thumbnails.find("li").on('click', function () {
2580        var item = $(this);
2581        item.parent().find('.active').removeClass('active');
2582        var image = item.parents(".product-single").find(".product-image-area");
2583        image.removeClass('animateImageIn');
2584        image.addClass('animateImageOut');
2585        item.addClass('active');
2586        setTimeout(function () {
2587          var src = item.find("img").attr("src");
2588          if (item.attr('data-large-image')) {
2589            src = item.attr('data-large-image');
2590          }
2591          image.attr("src", src);
2592          image.removeClass('animateImageOut');
2593          image.addClass('animateImageIn');
2594        }, 300);
2595      })
2596    }
2597  }
2598
2599  var $body = $('body'),
2600    $pagination = $('.slick-books-wrap .slick-dots li'),
2601    $toggle = $('.block-with-details');
2602
2603  $toggle.each(function (index) {
2604    var $this = $($toggle[index]);
2605
2606    $body.on('click', $this, function (event) {
2607      $this.removeClass('details-open');
2608      console.log('body click');
2609    });
2610
2611    $this.on('click', function (event) {
2612      event.stopPropagation();
2613      $toggle.not($this).removeClass('details-open');
2614      $this.toggleClass('details-open');
2615      console.log('toggle click: ' + index);
2616    });
2617
2618    $pagination.on('click', function (event) {
2619      $toggle.removeClass('details-open');
2620      console.log('pagination click');
2621    });
2622
2623    $(this).children('.show-details').on('click', function (event) {
2624      event.preventDefault();
2625    });
2626  });
2627
2628  /**
2629   * Isotope
2630   * @description Enables Isotope plugin
2631   */
2632  if (plugins.isotope.length) {
2633    var i, isogroup = [];
2634    for (i = 0; i < plugins.isotope.length; i++) {
2635      var isotopeItem = plugins.isotope[i],
2636        iso = new Isotope(isotopeItem, {
2637          itemSelector: '.isotope-item',
2638          layoutMode: isotopeItem.getAttribute('data-isotope-layout') ? isotopeItem.getAttribute('data-isotope-layout') : 'masonry',
2639          filter: '*',
2640          masonry: {
2641            columnWidth: isotopeItem.getAttribute('data-column-width') ? parseFloat(isotopeItem.getAttribute('data-column-width')) : 0.312
2642          }
2643        });
2644
2645      isogroup.push(iso);
2646    }
2647
2648    $(window).on('load', function () {
2649      setTimeout(function () {
2650        var i;
2651        for (i = 0; i < isogroup.length; i++) {
2652          isogroup[i].element.className += " isotope--loaded";
2653          isogroup[i].layout();
2654        }
2655      }, 600);
2656    });
2657
2658    var resizeTimout;
2659
2660    $("[data-isotope-filter]").on("click", function (e) {
2661      e.preventDefault();
2662      var filter = $(this);
2663      clearTimeout(resizeTimout);
2664      filter.parents(".isotope-filters").find('.active').removeClass("active");
2665      filter.addClass("active");
2666      var iso = $('.isotope[data-isotope-group="' + this.getAttribute("data-isotope-group") + '"]');
2667      iso.isotope({
2668        itemSelector: '.isotope-item',
2669        layoutMode: iso.attr('data-isotope-layout') ? iso.attr('data-isotope-layout') : 'masonry',
2670        filter: this.getAttribute("data-isotope-filter") == '*' ? '*' : '[data-filter*="' + this.getAttribute("data-isotope-filter") + '"]',
2671        masonry: {
2672          columnWidth: isotopeItem.getAttribute('data-column-width') ? parseFloat(isotopeItem.getAttribute('data-column-width')) : 0.312
2673        }
2674      });
2675    }).eq(0).trigger("click")
2676  }
2677
2678  /**
2679   * D3 Charts
2680   * @description Enables D3 Charts plugin
2681   */
2682  if (plugins.d3Charts.length) {
2683    // for (i = 0; i < plugins.d3Charts.length; i++) {
2684    //   var d3ChartsItem = $(plugins.d3Charts[i]),
2685    //     d3ChartItemObject = parseJSONObject(d3ChartsItem, 'data-graph-object');
2686    //   c3ChartsArray.push(c3.generate(d3ChartItemObject));
2687    // }
2688  }
2689
2690  function fillNumbers(n) {
2691    return Array.apply(null, {length: n}).map(Function.call, Number);
2692  }
2693
2694  var lineChart,
2695      lineChartObject = {
2696      bindto: '#line-chart',
2697      color: {
2698        pattern: ['#a820d3', '#ed1c94']
2699      },
2700      point: {
2701        show: false,
2702        r: 4
2703      },
2704      padding: {
2705        left: 30,
2706        right: 30,
2707        top: 0,
2708        bottom: 0
2709      },
2710      data: {
2711        x: 'x',
2712        columns: [
2713          ['x', '2016-03-01', '2016-04-01', '2016-05-01', '2016-06-01', '2016-07-01', '2016-08-01', '2016-09-01', '2016-10-01', '2016-11-01'],
2714          ['data1', 24600, 27900, 29200, 32200, 37300, 41500, 42950, 43100, 42900, 43000],
2715          ['data2', 22900, 25600, 26800, 28200, 32700, 36200, 37500, 36700, 35100, 33700]
2716        ],
2717        axes: {
2718          data1: 'y'
2719        },
2720        type: 'spline',
2721        names: {
2722          data1: 'Profit',
2723          data2: 'Expenses'
2724        }
2725      },
2726      legend: {
2727        show: true,
2728        position: 'bottom'
2729      },
2730      grid: {
2731        x: {
2732          show: true
2733        },
2734        y: {
2735          show: false
2736        }
2737      },
2738      labels: true,
2739      axis: {
2740        x: {
2741          type: 'timeseries',
2742          min: '2016-02-01',
2743          tick: {
2744            format: '%b %Y',
2745            outer: false
2746          },
2747          padding: {
2748            left: 0,
2749            right: 10
2750          }
2751        },
2752        y: {
2753          min: 16000,
2754          max: 49000,
2755          label: {
2756            text: '$',
2757            position: 'inner-top'
2758          },
2759          tick: {
2760            format: function (x) {
2761              if (x > 1000) {
2762                return Math.round(x / 1000) + 'K';
2763              } else {
2764                return x;
2765              }
2766            },
2767            outer: false
2768          },
2769          padding: {
2770            top: 0,
2771            bottom: 0
2772          }
2773        }
2774      },
2775      line: {
2776        connectNull: true
2777      },
2778      oninit: wrapLabels(),
2779      onrendered: wrapLabels(),
2780      onresized: wrapLabels(),
2781      onmouseout: wrapLabels()
2782    };
2783
2784  function wrap(text, width) {
2785    text.each(function() {
2786      var text = d3.select(this);
2787      if (text.selectAll('tspan').size() > 1) return;
2788
2789      var words = text.text().split(/\s+/).reverse(),
2790        word,
2791        line = [],
2792        lineNumber = 0,
2793        lineHeight = 1.2, // ems
2794        y = text.attr("y"),
2795        dy = parseFloat(text.attr("dy")),
2796        tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
2797
2798      while (word = words.pop()) {
2799        line.push(word);
2800        tspan.text(line.join(" "));
2801        if (tspan.node().getComputedTextLength() > width) {
2802          line.pop();
2803          tspan.text(line.join(" "));
2804          line = [word];
2805          tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
2806        }
2807      }
2808    });
2809  }
2810
2811  lineChart = c3.generate(lineChartObject);
2812  wrapLabels();
2813
2814  $window.on('resize orientationchange', function () {
2815    wrapLabels();
2816  });
2817
2818  function wrapLabels() {
2819    d3.select('#line-chart').selectAll(".c3-axis-x .tick text")
2820      .attr('dy', '0.5em')
2821      .call(wrap, 30);
2822  }
2823
2824  d3.select('.d3-chart-wrap').insert('div', '.d3-chart + *').attr('class', 'd3-chart-legend').selectAll('span')
2825    .data(['data1', 'data2'])
2826    .enter().append('span')
2827    .attr('data-id', function (id) { return id; })
2828    .html(function (id) {
2829      return lineChartObject.data.names[id] ? lineChartObject.data.names[id] : id;
2830    })
2831    .on('mouseover', function (id) {
2832      lineChart.focus(id);
2833    })
2834    .on('mouseout', function (id) {
2835      lineChart.revert();
2836    });
2837
2838
2839  /**
2840   * WOW
2841   * @description Enables Wow animation plugin
2842   */
2843  if (isDesktop && !isNoviBuilder && $html.hasClass("wow-animation") && $(".wow").length) {
2844    new WOW().init();
2845  }
2846
2847  /**
2848   * @module       Magnific Popup
2849   * @author       Dmitry Semenov
2850   * @see          http://dimsemenov.com/plugins/magnific-popup/
2851   * @version      v1.0.0
2852   */
2853  if (!isNoviBuilder && (plugins.mfp.length > 0 || plugins.mfpGallery.length > 0)) {  
2854    if (plugins.mfp.length) {
2855      for (i = 0; i < plugins.mfp.length; i++) { 
2856        var mfpItem = plugins.mfp[i];
2857
2858        $(mfpItem).magnificPopup({
2859          type: mfpItem.getAttribute("data-lightbox")
2860        });
2861      }
2862    }
2863    if (plugins.mfpGallery.length) {
2864      for (i = 0; i < plugins.mfpGallery.length; i++) {
2865        var mfpGalleryItem = $(plugins.mfpGallery[i]).find('[data-lightbox]');
2866
2867        for (var c = 0; c < mfpGalleryItem.length; c++) {
2868          $(mfpGalleryItem).addClass("mfp-" + $(mfpGalleryItem).attr("data-lightbox"));
2869        }
2870
2871        mfpGalleryItem.end()
2872          .magnificPopup({
2873            delegate: '[data-lightbox]',
2874            type: "image",
2875            gallery: {
2876              enabled: true
2877            }
2878          });
2879      }
2880    }
2881  }
2882});

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.