PageSourceSearch

https://static.vetrinamotori.it/assets/js/app/app.js?v=vp6.47

js vetrinamotori.it collected 2026-10-07 23:45:16 UTC 451,260 bytes, 11,869 lines download raw bytes

vendor: 4,281 bytes, lines 1-142
1/*! js-cookie v3.0.5 | MIT */
2;
3(function (global, factory) {
4  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
5  typeof define === 'function' && define.amd ? define(factory) :
6  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, (function () {
7    var current = global.Cookies;
8    var exports = global.Cookies = factory();
9    exports.noConflict = function () { global.Cookies = current; return exports; };
10  })());
11})(this, (function () { 'use strict';
12
13  /* eslint-disable no-var */
14  function assign (target) {
15    for (var i = 1; i < arguments.length; i++) {
16      var source = arguments[i];
17      for (var key in source) {
18        target[key] = source[key];
19      }
20    }
21    return target
22  }
23  /* eslint-enable no-var */
24
25  /* eslint-disable no-var */
26  var defaultConverter = {
27    read: function (value) {
28      if (value[0] === '"') {
29        value = value.slice(1, -1);
30      }
31      return value.replace(/(%[\dA-F]{2})+/gi, decodeURIComponent)
32    },
33    write: function (value) {
34      return encodeURIComponent(value).replace(
35        /%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,
36        decodeURIComponent
37      )
38    }
39  };
40  /* eslint-enable no-var */
41
42  /* eslint-disable no-var */
43
44  function init (converter, defaultAttributes) {
45    function set (name, value, attributes) {
46      if (typeof document === 'undefined') {
47        return
48      }
49
50      attributes = assign({}, defaultAttributes, attributes);
51
52      if (typeof attributes.expires === 'number') {
53        attributes.expires = new Date(Date.now() + attributes.expires * 864e5);
54      }
55      if (attributes.expires) {
56        attributes.expires = attributes.expires.toUTCString();
57      }
58
59      name = encodeURIComponent(name)
60        .replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent)
61        .replace(/[()]/g, escape);
62
63      var stringifiedAttributes = '';
64      for (var attributeName in attributes) {
65        if (!attributes[attributeName]) {
66          continue
67        }
68
69        stringifiedAttributes += '; ' + attributeName;
70
71        if (attributes[attributeName] === true) {
72          continue
73        }
74
75        // Considers RFC 6265 section 5.2:
76        // ...
77        // 3.  If the remaining unparsed-attributes contains a %x3B (";")
78        //     character:
79        // Consume the characters of the unparsed-attributes up to,
80        // not including, the first %x3B (";") character.
81        // ...
82        stringifiedAttributes += '=' + attributes[attributeName].split(';')[0];
83      }
84
85      return (document.cookie =
86        name + '=' + converter.write(value, name) + stringifiedAttributes)
87    }
88
89    function get (name) {
90      if (typeof document === 'undefined' || (arguments.length && !name)) {
91        return
92      }
93
94      // To prevent the for loop in the first place assign an empty array
95      // in case there are no cookies at all.
96      var cookies = document.cookie ? document.cookie.split('; ') : [];
97      var jar = {};
98      for (var i = 0; i < cookies.length; i++) {
99        var parts = cookies[i].split('=');
100        var value = parts.slice(1).join('=');
101
102        try {
103          var found = decodeURIComponent(parts[0]);
104          jar[found] = converter.read(value, found);
105
106          if (name === found) {
107            break
108          }
109        } catch (e) {}
110      }
111
112      return name ? jar[name] : jar
113    }
114
115    return Object.create(
116      {
117        set,
118        get,
119        remove: function (name, attributes) {
120          set(
121            name,
122            '',
123            assign({}, attributes, {
124              expires: -1
125            })
126          );
127        },
128        withAttributes: function (attributes) {
129          return init(this.converter, assign({}, this.attributes, attributes))
130        },
131        withConverter: function (converter) {
132          return init(assign({}, this.converter, converter), this.attributes)
133        }
134      },
135      {
136        attributes: { value: Object.freeze(defaultAttributes) },
137        converter: { value: Object.freeze(converter) }
138      }
139    )
140  }
141
142  var api = init(defaultConverter, { path: '/' });
vendor: 4,390 bytes, lines 143-294
143  /* eslint-enable no-var */
144
145  return api;
146
147}));
148
149/*!
150 * Bootstrap v3.3.7 (http://getbootstrap.com)
151 * Copyright 2011-2016 Twitter, Inc.
152 * Licensed under the MIT license
153 */
154
155if (typeof jQuery === 'undefined') {
156  throw new Error('Bootstrap\'s JavaScript requires jQuery')
157}
158
159+function ($) {
160  'use strict';
161  var version = $.fn.jquery.split(' ')[0].split('.')
162  if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 3)) {
163    throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4')
164  }
165}(jQuery);
166
167/* ========================================================================
168 * Bootstrap: transition.js v3.3.7
169 * http://getbootstrap.com/javascript/#transitions
170 * ========================================================================
171 * Copyright 2011-2016 Twitter, Inc.
172 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
173 * ======================================================================== */
174
175
176+function ($) {
177  'use strict';
178
179  // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
180  // ============================================================
181
182  function transitionEnd() {
183    var el = document.createElement('bootstrap')
184
185    var transEndEventNames = {
186      WebkitTransition : 'webkitTransitionEnd',
187      MozTransition    : 'transitionend',
188      OTransition      : 'oTransitionEnd otransitionend',
189      transition       : 'transitionend'
190    }
191
192    for (var name in transEndEventNames) {
193      if (el.style[name] !== undefined) {
194        return { end: transEndEventNames[name] }
195      }
196    }
197
198    return false // explicit for ie8 (  ._.)
199  }
200
201  // http://blog.alexmaccaw.com/css-transitions
202  $.fn.emulateTransitionEnd = function (duration) {
203    var called = false
204    var $el = this
205    $(this).one('bsTransitionEnd', function () { called = true })
206    var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
207    setTimeout(callback, duration)
208    return this
209  }
210
211  $(function () {
212    $.support.transition = transitionEnd()
213
214    if (!$.support.transition) return
215
216    $.event.special.bsTransitionEnd = {
217      bindType: $.support.transition.end,
218      delegateType: $.support.transition.end,
219      handle: function (e) {
220        if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)
221      }
222    }
223  })
224
225}(jQuery);
226
227/* ========================================================================
228 * Bootstrap: alert.js v3.3.7
229 * http://getbootstrap.com/javascript/#alerts
230 * ========================================================================
231 * Copyright 2011-2016 Twitter, Inc.
232 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
233 * ======================================================================== */
234
235
236+function ($) {
237  'use strict';
238
239  // ALERT CLASS DEFINITION
240  // ======================
241
242  var dismiss = '[data-dismiss="alert"]'
243  var Alert   = function (el) {
244    $(el).on('click', dismiss, this.close)
245  }
246
247  Alert.VERSION = '3.3.7'
248
249  Alert.TRANSITION_DURATION = 150
250
251  Alert.prototype.close = function (e) {
252    var $this    = $(this)
253    var selector = $this.attr('data-target')
254
255    if (!selector) {
256      selector = $this.attr('href')
257      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
258    }
259
260    var $parent = $(selector === '#' ? [] : selector)
261
262    if (e) e.preventDefault()
263
264    if (!$parent.length) {
265      $parent = $this.closest('.alert')
266    }
267
268    $parent.trigger(e = $.Event('close.bs.alert'))
269
270    if (e.isDefaultPrevented()) return
271
272    $parent.removeClass('in')
273
274    function removeElement() {
275      // detach from parent, fire event then clean up data
276      $parent.detach().trigger('closed.bs.alert').remove()
277    }
278
279    $.support.transition && $parent.hasClass('fade') ?
280      $parent
281        .one('bsTransitionEnd', removeElement)
282        .emulateTransitionEnd(Alert.TRANSITION_DURATION) :
283      removeElement()
284  }
285
286
287  // ALERT PLUGIN DEFINITION
288  // =======================
289
290  function Plugin(option) {
291    return this.each(function () {
292      var $this = $(this)
293      var data  = $this.data('bs.alert')
294
vendor: 4,161 bytes, lines 295-438
295      if (!data) $this.data('bs.alert', (data = new Alert(this)))
296      if (typeof option == 'string') data[option].call($this)
297    })
298  }
299
300  var old = $.fn.alert
301
302  $.fn.alert             = Plugin
303  $.fn.alert.Constructor = Alert
304
305
306  // ALERT NO CONFLICT
307  // =================
308
309  $.fn.alert.noConflict = function () {
310    $.fn.alert = old
311    return this
312  }
313
314
315  // ALERT DATA-API
316  // ==============
317
318  $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
319
320}(jQuery);
321
322/* ========================================================================
323 * Bootstrap: button.js v3.3.7
324 * http://getbootstrap.com/javascript/#buttons
325 * ========================================================================
326 * Copyright 2011-2016 Twitter, Inc.
327 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
328 * ======================================================================== */
329
330
331+function ($) {
332  'use strict';
333
334  // BUTTON PUBLIC CLASS DEFINITION
335  // ==============================
336
337  var Button = function (element, options) {
338    this.$element  = $(element)
339    this.options   = $.extend({}, Button.DEFAULTS, options)
340    this.isLoading = false
341  }
342
343  Button.VERSION  = '3.3.7'
344
345  Button.DEFAULTS = {
346    loadingText: 'loading...'
347  }
348
349  Button.prototype.setState = function (state) {
350    var d    = 'disabled'
351    var $el  = this.$element
352    var val  = $el.is('input') ? 'val' : 'html'
353    var data = $el.data()
354
355    state += 'Text'
356
357    if (data.resetText == null) $el.data('resetText', $el[val]())
358
359    // push to event loop to allow forms to submit
360    setTimeout($.proxy(function () {
361      $el[val](data[state] == null ? this.options[state] : data[state])
362
363      if (state == 'loadingText') {
364        this.isLoading = true
365        $el.addClass(d).attr(d, d).prop(d, true)
366      } else if (this.isLoading) {
367        this.isLoading = false
368        $el.removeClass(d).removeAttr(d).prop(d, false)
369      }
370    }, this), 0)
371  }
372
373  Button.prototype.toggle = function () {
374    var changed = true
375    var $parent = this.$element.closest('[data-toggle="buttons"]')
376
377    if ($parent.length) {
378      var $input = this.$element.find('input')
379      if ($input.prop('type') == 'radio') {
380        if ($input.prop('checked')) changed = false
381        $parent.find('.active').removeClass('active')
382        this.$element.addClass('active')
383      } else if ($input.prop('type') == 'checkbox') {
384        if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false
385        this.$element.toggleClass('active')
386      }
387      $input.prop('checked', this.$element.hasClass('active'))
388      if (changed) $input.trigger('change')
389    } else {
390      this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
391      this.$element.toggleClass('active')
392    }
393  }
394
395
396  // BUTTON PLUGIN DEFINITION
397  // ========================
398
399  function Plugin(option) {
400    return this.each(function () {
401      var $this   = $(this)
402      var data    = $this.data('bs.button')
403      var options = typeof option == 'object' && option
404
405      if (!data) $this.data('bs.button', (data = new Button(this, options)))
406
407      if (option == 'toggle') data.toggle()
408      else if (option) data.setState(option)
409    })
410  }
411
412  var old = $.fn.button
413
414  $.fn.button             = Plugin
415  $.fn.button.Constructor = Button
416
417
418  // BUTTON NO CONFLICT
419  // ==================
420
421  $.fn.button.noConflict = function () {
422    $.fn.button = old
423    return this
424  }
425
426
427  // BUTTON DATA-API
428  // ===============
429
430  $(document)
431    .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
432      var $btn = $(e.target).closest('.btn')
433      Plugin.call($btn, 'toggle')
434      if (!($(e.target).is('input[type="radio"], input[type="checkbox"]'))) {
435        // Prevent double click on radios, and the double selections (so cancellation) on checkboxes
436        e.preventDefault()
437        // The target component still receive the focus
438        if ($btn.is('input,button')) $btn.trigger('focus')
vendor: 4,986 bytes, lines 439-591
439        else $btn.find('input:visible,button:visible').first().trigger('focus')
440      }
441    })
442    .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
443      $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
444    })
445
446}(jQuery);
447
448/* ========================================================================
449 * Bootstrap: carousel.js v3.3.7
450 * http://getbootstrap.com/javascript/#carousel
451 * ========================================================================
452 * Copyright 2011-2016 Twitter, Inc.
453 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
454 * ======================================================================== */
455
456
457+function ($) {
458  'use strict';
459
460  // CAROUSEL CLASS DEFINITION
461  // =========================
462
463  var Carousel = function (element, options) {
464    this.$element    = $(element)
465    this.$indicators = this.$element.find('.carousel-indicators')
466    this.options     = options
467    this.paused      = null
468    this.sliding     = null
469    this.interval    = null
470    this.$active     = null
471    this.$items      = null
472
473    this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))
474
475    this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element
476      .on('mouseenter.bs.carousel', $.proxy(this.pause, this))
477      .on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
478  }
479
480  Carousel.VERSION  = '3.3.7'
481
482  Carousel.TRANSITION_DURATION = 600
483
484  Carousel.DEFAULTS = {
485    interval: 5000,
486    pause: 'hover',
487    wrap: true,
488    keyboard: true
489  }
490
491  Carousel.prototype.keydown = function (e) {
492    if (/input|textarea/i.test(e.target.tagName)) return
493    switch (e.which) {
494      case 37: this.prev(); break
495      case 39: this.next(); break
496      default: return
497    }
498
499    e.preventDefault()
500  }
501
502  Carousel.prototype.cycle = function (e) {
503    e || (this.paused = false)
504
505    this.interval && clearInterval(this.interval)
506
507    this.options.interval
508      && !this.paused
509      && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
510
511    return this
512  }
513
514  Carousel.prototype.getItemIndex = function (item) {
515    this.$items = item.parent().children('.item')
516    return this.$items.index(item || this.$active)
517  }
518
519  Carousel.prototype.getItemForDirection = function (direction, active) {
520    var activeIndex = this.getItemIndex(active)
521    var willWrap = (direction == 'prev' && activeIndex === 0)
522                || (direction == 'next' && activeIndex == (this.$items.length - 1))
523    if (willWrap && !this.options.wrap) return active
524    var delta = direction == 'prev' ? -1 : 1
525    var itemIndex = (activeIndex + delta) % this.$items.length
526    return this.$items.eq(itemIndex)
527  }
528
529  Carousel.prototype.to = function (pos) {
530    var that        = this
531    var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active'))
532
533    if (pos > (this.$items.length - 1) || pos < 0) return
534
535    if (this.sliding)       return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid"
536    if (activeIndex == pos) return this.pause().cycle()
537
538    return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos))
539  }
540
541  Carousel.prototype.pause = function (e) {
542    e || (this.paused = true)
543
544    if (this.$element.find('.next, .prev').length && $.support.transition) {
545      this.$element.trigger($.support.transition.end)
546      this.cycle(true)
547    }
548
549    this.interval = clearInterval(this.interval)
550
551    return this
552  }
553
554  Carousel.prototype.next = function () {
555    if (this.sliding) return
556    return this.slide('next')
557  }
558
559  Carousel.prototype.prev = function () {
560    if (this.sliding) return
561    return this.slide('prev')
562  }
563
564  Carousel.prototype.slide = function (type, next) {
565    var $active   = this.$element.find('.item.active')
566    var $next     = next || this.getItemForDirection(type, $active)
567    var isCycling = this.interval
568    var direction = type == 'next' ? 'left' : 'right'
569    var that      = this
570
571    if ($next.hasClass('active')) return (this.sliding = false)
572
573    var relatedTarget = $next[0]
574    var slideEvent = $.Event('slide.bs.carousel', {
575      relatedTarget: relatedTarget,
576      direction: direction
577    })
578    this.$element.trigger(slideEvent)
579    if (slideEvent.isDefaultPrevented()) return
580
581    this.sliding = true
582
583    isCycling && this.pause()
584
585    if (this.$indicators.length) {
586      this.$indicators.find('.active').removeClass('active')
587      var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)])
588      $nextIndicator && $nextIndicator.addClass('active')
589    }
590
591    var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid"
vendor: 18,259 bytes, lines 592-1217
592    if ($.support.transition && this.$element.hasClass('slide')) {
593      $next.addClass(type)
594      $next[0].offsetWidth // force reflow
595      $active.addClass(direction)
596      $next.addClass(direction)
597      $active
598        .one('bsTransitionEnd', function () {
599          $next.removeClass([type, direction].join(' ')).addClass('active')
600          $active.removeClass(['active', direction].join(' '))
601          that.sliding = false
602          setTimeout(function () {
603            that.$element.trigger(slidEvent)
604          }, 0)
605        })
606        .emulateTransitionEnd(Carousel.TRANSITION_DURATION)
607    } else {
608      $active.removeClass('active')
609      $next.addClass('active')
610      this.sliding = false
611      this.$element.trigger(slidEvent)
612    }
613
614    isCycling && this.cycle()
615
616    return this
617  }
618
619
620  // CAROUSEL PLUGIN DEFINITION
621  // ==========================
622
623  function Plugin(option) {
624    return this.each(function () {
625      var $this   = $(this)
626      var data    = $this.data('bs.carousel')
627      var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
628      var action  = typeof option == 'string' ? option : options.slide
629
630      if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))
631      if (typeof option == 'number') data.to(option)
632      else if (action) data[action]()
633      else if (options.interval) data.pause().cycle()
634    })
635  }
636
637  var old = $.fn.carousel
638
639  $.fn.carousel             = Plugin
640  $.fn.carousel.Constructor = Carousel
641
642
643  // CAROUSEL NO CONFLICT
644  // ====================
645
646  $.fn.carousel.noConflict = function () {
647    $.fn.carousel = old
648    return this
649  }
650
651
652  // CAROUSEL DATA-API
653  // =================
654
655  var clickHandler = function (e) {
656    var href
657    var $this   = $(this)
658    var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
659    if (!$target.hasClass('carousel')) return
660    var options = $.extend({}, $target.data(), $this.data())
661    var slideIndex = $this.attr('data-slide-to')
662    if (slideIndex) options.interval = false
663
664    Plugin.call($target, options)
665
666    if (slideIndex) {
667      $target.data('bs.carousel').to(slideIndex)
668    }
669
670    e.preventDefault()
671  }
672
673  $(document)
674    .on('click.bs.carousel.data-api', '[data-slide]', clickHandler)
675    .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler)
676
677  $(window).on('load', function () {
678    $('[data-ride="carousel"]').each(function () {
679      var $carousel = $(this)
680      Plugin.call($carousel, $carousel.data())
681    })
682  })
683
684}(jQuery);
685
686/* ========================================================================
687 * Bootstrap: collapse.js v3.3.7
688 * http://getbootstrap.com/javascript/#collapse
689 * ========================================================================
690 * Copyright 2011-2016 Twitter, Inc.
691 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
692 * ======================================================================== */
693
694/* jshint latedef: false */
695
696+function ($) {
697  'use strict';
698
699  // COLLAPSE PUBLIC CLASS DEFINITION
700  // ================================
701
702  var Collapse = function (element, options) {
703    this.$element      = $(element)
704    this.options       = $.extend({}, Collapse.DEFAULTS, options)
705    this.$trigger      = $('[data-toggle="collapse"][href="#' + element.id + '"],' +
706                           '[data-toggle="collapse"][data-target="#' + element.id + '"]')
707    this.transitioning = null
708
709    if (this.options.parent) {
710      this.$parent = this.getParent()
711    } else {
712      this.addAriaAndCollapsedClass(this.$element, this.$trigger)
713    }
714
715    if (this.options.toggle) this.toggle()
716  }
717
718  Collapse.VERSION  = '3.3.7'
719
720  Collapse.TRANSITION_DURATION = 350
721
722  Collapse.DEFAULTS = {
723    toggle: true
724  }
725
726  Collapse.prototype.dimension = function () {
727    var hasWidth = this.$element.hasClass('width')
728    return hasWidth ? 'width' : 'height'
729  }
730
731  Collapse.prototype.show = function () {
732    if (this.transitioning || this.$element.hasClass('in')) return
733
734    var activesData
735    var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing')
736
737    if (actives && actives.length) {
738      activesData = actives.data('bs.collapse')
739      if (activesData && activesData.transitioning) return
740    }
741
742    var startEvent = $.Event('show.bs.collapse')
743    this.$element.trigger(startEvent)
744    if (startEvent.isDefaultPrevented()) return
745
746    if (actives && actives.length) {
747      Plugin.call(actives, 'hide')
748      activesData || actives.data('bs.collapse', null)
749    }
750
751    var dimension = this.dimension()
752
753    this.$element
754      .removeClass('collapse')
755      .addClass('collapsing')[dimension](0)
756      .attr('aria-expanded', true)
757
758    this.$trigger
759      .removeClass('collapsed')
760      .attr('aria-expanded', true)
761
762    this.transitioning = 1
763
764    var complete = function () {
765      this.$element
766        .removeClass('collapsing')
767        .addClass('collapse in')[dimension]('')
768      this.transitioning = 0
769      this.$element
770        .trigger('shown.bs.collapse')
771    }
772
773    if (!$.support.transition) return complete.call(this)
774
775    var scrollSize = $.camelCase(['scroll', dimension].join('-'))
776
777    this.$element
778      .one('bsTransitionEnd', $.proxy(complete, this))
779      .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
780  }
781
782  Collapse.prototype.hide = function () {
783    if (this.transitioning || !this.$element.hasClass('in')) return
784
785    var startEvent = $.Event('hide.bs.collapse')
786    this.$element.trigger(startEvent)
787    if (startEvent.isDefaultPrevented()) return
788
789    var dimension = this.dimension()
790
791    this.$element[dimension](this.$element[dimension]())[0].offsetHeight
792
793    this.$element
794      .addClass('collapsing')
795      .removeClass('collapse in')
796      .attr('aria-expanded', false)
797
798    this.$trigger
799      .addClass('collapsed')
800      .attr('aria-expanded', false)
801
802    this.transitioning = 1
803
804    var complete = function () {
805      this.transitioning = 0
806      this.$element
807        .removeClass('collapsing')
808        .addClass('collapse')
809        .trigger('hidden.bs.collapse')
810    }
811
812    if (!$.support.transition) return complete.call(this)
813
814    this.$element
815      [dimension](0)
816      .one('bsTransitionEnd', $.proxy(complete, this))
817      .emulateTransitionEnd(Collapse.TRANSITION_DURATION)
818  }
819
820  Collapse.prototype.toggle = function () {
821    this[this.$element.hasClass('in') ? 'hide' : 'show']()
822  }
823
824  Collapse.prototype.getParent = function () {
825    return $(this.options.parent)
826      .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
827      .each($.proxy(function (i, element) {
828        var $element = $(element)
829        this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)
830      }, this))
831      .end()
832  }
833
834  Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
835    var isOpen = $element.hasClass('in')
836
837    $element.attr('aria-expanded', isOpen)
838    $trigger
839      .toggleClass('collapsed', !isOpen)
840      .attr('aria-expanded', isOpen)
841  }
842
843  function getTargetFromTrigger($trigger) {
844    var href
845    var target = $trigger.attr('data-target')
846      || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
847
848    return $(target)
849  }
850
851
852  // COLLAPSE PLUGIN DEFINITION
853  // ==========================
854
855  function Plugin(option) {
856    return this.each(function () {
857      var $this   = $(this)
858      var data    = $this.data('bs.collapse')
859      var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
860
861      if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false
862      if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
863      if (typeof option == 'string') data[option]()
864    })
865  }
866
867  var old = $.fn.collapse
868
869  $.fn.collapse             = Plugin
870  $.fn.collapse.Constructor = Collapse
871
872
873  // COLLAPSE NO CONFLICT
874  // ====================
875
876  $.fn.collapse.noConflict = function () {
877    $.fn.collapse = old
878    return this
879  }
880
881
882  // COLLAPSE DATA-API
883  // =================
884
885  $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
886    var $this   = $(this)
887
888    if (!$this.attr('data-target')) e.preventDefault()
889
890    var $target = getTargetFromTrigger($this)
891    var data    = $target.data('bs.collapse')
892    var option  = data ? 'toggle' : $this.data()
893
894    Plugin.call($target, option)
895  })
896
897}(jQuery);
898
899/* ========================================================================
900 * Bootstrap: dropdown.js v3.3.7
901 * http://getbootstrap.com/javascript/#dropdowns
902 * ========================================================================
903 * Copyright 2011-2016 Twitter, Inc.
904 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
905 * ======================================================================== */
906
907
908+function ($) {
909  'use strict';
910
911  // DROPDOWN CLASS DEFINITION
912  // =========================
913
914  var backdrop = '.dropdown-backdrop'
915  var toggle   = '[data-toggle="dropdown"]'
916  var Dropdown = function (element) {
917    $(element).on('click.bs.dropdown', this.toggle)
918  }
919
920  Dropdown.VERSION = '3.3.7'
921
922  function getParent($this) {
923    var selector = $this.attr('data-target')
924
925    if (!selector) {
926      selector = $this.attr('href')
927      selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
928    }
929
930    var $parent = selector && $(selector)
931
932    return $parent && $parent.length ? $parent : $this.parent()
933  }
934
935  function clearMenus(e) {
936    if (e && e.which === 3) return
937    $(backdrop).remove()
938    $(toggle).each(function () {
939      var $this         = $(this)
940      var $parent       = getParent($this)
941      var relatedTarget = { relatedTarget: this }
942
943      if (!$parent.hasClass('open')) return
944
945      if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return
946
947      $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
948
949      if (e.isDefaultPrevented()) return
950
951      $this.attr('aria-expanded', 'false')
952      $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget))
953    })
954  }
955
956  Dropdown.prototype.toggle = function (e) {
957    var $this = $(this)
958
959    if ($this.is('.disabled, :disabled')) return
960
961    var $parent  = getParent($this)
962    var isActive = $parent.hasClass('open')
963
964    clearMenus()
965
966    if (!isActive) {
967      if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
968        // if mobile we use a backdrop because click events don't delegate
969        $(document.createElement('div'))
970          .addClass('dropdown-backdrop')
971          .insertAfter($(this))
972          .on('click', clearMenus)
973      }
974
975      var relatedTarget = { relatedTarget: this }
976      $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
977
978      if (e.isDefaultPrevented()) return
979
980      $this
981        .trigger('focus')
982        .attr('aria-expanded', 'true')
983
984      $parent
985        .toggleClass('open')
986        .trigger($.Event('shown.bs.dropdown', relatedTarget))
987    }
988
989    return false
990  }
991
992  Dropdown.prototype.keydown = function (e) {
993    if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return
994
995    var $this = $(this)
996
997    e.preventDefault()
998    e.stopPropagation()
999
1000    if ($this.is('.disabled, :disabled')) return
1001
1002    var $parent  = getParent($this)
1003    var isActive = $parent.hasClass('open')
1004
1005    if (!isActive && e.which != 27 || isActive && e.which == 27) {
1006      if (e.which == 27) $parent.find(toggle).trigger('focus')
1007      return $this.trigger('click')
1008    }
1009
1010    var desc = ' li:not(.disabled):visible a'
1011    var $items = $parent.find('.dropdown-menu' + desc)
1012
1013    if (!$items.length) return
1014
1015    var index = $items.index(e.target)
1016
1017    if (e.which == 38 && index > 0)                 index--         // up
1018    if (e.which == 40 && index < $items.length - 1) index++         // down
1019    if (!~index)                                    index = 0
1020
1021    $items.eq(index).trigger('focus')
1022  }
1023
1024
1025  // DROPDOWN PLUGIN DEFINITION
1026  // ==========================
1027
1028  function Plugin(option) {
1029    return this.each(function () {
1030      var $this = $(this)
1031      var data  = $this.data('bs.dropdown')
1032
1033      if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
1034      if (typeof option == 'string') data[option].call($this)
1035    })
1036  }
1037
1038  var old = $.fn.dropdown
1039
1040  $.fn.dropdown             = Plugin
1041  $.fn.dropdown.Constructor = Dropdown
1042
1043
1044  // DROPDOWN NO CONFLICT
1045  // ====================
1046
1047  $.fn.dropdown.noConflict = function () {
1048    $.fn.dropdown = old
1049    return this
1050  }
1051
1052
1053  // APPLY TO STANDARD DROPDOWN ELEMENTS
1054  // ===================================
1055
1056  $(document)
1057    .on('click.bs.dropdown.data-api', clearMenus)
1058    .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
1059    .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
1060    .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
1061    .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown)
1062
1063}(jQuery);
1064
1065/* ========================================================================
1066 * Bootstrap: modal.js v3.3.7
1067 * http://getbootstrap.com/javascript/#modals
1068 * ========================================================================
1069 * Copyright 2011-2016 Twitter, Inc.
1070 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
1071 * ======================================================================== */
1072
1073
1074+function ($) {
1075  'use strict';
1076
1077  // MODAL CLASS DEFINITION
1078  // ======================
1079
1080  var Modal = function (element, options) {
1081    this.options             = options
1082    this.$body               = $(document.body)
1083    this.$element            = $(element)
1084    this.$dialog             = this.$element.find('.modal-dialog')
1085    this.$backdrop           = null
1086    this.isShown             = null
1087    this.originalBodyPad     = null
1088    this.scrollbarWidth      = 0
1089    this.ignoreBackdropClick = false
1090
1091    if (this.options.remote) {
1092      this.$element
1093        .find('.modal-content')
1094        .load(this.options.remote, $.proxy(function () {
1095          this.$element.trigger('loaded.bs.modal')
1096        }, this))
1097    }
1098  }
1099
1100  Modal.VERSION  = '3.3.7'
1101
1102  Modal.TRANSITION_DURATION = 300
1103  Modal.BACKDROP_TRANSITION_DURATION = 150
1104
1105  Modal.DEFAULTS = {
1106    backdrop: true,
1107    keyboard: true,
1108    show: true
1109  }
1110
1111  Modal.prototype.toggle = function (_relatedTarget) {
1112    return this.isShown ? this.hide() : this.show(_relatedTarget)
1113  }
1114
1115  Modal.prototype.show = function (_relatedTarget) {
1116    var that = this
1117    var e    = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
1118
1119    this.$element.trigger(e)
1120
1121    if (this.isShown || e.isDefaultPrevented()) return
1122
1123    this.isShown = true
1124
1125    this.checkScrollbar()
1126    this.setScrollbar()
1127    this.$body.addClass('modal-open')
1128
1129    this.escape()
1130    this.resize()
1131
1132    this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
1133
1134    this.$dialog.on('mousedown.dismiss.bs.modal', function () {
1135      that.$element.one('mouseup.dismiss.bs.modal', function (e) {
1136        if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
1137      })
1138    })
1139
1140    this.backdrop(function () {
1141      var transition = $.support.transition && that.$element.hasClass('fade')
1142
1143      if (!that.$element.parent().length) {
1144        that.$element.appendTo(that.$body) // don't move modals dom position
1145      }
1146
1147      that.$element
1148        .show()
1149        .scrollTop(0)
1150
1151      that.adjustDialog()
1152
1153      if (transition) {
1154        that.$element[0].offsetWidth // force reflow
1155      }
1156
1157      that.$element.addClass('in')
1158
1159      that.enforceFocus()
1160
1161      var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
1162
1163      transition ?
1164        that.$dialog // wait for modal to slide in
1165          .one('bsTransitionEnd', function () {
1166            that.$element.trigger('focus').trigger(e)
1167          })
1168          .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
1169        that.$element.trigger('focus').trigger(e)
1170    })
1171  }
1172
1173  Modal.prototype.hide = function (e) {
1174    if (e) e.preventDefault()
1175
1176    e = $.Event('hide.bs.modal')
1177
1178    this.$element.trigger(e)
1179
1180    if (!this.isShown || e.isDefaultPrevented()) return
1181
1182    this.isShown = false
1183
1184    this.escape()
1185    this.resize()
1186
1187    $(document).off('focusin.bs.modal')
1188
1189    this.$element
1190      .removeClass('in')
1191      .off('click.dismiss.bs.modal')
1192      .off('mouseup.dismiss.bs.modal')
1193
1194    this.$dialog.off('mousedown.dismiss.bs.modal')
1195
1196    $.support.transition && this.$element.hasClass('fade') ?
1197      this.$element
1198        .one('bsTransitionEnd', $.proxy(this.hideModal, this))
1199        .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
1200      this.hideModal()
1201  }
1202
1203  Modal.prototype.enforceFocus = function () {
1204    $(document)
1205      .off('focusin.bs.modal') // guard against infinite focus loop
1206      .on('focusin.bs.modal', $.proxy(function (e) {
1207        if (document !== e.target &&
1208            this.$element[0] !== e.target &&
1209            !this.$element.has(e.target).length) {
1210          this.$element.trigger('focus')
1211        }
1212      }, this))
1213  }
1214
1215  Modal.prototype.escape = function () {
1216    if (this.isShown && this.options.keyboard) {
1217      this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
vendor: 9,817 bytes, lines 1218-1523
1218        e.which == 27 && this.hide()
1219      }, this))
1220    } else if (!this.isShown) {
1221      this.$element.off('keydown.dismiss.bs.modal')
1222    }
1223  }
1224
1225  Modal.prototype.resize = function () {
1226    if (this.isShown) {
1227      $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
1228    } else {
1229      $(window).off('resize.bs.modal')
1230    }
1231  }
1232
1233  Modal.prototype.hideModal = function () {
1234    var that = this
1235    this.$element.hide()
1236    this.backdrop(function () {
1237      that.$body.removeClass('modal-open')
1238      that.resetAdjustments()
1239      that.resetScrollbar()
1240      that.$element.trigger('hidden.bs.modal')
1241    })
1242  }
1243
1244  Modal.prototype.removeBackdrop = function () {
1245    this.$backdrop && this.$backdrop.remove()
1246    this.$backdrop = null
1247  }
1248
1249  Modal.prototype.backdrop = function (callback) {
1250    var that = this
1251    var animate = this.$element.hasClass('fade') ? 'fade' : ''
1252
1253    if (this.isShown && this.options.backdrop) {
1254      var doAnimate = $.support.transition && animate
1255
1256      this.$backdrop = $(document.createElement('div'))
1257        .addClass('modal-backdrop ' + animate)
1258        .appendTo(this.$body)
1259
1260      this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
1261        if (this.ignoreBackdropClick) {
1262          this.ignoreBackdropClick = false
1263          return
1264        }
1265        if (e.target !== e.currentTarget) return
1266        this.options.backdrop == 'static'
1267          ? this.$element[0].focus()
1268          : this.hide()
1269      }, this))
1270
1271      if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
1272
1273      this.$backdrop.addClass('in')
1274
1275      if (!callback) return
1276
1277      doAnimate ?
1278        this.$backdrop
1279          .one('bsTransitionEnd', callback)
1280          .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
1281        callback()
1282
1283    } else if (!this.isShown && this.$backdrop) {
1284      this.$backdrop.removeClass('in')
1285
1286      var callbackRemove = function () {
1287        that.removeBackdrop()
1288        callback && callback()
1289      }
1290      $.support.transition && this.$element.hasClass('fade') ?
1291        this.$backdrop
1292          .one('bsTransitionEnd', callbackRemove)
1293          .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
1294        callbackRemove()
1295
1296    } else if (callback) {
1297      callback()
1298    }
1299  }
1300
1301  // these following methods are used to handle overflowing modals
1302
1303  Modal.prototype.handleUpdate = function () {
1304    this.adjustDialog()
1305  }
1306
1307  Modal.prototype.adjustDialog = function () {
1308    var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
1309
1310    this.$element.css({
1311      paddingLeft:  !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
1312      paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
1313    })
1314  }
1315
1316  Modal.prototype.resetAdjustments = function () {
1317    this.$element.css({
1318      paddingLeft: '',
1319      paddingRight: ''
1320    })
1321  }
1322
1323  Modal.prototype.checkScrollbar = function () {
1324    var fullWindowWidth = window.innerWidth
1325    if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
1326      var documentElementRect = document.documentElement.getBoundingClientRect()
1327      fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)
1328    }
1329    this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth
1330    this.scrollbarWidth = this.measureScrollbar()
1331  }
1332
1333  Modal.prototype.setScrollbar = function () {
1334    var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
1335    this.originalBodyPad = document.body.style.paddingRight || ''
1336    if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
1337  }
1338
1339  Modal.prototype.resetScrollbar = function () {
1340    this.$body.css('padding-right', this.originalBodyPad)
1341  }
1342
1343  Modal.prototype.measureScrollbar = function () { // thx walsh
1344    var scrollDiv = document.createElement('div')
1345    scrollDiv.className = 'modal-scrollbar-measure'
1346    this.$body.append(scrollDiv)
1347    var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
1348    this.$body[0].removeChild(scrollDiv)
1349    return scrollbarWidth
1350  }
1351
1352
1353  // MODAL PLUGIN DEFINITION
1354  // =======================
1355
1356  function Plugin(option, _relatedTarget) {
1357    return this.each(function () {
1358      var $this   = $(this)
1359      var data    = $this.data('bs.modal')
1360      var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
1361
1362      if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
1363      if (typeof option == 'string') data[option](_relatedTarget)
1364      else if (options.show) data.show(_relatedTarget)
1365    })
1366  }
1367
1368  var old = $.fn.modal
1369
1370  $.fn.modal             = Plugin
1371  $.fn.modal.Constructor = Modal
1372
1373
1374  // MODAL NO CONFLICT
1375  // =================
1376
1377  $.fn.modal.noConflict = function () {
1378    $.fn.modal = old
1379    return this
1380  }
1381
1382
1383  // MODAL DATA-API
1384  // ==============
1385
1386  $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
1387    var $this   = $(this)
1388    var href    = $this.attr('href')
1389    var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
1390    var option  = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
1391
1392    if ($this.is('a')) e.preventDefault()
1393
1394    $target.one('show.bs.modal', function (showEvent) {
1395      if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
1396      $target.one('hidden.bs.modal', function () {
1397        $this.is(':visible') && $this.trigger('focus')
1398      })
1399    })
1400    Plugin.call($target, option, this)
1401  })
1402
1403}(jQuery);
1404
1405/* ========================================================================
1406 * Bootstrap: tooltip.js v3.3.7
1407 * http://getbootstrap.com/javascript/#tooltip
1408 * Inspired by the original jQuery.tipsy by Jason Frame
1409 * ========================================================================
1410 * Copyright 2011-2016 Twitter, Inc.
1411 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
1412 * ======================================================================== */
1413
1414
1415+function ($) {
1416  'use strict';
1417
1418  // TOOLTIP PUBLIC CLASS DEFINITION
1419  // ===============================
1420
1421  var Tooltip = function (element, options) {
1422    this.type       = null
1423    this.options    = null
1424    this.enabled    = null
1425    this.timeout    = null
1426    this.hoverState = null
1427    this.$element   = null
1428    this.inState    = null
1429
1430    this.init('tooltip', element, options)
1431  }
1432
1433  Tooltip.VERSION  = '3.3.7'
1434
1435  Tooltip.TRANSITION_DURATION = 150
1436
1437  Tooltip.DEFAULTS = {
1438    animation: true,
1439    placement: 'top',
1440    selector: false,
1441    template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
1442    trigger: 'hover focus',
1443    title: '',
1444    delay: 0,
1445    html: false,
1446    container: false,
1447    viewport: {
1448      selector: 'body',
1449      padding: 0
1450    }
1451  }
1452
1453  Tooltip.prototype.init = function (type, element, options) {
1454    this.enabled   = true
1455    this.type      = type
1456    this.$element  = $(element)
1457    this.options   = this.getOptions(options)
1458    this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport))
1459    this.inState   = { click: false, hover: false, focus: false }
1460
1461    if (this.$element[0] instanceof document.constructor && !this.options.selector) {
1462      throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!')
1463    }
1464
1465    var triggers = this.options.trigger.split(' ')
1466
1467    for (var i = triggers.length; i--;) {
1468      var trigger = triggers[i]
1469
1470      if (trigger == 'click') {
1471        this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
1472      } else if (trigger != 'manual') {
1473        var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
1474        var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
1475
1476        this.$element.on(eventIn  + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
1477        this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
1478      }
1479    }
1480
1481    this.options.selector ?
1482      (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
1483      this.fixTitle()
1484  }
1485
1486  Tooltip.prototype.getDefaults = function () {
1487    return Tooltip.DEFAULTS
1488  }
1489
1490  Tooltip.prototype.getOptions = function (options) {
1491    options = $.extend({}, this.getDefaults(), this.$element.data(), options)
1492
1493    if (options.delay && typeof options.delay == 'number') {
1494      options.delay = {
1495        show: options.delay,
1496        hide: options.delay
1497      }
1498    }
1499
1500    return options
1501  }
1502
1503  Tooltip.prototype.getDelegateOptions = function () {
1504    var options  = {}
1505    var defaults = this.getDefaults()
1506
1507    this._options && $.each(this._options, function (key, value) {
1508      if (defaults[key] != value) options[key] = value
1509    })
1510
1511    return options
1512  }
1513
1514  Tooltip.prototype.enter = function (obj) {
1515    var self = obj instanceof this.constructor ?
1516      obj : $(obj.currentTarget).data('bs.' + this.type)
1517
1518    if (!self) {
1519      self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
1520      $(obj.currentTarget).data('bs.' + this.type, self)
1521    }
1522
1523    if (obj instanceof $.Event) {
vendor: 10,031 bytes, lines 1524-1802
1524      self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true
1525    }
1526
1527    if (self.tip().hasClass('in') || self.hoverState == 'in') {
1528      self.hoverState = 'in'
1529      return
1530    }
1531
1532    clearTimeout(self.timeout)
1533
1534    self.hoverState = 'in'
1535
1536    if (!self.options.delay || !self.options.delay.show) return self.show()
1537
1538    self.timeout = setTimeout(function () {
1539      if (self.hoverState == 'in') self.show()
1540    }, self.options.delay.show)
1541  }
1542
1543  Tooltip.prototype.isInStateTrue = function () {
1544    for (var key in this.inState) {
1545      if (this.inState[key]) return true
1546    }
1547
1548    return false
1549  }
1550
1551  Tooltip.prototype.leave = function (obj) {
1552    var self = obj instanceof this.constructor ?
1553      obj : $(obj.currentTarget).data('bs.' + this.type)
1554
1555    if (!self) {
1556      self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
1557      $(obj.currentTarget).data('bs.' + this.type, self)
1558    }
1559
1560    if (obj instanceof $.Event) {
1561      self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false
1562    }
1563
1564    if (self.isInStateTrue()) return
1565
1566    clearTimeout(self.timeout)
1567
1568    self.hoverState = 'out'
1569
1570    if (!self.options.delay || !self.options.delay.hide) return self.hide()
1571
1572    self.timeout = setTimeout(function () {
1573      if (self.hoverState == 'out') self.hide()
1574    }, self.options.delay.hide)
1575  }
1576
1577  Tooltip.prototype.show = function () {
1578    var e = $.Event('show.bs.' + this.type)
1579
1580    if (this.hasContent() && this.enabled) {
1581      this.$element.trigger(e)
1582
1583      var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
1584      if (e.isDefaultPrevented() || !inDom) return
1585      var that = this
1586
1587      var $tip = this.tip()
1588
1589      var tipId = this.getUID(this.type)
1590
1591      this.setContent()
1592      $tip.attr('id', tipId)
1593      this.$element.attr('aria-describedby', tipId)
1594
1595      if (this.options.animation) $tip.addClass('fade')
1596
1597      var placement = typeof this.options.placement == 'function' ?
1598        this.options.placement.call(this, $tip[0], this.$element[0]) :
1599        this.options.placement
1600
1601      var autoToken = /\s?auto?\s?/i
1602      var autoPlace = autoToken.test(placement)
1603      if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
1604
1605      $tip
1606        .detach()
1607        .css({ top: 0, left: 0, display: 'block' })
1608        .addClass(placement)
1609        .data('bs.' + this.type, this)
1610
1611      this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
1612      this.$element.trigger('inserted.bs.' + this.type)
1613
1614      var pos          = this.getPosition()
1615      var actualWidth  = $tip[0].offsetWidth
1616      var actualHeight = $tip[0].offsetHeight
1617
1618      if (autoPlace) {
1619        var orgPlacement = placement
1620        var viewportDim = this.getPosition(this.$viewport)
1621
1622        placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top'    :
1623                    placement == 'top'    && pos.top    - actualHeight < viewportDim.top    ? 'bottom' :
1624                    placement == 'right'  && pos.right  + actualWidth  > viewportDim.width  ? 'left'   :
1625                    placement == 'left'   && pos.left   - actualWidth  < viewportDim.left   ? 'right'  :
1626                    placement
1627
1628        $tip
1629          .removeClass(orgPlacement)
1630          .addClass(placement)
1631      }
1632
1633      var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
1634
1635      this.applyPlacement(calculatedOffset, placement)
1636
1637      var complete = function () {
1638        var prevHoverState = that.hoverState
1639        that.$element.trigger('shown.bs.' + that.type)
1640        that.hoverState = null
1641
1642        if (prevHoverState == 'out') that.leave(that)
1643      }
1644
1645      $.support.transition && this.$tip.hasClass('fade') ?
1646        $tip
1647          .one('bsTransitionEnd', complete)
1648          .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
1649        complete()
1650    }
1651  }
1652
1653  Tooltip.prototype.applyPlacement = function (offset, placement) {
1654    var $tip   = this.tip()
1655    var width  = $tip[0].offsetWidth
1656    var height = $tip[0].offsetHeight
1657
1658    // manually read margins because getBoundingClientRect includes difference
1659    var marginTop = parseInt($tip.css('margin-top'), 10)
1660    var marginLeft = parseInt($tip.css('margin-left'), 10)
1661
1662    // we must check for NaN for ie 8/9
1663    if (isNaN(marginTop))  marginTop  = 0
1664    if (isNaN(marginLeft)) marginLeft = 0
1665
1666    offset.top  += marginTop
1667    offset.left += marginLeft
1668
1669    // $.fn.offset doesn't round pixel values
1670    // so we use setOffset directly with our own function B-0
1671    $.offset.setOffset($tip[0], $.extend({
1672      using: function (props) {
1673        $tip.css({
1674          top: Math.round(props.top),
1675          left: Math.round(props.left)
1676        })
1677      }
1678    }, offset), 0)
1679
1680    $tip.addClass('in')
1681
1682    // check to see if placing tip in new offset caused the tip to resize itself
1683    var actualWidth  = $tip[0].offsetWidth
1684    var actualHeight = $tip[0].offsetHeight
1685
1686    if (placement == 'top' && actualHeight != height) {
1687      offset.top = offset.top + height - actualHeight
1688    }
1689
1690    var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
1691
1692    if (delta.left) offset.left += delta.left
1693    else offset.top += delta.top
1694
1695    var isVertical          = /top|bottom/.test(placement)
1696    var arrowDelta          = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
1697    var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
1698
1699    $tip.offset(offset)
1700    this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
1701  }
1702
1703  Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) {
1704    this.arrow()
1705      .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
1706      .css(isVertical ? 'top' : 'left', '')
1707  }
1708
1709  Tooltip.prototype.setContent = function () {
1710    var $tip  = this.tip()
1711    var title = this.getTitle()
1712
1713    $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
1714    $tip.removeClass('fade in top bottom left right')
1715  }
1716
1717  Tooltip.prototype.hide = function (callback) {
1718    var that = this
1719    var $tip = $(this.$tip)
1720    var e    = $.Event('hide.bs.' + this.type)
1721
1722    function complete() {
1723      if (that.hoverState != 'in') $tip.detach()
1724      if (that.$element) { // TODO: Check whether guarding this code with this `if` is really necessary.
1725        that.$element
1726          .removeAttr('aria-describedby')
1727          .trigger('hidden.bs.' + that.type)
1728      }
1729      callback && callback()
1730    }
1731
1732    this.$element.trigger(e)
1733
1734    if (e.isDefaultPrevented()) return
1735
1736    $tip.removeClass('in')
1737
1738    $.support.transition && $tip.hasClass('fade') ?
1739      $tip
1740        .one('bsTransitionEnd', complete)
1741        .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
1742      complete()
1743
1744    this.hoverState = null
1745
1746    return this
1747  }
1748
1749  Tooltip.prototype.fixTitle = function () {
1750    var $e = this.$element
1751    if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') {
1752      $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
1753    }
1754  }
1755
1756  Tooltip.prototype.hasContent = function () {
1757    return this.getTitle()
1758  }
1759
1760  Tooltip.prototype.getPosition = function ($element) {
1761    $element   = $element || this.$element
1762
1763    var el     = $element[0]
1764    var isBody = el.tagName == 'BODY'
1765
1766    var elRect    = el.getBoundingClientRect()
1767    if (elRect.width == null) {
1768      // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
1769      elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
1770    }
1771    var isSvg = window.SVGElement && el instanceof window.SVGElement
1772    // Avoid using $.offset() on SVGs since it gives incorrect results in jQuery 3.
1773    // See https://github.com/twbs/bootstrap/issues/20280
1774    var elOffset  = isBody ? { top: 0, left: 0 } : (isSvg ? null : $element.offset())
1775    var scroll    = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
1776    var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
1777
1778    return $.extend({}, elRect, scroll, outerDims, elOffset)
1779  }
1780
1781  Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
1782    return placement == 'bottom' ? { top: pos.top + pos.height,   left: pos.left + pos.width / 2 - actualWidth / 2 } :
1783           placement == 'top'    ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
1784           placement == 'left'   ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
1785        /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
1786
1787  }
1788
1789  Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
1790    var delta = { top: 0, left: 0 }
1791    if (!this.$viewport) return delta
1792
1793    var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
1794    var viewportDimensions = this.getPosition(this.$viewport)
1795
1796    if (/right|left/.test(placement)) {
1797      var topEdgeOffset    = pos.top - viewportPadding - viewportDimensions.scroll
1798      var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
1799      if (topEdgeOffset < viewportDimensions.top) { // top overflow
1800        delta.top = viewportDimensions.top - topEdgeOffset
1801      } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
1802        delta.top = viewportDimensions.top + viewportDimensions.height - bottom
vendor: 8,124 bytes, lines 1802-2081
1802EdgeOffset
1803      }
1804    } else {
1805      var leftEdgeOffset  = pos.left - viewportPadding
1806      var rightEdgeOffset = pos.left + viewportPadding + actualWidth
1807      if (leftEdgeOffset < viewportDimensions.left) { // left overflow
1808        delta.left = viewportDimensions.left - leftEdgeOffset
1809      } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow
1810        delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
1811      }
1812    }
1813
1814    return delta
1815  }
1816
1817  Tooltip.prototype.getTitle = function () {
1818    var title
1819    var $e = this.$element
1820    var o  = this.options
1821
1822    title = $e.attr('data-original-title')
1823      || (typeof o.title == 'function' ? o.title.call($e[0]) :  o.title)
1824
1825    return title
1826  }
1827
1828  Tooltip.prototype.getUID = function (prefix) {
1829    do prefix += ~~(Math.random() * 1000000)
1830    while (document.getElementById(prefix))
1831    return prefix
1832  }
1833
1834  Tooltip.prototype.tip = function () {
1835    if (!this.$tip) {
1836      this.$tip = $(this.options.template)
1837      if (this.$tip.length != 1) {
1838        throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!')
1839      }
1840    }
1841    return this.$tip
1842  }
1843
1844  Tooltip.prototype.arrow = function () {
1845    return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
1846  }
1847
1848  Tooltip.prototype.enable = function () {
1849    this.enabled = true
1850  }
1851
1852  Tooltip.prototype.disable = function () {
1853    this.enabled = false
1854  }
1855
1856  Tooltip.prototype.toggleEnabled = function () {
1857    this.enabled = !this.enabled
1858  }
1859
1860  Tooltip.prototype.toggle = function (e) {
1861    var self = this
1862    if (e) {
1863      self = $(e.currentTarget).data('bs.' + this.type)
1864      if (!self) {
1865        self = new this.constructor(e.currentTarget, this.getDelegateOptions())
1866        $(e.currentTarget).data('bs.' + this.type, self)
1867      }
1868    }
1869
1870    if (e) {
1871      self.inState.click = !self.inState.click
1872      if (self.isInStateTrue()) self.enter(self)
1873      else self.leave(self)
1874    } else {
1875      self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
1876    }
1877  }
1878
1879  Tooltip.prototype.destroy = function () {
1880    var that = this
1881    clearTimeout(this.timeout)
1882    this.hide(function () {
1883      that.$element.off('.' + that.type).removeData('bs.' + that.type)
1884      if (that.$tip) {
1885        that.$tip.detach()
1886      }
1887      that.$tip = null
1888      that.$arrow = null
1889      that.$viewport = null
1890      that.$element = null
1891    })
1892  }
1893
1894
1895  // TOOLTIP PLUGIN DEFINITION
1896  // =========================
1897
1898  function Plugin(option) {
1899    return this.each(function () {
1900      var $this   = $(this)
1901      var data    = $this.data('bs.tooltip')
1902      var options = typeof option == 'object' && option
1903
1904      if (!data && /destroy|hide/.test(option)) return
1905      if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
1906      if (typeof option == 'string') data[option]()
1907    })
1908  }
1909
1910  var old = $.fn.tooltip
1911
1912  $.fn.tooltip             = Plugin
1913  $.fn.tooltip.Constructor = Tooltip
1914
1915
1916  // TOOLTIP NO CONFLICT
1917  // ===================
1918
1919  $.fn.tooltip.noConflict = function () {
1920    $.fn.tooltip = old
1921    return this
1922  }
1923
1924}(jQuery);
1925
1926/* ========================================================================
1927 * Bootstrap: popover.js v3.3.7
1928 * http://getbootstrap.com/javascript/#popovers
1929 * ========================================================================
1930 * Copyright 2011-2016 Twitter, Inc.
1931 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
1932 * ======================================================================== */
1933
1934
1935+function ($) {
1936  'use strict';
1937
1938  // POPOVER PUBLIC CLASS DEFINITION
1939  // ===============================
1940
1941  var Popover = function (element, options) {
1942    this.init('popover', element, options)
1943  }
1944
1945  if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
1946
1947  Popover.VERSION  = '3.3.7'
1948
1949  Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
1950    placement: 'right',
1951    trigger: 'click',
1952    content: '',
1953    template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
1954  })
1955
1956
1957  // NOTE: POPOVER EXTENDS tooltip.js
1958  // ================================
1959
1960  Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
1961
1962  Popover.prototype.constructor = Popover
1963
1964  Popover.prototype.getDefaults = function () {
1965    return Popover.DEFAULTS
1966  }
1967
1968  Popover.prototype.setContent = function () {
1969    var $tip    = this.tip()
1970    var title   = this.getTitle()
1971    var content = this.getContent()
1972
1973    $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
1974    $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events
1975      this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
1976    ](content)
1977
1978    $tip.removeClass('fade top bottom left right in')
1979
1980    // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
1981    // this manually by checking the contents.
1982    if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
1983  }
1984
1985  Popover.prototype.hasContent = function () {
1986    return this.getTitle() || this.getContent()
1987  }
1988
1989  Popover.prototype.getContent = function () {
1990    var $e = this.$element
1991    var o  = this.options
1992
1993    return $e.attr('data-content')
1994      || (typeof o.content == 'function' ?
1995            o.content.call($e[0]) :
1996            o.content)
1997  }
1998
1999  Popover.prototype.arrow = function () {
2000    return (this.$arrow = this.$arrow || this.tip().find('.arrow'))
2001  }
2002
2003
2004  // POPOVER PLUGIN DEFINITION
2005  // =========================
2006
2007  function Plugin(option) {
2008    return this.each(function () {
2009      var $this   = $(this)
2010      var data    = $this.data('bs.popover')
2011      var options = typeof option == 'object' && option
2012
2013      if (!data && /destroy|hide/.test(option)) return
2014      if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
2015      if (typeof option == 'string') data[option]()
2016    })
2017  }
2018
2019  var old = $.fn.popover
2020
2021  $.fn.popover             = Plugin
2022  $.fn.popover.Constructor = Popover
2023
2024
2025  // POPOVER NO CONFLICT
2026  // ===================
2027
2028  $.fn.popover.noConflict = function () {
2029    $.fn.popover = old
2030    return this
2031  }
2032
2033}(jQuery);
2034
2035/* ========================================================================
2036 * Bootstrap: scrollspy.js v3.3.7
2037 * http://getbootstrap.com/javascript/#scrollspy
2038 * ========================================================================
2039 * Copyright 2011-2016 Twitter, Inc.
2040 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
2041 * ======================================================================== */
2042
2043
2044+function ($) {
2045  'use strict';
2046
2047  // SCROLLSPY CLASS DEFINITION
2048  // ==========================
2049
2050  function ScrollSpy(element, options) {
2051    this.$body          = $(document.body)
2052    this.$scrollElement = $(element).is(document.body) ? $(window) : $(element)
2053    this.options        = $.extend({}, ScrollSpy.DEFAULTS, options)
2054    this.selector       = (this.options.target || '') + ' .nav li > a'
2055    this.offsets        = []
2056    this.targets        = []
2057    this.activeTarget   = null
2058    this.scrollHeight   = 0
2059
2060    this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this))
2061    this.refresh()
2062    this.process()
2063  }
2064
2065  ScrollSpy.VERSION  = '3.3.7'
2066
2067  ScrollSpy.DEFAULTS = {
2068    offset: 10
2069  }
2070
2071  ScrollSpy.prototype.getScrollHeight = function () {
2072    return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
2073  }
2074
2075  ScrollSpy.prototype.refresh = function () {
2076    var that          = this
2077    var offsetMethod  = 'offset'
2078    var offsetBase    = 0
2079
2080    this.offsets      = []
2081    this.targets      = []
vendor: 14,034 bytes, lines 2082-2563
2082    this.scrollHeight = this.getScrollHeight()
2083
2084    if (!$.isWindow(this.$scrollElement[0])) {
2085      offsetMethod = 'position'
2086      offsetBase   = this.$scrollElement.scrollTop()
2087    }
2088
2089    this.$body
2090      .find(this.selector)
2091      .map(function () {
2092        var $el   = $(this)
2093        var href  = $el.data('target') || $el.attr('href')
2094        var $href = /^#./.test(href) && $(href)
2095
2096        return ($href
2097          && $href.length
2098          && $href.is(':visible')
2099          && [[$href[offsetMethod]().top + offsetBase, href]]) || null
2100      })
2101      .sort(function (a, b) { return a[0] - b[0] })
2102      .each(function () {
2103        that.offsets.push(this[0])
2104        that.targets.push(this[1])
2105      })
2106  }
2107
2108  ScrollSpy.prototype.process = function () {
2109    var scrollTop    = this.$scrollElement.scrollTop() + this.options.offset
2110    var scrollHeight = this.getScrollHeight()
2111    var maxScroll    = this.options.offset + scrollHeight - this.$scrollElement.height()
2112    var offsets      = this.offsets
2113    var targets      = this.targets
2114    var activeTarget = this.activeTarget
2115    var i
2116
2117    if (this.scrollHeight != scrollHeight) {
2118      this.refresh()
2119    }
2120
2121    if (scrollTop >= maxScroll) {
2122      return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
2123    }
2124
2125    if (activeTarget && scrollTop < offsets[0]) {
2126      this.activeTarget = null
2127      return this.clear()
2128    }
2129
2130    for (i = offsets.length; i--;) {
2131      activeTarget != targets[i]
2132        && scrollTop >= offsets[i]
2133        && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1])
2134        && this.activate(targets[i])
2135    }
2136  }
2137
2138  ScrollSpy.prototype.activate = function (target) {
2139    this.activeTarget = target
2140
2141    this.clear()
2142
2143    var selector = this.selector +
2144      '[data-target="' + target + '"],' +
2145      this.selector + '[href="' + target + '"]'
2146
2147    var active = $(selector)
2148      .parents('li')
2149      .addClass('active')
2150
2151    if (active.parent('.dropdown-menu').length) {
2152      active = active
2153        .closest('li.dropdown')
2154        .addClass('active')
2155    }
2156
2157    active.trigger('activate.bs.scrollspy')
2158  }
2159
2160  ScrollSpy.prototype.clear = function () {
2161    $(this.selector)
2162      .parentsUntil(this.options.target, '.active')
2163      .removeClass('active')
2164  }
2165
2166
2167  // SCROLLSPY PLUGIN DEFINITION
2168  // ===========================
2169
2170  function Plugin(option) {
2171    return this.each(function () {
2172      var $this   = $(this)
2173      var data    = $this.data('bs.scrollspy')
2174      var options = typeof option == 'object' && option
2175
2176      if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
2177      if (typeof option == 'string') data[option]()
2178    })
2179  }
2180
2181  var old = $.fn.scrollspy
2182
2183  $.fn.scrollspy             = Plugin
2184  $.fn.scrollspy.Constructor = ScrollSpy
2185
2186
2187  // SCROLLSPY NO CONFLICT
2188  // =====================
2189
2190  $.fn.scrollspy.noConflict = function () {
2191    $.fn.scrollspy = old
2192    return this
2193  }
2194
2195
2196  // SCROLLSPY DATA-API
2197  // ==================
2198
2199  $(window).on('load.bs.scrollspy.data-api', function () {
2200    $('[data-spy="scroll"]').each(function () {
2201      var $spy = $(this)
2202      Plugin.call($spy, $spy.data())
2203    })
2204  })
2205
2206}(jQuery);
2207
2208/* ========================================================================
2209 * Bootstrap: tab.js v3.3.7
2210 * http://getbootstrap.com/javascript/#tabs
2211 * ========================================================================
2212 * Copyright 2011-2016 Twitter, Inc.
2213 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
2214 * ======================================================================== */
2215
2216
2217+function ($) {
2218  'use strict';
2219
2220  // TAB CLASS DEFINITION
2221  // ====================
2222
2223  var Tab = function (element) {
2224    // jscs:disable requireDollarBeforejQueryAssignment
2225    this.element = $(element)
2226    // jscs:enable requireDollarBeforejQueryAssignment
2227  }
2228
2229  Tab.VERSION = '3.3.7'
2230
2231  Tab.TRANSITION_DURATION = 150
2232
2233  Tab.prototype.show = function () {
2234    var $this    = this.element
2235    var $ul      = $this.closest('ul:not(.dropdown-menu)')
2236    var selector = $this.data('target')
2237
2238    if (!selector) {
2239      selector = $this.attr('href')
2240      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
2241    }
2242
2243    if ($this.parent('li').hasClass('active')) return
2244
2245    var $previous = $ul.find('.active:last a')
2246    var hideEvent = $.Event('hide.bs.tab', {
2247      relatedTarget: $this[0]
2248    })
2249    var showEvent = $.Event('show.bs.tab', {
2250      relatedTarget: $previous[0]
2251    })
2252
2253    $previous.trigger(hideEvent)
2254    $this.trigger(showEvent)
2255
2256    if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
2257
2258    var $target = $(selector)
2259
2260    this.activate($this.closest('li'), $ul)
2261    this.activate($target, $target.parent(), function () {
2262      $previous.trigger({
2263        type: 'hidden.bs.tab',
2264        relatedTarget: $this[0]
2265      })
2266      $this.trigger({
2267        type: 'shown.bs.tab',
2268        relatedTarget: $previous[0]
2269      })
2270    })
2271  }
2272
2273  Tab.prototype.activate = function (element, container, callback) {
2274    var $active    = container.find('> .active')
2275    var transition = callback
2276      && $.support.transition
2277      && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length)
2278
2279    function next() {
2280      $active
2281        .removeClass('active')
2282        .find('> .dropdown-menu > .active')
2283          .removeClass('active')
2284        .end()
2285        .find('[data-toggle="tab"]')
2286          .attr('aria-expanded', false)
2287
2288      element
2289        .addClass('active')
2290        .find('[data-toggle="tab"]')
2291          .attr('aria-expanded', true)
2292
2293      if (transition) {
2294        element[0].offsetWidth // reflow for transition
2295        element.addClass('in')
2296      } else {
2297        element.removeClass('fade')
2298      }
2299
2300      if (element.parent('.dropdown-menu').length) {
2301        element
2302          .closest('li.dropdown')
2303            .addClass('active')
2304          .end()
2305          .find('[data-toggle="tab"]')
2306            .attr('aria-expanded', true)
2307      }
2308
2309      callback && callback()
2310    }
2311
2312    $active.length && transition ?
2313      $active
2314        .one('bsTransitionEnd', next)
2315        .emulateTransitionEnd(Tab.TRANSITION_DURATION) :
2316      next()
2317
2318    $active.removeClass('in')
2319  }
2320
2321
2322  // TAB PLUGIN DEFINITION
2323  // =====================
2324
2325  function Plugin(option) {
2326    return this.each(function () {
2327      var $this = $(this)
2328      var data  = $this.data('bs.tab')
2329
2330      if (!data) $this.data('bs.tab', (data = new Tab(this)))
2331      if (typeof option == 'string') data[option]()
2332    })
2333  }
2334
2335  var old = $.fn.tab
2336
2337  $.fn.tab             = Plugin
2338  $.fn.tab.Constructor = Tab
2339
2340
2341  // TAB NO CONFLICT
2342  // ===============
2343
2344  $.fn.tab.noConflict = function () {
2345    $.fn.tab = old
2346    return this
2347  }
2348
2349
2350  // TAB DATA-API
2351  // ============
2352
2353  var clickHandler = function (e) {
2354    e.preventDefault()
2355    Plugin.call($(this), 'show')
2356  }
2357
2358  $(document)
2359    .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
2360    .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
2361
2362}(jQuery);
2363
2364/* ========================================================================
2365 * Bootstrap: affix.js v3.3.7
2366 * http://getbootstrap.com/javascript/#affix
2367 * ========================================================================
2368 * Copyright 2011-2016 Twitter, Inc.
2369 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
2370 * ======================================================================== */
2371
2372
2373+function ($) {
2374  'use strict';
2375
2376  // AFFIX CLASS DEFINITION
2377  // ======================
2378
2379  var Affix = function (element, options) {
2380    this.options = $.extend({}, Affix.DEFAULTS, options)
2381
2382    this.$target = $(this.options.target)
2383      .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
2384      .on('click.bs.affix.data-api',  $.proxy(this.checkPositionWithEventLoop, this))
2385
2386    this.$element     = $(element)
2387    this.affixed      = null
2388    this.unpin        = null
2389    this.pinnedOffset = null
2390
2391    this.checkPosition()
2392  }
2393
2394  Affix.VERSION  = '3.3.7'
2395
2396  Affix.RESET    = 'affix affix-top affix-bottom'
2397
2398  Affix.DEFAULTS = {
2399    offset: 0,
2400    target: window
2401  }
2402
2403  Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
2404    var scrollTop    = this.$target.scrollTop()
2405    var position     = this.$element.offset()
2406    var targetHeight = this.$target.height()
2407
2408    if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false
2409
2410    if (this.affixed == 'bottom') {
2411      if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
2412      return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
2413    }
2414
2415    var initializing   = this.affixed == null
2416    var colliderTop    = initializing ? scrollTop : position.top
2417    var colliderHeight = initializing ? targetHeight : height
2418
2419    if (offsetTop != null && scrollTop <= offsetTop) return 'top'
2420    if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'
2421
2422    return false
2423  }
2424
2425  Affix.prototype.getPinnedOffset = function () {
2426    if (this.pinnedOffset) return this.pinnedOffset
2427    this.$element.removeClass(Affix.RESET).addClass('affix')
2428    var scrollTop = this.$target.scrollTop()
2429    var position  = this.$element.offset()
2430    return (this.pinnedOffset = position.top - scrollTop)
2431  }
2432
2433  Affix.prototype.checkPositionWithEventLoop = function () {
2434    setTimeout($.proxy(this.checkPosition, this), 1)
2435  }
2436
2437  Affix.prototype.checkPosition = function () {
2438    if (!this.$element.is(':visible')) return
2439
2440    var height       = this.$element.height()
2441    var offset       = this.options.offset
2442    var offsetTop    = offset.top
2443    var offsetBottom = offset.bottom
2444    var scrollHeight = Math.max($(document).height(), $(document.body).height())
2445
2446    if (typeof offset != 'object')         offsetBottom = offsetTop = offset
2447    if (typeof offsetTop == 'function')    offsetTop    = offset.top(this.$element)
2448    if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
2449
2450    var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)
2451
2452    if (this.affixed != affix) {
2453      if (this.unpin != null) this.$element.css('top', '')
2454
2455      var affixType = 'affix' + (affix ? '-' + affix : '')
2456      var e         = $.Event(affixType + '.bs.affix')
2457
2458      this.$element.trigger(e)
2459
2460      if (e.isDefaultPrevented()) return
2461
2462      this.affixed = affix
2463      this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
2464
2465      this.$element
2466        .removeClass(Affix.RESET)
2467        .addClass(affixType)
2468        .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
2469    }
2470
2471    if (affix == 'bottom') {
2472      this.$element.offset({
2473        top: scrollHeight - height - offsetBottom
2474      })
2475    }
2476  }
2477
2478
2479  // AFFIX PLUGIN DEFINITION
2480  // =======================
2481
2482  function Plugin(option) {
2483    return this.each(function () {
2484      var $this   = $(this)
2485      var data    = $this.data('bs.affix')
2486      var options = typeof option == 'object' && option
2487
2488      if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
2489      if (typeof option == 'string') data[option]()
2490    })
2491  }
2492
2493  var old = $.fn.affix
2494
2495  $.fn.affix             = Plugin
2496  $.fn.affix.Constructor = Affix
2497
2498
2499  // AFFIX NO CONFLICT
2500  // =================
2501
2502  $.fn.affix.noConflict = function () {
2503    $.fn.affix = old
2504    return this
2505  }
2506
2507
2508  // AFFIX DATA-API
2509  // ==============
2510
2511  $(window).on('load', function () {
2512    $('[data-spy="affix"]').each(function () {
2513      var $spy = $(this)
2514      var data = $spy.data()
2515
2516      data.offset = data.offset || {}
2517
2518      if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom
2519      if (data.offsetTop    != null) data.offset.top    = data.offsetTop
2520
2521      Plugin.call($spy, data)
2522    })
2523  })
2524
2525}(jQuery);
2526
2527/*!
2528 * @fileOverview TouchSwipe - jQuery Plugin
2529 * @version 1.6.18
2530 *
2531 * @author Matt Bryson http://www.github.com/mattbryson
2532 * @see https://github.com/mattbryson/TouchSwipe-Jquery-Plugin
2533 * @see http://labs.rampinteractive.co.uk/touchSwipe/
2534 * @see http://plugins.jquery.com/project/touchSwipe
2535 * @license
2536 * Copyright (c) 2010-2015 Matt Bryson
2537 * Dual licensed under the MIT or GPL Version 2 licenses.
2538 *
2539 *
2540 * taken from https://cdnjs.cloudflare.com/ajax/libs/jquery.touchswipe/1.6.18/jquery.touchSwipe.js
2541 */
2542
2543/*
2544 *
2545 * Changelog
2546 * $Date: 2010-12-12 (Wed, 12 Dec 2010) $
2547 * $version: 1.0.0
2548 * $version: 1.0.1 - removed multibyte comments
2549 *
2550 * $Date: 2011-21-02 (Mon, 21 Feb 2011) $
2551 * $version: 1.1.0 	- added allowPageScroll property to allow swiping and scrolling of page
2552 *					- changed handler signatures so one handler can be used for multiple events
2553 * $Date: 2011-23-02 (Wed, 23 Feb 2011) $
2554 * $version: 1.2.0 	- added click handler. This is fired if the user simply clicks and does not swipe. The event object and click target are passed to handler.
2555 *					- If you use the http://code.google.com/p/jquery-ui-for-ipad-and-iphone/ plugin, you can also assign jQuery mouse events to children of a touchSwipe object.
2556 * $version: 1.2.1 	- removed console log!
2557 *
2558 * $version: 1.2.2 	- Fixed bug where scope was not preserved in callback methods.
2559 *
2560 * $Date: 2011-28-04 (Thurs, 28 April 2011) $
2561 * $version: 1.2.4 	- Changed licence terms to be MIT or GPL inline with jQuery. Added check for support of touch events to stop non compatible browsers erroring.
2562 *
2563 * $Date: 2011-27-09 (Tues, 27 September 2011) $
2564 * $version: 1.2.5 	- Added support for testing swipes with mouse on desktop browser (thanks to https://github.com/joelhy)
2565 *
2566 * $Date: 2012-14-05 (Mon, 14 May 2012) $
2567 * $version: 1.2.6 	- Added timeThreshold between start and end touch, so user can ignore slow swipes (thanks to Mark Chase). Default is null, all swipes are detected
2568 *
2569 * $Date: 2012-05-06 (Tues, 05 June 2012) $
2570 * $version: 1.2.7 	- Changed time threshold to have null default for backwards compatibility. Added duration param passed back in events, and refactored how time is handled.
2571 *
2572 * $Date: 2012-05-06 (Tues, 05 June 2012) $
2573 * $version: 1.2.8 	- Added the possibility to return a value like null or false in the trigger callback. In that way we can control when the touch start/move should take effect or not (simply by returning in some cases return null; or return false;) This effects the ontouchstart/ontouchmove event.
2574 *
2575 * $Date: 2012-06-06 (Wed, 06 June 2012) $
2576 * $version: 1.3.0 	- Refactored whole plugin to allow for methods to be executed, as well as exposed defaults for user override. Added 'enable', 'disable', and 'destroy' methods
2577 *
2578 * $Date: 2012-05-06 (Fri, 05 June 2012) $
2579 * $version: 1.3.1 	- Bug fixes  - bind() with false as last argument is no longer supported in jQuery 1.6, also, if you just click, the duration is now returned correctly.
2580 *
2581 * $Date: 2012-29-07 (Sun, 29 July 2012) $
2582 * $version: 1.3.2	- Added fallbackToMouseEvents option to NOT capture mouse events on non touch devices.
2583 * 			- Added "all" fingers value to the fingers property, so any combination of fingers triggers the swipe, allowing event handlers to check the finger count
2584 *
2585 * $Date: 2012-09-08 (Thurs, 9 Aug 2012) $
2586 * $version: 1.3.3	- Code tidy prep for minefied version
2587 *
2588 * $Date: 2012-04-10 (wed, 4 Oct 2012) $
2589 * $version: 1.4.0	- Added pinch support, pinchIn and pinchOut
2590 *
2591 * $Date: 2012-11-10 (Thurs, 11 Oct 2012) $
2592 * $version: 1.5.0	- Added excludedElements, a jquery selector that specifies child elements that do NOT trigger swipes. By default, this is .noSwipe
2593 *
2594 * $Date: 2012-22-10 (Mon, 22 Oct 2012) $
2595 * $version: 1.5.1	- Fixed bug with jQuery 1.8 and trailing comma in excludedElements
2596 *					- Fixed bug with IE and eventPreventDefault()
2597 * $Date: 2013-01-12 (Fri, 12 Jan 2013) $
2598 * $version: 1.6.0	- Fixed bugs with pinching, mainly when both pinch and swipe enabled, as well as adding time threshold for multifinger gestures, so releasing one finger beofre the other doesnt trigger as single finger gesture.
2599 *					- made the demo site all static local HTML pages so they can be run locally by a developer
2600 *					- added jsDoc comments and added documentation for the plugin
2601 *					- code tidy
2602 *					- added triggerOnTouchLeave property that will end the event when the user swipes off the element.
2603 * $Date: 2013-03-23 (Sat, 23 Mar 2013) $
2604 * $version: 1.6.1	- Added support for ie8 touch events
2605 * $version: 1.6.2	- Added support for events binding with on / off / bind in jQ for all callback names.
2606 *                   - Deprecated the 'click' handler in favour of tap.
2607 *                   - added cancelThreshold property
2608 *                   - added option method to update init options at runtime
2609 * $version 1.6.3    - added doubletap, longtap events and longTapThreshold, doubleTapThreshold property
2610 *
2611 * $Date: 2013-04-04 (Thurs, 04 April 2013) $
2612 * $version 1.6.4    - Fixed bug with cancelThreshold introduced in 1.6.3, where swipe status no longer fired start event, and stopped once swiping back.
2613 *
2614 * $Date: 2013-08-24 (Sat, 24 Aug 2013) $
2615 * $version 1.6.5    - Merged a few pull requests fixing various bugs, added AMD support.
2616 *
2617 * $Date: 2014-06-04 (Wed, 04 June 2014) $
2618 * $version 1.6.6 	- Merge of pull requests.
2619 *    				- IE10 touch support
2620 *    				- Only prevent default event handling on valid swipe
2621 *    				- Separate license/changelog comment
2622 *    				- Detect if the swipe is valid at the end of the touch event.
2623 *    				- Pass fingerdata to event handlers.
2624 *    				- Add 'hold' gesture
2625 *    				- Be more tolerant about the tap distance
2626 *    				- Typos and minor fixes
2627 *
2628 * $Date: 2015-22-01 (Thurs, 22 Jan 2015) $
2629 * $version 1.6.7    - Added patch from https://github.com/mattbryson/TouchSwipe-Jquery-Plugin/issues/206 to fix memory leak
2630 *
2631 * $Date: 2015-2-2 (Mon, 2 Feb 2015) $
2632 * $version 1.6.8    - Added preventDefaultEvents option to proxy events regardless.
2633 *					- Fixed issue with swipe and pinch not triggering at the same time
2634 *
2635 * $Date: 2015-9-6 (Tues, 9 June 2015) $
2636 * $version 1.6.9    - Added PR from jdalton/hybrid to fix pointer events
2637 *					- Added scrolling demo
2638 *					- Added version property to plugin
2639 *
2640 * $Date: 2015-1-10 (Wed, 1 October 2015) $
2641 * $version 1.6.10    - Added PR from beatspace to fix tap events
2642 * $version 1.6.11    - Added PRs from indri-indri ( Doc tidyup), kkirsche ( Bower tidy up ), UziTech (preventDefaultEvents fixes )
2643 *					 - Allowed setting multiple options via .swipe("options", options_hash) and more simply .swipe(options_hash) or exisitng instances
2644 * $version 1.6.12    - Fixed bug with multi finger releases above 2 not triggering events
2645 *
2646 * $Date: 2015-12-18 (Fri, 18 December 2015) $
2647 * $version 1.6.13    - Added PRs
2648 *                    - Fixed #267 allowPageScroll not working correctly
2649 * $version 1.6.14    - Fixed #220 / #248 doubletap not firing with swipes, #223 commonJS compatible
2650 * $version 1.6.15    - More bug fixes
2651 *
2652 * $Date: 2016-04-29 (Fri, 29 April 2016) $
2653 * $version 1.6.16    - Swipes with 0 distance now allow default events to trigger.  So tapping any form elements or A tags will allow default interaction, but swiping will trigger a swipe.
2654                        Removed the a, input, select etc from the excluded Children list as the 0 distance tap solves that issue.
2655* $Date: 2016-05-19  (Fri, 29 April 2016) $
2656* $version 1.6.17     - Fixed context issue when calling instance methods via $("selector").swipe("method");
2657* $version 1.6.18     - now honors fallbackToMouseEvents=false for MS Pointer events when a Mouse is used.
2658
2659 */
2660
2661/**
2662 * See (http://jquery.com/).
2663 * @name $
2664 * @class
2665 * See the jQuery Library  (http://jquery.com/) for full details.  This just
2666 * documents the function and classes that are added to jQuery by this plug-in.
2667 */
2668
2669/**
2670 * See (http://jquery.com/)
2671 * @name fn
2672 * @class
2673 * See the jQuery Library  (http://jquery.com/) for full details.  This just
2674 * documents the function and classes that are added to jQuery by this plug-in.
2675 * @memberOf $
2676 */
2677
2678
2679(function(factory) {
2680  if (typeof define === 'function' && define.amd && define.amd.jQuery) {
2681    // AMD. Register as anonymous module.
2682    define(['jquery'], factory);
2683  } else if (typeof module !== 'undefined' && module.exports) {
2684    // CommonJS Module
2685    factory(require("jquery"));
2686  } else {
2687    // Browser globals.
2688    factory(jQuery);
2689  }
2690}(function($) {
2691  "use strict";
2692
2693  //Constants
2694  var VERSION = "1.6.18",
2695    LEFT = "left",
2696    RIGHT = "right",
2697    UP = "up",
2698    DOWN = "down",
2699    IN = "in",
2700    OUT = "out",
2701
2702    NONE = "none",
2703    AUTO = "auto",
2704
2705    SWIPE = "swipe",
2706    PINCH = "pinch",
2707    TAP = "tap",
2708    DOUBLE_TAP = "doubletap",
2709    LONG_TAP = "longtap",
2710    HOLD = "hold",
2711
2712    HORIZONTAL = "horizontal",
2713    VERTICAL = "vertical",
2714
2715    ALL_FINGERS = "all",
2716
2717    DOUBLE_TAP_THRESHOLD = 10,
2718
2719    PHASE_START = "start",
2720    PHASE_MOVE = "move",
2721    PHASE_END = "end",
2722    PHASE_CANCEL = "cancel",
2723
2724    SUPPORTS_TOUCH = 'ontouchstart' in window,
2725
2726    SUPPORTS_POINTER_IE10 = window.navigator.msPointerEnabled && !window.navigator.pointerEnabled && !SUPPORTS_TOUCH,
2727
2728    SUPPORTS_POINTER = (window.navigator.pointerEnabled || window.navigator.msPointerEnabled) && !SUPPORTS_TOUCH,
2729
2730    PLUGIN_NS = 'TouchSwipe';
2731
2732
2733
2734  /**
2735  * The default configuration, and available options to configure touch swipe with.
2736  * You can set the default values by updating any of the properties prior to instantiation.
2737  * @name $.fn.swipe.defaults
2738  * @namespace
2739  * @property {int} [fingers=1] The number of fingers to detect in a swipe. Any swipes that do not meet this requirement will NOT trigger swipe handlers.
2740  * @property {int} [threshold=75] The number of pixels that the user must move their finger by before it is considered a swipe.
2741  * @property {int} [cancelThreshold=null] The number of pixels that the user must move their finger back from the original swipe direction to cancel the gesture.
2742  * @property {int} [pinchThreshold=20] The number of pixels that the user must pinch their finger by before it is considered a pinch.
2743  * @property {int} [maxTimeThreshold=null] Time, in milliseconds, between touchStart and touchEnd must NOT exceed in order to be considered a swipe.
2744  * @property {int} [fingerReleaseThreshold=250] Time in milliseconds between releasing multiple fingers.  If 2 fingers are down, and are released one after the other, if they are within this threshold, it counts as a simultaneous release.
2745  * @property {int} [longTapThreshold=500] Time in milliseconds between tap and release for a long tap
2746  * @property {int} [doubleTapThreshold=200] Time in milliseconds between 2 taps to count as a double tap
2747  * @property {function} [swipe=null] A handler to catch all swipes. See {@link $.fn.swipe#event:swipe}
2748  * @property {function} [swipeLeft=null] A handler that is triggered for "left" swipes. See {@link $.fn.swipe#event:swipeLeft}
2749  * @property {function} [swipeRight=null] A handler that is triggered for "right" swipes. See {@link $.fn.swipe#event:swipeRight}
2750  * @property {function} [swipeUp=null] A handler that is triggered for "up" swipes. See {@link $.fn.swipe#event:swipeUp}
2751  * @property {function} [swipeDown=null] A handler that is triggered for "down" swipes. See {@link $.fn.swipe#event:swipeDown}
2752  * @property {function} [swipeStatus=null] A handler triggered for every phase of the swipe. See {@link $.fn.swipe#event:swipeStatus}
2753  * @property {function} [pinchIn=null] A handler triggered for pinch in events. See {@link $.fn.swipe#event:pinchIn}
2754  * @property {function} [pinchOut=null] A handler triggered for pinch out events. See {@link $.fn.swipe#event:pinchOut}
2755  * @property {function} [pinchStatus=null] A handler triggered for every phase of a pinch. See {@link $.fn.swipe#event:pinchStatus}
2756  * @property {function} [tap=null] A handler triggered when a user just taps on the item, rather than swipes it. If they do not move, tap is triggered, if they do move, it is not.
2757  * @property {function} [doubleTap=null] A handler triggered when a user double taps on the item. The delay between taps can be set with the doubleTapThreshold property. See {@link $.fn.swipe.defaults#doubleTapThreshold}
2758  * @property {function} [longTap=null] A handler triggered when a user long taps on the item. The delay between start and end can be set with the longTapThreshold property. See {@link $.fn.swipe.defaults#longTapThreshold}
2759  * @property (function) [hold=null] A handler triggered when a user reaches longTapThreshold on the item. See {@link $.fn.swipe.defaults#longTapThreshold}
2760  * @property {boolean} [triggerOnTouchEnd=true] If true, the swipe events are triggered when the touch end event is received (user releases finger).  If false, it will be triggered on reaching the threshold, and then cancel the touch event automatically.
2761  * @property {boolean} [triggerOnTouchLeave=false] If true, then when the user leaves the swipe object, the swipe will end and trigger appropriate handlers.
2762  * @property {string|undefined} [allowPageScroll='auto'] How the browser handles page scrolls when the user is swiping on a touchSwipe object. See {@link $.fn.swipe.pageScroll}.  <br/><br/>
2763  									<code>"auto"</code> : all undefined swipes will cause the page to scroll in that direction. <br/>
2764  									<code>"none"</code> : the page will not scroll when user swipes. <br/>
2765  									<code>"horizontal"</code> : will force page to scroll on horizontal swipes. <br/>
2766  									<code>"vertical"</code> : will force page to scroll on vertical swipes. <br/>
2767  * @property {boolean} [fallbackToMouseEvents=true] If true mouse events are used when run on a non touch device, false will stop swipes being triggered by mouse events on non tocuh devices.
2768  * @property {string} [excludedElements=".noSwipe"] A jquery selector that specifies child elements that do NOT trigger swipes. By default this excludes elements with the class .noSwipe .
2769  * @property {boolean} [preventDefaultEvents=true] by default default events are cancelled, so the page doesn't move.  You can dissable this so both native events fire as well as your handlers.
2770
2771  */
2772  var defaults = {
2773    fingers: 1,
2774    threshold: 75,
2775    cancelThreshold: null,
2776    pinchThreshold: 20,
2777    maxTimeThreshold: null,
2778    fingerReleaseThreshold: 250,
2779    longTapThreshold: 500,
2780    doubleTapThreshold: 200,
2781    swipe: null,
2782    swipeLeft: null,
2783    swipeRight: null,
2784    swipeUp: null,
2785    swipeDown: null,
2786    swipeStatus: null,
2787    pinchIn: null,
2788    pinchOut: null,
2789    pinchStatus: null,
2790    click: null, //Deprecated since 1.6.2
2791    tap: null,
2792    doubleTap: null,
2793    longTap: null,
2794    hold: null,
2795    triggerOnTouchEnd: true,
2796    triggerOnTouchLeave: false,
2797    allowPageScroll: "auto",
2798    fallbackToMouseEvents: true,
vendor: 55,804 bytes, lines 2799-4424
2799    excludedElements: ".noSwipe",
2800    preventDefaultEvents: true
2801  };
2802
2803
2804
2805  /**
2806   * Applies TouchSwipe behaviour to one or more jQuery objects.
2807   * The TouchSwipe plugin can be instantiated via this method, or methods within
2808   * TouchSwipe can be executed via this method as per jQuery plugin architecture.
2809   * An existing plugin can have its options changed simply by re calling .swipe(options)
2810   * @see TouchSwipe
2811   * @class
2812   * @param {Mixed} method If the current DOMNode is a TouchSwipe object, and <code>method</code> is a TouchSwipe method, then
2813   * the <code>method</code> is executed, and any following arguments are passed to the TouchSwipe method.
2814   * If <code>method</code> is an object, then the TouchSwipe class is instantiated on the current DOMNode, passing the
2815   * configuration properties defined in the object. See TouchSwipe
2816   *
2817   */
2818  $.fn.swipe = function(method) {
2819    var $this = $(this),
2820      plugin = $this.data(PLUGIN_NS);
2821
2822    //Check if we are already instantiated and trying to execute a method
2823    if (plugin && typeof method === 'string') {
2824      if (plugin[method]) {
2825        return plugin[method].apply(plugin, Array.prototype.slice.call(arguments, 1));
2826      } else {
2827        $.error('Method ' + method + ' does not exist on jQuery.swipe');
2828      }
2829    }
2830
2831    //Else update existing plugin with new options hash
2832    else if (plugin && typeof method === 'object') {
2833      plugin['option'].apply(plugin, arguments);
2834    }
2835
2836    //Else not instantiated and trying to pass init object (or nothing)
2837    else if (!plugin && (typeof method === 'object' || !method)) {
2838      return init.apply(this, arguments);
2839    }
2840
2841    return $this;
2842  };
2843
2844  /**
2845   * The version of the plugin
2846   * @readonly
2847   */
2848  $.fn.swipe.version = VERSION;
2849
2850
2851
2852  //Expose our defaults so a user could override the plugin defaults
2853  $.fn.swipe.defaults = defaults;
2854
2855  /**
2856   * The phases that a touch event goes through.  The <code>phase</code> is passed to the event handlers.
2857   * These properties are read only, attempting to change them will not alter the values passed to the event handlers.
2858   * @namespace
2859   * @readonly
2860   * @property {string} PHASE_START Constant indicating the start phase of the touch event. Value is <code>"start"</code>.
2861   * @property {string} PHASE_MOVE Constant indicating the move phase of the touch event. Value is <code>"move"</code>.
2862   * @property {string} PHASE_END Constant indicating the end phase of the touch event. Value is <code>"end"</code>.
2863   * @property {string} PHASE_CANCEL Constant indicating the cancel phase of the touch event. Value is <code>"cancel"</code>.
2864   */
2865  $.fn.swipe.phases = {
2866    PHASE_START: PHASE_START,
2867    PHASE_MOVE: PHASE_MOVE,
2868    PHASE_END: PHASE_END,
2869    PHASE_CANCEL: PHASE_CANCEL
2870  };
2871
2872  /**
2873   * The direction constants that are passed to the event handlers.
2874   * These properties are read only, attempting to change them will not alter the values passed to the event handlers.
2875   * @namespace
2876   * @readonly
2877   * @property {string} LEFT Constant indicating the left direction. Value is <code>"left"</code>.
2878   * @property {string} RIGHT Constant indicating the right direction. Value is <code>"right"</code>.
2879   * @property {string} UP Constant indicating the up direction. Value is <code>"up"</code>.
2880   * @property {string} DOWN Constant indicating the down direction. Value is <code>"cancel"</code>.
2881   * @property {string} IN Constant indicating the in direction. Value is <code>"in"</code>.
2882   * @property {string} OUT Constant indicating the out direction. Value is <code>"out"</code>.
2883   */
2884  $.fn.swipe.directions = {
2885    LEFT: LEFT,
2886    RIGHT: RIGHT,
2887    UP: UP,
2888    DOWN: DOWN,
2889    IN: IN,
2890    OUT: OUT
2891  };
2892
2893  /**
2894   * The page scroll constants that can be used to set the value of <code>allowPageScroll</code> option
2895   * These properties are read only
2896   * @namespace
2897   * @readonly
2898   * @see $.fn.swipe.defaults#allowPageScroll
2899   * @property {string} NONE Constant indicating no page scrolling is allowed. Value is <code>"none"</code>.
2900   * @property {string} HORIZONTAL Constant indicating horizontal page scrolling is allowed. Value is <code>"horizontal"</code>.
2901   * @property {string} VERTICAL Constant indicating vertical page scrolling is allowed. Value is <code>"vertical"</code>.
2902   * @property {string} AUTO Constant indicating either horizontal or vertical will be allowed, depending on the swipe handlers registered. Value is <code>"auto"</code>.
2903   */
2904  $.fn.swipe.pageScroll = {
2905    NONE: NONE,
2906    HORIZONTAL: HORIZONTAL,
2907    VERTICAL: VERTICAL,
2908    AUTO: AUTO
2909  };
2910
2911  /**
2912   * Constants representing the number of fingers used in a swipe.  These are used to set both the value of <code>fingers</code> in the
2913   * options object, as well as the value of the <code>fingers</code> event property.
2914   * These properties are read only, attempting to change them will not alter the values passed to the event handlers.
2915   * @namespace
2916   * @readonly
2917   * @see $.fn.swipe.defaults#fingers
2918   * @property {string} ONE Constant indicating 1 finger is to be detected / was detected. Value is <code>1</code>.
2919   * @property {string} TWO Constant indicating 2 fingers are to be detected / were detected. Value is <code>2</code>.
2920   * @property {string} THREE Constant indicating 3 finger are to be detected / were detected. Value is <code>3</code>.
2921   * @property {string} FOUR Constant indicating 4 finger are to be detected / were detected. Not all devices support this. Value is <code>4</code>.
2922   * @property {string} FIVE Constant indicating 5 finger are to be detected / were detected. Not all devices support this. Value is <code>5</code>.
2923   * @property {string} ALL Constant indicating any combination of finger are to be detected.  Value is <code>"all"</code>.
2924   */
2925  $.fn.swipe.fingers = {
2926    ONE: 1,
2927    TWO: 2,
2928    THREE: 3,
2929    FOUR: 4,
2930    FIVE: 5,
2931    ALL: ALL_FINGERS
2932  };
2933
2934  /**
2935   * Initialise the plugin for each DOM element matched
2936   * This creates a new instance of the main TouchSwipe class for each DOM element, and then
2937   * saves a reference to that instance in the elements data property.
2938   * @internal
2939   */
2940  function init(options) {
2941    //Prep and extend the options
2942    if (options && (options.allowPageScroll === undefined && (options.swipe !== undefined || options.swipeStatus !== undefined))) {
2943      options.allowPageScroll = NONE;
2944    }
2945
2946    //Check for deprecated options
2947    //Ensure that any old click handlers are assigned to the new tap, unless we have a tap
2948    if (options.click !== undefined && options.tap === undefined) {
2949      options.tap = options.click;
2950    }
2951
2952    if (!options) {
2953      options = {};
2954    }
2955
2956    //pass empty object so we dont modify the defaults
2957    options = $.extend({}, $.fn.swipe.defaults, options);
2958
2959    //For each element instantiate the plugin
2960    return this.each(function() {
2961      var $this = $(this);
2962
2963      //Check we havent already initialised the plugin
2964      var plugin = $this.data(PLUGIN_NS);
2965
2966      if (!plugin) {
2967        plugin = new TouchSwipe(this, options);
2968        $this.data(PLUGIN_NS, plugin);
2969      }
2970    });
2971  }
2972
2973  /**
2974   * Main TouchSwipe Plugin Class.
2975   * Do not use this to construct your TouchSwipe object, use the jQuery plugin method $.fn.swipe(); {@link $.fn.swipe}
2976   * @private
2977   * @name TouchSwipe
2978   * @param {DOMNode} element The HTML DOM object to apply to plugin to
2979   * @param {Object} options The options to configure the plugin with.  @link {$.fn.swipe.defaults}
2980   * @see $.fh.swipe.defaults
2981   * @see $.fh.swipe
2982   * @class
2983   */
2984  function TouchSwipe(element, options) {
2985
2986    //take a local/instacne level copy of the options - should make it this.options really...
2987    var options = $.extend({}, options);
2988
2989    var useTouchEvents = (SUPPORTS_TOUCH || SUPPORTS_POINTER || !options.fallbackToMouseEvents),
2990      START_EV = useTouchEvents ? (SUPPORTS_POINTER ? (SUPPORTS_POINTER_IE10 ? 'MSPointerDown' : 'pointerdown') : 'touchstart') : 'mousedown',
2991      MOVE_EV = useTouchEvents ? (SUPPORTS_POINTER ? (SUPPORTS_POINTER_IE10 ? 'MSPointerMove' : 'pointermove') : 'touchmove') : 'mousemove',
2992      END_EV = useTouchEvents ? (SUPPORTS_POINTER ? (SUPPORTS_POINTER_IE10 ? 'MSPointerUp' : 'pointerup') : 'touchend') : 'mouseup',
2993      LEAVE_EV = useTouchEvents ? (SUPPORTS_POINTER ? 'mouseleave' : null) : 'mouseleave', //we manually detect leave on touch devices, so null event here
2994      CANCEL_EV = (SUPPORTS_POINTER ? (SUPPORTS_POINTER_IE10 ? 'MSPointerCancel' : 'pointercancel') : 'touchcancel');
2995
2996
2997
2998    //touch properties
2999    var distance = 0,
3000      direction = null,
3001      currentDirection = null,
3002      duration = 0,
3003      startTouchesDistance = 0,
3004      endTouchesDistance = 0,
3005      pinchZoom = 1,
3006      pinchDistance = 0,
3007      pinchDirection = 0,
3008      maximumsMap = null;
3009
3010
3011
3012    //jQuery wrapped element for this instance
3013    var $element = $(element);
3014
3015    //Current phase of th touch cycle
3016    var phase = "start";
3017
3018    // the current number of fingers being used.
3019    var fingerCount = 0;
3020
3021    //track mouse points / delta
3022    var fingerData = {};
3023
3024    //track times
3025    var startTime = 0,
3026      endTime = 0,
3027      previousTouchEndTime = 0,
3028      fingerCountAtRelease = 0,
3029      doubleTapStartTime = 0;
3030
3031    //Timeouts
3032    var singleTapTimeout = null,
3033      holdTimeout = null;
3034
3035    // Add gestures to all swipable areas if supported
3036    try {
3037      $element.bind(START_EV, touchStart);
3038      $element.bind(CANCEL_EV, touchCancel);
3039    } catch (e) {
3040      $.error('events not supported ' + START_EV + ',' + CANCEL_EV + ' on jQuery.swipe');
3041    }
3042
3043    //
3044    //Public methods
3045    //
3046
3047    /**
3048     * re-enables the swipe plugin with the previous configuration
3049     * @function
3050     * @name $.fn.swipe#enable
3051     * @return {DOMNode} The Dom element that was registered with TouchSwipe
3052     * @example $("#element").swipe("enable");
3053     */
3054    this.enable = function() {
3055      //Incase we are already enabled, clean up...
3056      this.disable();
3057      $element.bind(START_EV, touchStart);
3058      $element.bind(CANCEL_EV, touchCancel);
3059      return $element;
3060    };
3061
3062    /**
3063     * disables the swipe plugin
3064     * @function
3065     * @name $.fn.swipe#disable
3066     * @return {DOMNode} The Dom element that is now registered with TouchSwipe
3067     * @example $("#element").swipe("disable");
3068     */
3069    this.disable = function() {
3070      removeListeners();
3071      return $element;
3072    };
3073
3074    /**
3075     * Destroy the swipe plugin completely. To use any swipe methods, you must re initialise the plugin.
3076     * @function
3077     * @name $.fn.swipe#destroy
3078     * @example $("#element").swipe("destroy");
3079     */
3080    this.destroy = function() {
3081      removeListeners();
3082      $element.data(PLUGIN_NS, null);
3083      $element = null;
3084    };
3085
3086
3087    /**
3088     * Allows run time updating of the swipe configuration options.
3089     * @function
3090     * @name $.fn.swipe#option
3091     * @param {String} property The option property to get or set, or a has of multiple options to set
3092     * @param {Object} [value] The value to set the property to
3093     * @return {Object} If only a property name is passed, then that property value is returned. If nothing is passed the current options hash is returned.
3094     * @example $("#element").swipe("option", "threshold"); // return the threshold
3095     * @example $("#element").swipe("option", "threshold", 100); // set the threshold after init
3096     * @example $("#element").swipe("option", {threshold:100, fingers:3} ); // set multiple properties after init
3097     * @example $("#element").swipe({threshold:100, fingers:3} ); // set multiple properties after init - the "option" method is optional!
3098     * @example $("#element").swipe("option"); // Return the current options hash
3099     * @see $.fn.swipe.defaults
3100     *
3101     */
3102    this.option = function(property, value) {
3103
3104      if (typeof property === 'object') {
3105        options = $.extend(options, property);
3106      } else if (options[property] !== undefined) {
3107        if (value === undefined) {
3108          return options[property];
3109        } else {
3110          options[property] = value;
3111        }
3112      } else if (!property) {
3113        return options;
3114      } else {
3115        $.error('Option ' + property + ' does not exist on jQuery.swipe.options');
3116      }
3117
3118      return null;
3119    }
3120
3121
3122
3123    //
3124    // Private methods
3125    //
3126
3127    //
3128    // EVENTS
3129    //
3130    /**
3131     * Event handler for a touch start event.
3132     * Stops the default click event from triggering and stores where we touched
3133     * @inner
3134     * @param {object} jqEvent The normalised jQuery event object.
3135     */
3136    function touchStart(jqEvent) {
3137
3138      //If we already in a touch event (a finger already in use) then ignore subsequent ones..
3139      if (getTouchInProgress()) {
3140        return;
3141      }
3142
3143      //Check if this element matches any in the excluded elements selectors,  or its parent is excluded, if so, DON'T swipe
3144      if ($(jqEvent.target).closest(options.excludedElements, $element).length > 0) {
3145        return;
3146      }
3147
3148      //As we use Jquery bind for events, we need to target the original event object
3149      //If these events are being programmatically triggered, we don't have an original event object, so use the Jq one.
3150      var event = jqEvent.originalEvent ? jqEvent.originalEvent : jqEvent;
3151
3152
3153      //If we have a pointer event, whoes type is 'mouse' and we have said NO mouse events, then dont do anything.
3154      if(event.pointerType && event.pointerType=="mouse" && options.fallbackToMouseEvents==false) {
3155        return;
3156      };
3157
3158      var ret,
3159        touches = event.touches,
3160        evt = touches ? touches[0] : event;
3161
3162      phase = PHASE_START;
3163
3164      //If we support touches, get the finger count
3165      if (touches) {
3166        // get the total number of fingers touching the screen
3167        fingerCount = touches.length;
3168      }
3169      //Else this is the desktop, so stop the browser from dragging content
3170      else if (options.preventDefaultEvents !== false) {
3171        jqEvent.preventDefault(); //call this on jq event so we are cross browser
3172      }
3173
3174      //clear vars..
3175      distance = 0;
3176      direction = null;
3177      currentDirection=null;
3178      pinchDirection = null;
3179      duration = 0;
3180      startTouchesDistance = 0;
3181      endTouchesDistance = 0;
3182      pinchZoom = 1;
3183      pinchDistance = 0;
3184      maximumsMap = createMaximumsData();
3185      cancelMultiFingerRelease();
3186
3187      //Create the default finger data
3188      createFingerData(0, evt);
3189
3190      // check the number of fingers is what we are looking for, or we are capturing pinches
3191      if (!touches || (fingerCount === options.fingers || options.fingers === ALL_FINGERS) || hasPinches()) {
3192        // get the coordinates of the touch
3193        startTime = getTimeStamp();
3194
3195        if (fingerCount == 2) {
3196          //Keep track of the initial pinch distance, so we can calculate the diff later
3197          //Store second finger data as start
3198          createFingerData(1, touches[1]);
3199          startTouchesDistance = endTouchesDistance = calculateTouchesDistance(fingerData[0].start, fingerData[1].start);
3200        }
3201
3202        if (options.swipeStatus || options.pinchStatus) {
3203          ret = triggerHandler(event, phase);
3204        }
3205      } else {
3206        //A touch with more or less than the fingers we are looking for, so cancel
3207        ret = false;
3208      }
3209
3210      //If we have a return value from the users handler, then return and cancel
3211      if (ret === false) {
3212        phase = PHASE_CANCEL;
3213        triggerHandler(event, phase);
3214        return ret;
3215      } else {
3216        if (options.hold) {
3217          holdTimeout = setTimeout($.proxy(function() {
3218            //Trigger the event
3219            $element.trigger('hold', [event.target]);
3220            //Fire the callback
3221            if (options.hold) {
3222              ret = options.hold.call($element, event, event.target);
3223            }
3224          }, this), options.longTapThreshold);
3225        }
3226
3227        setTouchInProgress(true);
3228      }
3229
3230      return null;
3231    };
3232
3233
3234
3235    /**
3236     * Event handler for a touch move event.
3237     * If we change fingers during move, then cancel the event
3238     * @inner
3239     * @param {object} jqEvent The normalised jQuery event object.
3240     */
3241    function touchMove(jqEvent) {
3242
3243      //As we use Jquery bind for events, we need to target the original event object
3244      //If these events are being programmatically triggered, we don't have an original event object, so use the Jq one.
3245      var event = jqEvent.originalEvent ? jqEvent.originalEvent : jqEvent;
3246
3247      //If we are ending, cancelling, or within the threshold of 2 fingers being released, don't track anything..
3248      if (phase === PHASE_END || phase === PHASE_CANCEL || inMultiFingerRelease())
3249        return;
3250
3251      var ret,
3252        touches = event.touches,
3253        evt = touches ? touches[0] : event;
3254
3255
3256      //Update the  finger data
3257      var currentFinger = updateFingerData(evt);
3258      endTime = getTimeStamp();
3259
3260      if (touches) {
3261        fingerCount = touches.length;
3262      }
3263
3264      if (options.hold) {
3265        clearTimeout(holdTimeout);
3266      }
3267
3268      phase = PHASE_MOVE;
3269
3270      //If we have 2 fingers get Touches distance as well
3271      if (fingerCount == 2) {
3272
3273        //Keep track of the initial pinch distance, so we can calculate the diff later
3274        //We do this here as well as the start event, in case they start with 1 finger, and the press 2 fingers
3275        if (startTouchesDistance == 0) {
3276          //Create second finger if this is the first time...
3277          createFingerData(1, touches[1]);
3278
3279          startTouchesDistance = endTouchesDistance = calculateTouchesDistance(fingerData[0].start, fingerData[1].start);
3280        } else {
3281          //Else just update the second finger
3282          updateFingerData(touches[1]);
3283
3284          endTouchesDistance = calculateTouchesDistance(fingerData[0].end, fingerData[1].end);
3285          pinchDirection = calculatePinchDirection(fingerData[0].end, fingerData[1].end);
3286        }
3287
3288        pinchZoom = calculatePinchZoom(startTouchesDistance, endTouchesDistance);
3289        pinchDistance = Math.abs(startTouchesDistance - endTouchesDistance);
3290      }
3291
3292      if ((fingerCount === options.fingers || options.fingers === ALL_FINGERS) || !touches || hasPinches()) {
3293
3294        //The overall direction of the swipe. From start to now.
3295        direction = calculateDirection(currentFinger.start, currentFinger.end);
3296
3297        //The immediate direction of the swipe, direction between the last movement and this one.
3298        currentDirection = calculateDirection(currentFinger.last, currentFinger.end);
3299
3300        //Check if we need to prevent default event (page scroll / pinch zoom) or not
3301        validateDefaultEvent(jqEvent, currentDirection);
3302
3303        //Distance and duration are all off the main finger
3304        distance = calculateDistance(currentFinger.start, currentFinger.end);
3305        duration = calculateDuration();
3306
3307        //Cache the maximum distance we made in this direction
3308        setMaxDistance(direction, distance);
3309
3310        //Trigger status handler
3311        ret = triggerHandler(event, phase);
3312
3313
3314        //If we trigger end events when threshold are met, or trigger events when touch leaves element
3315        if (!options.triggerOnTouchEnd || options.triggerOnTouchLeave) {
3316
3317          var inBounds = true;
3318
3319          //If checking if we leave the element, run the bounds check (we can use touchleave as its not supported on webkit)
3320          if (options.triggerOnTouchLeave) {
3321            var bounds = getbounds(this);
3322            inBounds = isInBounds(currentFinger.end, bounds);
3323          }
3324
3325          //Trigger end handles as we swipe if thresholds met or if we have left the element if the user has asked to check these..
3326          if (!options.triggerOnTouchEnd && inBounds) {
3327            phase = getNextPhase(PHASE_MOVE);
3328          }
3329          //We end if out of bounds here, so set current phase to END, and check if its modified
3330          else if (options.triggerOnTouchLeave && !inBounds) {
3331            phase = getNextPhase(PHASE_END);
3332          }
3333
3334          if (phase == PHASE_CANCEL || phase == PHASE_END) {
3335            triggerHandler(event, phase);
3336          }
3337        }
3338      } else {
3339        phase = PHASE_CANCEL;
3340        triggerHandler(event, phase);
3341      }
3342
3343      if (ret === false) {
3344        phase = PHASE_CANCEL;
3345        triggerHandler(event, phase);
3346      }
3347    }
3348
3349
3350
3351
3352    /**
3353     * Event handler for a touch end event.
3354     * Calculate the direction and trigger events
3355     * @inner
3356     * @param {object} jqEvent The normalised jQuery event object.
3357     */
3358    function touchEnd(jqEvent) {
3359      //As we use Jquery bind for events, we need to target the original event object
3360      //If these events are being programmatically triggered, we don't have an original event object, so use the Jq one.
3361      var event = jqEvent.originalEvent ? jqEvent.originalEvent : jqEvent,
3362        touches = event.touches;
3363
3364      //If we are still in a touch with the device wait a fraction and see if the other finger comes up
3365      //if it does within the threshold, then we treat it as a multi release, not a single release and end the touch / swipe
3366      if (touches) {
3367        if (touches.length && !inMultiFingerRelease()) {
3368          startMultiFingerRelease(event);
3369          return true;
3370        } else if (touches.length && inMultiFingerRelease()) {
3371          return true;
3372        }
3373      }
3374
3375      //If a previous finger has been released, check how long ago, if within the threshold, then assume it was a multifinger release.
3376      //This is used to allow 2 fingers to release fractionally after each other, whilst maintaining the event as containing 2 fingers, not 1
3377      if (inMultiFingerRelease()) {
3378        fingerCount = fingerCountAtRelease;
3379      }
3380
3381      //Set end of swipe
3382      endTime = getTimeStamp();
3383
3384      //Get duration incase move was never fired
3385      duration = calculateDuration();
3386
3387      //If we trigger handlers at end of swipe OR, we trigger during, but they didnt trigger and we are still in the move phase
3388      if (didSwipeBackToCancel() || !validateSwipeDistance()) {
3389        phase = PHASE_CANCEL;
3390        triggerHandler(event, phase);
3391      } else if (options.triggerOnTouchEnd || (options.triggerOnTouchEnd === false && phase === PHASE_MOVE)) {
3392        //call this on jq event so we are cross browser
3393        if (options.preventDefaultEvents !== false) {
3394          jqEvent.preventDefault();
3395        }
3396        phase = PHASE_END;
3397        triggerHandler(event, phase);
3398      }
3399      //Special cases - A tap should always fire on touch end regardless,
3400      //So here we manually trigger the tap end handler by itself
3401      //We dont run trigger handler as it will re-trigger events that may have fired already
3402      else if (!options.triggerOnTouchEnd && hasTap()) {
3403        //Trigger the pinch events...
3404        phase = PHASE_END;
3405        triggerHandlerForGesture(event, phase, TAP);
3406      } else if (phase === PHASE_MOVE) {
3407        phase = PHASE_CANCEL;
3408        triggerHandler(event, phase);
3409      }
3410
3411      setTouchInProgress(false);
3412
3413      return null;
3414    }
3415
3416
3417
3418    /**
3419     * Event handler for a touch cancel event.
3420     * Clears current vars
3421     * @inner
3422     */
3423    function touchCancel() {
3424      // reset the variables back to default values
3425      fingerCount = 0;
3426      endTime = 0;
3427      startTime = 0;
3428      startTouchesDistance = 0;
3429      endTouchesDistance = 0;
3430      pinchZoom = 1;
3431
3432      //If we were in progress of tracking a possible multi touch end, then re set it.
3433      cancelMultiFingerRelease();
3434
3435      setTouchInProgress(false);
3436    }
3437
3438
3439    /**
3440     * Event handler for a touch leave event.
3441     * This is only triggered on desktops, in touch we work this out manually
3442     * as the touchleave event is not supported in webkit
3443     * @inner
3444     */
3445    function touchLeave(jqEvent) {
3446      //If these events are being programmatically triggered, we don't have an original event object, so use the Jq one.
3447      var event = jqEvent.originalEvent ? jqEvent.originalEvent : jqEvent;
3448
3449      //If we have the trigger on leave property set....
3450      if (options.triggerOnTouchLeave) {
3451        phase = getNextPhase(PHASE_END);
3452        triggerHandler(event, phase);
3453      }
3454    }
3455
3456    /**
3457     * Removes all listeners that were associated with the plugin
3458     * @inner
3459     */
3460    function removeListeners() {
3461      $element.unbind(START_EV, touchStart);
3462      $element.unbind(CANCEL_EV, touchCancel);
3463      $element.unbind(MOVE_EV, touchMove);
3464      $element.unbind(END_EV, touchEnd);
3465
3466      //we only have leave events on desktop, we manually calculate leave on touch as its not supported in webkit
3467      if (LEAVE_EV) {
3468        $element.unbind(LEAVE_EV, touchLeave);
3469      }
3470
3471      setTouchInProgress(false);
3472    }
3473
3474
3475    /**
3476     * Checks if the time and distance thresholds have been met, and if so then the appropriate handlers are fired.
3477     */
3478    function getNextPhase(currentPhase) {
3479
3480      var nextPhase = currentPhase;
3481
3482      // Ensure we have valid swipe (under time and over distance  and check if we are out of bound...)
3483      var validTime = validateSwipeTime();
3484      var validDistance = validateSwipeDistance();
3485      var didCancel = didSwipeBackToCancel();
3486
3487      //If we have exceeded our time, then cancel
3488      if (!validTime || didCancel) {
3489        nextPhase = PHASE_CANCEL;
3490      }
3491      //Else if we are moving, and have reached distance then end
3492      else if (validDistance && currentPhase == PHASE_MOVE && (!options.triggerOnTouchEnd || options.triggerOnTouchLeave)) {
3493        nextPhase = PHASE_END;
3494      }
3495      //Else if we have ended by leaving and didn't reach distance, then cancel
3496      else if (!validDistance && currentPhase == PHASE_END && options.triggerOnTouchLeave) {
3497        nextPhase = PHASE_CANCEL;
3498      }
3499
3500      return nextPhase;
3501    }
3502
3503
3504    /**
3505     * Trigger the relevant event handler
3506     * The handlers are passed the original event, the element that was swiped, and in the case of the catch all handler, the direction that was swiped, "left", "right", "up", or "down"
3507     * @param {object} event the original event object
3508     * @param {string} phase the phase of the swipe (start, end cancel etc) {@link $.fn.swipe.phases}
3509     * @inner
3510     */
3511    function triggerHandler(event, phase) {
3512
3513
3514
3515      var ret,
3516        touches = event.touches;
3517
3518      // SWIPE GESTURES
3519      if (didSwipe() || hasSwipes()) {
3520          ret = triggerHandlerForGesture(event, phase, SWIPE);
3521      }
3522
3523      // PINCH GESTURES (if the above didn't cancel)
3524      if ((didPinch() || hasPinches()) && ret !== false) {
3525          ret = triggerHandlerForGesture(event, phase, PINCH);
3526      }
3527
3528      // CLICK / TAP (if the above didn't cancel)
3529      if (didDoubleTap() && ret !== false) {
3530        //Trigger the tap events...
3531        ret = triggerHandlerForGesture(event, phase, DOUBLE_TAP);
3532      }
3533
3534      // CLICK / TAP (if the above didn't cancel)
3535      else if (didLongTap() && ret !== false) {
3536        //Trigger the tap events...
3537        ret = triggerHandlerForGesture(event, phase, LONG_TAP);
3538      }
3539
3540      // CLICK / TAP (if the above didn't cancel)
3541      else if (didTap() && ret !== false) {
3542        //Trigger the tap event..
3543        ret = triggerHandlerForGesture(event, phase, TAP);
3544      }
3545
3546
3547
3548      // If we are cancelling the gesture, then manually trigger the reset handler
3549      if (phase === PHASE_CANCEL) {
3550
3551        touchCancel(event);
3552      }
3553
3554
3555
3556
3557      // If we are ending the gesture, then manually trigger the reset handler IF all fingers are off
3558      if (phase === PHASE_END) {
3559        //If we support touch, then check that all fingers are off before we cancel
3560        if (touches) {
3561          if (!touches.length) {
3562            touchCancel(event);
3563          }
3564        } else {
3565          touchCancel(event);
3566        }
3567      }
3568
3569      return ret;
3570    }
3571
3572
3573
3574    /**
3575     * Trigger the relevant event handler
3576     * The handlers are passed the original event, the element that was swiped, and in the case of the catch all handler, the direction that was swiped, "left", "right", "up", or "down"
3577     * @param {object} event the original event object
3578     * @param {string} phase the phase of the swipe (start, end cancel etc) {@link $.fn.swipe.phases}
3579     * @param {string} gesture the gesture to trigger a handler for : PINCH or SWIPE {@link $.fn.swipe.gestures}
3580     * @return Boolean False, to indicate that the event should stop propagation, or void.
3581     * @inner
3582     */
3583    function triggerHandlerForGesture(event, phase, gesture) {
3584
3585      var ret;
3586
3587      //SWIPES....
3588      if (gesture == SWIPE) {
3589        //Trigger status every time..
3590        $element.trigger('swipeStatus', [phase, direction || null, distance || 0, duration || 0, fingerCount, fingerData, currentDirection]);
3591
3592        if (options.swipeStatus) {
3593          ret = options.swipeStatus.call($element, event, phase, direction || null, distance || 0, duration || 0, fingerCount, fingerData, currentDirection);
3594          //If the status cancels, then dont run the subsequent event handlers..
3595          if (ret === false) return false;
3596        }
3597
3598        if (phase == PHASE_END && validateSwipe()) {
3599
3600          //Cancel any taps that were in progress...
3601          clearTimeout(singleTapTimeout);
3602          clearTimeout(holdTimeout);
3603
3604          $element.trigger('swipe', [direction, distance, duration, fingerCount, fingerData, currentDirection]);
3605
3606          if (options.swipe) {
3607            ret = options.swipe.call($element, event, direction, distance, duration, fingerCount, fingerData, currentDirection);
3608            //If the status cancels, then dont run the subsequent event handlers..
3609            if (ret === false) return false;
3610          }
3611
3612          //trigger direction specific event handlers
3613          switch (direction) {
3614            case LEFT:
3615              $element.trigger('swipeLeft', [direction, distance, duration, fingerCount, fingerData, currentDirection]);
3616
3617              if (options.swipeLeft) {
3618                ret = options.swipeLeft.call($element, event, direction, distance, duration, fingerCount, fingerData, currentDirection);
3619              }
3620              break;
3621
3622            case RIGHT:
3623              $element.trigger('swipeRight', [direction, distance, duration, fingerCount, fingerData, currentDirection]);
3624
3625              if (options.swipeRight) {
3626                ret = options.swipeRight.call($element, event, direction, distance, duration, fingerCount, fingerData, currentDirection);
3627              }
3628              break;
3629
3630            case UP:
3631              $element.trigger('swipeUp', [direction, distance, duration, fingerCount, fingerData, currentDirection]);
3632
3633              if (options.swipeUp) {
3634                ret = options.swipeUp.call($element, event, direction, distance, duration, fingerCount, fingerData, currentDirection);
3635              }
3636              break;
3637
3638            case DOWN:
3639              $element.trigger('swipeDown', [direction, distance, duration, fingerCount, fingerData, currentDirection]);
3640
3641              if (options.swipeDown) {
3642                ret = options.swipeDown.call($element, event, direction, distance, duration, fingerCount, fingerData, currentDirection);
3643              }
3644              break;
3645          }
3646        }
3647      }
3648
3649
3650      //PINCHES....
3651      if (gesture == PINCH) {
3652        $element.trigger('pinchStatus', [phase, pinchDirection || null, pinchDistance || 0, duration || 0, fingerCount, pinchZoom, fingerData]);
3653
3654        if (options.pinchStatus) {
3655          ret = options.pinchStatus.call($element, event, phase, pinchDirection || null, pinchDistance || 0, duration || 0, fingerCount, pinchZoom, fingerData);
3656          //If the status cancels, then dont run the subsequent event handlers..
3657          if (ret === false) return false;
3658        }
3659
3660        if (phase == PHASE_END && validatePinch()) {
3661
3662          switch (pinchDirection) {
3663            case IN:
3664              $element.trigger('pinchIn', [pinchDirection || null, pinchDistance || 0, duration || 0, fingerCount, pinchZoom, fingerData]);
3665
3666              if (options.pinchIn) {
3667                ret = options.pinchIn.call($element, event, pinchDirection || null, pinchDistance || 0, duration || 0, fingerCount, pinchZoom, fingerData);
3668              }
3669              break;
3670
3671            case OUT:
3672              $element.trigger('pinchOut', [pinchDirection || null, pinchDistance || 0, duration || 0, fingerCount, pinchZoom, fingerData]);
3673
3674              if (options.pinchOut) {
3675                ret = options.pinchOut.call($element, event, pinchDirection || null, pinchDistance || 0, duration || 0, fingerCount, pinchZoom, fingerData);
3676              }
3677              break;
3678          }
3679        }
3680      }
3681
3682      if (gesture == TAP) {
3683        if (phase === PHASE_CANCEL || phase === PHASE_END) {
3684
3685          clearTimeout(singleTapTimeout);
3686          clearTimeout(holdTimeout);
3687
3688          //If we are also looking for doubelTaps, wait incase this is one...
3689          if (hasDoubleTap() && !inDoubleTap()) {
3690            doubleTapStartTime = getTimeStamp();
3691
3692            //Now wait for the double tap timeout, and trigger this single tap
3693            //if its not cancelled by a double tap
3694            singleTapTimeout = setTimeout($.proxy(function() {
3695              doubleTapStartTime = null;
3696              $element.trigger('tap', [event.target]);
3697
3698              if (options.tap) {
3699                ret = options.tap.call($element, event, event.target);
3700              }
3701            }, this), options.doubleTapThreshold);
3702
3703          } else {
3704            doubleTapStartTime = null;
3705            $element.trigger('tap', [event.target]);
3706            if (options.tap) {
3707              ret = options.tap.call($element, event, event.target);
3708            }
3709          }
3710        }
3711      } else if (gesture == DOUBLE_TAP) {
3712        if (phase === PHASE_CANCEL || phase === PHASE_END) {
3713          clearTimeout(singleTapTimeout);
3714          clearTimeout(holdTimeout);
3715          doubleTapStartTime = null;
3716          $element.trigger('doubletap', [event.target]);
3717
3718          if (options.doubleTap) {
3719            ret = options.doubleTap.call($element, event, event.target);
3720          }
3721        }
3722      } else if (gesture == LONG_TAP) {
3723        if (phase === PHASE_CANCEL || phase === PHASE_END) {
3724          clearTimeout(singleTapTimeout);
3725          doubleTapStartTime = null;
3726
3727          $element.trigger('longtap', [event.target]);
3728          if (options.longTap) {
3729            ret = options.longTap.call($element, event, event.target);
3730          }
3731        }
3732      }
3733
3734      return ret;
3735    }
3736
3737
3738    //
3739    // GESTURE VALIDATION
3740    //
3741
3742    /**
3743     * Checks the user has swipe far enough
3744     * @return Boolean if <code>threshold</code> has been set, return true if the threshold was met, else false.
3745     * If no threshold was set, then we return true.
3746     * @inner
3747     */
3748    function validateSwipeDistance() {
3749      var valid = true;
3750      //If we made it past the min swipe distance..
3751      if (options.threshold !== null) {
3752        valid = distance >= options.threshold;
3753      }
3754
3755      return valid;
3756    }
3757
3758    /**
3759     * Checks the user has swiped back to cancel.
3760     * @return Boolean if <code>cancelThreshold</code> has been set, return true if the cancelThreshold was met, else false.
3761     * If no cancelThreshold was set, then we return true.
3762     * @inner
3763     */
3764    function didSwipeBackToCancel() {
3765      var cancelled = false;
3766      if (options.cancelThreshold !== null && direction !== null) {
3767        cancelled = (getMaxDistance(direction) - distance) >= options.cancelThreshold;
3768      }
3769
3770      return cancelled;
3771    }
3772
3773    /**
3774     * Checks the user has pinched far enough
3775     * @return Boolean if <code>pinchThreshold</code> has been set, return true if the threshold was met, else false.
3776     * If no threshold was set, then we return true.
3777     * @inner
3778     */
3779    function validatePinchDistance() {
3780      if (options.pinchThreshold !== null) {
3781        return pinchDistance >= options.pinchThreshold;
3782      }
3783      return true;
3784    }
3785
3786    /**
3787     * Checks that the time taken to swipe meets the minimum / maximum requirements
3788     * @return Boolean
3789     * @inner
3790     */
3791    function validateSwipeTime() {
3792      var result;
3793      //If no time set, then return true
3794      if (options.maxTimeThreshold) {
3795        if (duration >= options.maxTimeThreshold) {
3796          result = false;
3797        } else {
3798          result = true;
3799        }
3800      } else {
3801        result = true;
3802      }
3803
3804      return result;
3805    }
3806
3807
3808    /**
3809     * Checks direction of the swipe and the value allowPageScroll to see if we should allow or prevent the default behaviour from occurring.
3810     * This will essentially allow page scrolling or not when the user is swiping on a touchSwipe object.
3811     * @param {object} jqEvent The normalised jQuery representation of the event object.
3812     * @param {string} direction The direction of the event. See {@link $.fn.swipe.directions}
3813     * @see $.fn.swipe.directions
3814     * @inner
3815     */
3816    function validateDefaultEvent(jqEvent, direction) {
3817
3818      //If the option is set, allways allow the event to bubble up (let user handle weirdness)
3819      if (options.preventDefaultEvents === false) {
3820        return;
3821      }
3822
3823      if (options.allowPageScroll === NONE) {
3824        jqEvent.preventDefault();
3825      } else {
3826        var auto = options.allowPageScroll === AUTO;
3827
3828        switch (direction) {
3829          case LEFT:
3830            if ((options.swipeLeft && auto) || (!auto && options.allowPageScroll != HORIZONTAL)) {
3831              jqEvent.preventDefault();
3832            }
3833            break;
3834
3835          case RIGHT:
3836            if ((options.swipeRight && auto) || (!auto && options.allowPageScroll != HORIZONTAL)) {
3837              jqEvent.preventDefault();
3838            }
3839            break;
3840
3841          case UP:
3842            if ((options.swipeUp && auto) || (!auto && options.allowPageScroll != VERTICAL)) {
3843              jqEvent.preventDefault();
3844            }
3845            break;
3846
3847          case DOWN:
3848            if ((options.swipeDown && auto) || (!auto && options.allowPageScroll != VERTICAL)) {
3849              jqEvent.preventDefault();
3850            }
3851            break;
3852
3853          case NONE:
3854
3855            break;
3856        }
3857      }
3858    }
3859
3860
3861    // PINCHES
3862    /**
3863     * Returns true of the current pinch meets the thresholds
3864     * @return Boolean
3865     * @inner
3866     */
3867    function validatePinch() {
3868      var hasCorrectFingerCount = validateFingers();
3869      var hasEndPoint = validateEndPoint();
3870      var hasCorrectDistance = validatePinchDistance();
3871      return hasCorrectFingerCount && hasEndPoint && hasCorrectDistance;
3872
3873    }
3874
3875    /**
3876     * Returns true if any Pinch events have been registered
3877     * @return Boolean
3878     * @inner
3879     */
3880    function hasPinches() {
3881      //Enure we dont return 0 or null for false values
3882      return !!(options.pinchStatus || options.pinchIn || options.pinchOut);
3883    }
3884
3885    /**
3886     * Returns true if we are detecting pinches, and have one
3887     * @return Boolean
3888     * @inner
3889     */
3890    function didPinch() {
3891      //Enure we dont return 0 or null for false values
3892      return !!(validatePinch() && hasPinches());
3893    }
3894
3895
3896
3897
3898    // SWIPES
3899    /**
3900     * Returns true if the current swipe meets the thresholds
3901     * @return Boolean
3902     * @inner
3903     */
3904    function validateSwipe() {
3905      //Check validity of swipe
3906      var hasValidTime = validateSwipeTime();
3907      var hasValidDistance = validateSwipeDistance();
3908      var hasCorrectFingerCount = validateFingers();
3909      var hasEndPoint = validateEndPoint();
3910      var didCancel = didSwipeBackToCancel();
3911
3912      // if the user swiped more than the minimum length, perform the appropriate action
3913      // hasValidDistance is null when no distance is set
3914      var valid = !didCancel && hasEndPoint && hasCorrectFingerCount && hasValidDistance && hasValidTime;
3915
3916      return valid;
3917    }
3918
3919    /**
3920     * Returns true if any Swipe events have been registered
3921     * @return Boolean
3922     * @inner
3923     */
3924    function hasSwipes() {
3925      //Enure we dont return 0 or null for false values
3926      return !!(options.swipe || options.swipeStatus || options.swipeLeft || options.swipeRight || options.swipeUp || options.swipeDown);
3927    }
3928
3929
3930    /**
3931     * Returns true if we are detecting swipes and have one
3932     * @return Boolean
3933     * @inner
3934     */
3935    function didSwipe() {
3936      //Enure we dont return 0 or null for false values
3937      return !!(validateSwipe() && hasSwipes());
3938    }
3939
3940    /**
3941     * Returns true if we have matched the number of fingers we are looking for
3942     * @return Boolean
3943     * @inner
3944     */
3945    function validateFingers() {
3946      //The number of fingers we want were matched, or on desktop we ignore
3947      return ((fingerCount === options.fingers || options.fingers === ALL_FINGERS) || !SUPPORTS_TOUCH);
3948    }
3949
3950    /**
3951     * Returns true if we have an end point for the swipe
3952     * @return Boolean
3953     * @inner
3954     */
3955    function validateEndPoint() {
3956      //We have an end value for the finger
3957      return fingerData[0].end.x !== 0;
3958    }
3959
3960    // TAP / CLICK
3961    /**
3962     * Returns true if a click / tap events have been registered
3963     * @return Boolean
3964     * @inner
3965     */
3966    function hasTap() {
3967      //Enure we dont return 0 or null for false values
3968      return !!(options.tap);
3969    }
3970
3971    /**
3972     * Returns true if a double tap events have been registered
3973     * @return Boolean
3974     * @inner
3975     */
3976    function hasDoubleTap() {
3977      //Enure we dont return 0 or null for false values
3978      return !!(options.doubleTap);
3979    }
3980
3981    /**
3982     * Returns true if any long tap events have been registered
3983     * @return Boolean
3984     * @inner
3985     */
3986    function hasLongTap() {
3987      //Enure we dont return 0 or null for false values
3988      return !!(options.longTap);
3989    }
3990
3991    /**
3992     * Returns true if we could be in the process of a double tap (one tap has occurred, we are listening for double taps, and the threshold hasn't past.
3993     * @return Boolean
3994     * @inner
3995     */
3996    function validateDoubleTap() {
3997      if (doubleTapStartTime == null) {
3998        return false;
3999      }
4000      var now = getTimeStamp();
4001      return (hasDoubleTap() && ((now - doubleTapStartTime) <= options.doubleTapThreshold));
4002    }
4003
4004    /**
4005     * Returns true if we could be in the process of a double tap (one tap has occurred, we are listening for double taps, and the threshold hasn't past.
4006     * @return Boolean
4007     * @inner
4008     */
4009    function inDoubleTap() {
4010      return validateDoubleTap();
4011    }
4012
4013
4014    /**
4015     * Returns true if we have a valid tap
4016     * @return Boolean
4017     * @inner
4018     */
4019    function validateTap() {
4020      return ((fingerCount === 1 || !SUPPORTS_TOUCH) && (isNaN(distance) || distance < options.threshold));
4021    }
4022
4023    /**
4024     * Returns true if we have a valid long tap
4025     * @return Boolean
4026     * @inner
4027     */
4028    function validateLongTap() {
4029      //slight threshold on moving finger
4030      return ((duration > options.longTapThreshold) && (distance < DOUBLE_TAP_THRESHOLD));
4031    }
4032
4033    /**
4034     * Returns true if we are detecting taps and have one
4035     * @return Boolean
4036     * @inner
4037     */
4038    function didTap() {
4039      //Enure we dont return 0 or null for false values
4040      return !!(validateTap() && hasTap());
4041    }
4042
4043
4044    /**
4045     * Returns true if we are detecting double taps and have one
4046     * @return Boolean
4047     * @inner
4048     */
4049    function didDoubleTap() {
4050      //Enure we dont return 0 or null for false values
4051      return !!(validateDoubleTap() && hasDoubleTap());
4052    }
4053
4054    /**
4055     * Returns true if we are detecting long taps and have one
4056     * @return Boolean
4057     * @inner
4058     */
4059    function didLongTap() {
4060      //Enure we dont return 0 or null for false values
4061      return !!(validateLongTap() && hasLongTap());
4062    }
4063
4064
4065
4066
4067    // MULTI FINGER TOUCH
4068    /**
4069     * Starts tracking the time between 2 finger releases, and keeps track of how many fingers we initially had up
4070     * @inner
4071     */
4072    function startMultiFingerRelease(event) {
4073      previousTouchEndTime = getTimeStamp();
4074      fingerCountAtRelease = event.touches.length + 1;
4075    }
4076
4077    /**
4078     * Cancels the tracking of time between 2 finger releases, and resets counters
4079     * @inner
4080     */
4081    function cancelMultiFingerRelease() {
4082      previousTouchEndTime = 0;
4083      fingerCountAtRelease = 0;
4084    }
4085
4086    /**
4087     * Checks if we are in the threshold between 2 fingers being released
4088     * @return Boolean
4089     * @inner
4090     */
4091    function inMultiFingerRelease() {
4092
4093      var withinThreshold = false;
4094
4095      if (previousTouchEndTime) {
4096        var diff = getTimeStamp() - previousTouchEndTime
4097        if (diff <= options.fingerReleaseThreshold) {
4098          withinThreshold = true;
4099        }
4100      }
4101
4102      return withinThreshold;
4103    }
4104
4105
4106    /**
4107     * gets a data flag to indicate that a touch is in progress
4108     * @return Boolean
4109     * @inner
4110     */
4111    function getTouchInProgress() {
4112      //strict equality to ensure only true and false are returned
4113      return !!($element.data(PLUGIN_NS + '_intouch') === true);
4114    }
4115
4116    /**
4117     * Sets a data flag to indicate that a touch is in progress
4118     * @param {boolean} val The value to set the property to
4119     * @inner
4120     */
4121    function setTouchInProgress(val) {
4122
4123      //If destroy is called in an event handler, we have no el, and we have already cleaned up, so return.
4124      if(!$element) { return; }
4125
4126      //Add or remove event listeners depending on touch status
4127      if (val === true) {
4128        $element.bind(MOVE_EV, touchMove);
4129        $element.bind(END_EV, touchEnd);
4130
4131        //we only have leave events on desktop, we manually calcuate leave on touch as its not supported in webkit
4132        if (LEAVE_EV) {
4133          $element.bind(LEAVE_EV, touchLeave);
4134        }
4135      } else {
4136
4137        $element.unbind(MOVE_EV, touchMove, false);
4138        $element.unbind(END_EV, touchEnd, false);
4139
4140        //we only have leave events on desktop, we manually calcuate leave on touch as its not supported in webkit
4141        if (LEAVE_EV) {
4142          $element.unbind(LEAVE_EV, touchLeave, false);
4143        }
4144      }
4145
4146
4147      //strict equality to ensure only true and false can update the value
4148      $element.data(PLUGIN_NS + '_intouch', val === true);
4149    }
4150
4151
4152    /**
4153     * Creates the finger data for the touch/finger in the event object.
4154     * @param {int} id The id to store the finger data under (usually the order the fingers were pressed)
4155     * @param {object} evt The event object containing finger data
4156     * @return finger data object
4157     * @inner
4158     */
4159    function createFingerData(id, evt) {
4160      var f = {
4161        start: {
4162          x: 0,
4163          y: 0
4164        },
4165        last: {
4166          x: 0,
4167          y: 0
4168        },
4169        end: {
4170          x: 0,
4171          y: 0
4172        }
4173      };
4174      f.start.x = f.last.x = f.end.x = evt.pageX || evt.clientX;
4175      f.start.y = f.last.y = f.end.y = evt.pageY || evt.clientY;
4176      fingerData[id] = f;
4177      return f;
4178    }
4179
4180    /**
4181     * Updates the finger data for a particular event object
4182     * @param {object} evt The event object containing the touch/finger data to upadte
4183     * @return a finger data object.
4184     * @inner
4185     */
4186    function updateFingerData(evt) {
4187      var id = evt.identifier !== undefined ? evt.identifier : 0;
4188      var f = getFingerData(id);
4189
4190      if (f === null) {
4191        f = createFingerData(id, evt);
4192      }
4193
4194      f.last.x = f.end.x;
4195      f.last.y = f.end.y;
4196
4197      f.end.x = evt.pageX || evt.clientX;
4198      f.end.y = evt.pageY || evt.clientY;
4199
4200      return f;
4201    }
4202
4203    /**
4204     * Returns a finger data object by its event ID.
4205     * Each touch event has an identifier property, which is used
4206     * to track repeat touches
4207     * @param {int} id The unique id of the finger in the sequence of touch events.
4208     * @return a finger data object.
4209     * @inner
4210     */
4211    function getFingerData(id) {
4212      return fingerData[id] || null;
4213    }
4214
4215
4216    /**
4217     * Sets the maximum distance swiped in the given direction.
4218     * If the new value is lower than the current value, the max value is not changed.
4219     * @param {string}  direction The direction of the swipe
4220     * @param {int}  distance The distance of the swipe
4221     * @inner
4222     */
4223    function setMaxDistance(direction, distance) {
4224      if(direction==NONE) return;
4225      distance = Math.max(distance, getMaxDistance(direction));
4226      maximumsMap[direction].distance = distance;
4227    }
4228
4229    /**
4230     * gets the maximum distance swiped in the given direction.
4231     * @param {string}  direction The direction of the swipe
4232     * @return int  The distance of the swipe
4233     * @inner
4234     */
4235    function getMaxDistance(direction) {
4236      if (maximumsMap[direction]) return maximumsMap[direction].distance;
4237      return undefined;
4238    }
4239
4240    /**
4241     * Creats a map of directions to maximum swiped values.
4242     * @return Object A dictionary of maximum values, indexed by direction.
4243     * @inner
4244     */
4245    function createMaximumsData() {
4246      var maxData = {};
4247      maxData[LEFT] = createMaximumVO(LEFT);
4248      maxData[RIGHT] = createMaximumVO(RIGHT);
4249      maxData[UP] = createMaximumVO(UP);
4250      maxData[DOWN] = createMaximumVO(DOWN);
4251
4252      return maxData;
4253    }
4254
4255    /**
4256     * Creates a map maximum swiped values for a given swipe direction
4257     * @param {string} The direction that these values will be associated with
4258     * @return Object Maximum values
4259     * @inner
4260     */
4261    function createMaximumVO(dir) {
4262      return {
4263        direction: dir,
4264        distance: 0
4265      }
4266    }
4267
4268
4269    //
4270    // MATHS / UTILS
4271    //
4272
4273    /**
4274     * Calculate the duration of the swipe
4275     * @return int
4276     * @inner
4277     */
4278    function calculateDuration() {
4279      return endTime - startTime;
4280    }
4281
4282    /**
4283     * Calculate the distance between 2 touches (pinch)
4284     * @param {point} startPoint A point object containing x and y co-ordinates
4285     * @param {point} endPoint A point object containing x and y co-ordinates
4286     * @return int;
4287     * @inner
4288     */
4289    function calculateTouchesDistance(startPoint, endPoint) {
4290      var diffX = Math.abs(startPoint.x - endPoint.x);
4291      var diffY = Math.abs(startPoint.y - endPoint.y);
4292
4293      return Math.round(Math.sqrt(diffX * diffX + diffY * diffY));
4294    }
4295
4296    /**
4297     * Calculate the zoom factor between the start and end distances
4298     * @param {int} startDistance Distance (between 2 fingers) the user started pinching at
4299     * @param {int} endDistance Distance (between 2 fingers) the user ended pinching at
4300     * @return float The zoom value from 0 to 1.
4301     * @inner
4302     */
4303    function calculatePinchZoom(startDistance, endDistance) {
4304      var percent = (endDistance / startDistance) * 1;
4305      return percent.toFixed(2);
4306    }
4307
4308
4309    /**
4310     * Returns the pinch direction, either IN or OUT for the given points
4311     * @return string Either {@link $.fn.swipe.directions.IN} or {@link $.fn.swipe.directions.OUT}
4312     * @see $.fn.swipe.directions
4313     * @inner
4314     */
4315    function calculatePinchDirection() {
4316      if (pinchZoom < 1) {
4317        return OUT;
4318      } else {
4319        return IN;
4320      }
4321    }
4322
4323
4324    /**
4325     * Calculate the length / distance of the swipe
4326     * @param {point} startPoint A point object containing x and y co-ordinates
4327     * @param {point} endPoint A point object containing x and y co-ordinates
4328     * @return int
4329     * @inner
4330     */
4331    function calculateDistance(startPoint, endPoint) {
4332      return Math.round(Math.sqrt(Math.pow(endPoint.x - startPoint.x, 2) + Math.pow(endPoint.y - startPoint.y, 2)));
4333    }
4334
4335    /**
4336     * Calculate the angle of the swipe
4337     * @param {point} startPoint A point object containing x and y co-ordinates
4338     * @param {point} endPoint A point object containing x and y co-ordinates
4339     * @return int
4340     * @inner
4341     */
4342    function calculateAngle(startPoint, endPoint) {
4343      var x = startPoint.x - endPoint.x;
4344      var y = endPoint.y - startPoint.y;
4345      var r = Math.atan2(y, x); //radians
4346      var angle = Math.round(r * 180 / Math.PI); //degrees
4347
4348      //ensure value is positive
4349      if (angle < 0) {
4350        angle = 360 - Math.abs(angle);
4351      }
4352
4353      return angle;
4354    }
4355
4356    /**
4357     * Calculate the direction of the swipe
4358     * This will also call calculateAngle to get the latest angle of swipe
4359     * @param {point} startPoint A point object containing x and y co-ordinates
4360     * @param {point} endPoint A point object containing x and y co-ordinates
4361     * @return string Either {@link $.fn.swipe.directions.LEFT} / {@link $.fn.swipe.directions.RIGHT} / {@link $.fn.swipe.directions.DOWN} / {@link $.fn.swipe.directions.UP}
4362     * @see $.fn.swipe.directions
4363     * @inner
4364     */
4365    function calculateDirection(startPoint, endPoint) {
4366
4367      if( comparePoints(startPoint, endPoint) ) {
4368        return NONE;
4369      }
4370
4371      var angle = calculateAngle(startPoint, endPoint);
4372
4373      if ((angle <= 45) && (angle >= 0)) {
4374        return LEFT;
4375      } else if ((angle <= 360) && (angle >= 315)) {
4376        return LEFT;
4377      } else if ((angle >= 135) && (angle <= 225)) {
4378        return RIGHT;
4379      } else if ((angle > 45) && (angle < 135)) {
4380        return DOWN;
4381      } else {
4382        return UP;
4383      }
4384    }
4385
4386
4387    /**
4388     * Returns a MS time stamp of the current time
4389     * @return int
4390     * @inner
4391     */
4392    function getTimeStamp() {
4393      var now = new Date();
4394      return now.getTime();
4395    }
4396
4397
4398
4399    /**
4400     * Returns a bounds object with left, right, top and bottom properties for the element specified.
4401     * @param {DomNode} The DOM node to get the bounds for.
4402     */
4403    function getbounds(el) {
4404      el = $(el);
4405      var offset = el.offset();
4406
4407      var bounds = {
4408        left: offset.left,
4409        right: offset.left + el.outerWidth(),
4410        top: offset.top,
4411        bottom: offset.top + el.outerHeight()
4412      }
4413
4414      return bounds;
4415    }
4416
4417
4418    /**
4419     * Checks if the point object is in the bounds object.
4420     * @param {object} point A point object.
4421     * @param {int} point.x The x value of the point.
4422     * @param {int} point.y The x value of the point.
4423     * @param {object} bounds The bounds object to test
4424     * @param {int}
4424 bounds.left The leftmost value
4425     * @param {int} bounds.right The righttmost value
4426     * @param {int} bounds.top The topmost value
4427     * @param {int} bounds.bottom The bottommost value
4428     */
4429    function isInBounds(point, bounds) {
4430      return (point.x > bounds.left && point.x < bounds.right && point.y > bounds.top && point.y < bounds.bottom);
4431    };
4432
4433    /**
4434     * Checks if the two points are equal
4435     * @param {object} point A point object.
4436     * @param {object} point B point object.
4437     * @return true of the points match
4438     */
4439    function comparePoints(pointA, pointB) {
4440      return (pointA.x == pointB.x && pointA.y == pointB.y);
4441    }
4442
4443
4444  }
4445
4446
4447
4448
4449  /**
4450   * A catch all handler that is triggered for all swipe directions.
4451   * @name $.fn.swipe#swipe
4452   * @event
4453   * @default null
4454   * @param {EventObject} event The original event object
4455   * @param {int} direction The direction the user swiped in. See {@link $.fn.swipe.directions}
4456   * @param {int} distance The distance the user swiped
4457   * @param {int} duration The duration of the swipe in milliseconds
4458   * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers}
4459   * @param {object} fingerData The coordinates of fingers in event
4460   * @param {string} currentDirection The current direction the user is swiping.
4461   */
4462
4463
4464
4465
4466  /**
4467   * A handler that is triggered for "left" swipes.
4468   * @name $.fn.swipe#swipeLeft
4469   * @event
4470   * @default null
4471   * @param {EventObject} event The original event object
4472   * @param {int} direction The direction the user swiped in. See {@link $.fn.swipe.directions}
4473   * @param {int} distance The distance the user swiped
4474   * @param {int} duration The duration of the swipe in milliseconds
4475   * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers}
4476   * @param {object} fingerData The coordinates of fingers in event
4477   * @param {string} currentDirection The current direction the user is swiping.
4478   */
4479
4480  /**
4481   * A handler that is triggered for "right" swipes.
4482   * @name $.fn.swipe#swipeRight
4483   * @event
4484   * @default null
4485   * @param {EventObject} event The original event object
4486   * @param {int} direction The direction the user swiped in. See {@link $.fn.swipe.directions}
4487   * @param {int} distance The distance the user swiped
4488   * @param {int} duration The duration of the swipe in milliseconds
4489   * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers}
4490   * @param {object} fingerData The coordinates of fingers in event
4491   * @param {string} currentDirection The current direction the user is swiping.
4492   */
4493
4494  /**
4495   * A handler that is triggered for "up" swipes.
4496   * @name $.fn.swipe#swipeUp
4497   * @event
4498   * @default null
4499   * @param {EventObject} event The original event object
4500   * @param {int} direction The direction the user swiped in. See {@link $.fn.swipe.directions}
4501   * @param {int} distance The distance the user swiped
4502   * @param {int} duration The duration of the swipe in milliseconds
4503   * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers}
4504   * @param {object} fingerData The coordinates of fingers in event
4505   * @param {string} currentDirection The current direction the user is swiping.
4506   */
4507
4508  /**
4509   * A handler that is triggered for "down" swipes.
4510   * @name $.fn.swipe#swipeDown
4511   * @event
4512   * @default null
4513   * @param {EventObject} event The original event object
4514   * @param {int} direction The direction the user swiped in. See {@link $.fn.swipe.directions}
4515   * @param {int} distance The distance the user swiped
4516   * @param {int} duration The duration of the swipe in milliseconds
4517   * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers}
4518   * @param {object} fingerData The coordinates of fingers in event
4519   * @param {string} currentDirection The current direction the user is swiping.
4520   */
4521
4522  /**
4523   * A handler triggered for every phase of the swipe. This handler is constantly f
4523ired for the duration of the pinch.
4524   * This is triggered regardless of swipe thresholds.
4525   * @name $.fn.swipe#swipeStatus
4526   * @event
4527   * @default null
4528   * @param {EventObject} event The original event object
4529   * @param {string} phase The phase of the swipe event. See {@link $.fn.swipe.phases}
4530   * @param {string} direction The direction the user swiped in. This is null if the user has yet to move. See {@link $.fn.swipe.directions}
4531   * @param {int} distance The distance the user swiped. This is 0 if the user has yet to move.
4532   * @param {int} duration The duration of the swipe in milliseconds
4533   * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers}
4534   * @param {object} fingerData The coordinates of fingers in event
4535   * @param {string} currentDirection The current direction the user is swiping.
4536   */
4537
4538  /**
4539   * A handler triggered for pinch in events.
4540   * @name $.fn.swipe#pinchIn
4541   * @event
4542   * @default null
4543   * @param {EventObject} event The original event object
4544   * @param {int} direction The direction the user pinched in. See {@link $.fn.swipe.directions}
4545   * @param {int} distance The distance the user pinched
4546   * @param {int} duration The duration of the swipe in milliseconds
4547   * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers}
4548   * @param {int} zoom The zoom/scale level the user pinched too, 0-1.
4549   * @param {object} fingerData The coordinates of fingers in event
4550   */
4551
4552  /**
4553   * A handler triggered for pinch out events.
4554   * @name $.fn.swipe#pinchOut
4555   * @event
4556   * @default null
4557   * @param {EventObject} event The original event object
4558   * @param {int} direction The direction the user pinched in. See {@link $.fn.swipe.directions}
4559   * @param {int} distance The distance the user pinched
4560   * @param {int} duration The duration of the swipe in milliseconds
4561   * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers}
4562   * @param {int} zoom The zoom/scale level the user pinched too, 0-1.
4563   * @param {object} fingerData The coordinates of fingers in event
4564   */
4565
4566  /**
4567   * A handler triggered for all pinch events. This handler is constantly fired for the duration of the pinch. This is triggered regardless of thresholds.
4568   * @name $.fn.swipe#pinchStatus
4569   * @event
4570   * @default null
4571   * @param {EventObject} event The original event object
4572   * @param {int} direction The direction the user pinched in. See {@link $.fn.swipe.directions}
4573   * @param {int} distance The distance the user pinched
4574   * @param {int} duration The duration of the swipe in milliseconds
4575   * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers}
4576   * @param {int} zoom The zoom/scale level the user pinched too, 0-1.
4577   * @param {object} fingerData The coordinates of fingers in event
4578   */
4579
4580  /**
4581   * A click handler triggered when a user simply clicks, rather than swipes on an element.
4582   * This is deprecated since version 1.6.2, any assignment to click will be assigned to the tap handler.
4583   * You cannot use <code>on</code> to bind to this event as the default jQ <code>click</code> event will be triggered.
4584   * Use the <code>tap</code> event instead.
4585   * @name $.fn.swipe#click
4586   * @event
4587   * @deprecated since version 1.6.2, please use {@link $.fn.swipe#tap} instead
4588   * @default null
4589   * @param {EventObject} event The original event object
4590   * @param {DomObject} target The element clicked on.
4591   */
4592
4593  /**
4594   * A click / tap handler triggered when a user simply clicks or taps, rather than swipes on an element.
4595   * @name $.fn.swipe#tap
4596   * @event
4597   * @default null
4598   * @param {EventObject} event The original event object
4599   * @param {DomObject} target The element clicked on.
4600   */
4601
4602  /**
4603   * A double tap handler triggered when a user double clicks or taps on an element.
4604   * You can set the time delay for a double tap with the {@link $.fn.swipe.defaults#doubleTapThreshold} property.
4605   * Note: If you set both <code>doubleTap</code> and <code>tap</code> handlers, the <code>tap</code> event will be delayed by the <code>doubleTapThreshold</code>
4606   * as the script needs to check if its a double tap.
4607   * @name $.fn.swipe#doubleTap
4608   * @see  $.fn.swipe.defaults#doubleTapThreshold
4609   * @event
4610   * @default null
4611   * @param {EventObject} event The original event object
4612   * @param {DomObject} target The element clicked on.
4613   */
4614
4615  /**
4616   * A long tap handler triggered once a tap has been release if the tap was longer than the longTapThreshold.
4617   * You can set the time delay for a long tap with the {@link $.fn.swipe.defaults#longTapThreshold} property.
4618   * @name $.fn.swipe#longTap
4619   * @see  $.fn.swipe.defaults#longTapThreshold
4620   * @event
4621   * @default null
4622   * @param {EventObject} event The original event object
4623   * @param {DomObject} target The element clicked on.
4624   */
4625
4626  /**
4627   * A hold tap handler triggered as soon as the longTapThreshold is reached
4628   * You can set the time delay for a long tap with the {@link $.fn.swipe.defaults#longTapThreshold} property.
4629   * @name $.fn.swipe#hold
4630   * @see  $.fn.swipe.defaults#longTapThreshold
4631   * @event
4632   * @default null
4633   * @param {EventObject} event The original event object
4634   * @param {DomObject} target The element clicked on.
4635   */
4636
4637}));
4638
4639/*!
4640* Parsley.js
4641* Version 2.9.2 - built Tue, Dec 10th 2019, 6:18 pm
4642* http://parsleyjs.org
4643* Guillaume Potier - <[email protected]>
4644* Marc-Andre Lafortune - <[email protected]>
4645* MIT Licensed
4646*/
4647
4648// The source code below is generated by babel as
4649// Parsley is written in ECMAScript 6
4650//
4651
4652(function (global, factory) {
4653  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('jquery')) :
4654  typeof define === 'function' && define.amd ? define(['jquery'], factory) :
4655  (global = global || self, global.parsley = factory(global.jQuery));
4656}(this, (function ($) { 'use strict';
4657
4658  function _typeof(obj) {
4659    if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
4660      _typeof = function (obj) {
4661        return typeof obj;
4662      };
4663    } else {
4664      _typeof = function (obj) {
4665        return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
4666      };
4667    }
4668
4669    return _typeof(obj);
4670  }
4671
4672  function _extends() {
4673    _extends = Object.assign || function (target) {
4674      for (var i = 1; i < arguments.length; i++) {
4675        var source = arguments[i];
4676
4677        for (var key in source) {
4678          if (Object.prototype.hasOwnProperty.call(source, key)) {
4679            target[key] = source[key];
4680          }
4681        }
4682      }
4683
4684      return target;
4685    };
4686
4687    return _extends.apply(this, arguments);
4688  }
4689
4690  function _slicedToArray(arr, i) {
4691    return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
4692  }
4693
4694  function _toConsumableArray(arr) {
4695    return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
4696  }
4697
4698  function _arrayWithoutHoles(arr) {
4699    if (Array.isArray(arr)) {
4700      for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
4701
4702      return arr2;
4703    }
4704  }
4705
4706  function _arrayWithHoles(arr) {
4707    if (Array.isArray(arr)) return arr;
4708  }
4709
4710  function _iterableToArray(iter) {
4711    if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
4712  }
4713
4714  function _iterableToArrayLimit(arr, i) {
4715    if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) {
4716      return;
4717    }
4718
4719    var _arr = [];
4720    var _n = true;
4721    var _d = false;
4722    var _e = undefined;
4723
4724    try {
4725      for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
4726        _arr.push(_s.value);
4727
4728        if (i && _arr.length === i) break;
4729      }
4730    } catch (err) {
4731      _d = true;
4732      _e = err;
4733    } finally {
vendor: 94,581 bytes, lines 4734-7101
4734      try {
4735        if (!_n && _i["return"] != null) _i["return"]();
4736      } finally {
4737        if (_d) throw _e;
4738      }
4739    }
4740
4741    return _arr;
4742  }
4743
4744  function _nonIterableSpread() {
4745    throw new TypeError("Invalid attempt to spread non-iterable instance");
4746  }
4747
4748  function _nonIterableRest() {
4749    throw new TypeError("Invalid attempt to destructure non-iterable instance");
4750  }
4751
4752  var globalID = 1;
4753  var pastWarnings = {};
4754  var Utils = {
4755    // Parsley DOM-API
4756    // returns object from dom attributes and values
4757    attr: function attr(element, namespace, obj) {
4758      var i;
4759      var attribute;
4760      var attributes;
4761      var regex = new RegExp('^' + namespace, 'i');
4762      if ('undefined' === typeof obj) obj = {};else {
4763        // Clear all own properties. This won't affect prototype's values
4764        for (i in obj) {
4765          if (obj.hasOwnProperty(i)) delete obj[i];
4766        }
4767      }
4768      if (!element) return obj;
4769      attributes = element.attributes;
4770
4771      for (i = attributes.length; i--;) {
4772        attribute = attributes[i];
4773
4774        if (attribute && attribute.specified && regex.test(attribute.name)) {
4775          obj[this.camelize(attribute.name.slice(namespace.length))] = this.deserializeValue(attribute.value);
4776        }
4777      }
4778
4779      return obj;
4780    },
4781    checkAttr: function checkAttr(element, namespace, _checkAttr) {
4782      return element.hasAttribute(namespace + _checkAttr);
4783    },
4784    setAttr: function setAttr(element, namespace, attr, value) {
4785      element.setAttribute(this.dasherize(namespace + attr), String(value));
4786    },
4787    getType: function getType(element) {
4788      return element.getAttribute('type') || 'text';
4789    },
4790    generateID: function generateID() {
4791      return '' + globalID++;
4792    },
4793
4794    /** Third party functions **/
4795    deserializeValue: function deserializeValue(value) {
4796      var num;
4797
4798      try {
4799        return value ? value == "true" || (value == "false" ? false : value == "null" ? null : !isNaN(num = Number(value)) ? num : /^[\[\{]/.test(value) ? JSON.parse(value) : value) : value;
4800      } catch (e) {
4801        return value;
4802      }
4803    },
4804    // Zepto camelize function
4805    camelize: function camelize(str) {
4806      return str.replace(/-+(.)?/g, function (match, chr) {
4807        return chr ? chr.toUpperCase() : '';
4808      });
4809    },
4810    // Zepto dasherize function
4811    dasherize: function dasherize(str) {
4812      return str.replace(/::/g, '/').replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2').replace(/([a-z\d])([A-Z])/g, '$1_$2').replace(/_/g, '-').toLowerCase();
4813    },
4814    warn: function warn() {
4815      var _window$console;
4816
4817      if (window.console && 'function' === typeof window.console.warn) (_window$console = window.console).warn.apply(_window$console, arguments);
4818    },
4819    warnOnce: function warnOnce(msg) {
4820      if (!pastWarnings[msg]) {
4821        pastWarnings[msg] = true;
4822        this.warn.apply(this, arguments);
4823      }
4824    },
4825    _resetWarnings: function _resetWarnings() {
4826      pastWarnings = {};
4827    },
4828    trimString: function trimString(string) {
4829      return string.replace(/^\s+|\s+$/g, '');
4830    },
4831    parse: {
4832      date: function date(string) {
4833        var parsed = string.match(/^(\d{4,})-(\d\d)-(\d\d)$/);
4834        if (!parsed) return null;
4835
4836        var _parsed$map = parsed.map(function (x) {
4837          return parseInt(x, 10);
4838        }),
4839            _parsed$map2 = _slicedToArray(_parsed$map, 4),
4840            _ = _parsed$map2[0],
4841            year = _parsed$map2[1],
4842            month = _parsed$map2[2],
4843            day = _parsed$map2[3];
4844
4845        var date = new Date(year, month - 1, day);
4846        if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) return null;
4847        return date;
4848      },
4849      string: function string(_string) {
4850        return _string;
4851      },
4852      integer: function integer(string) {
4853        if (isNaN(string)) return null;
4854        return parseInt(string, 10);
4855      },
4856      number: function number(string) {
4857        if (isNaN(string)) throw null;
4858        return parseFloat(string);
4859      },
4860      'boolean': function _boolean(string) {
4861        return !/^\s*false\s*$/i.test(string);
4862      },
4863      object: function object(string) {
4864        return Utils.deserializeValue(string);
4865      },
4866      regexp: function regexp(_regexp) {
4867        var flags = ''; // Test if RegExp is literal, if not, nothing to be done, otherwise, we need to isolate flags and pattern
4868
4869        if (/^\/.*\/(?:[gimy]*)$/.test(_regexp)) {
4870          // Replace the regexp literal string with the first match group: ([gimy]*)
4871          // If no flag is present, this will be a blank string
4872          flags = _regexp.replace(/.*\/([gimy]*)$/, '$1'); // Again, replace the regexp literal string with the first match group:
4873          // everything excluding the opening and closing slashes and the flags
4874
4875          _regexp = _regexp.replace(new RegExp('^/(.*?)/' + flags + '$'), '$1');
4876        } else {
4877          // Anchor regexp:
4878          _regexp = '^' + _regexp + '$';
4879        }
4880
4881        return new RegExp(_regexp, flags);
4882      }
4883    },
4884    parseRequirement: function parseRequirement(requirementType, string) {
4885      var converter = this.parse[requirementType || 'string'];
4886      if (!converter) throw 'Unknown requirement specification: "' + requirementType + '"';
4887      var converted = converter(string);
4888      if (converted === null) throw "Requirement is not a ".concat(requirementType, ": \"").concat(string, "\"");
4889      return converted;
4890    },
4891    namespaceEvents: function namespaceEvents(events, namespace) {
4892      events = this.trimString(events || '').split(/\s+/);
4893      if (!events[0]) return '';
4894      return $.map(events, function (evt) {
4895        return "".concat(evt, ".").concat(namespace);
4896      }).join(' ');
4897    },
4898    difference: function difference(array, remove) {
4899      // This is O(N^2), should be optimized
4900      var result = [];
4901      $.each(array, function (_, elem) {
4902        if (remove.indexOf(elem) == -1) result.push(elem);
4903      });
4904      return result;
4905    },
4906    // Alter-ego to native Promise.all, but for jQuery
4907    all: function all(promises) {
4908      // jQuery treats $.when() and $.when(singlePromise) differently; let's avoid that and add spurious elements
4909      return $.when.apply($, _toConsumableArray(promises).concat([42, 42]));
4910    },
4911    // Object.create polyfill, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Polyfill
4912    objectCreate: Object.create || function () {
4913      var Object = function Object() {};
4914
4915      return function (prototype) {
4916        if (arguments.length > 1) {
4917          throw Error('Second argument not supported');
4918        }
4919
4920        if (_typeof(prototype) != 'object') {
4921          throw TypeError('Argument must be an object');
4922        }
4923
4924        Object.prototype = prototype;
4925        var result = new Object();
4926        Object.prototype = null;
4927        return result;
4928      };
4929    }(),
4930    _SubmitSelector: 'input[type="submit"], button:submit'
4931  };
4932
4933  // All these options could be overriden and specified directly in DOM using
4934  // `data-parsley-` default DOM-API
4935  // eg: `inputs` can be set in DOM using `data-parsley-inputs="input, textarea"`
4936  // eg: `data-parsley-stop-on-first-failing-constraint="false"`
4937  var Defaults = {
4938    // ### General
4939    // Default data-namespace for DOM API
4940    namespace: 'data-parsley-',
4941    // Supported inputs by default
4942    inputs: 'input, textarea, select',
4943    // Excluded inputs by default
4944    excluded: 'input[type=button], input[type=submit], input[type=reset], input[type=hidden]',
4945    // Stop validating field on highest priority failing constraint
4946    priorityEnabled: true,
4947    // ### Field only
4948    // identifier used to group together inputs (e.g. radio buttons...)
4949    multiple: null,
4950    // identifier (or array of identifiers) used to validate only a select group of inputs
4951    group: null,
4952    // ### UI
4953    // Enable\Disable error messages
4954    uiEnabled: true,
4955    // Key events threshold before validation
4956    validationThreshold: 3,
4957    // Focused field on form validation error. 'first'|'last'|'none'
4958    focus: 'first',
4959    // event(s) that will trigger validation before first failure. eg: `input`...
4960    trigger: false,
4961    // event(s) that will trigger validation after first failure.
4962    triggerAfterFailure: 'input',
4963    // Class that would be added on every failing validation Parsley field
4964    errorClass: 'parsley-error',
4965    // Same for success validation
4966    successClass: 'parsley-success',
4967    // Return the `$element` that will receive these above success or error classes
4968    // Could also be (and given directly from DOM) a valid selector like `'#div'`
4969    classHandler: function classHandler(Field) {},
4970    // Return the `$element` where errors will be appended
4971    // Could also be (and given directly from DOM) a valid selector like `'#div'`
4972    errorsContainer: function errorsContainer(Field) {},
4973    // ul elem that would receive errors' list
4974    errorsWrapper: '<ul class="parsley-errors-list"></ul>',
4975    // li elem that would receive error message
4976    errorTemplate: '<li></li>'
4977  };
4978
4979  var Base = function Base() {
4980    this.__id__ = Utils.generateID();
4981  };
4982
4983  Base.prototype = {
4984    asyncSupport: true,
4985    // Deprecated
4986    _pipeAccordingToValidationResult: function _pipeAccordingToValidationResult() {
4987      var _this = this;
4988
4989      var pipe = function pipe() {
4990        var r = $.Deferred();
4991        if (true !== _this.validationResult) r.reject();
4992        return r.resolve().promise();
4993      };
4994
4995      return [pipe, pipe];
4996    },
4997    actualizeOptions: function actualizeOptions() {
4998      Utils.attr(this.element, this.options.namespace, this.domOptions);
4999      if (this.parent && this.parent.actualizeOptions) this.parent.actualizeOptions();
5000      return this;
5001    },
5002    _resetOptions: function _resetOptions(initOptions) {
5003      this.domOptions = Utils.objectCreate(this.parent.options);
5004      this.options = Utils.objectCreate(this.domOptions); // Shallow copy of ownProperties of initOptions:
5005
5006      for (var i in initOptions) {
5007        if (initOptions.hasOwnProperty(i)) this.options[i] = initOptions[i];
5008      }
5009
5010      this.actualizeOptions();
5011    },
5012    _listeners: null,
5013    // Register a callback for the given event name
5014    // Callback is called with context as the first argument and the `this`
5015    // The context is the current parsley instance, or window.Parsley if global
5016    // A return value of `false` will interrupt the calls
5017    on: function on(name, fn) {
5018      this._listeners = this._listeners || {};
5019      var queue = this._listeners[name] = this._listeners[name] || [];
5020      queue.push(fn);
5021      return this;
5022    },
5023    // Deprecated. Use `on` instead
5024    subscribe: function subscribe(name, fn) {
5025      $.listenTo(this, name.toLowerCase(), fn);
5026    },
5027    // Unregister a callback (or all if none is given) for the given event name
5028    off: function off(name, fn) {
5029      var queue = this._listeners && this._listeners[name];
5030
5031      if (queue) {
5032        if (!fn) {
5033          delete this._listeners[name];
5034        } else {
5035          for (var i = queue.length; i--;) {
5036            if (queue[i] === fn) queue.splice(i, 1);
5037          }
5038        }
5039      }
5040
5041      return this;
5042    },
5043    // Deprecated. Use `off`
5044    unsubscribe: function unsubscribe(name, fn) {
5045      $.unsubscribeTo(this, name.toLowerCase());
5046    },
5047    // Trigger an event of the given name
5048    // A return value of `false` interrupts the callback chain
5049    // Returns false if execution was interrupted
5050    trigger: function trigger(name, target, extraArg) {
5051      target = target || this;
5052      var queue = this._listeners && this._listeners[name];
5053      var result;
5054
5055      if (queue) {
5056        for (var i = queue.length; i--;) {
5057          result = queue[i].call(target, target, extraArg);
5058          if (result === false) return result;
5059        }
5060      }
5061
5062      if (this.parent) {
5063        return this.parent.trigger(name, target, extraArg);
5064      }
5065
5066      return true;
5067    },
5068    asyncIsValid: function asyncIsValid(group, force) {
5069      Utils.warnOnce("asyncIsValid is deprecated; please use whenValid instead");
5070      return this.whenValid({
5071        group: group,
5072        force: force
5073      });
5074    },
5075    _findRelated: function _findRelated() {
5076      return this.options.multiple ? $(this.parent.element.querySelectorAll("[".concat(this.options.namespace, "multiple=\"").concat(this.options.multiple, "\"]"))) : this.$element;
5077    }
5078  };
5079
5080  var convertArrayRequirement = function convertArrayRequirement(string, length) {
5081    var m = string.match(/^\s*\[(.*)\]\s*$/);
5082    if (!m) throw 'Requirement is not an array: "' + string + '"';
5083    var values = m[1].split(',').map(Utils.trimString);
5084    if (values.length !== length) throw 'Requirement has ' + values.length + ' values when ' + length + ' are needed';
5085    return values;
5086  };
5087
5088  var convertExtraOptionRequirement = function convertExtraOptionRequirement(requirementSpec, string, extraOptionReader) {
5089    var main = null;
5090    var extra = {};
5091
5092    for (var key in requirementSpec) {
5093      if (key) {
5094        var value = extraOptionReader(key);
5095        if ('string' === typeof value) value = Utils.parseRequirement(requirementSpec[key], value);
5096        extra[key] = value;
5097      } else {
5098        main = Utils.parseRequirement(requirementSpec[key], string);
5099      }
5100    }
5101
5102    return [main, extra];
5103  }; // A Validator needs to implement the methods `validate` and `parseRequirements`
5104
5105
5106  var Validator = function Validator(spec) {
5107    $.extend(true, this, spec);
5108  };
5109
5110  Validator.prototype = {
5111    // Returns `true` iff the given `value` is valid according the given requirements.
5112    validate: function validate(value, requirementFirstArg) {
5113      if (this.fn) {
5114        // Legacy style validator
5115        if (arguments.length > 3) // If more args then value, requirement, instance...
5116          requirementFirstArg = [].slice.call(arguments, 1, -1); // Skip first arg (value) and last (instance), combining the rest
5117
5118        return this.fn(value, requirementFirstArg);
5119      }
5120
5121      if (Array.isArray(value)) {
5122        if (!this.validateMultiple) throw 'Validator `' + this.name + '` does not handle multiple values';
5123        return this.validateMultiple.apply(this, arguments);
5124      } else {
5125        var instance = arguments[arguments.length - 1];
5126
5127        if (this.validateDate && instance._isDateInput()) {
5128          arguments[0] = Utils.parse.date(arguments[0]);
5129          if (arguments[0] === null) return false;
5130          return this.validateDate.apply(this, arguments);
5131        }
5132
5133        if (this.validateNumber) {
5134          if (!value) // Builtin validators all accept empty strings, except `required` of course
5135            return true;
5136          if (isNaN(value)) return false;
5137          arguments[0] = parseFloat(arguments[0]);
5138          return this.validateNumber.apply(this, arguments);
5139        }
5140
5141        if (this.validateString) {
5142          return this.validateString.apply(this, arguments);
5143        }
5144
5145        throw 'Validator `' + this.name + '` only handles multiple values';
5146      }
5147    },
5148    // Parses `requirements` into an array of arguments,
5149    // according to `this.requirementType`
5150    parseRequirements: function parseRequirements(requirements, extraOptionReader) {
5151      if ('string' !== typeof requirements) {
5152        // Assume requirement already parsed
5153        // but make sure we return an array
5154        return Array.isArray(requirements) ? requirements : [requirements];
5155      }
5156
5157      var type = this.requirementType;
5158
5159      if (Array.isArray(type)) {
5160        var values = convertArrayRequirement(requirements, type.length);
5161
5162        for (var i = 0; i < values.length; i++) {
5163          values[i] = Utils.parseRequirement(type[i], values[i]);
5164        }
5165
5166        return values;
5167      } else if ($.isPlainObject(type)) {
5168        return convertExtraOptionRequirement(type, requirements, extraOptionReader);
5169      } else {
5170        return [Utils.parseRequirement(type, requirements)];
5171      }
5172    },
5173    // Defaults:
5174    requirementType: 'string',
5175    priority: 2
5176  };
5177
5178  var ValidatorRegistry = function ValidatorRegistry(validators, catalog) {
5179    this.__class__ = 'ValidatorRegistry'; // Default Parsley locale is en
5180
5181    this.locale = 'en';
5182    this.init(validators || {}, catalog || {});
5183  };
5184
5185  var typeTesters = {
5186    email: /^((([a-zA-Z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-zA-Z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-zA-Z]|\d|-|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-zA-Z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-zA-Z]|\d|-|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-zA-Z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))$/,
5187    // Follow https://www.w3.org/TR/html5/infrastructure.html#floating-point-numbers
5188    number: /^-?(\d*\.)?\d+(e[-+]?\d+)?$/i,
5189    integer: /^-?\d+$/,
5190    digits: /^\d+$/,
5191    alphanum: /^\w+$/i,
5192    date: {
5193      test: function test(value) {
5194        return Utils.parse.date(value) !== null;
5195      }
5196    },
5197    url: new RegExp("^" + // protocol identifier
5198    "(?:(?:https?|ftp)://)?" + // ** mod: make scheme optional
5199    // user:pass authentication
5200    "(?:\\S+(?::\\S*)?@)?" + "(?:" + // IP address exclusion
5201    // private & local networks
5202    // "(?!(?:10|127)(?:\\.\\d{1,3}){3})" +   // ** mod: allow local networks
5203    // "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" +  // ** mod: allow local networks
5204    // "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})" +  // ** mod: allow local networks
5205    // IP address dotted notation octets
5206    // excludes loopback network 0.0.0.0
5207    // excludes reserved space >= 224.0.0.0
5208    // excludes network & broacast addresses
5209    // (first & last IP address of each class)
5210    "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" + "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" + "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" + "|" + // host name
5211    "(?:(?:[a-zA-Z\\u00a1-\\uffff0-9]-*)*[a-zA-Z\\u00a1-\\uffff0-9]+)" + // domain name
5212    "(?:\\.(?:[a-zA-Z\\u00a1-\\uffff0-9]-*)*[a-zA-Z\\u00a1-\\uffff0-9]+)*" + // TLD identifier
5213    "(?:\\.(?:[a-zA-Z\\u00a1-\\uffff]{2,}))" + ")" + // port number
5214    "(?::\\d{2,5})?" + // resource path
5215    "(?:/\\S*)?" + "$")
5216  };
5217  typeTesters.range = typeTesters.number; // See http://stackoverflow.com/a/10454560/8279
5218
5219  var decimalPlaces = function decimalPlaces(num) {
5220    var match = ('' + num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
5221
5222    if (!match) {
5223      return 0;
5224    }
5225
5226    return Math.max(0, // Number of digits right of decimal point.
5227    (match[1] ? match[1].length : 0) - ( // Adjust for scientific notation.
5228    match[2] ? +match[2] : 0));
5229  }; // parseArguments('number', ['1', '2']) => [1, 2]
5230
5231
5232  var parseArguments = function parseArguments(type, args) {
5233    return args.map(Utils.parse[type]);
5234  }; // operatorToValidator returns a validating function for an operator function, applied to the given type
5235
5236
5237  var operatorToValidator = function operatorToValidator(type, operator) {
5238    return function (value) {
5239      for (var _len = arguments.length, requirementsAndInput = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
5240        requirementsAndInput[_key - 1] = arguments[_key];
5241      }
5242
5243      requirementsAndInput.pop(); // Get rid of `input` argument
5244
5245      return operator.apply(void 0, [value].concat(_toConsumableArray(parseArguments(type, requirementsAndInput))));
5246    };
5247  };
5248
5249  var comparisonOperator = function comparisonOperator(operator) {
5250    return {
5251      validateDate: operatorToValidator('date', operator),
5252      validateNumber: operatorToValidator('number', operator),
5253      requirementType: operator.length <= 2 ? 'string' : ['string', 'string'],
5254      // Support operators with a 1 or 2 requirement(s)
5255      priority: 30
5256    };
5257  };
5258
5259  ValidatorRegistry.prototype = {
5260    init: function init(validators, catalog) {
5261      this.catalog = catalog; // Copy prototype's validators:
5262
5263      this.validators = _extends({}, this.validators);
5264
5265      for (var name in validators) {
5266        this.addValidator(name, validators[name].fn, validators[name].priority);
5267      }
5268
5269      window.Parsley.trigger('parsley:validator:init');
5270    },
5271    // Set new messages locale if we have dictionary loaded in ParsleyConfig.i18n
5272    setLocale: function setLocale(locale) {
5273      if ('undefined' === typeof this.catalog[locale]) throw new Error(locale + ' is not available in the catalog');
5274      this.locale = locale;
5275      return this;
5276    },
5277    // Add a new messages catalog for a given locale. Set locale for this catalog if set === `true`
5278    addCatalog: function addCatalog(locale, messages, set) {
5279      if ('object' === _typeof(messages)) this.catalog[locale] = messages;
5280      if (true === set) return this.setLocale(locale);
5281      return this;
5282    },
5283    // Add a specific message for a given constraint in a given locale
5284    addMessage: function addMessage(locale, name, message) {
5285      if ('undefined' === typeof this.catalog[locale]) this.catalog[locale] = {};
5286      this.catalog[locale][name] = message;
5287      return this;
5288    },
5289    // Add messages for a given locale
5290    addMessages: function addMessages(locale, nameMessageObject) {
5291      for (var name in nameMessageObject) {
5292        this.addMessage(locale, name, nameMessageObject[name]);
5293      }
5294
5295      return this;
5296    },
5297    // Add a new validator
5298    //
5299    //    addValidator('custom', {
5300    //        requirementType: ['integer', 'integer'],
5301    //        validateString: function(value, from, to) {},
5302    //        priority: 22,
5303    //        messages: {
5304    //          en: "Hey, that's no good",
5305    //          fr: "Aye aye, pas bon du tout",
5306    //        }
5307    //    })
5308    //
5309    // Old API was addValidator(name, function, priority)
5310    //
5311    addValidator: function addValidator(name, arg1, arg2) {
5312      if (this.validators[name]) Utils.warn('Validator "' + name + '" is already defined.');else if (Defaults.hasOwnProperty(name)) {
5313        Utils.warn('"' + name + '" is a restricted keyword and is not a valid validator name.');
5314        return;
5315      }
5316      return this._setValidator.apply(this, arguments);
5317    },
5318    hasValidator: function hasValidator(name) {
5319      return !!this.validators[name];
5320    },
5321    updateValidator: function updateValidator(name, arg1, arg2) {
5322      if (!this.validators[name]) {
5323        Utils.warn('Validator "' + name + '" is not already defined.');
5324        return this.addValidator.apply(this, arguments);
5325      }
5326
5327      return this._setValidator.apply(this, arguments);
5328    },
5329    removeValidator: function removeValidator(name) {
5330      if (!this.validators[name]) Utils.warn('Validator "' + name + '" is not defined.');
5331      delete this.validators[name];
5332      return this;
5333    },
5334    _setValidator: function _setValidator(name, validator, priority) {
5335      if ('object' !== _typeof(validator)) {
5336        // Old style validator, with `fn` and `priority`
5337        validator = {
5338          fn: validator,
5339          priority: priority
5340        };
5341      }
5342
5343      if (!validator.validate) {
5344        validator = new Validator(validator);
5345      }
5346
5347      this.validators[name] = validator;
5348
5349      for (var locale in validator.messages || {}) {
5350        this.addMessage(locale, name, validator.messages[locale]);
5351      }
5352
5353      return this;
5354    },
5355    getErrorMessage: function getErrorMessage(constraint) {
5356      var message; // Type constraints are a bit different, we have to match their requirements too to find right error message
5357
5358      if ('type' === constraint.name) {
5359        var typeMessages = this.catalog[this.locale][constraint.name] || {};
5360        message = typeMessages[constraint.requirements];
5361      } else message = this.formatMessage(this.catalog[this.locale][constraint.name], constraint.requirements);
5362
5363      return message || this.catalog[this.locale].defaultMessage || this.catalog.en.defaultMessage;
5364    },
5365    // Kind of light `sprintf()` implementation
5366    formatMessage: function formatMessage(string, parameters) {
5367      if ('object' === _typeof(parameters)) {
5368        for (var i in parameters) {
5369          string = this.formatMessage(string, parameters[i]);
5370        }
5371
5372        return string;
5373      }
5374
5375      return 'string' === typeof string ? string.replace(/%s/i, parameters) : '';
5376    },
5377    // Here is the Parsley default validators list.
5378    // A validator is an object with the following key values:
5379    //  - priority: an integer
5380    //  - requirement: 'string' (default), 'integer', 'number', 'regexp' or an Array of these
5381    //  - validateString, validateMultiple, validateNumber: functions returning `true`, `false` or a promise
5382    // Alternatively, a validator can be a function that returns such an object
5383    //
5384    validators: {
5385      notblank: {
5386        validateString: function validateString(value) {
5387          return /\S/.test(value);
5388        },
5389        priority: 2
5390      },
5391      required: {
5392        validateMultiple: function validateMultiple(values) {
5393          return values.length > 0;
5394        },
5395        validateString: function validateString(value) {
5396          return /\S/.test(value);
5397        },
5398        priority: 512
5399      },
5400      type: {
5401        validateString: function validateString(value, type) {
5402          var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {},
5403              _ref$step = _ref.step,
5404              step = _ref$step === void 0 ? 'any' : _ref$step,
5405              _ref$base = _ref.base,
5406              base = _ref$base === void 0 ? 0 : _ref$base;
5407
5408          var tester = typeTesters[type];
5409
5410          if (!tester) {
5411            throw new Error('validator type `' + type + '` is not supported');
5412          }
5413
5414          if (!value) return true; // Builtin validators all accept empty strings, except `required` of course
5415
5416          if (!tester.test(value)) return false;
5417
5418          if ('number' === type) {
5419            if (!/^any$/i.test(step || '')) {
5420              var nb = Number(value);
5421              var decimals = Math.max(decimalPlaces(step), decimalPlaces(base));
5422              if (decimalPlaces(nb) > decimals) // Value can't have too many decimals
5423                return false; // Be careful of rounding errors by using integers.
5424
5425              var toInt = function toInt(f) {
5426                return Math.round(f * Math.pow(10, decimals));
5427              };
5428
5429              if ((toInt(nb) - toInt(base)) % toInt(step) != 0) return false;
5430            }
5431          }
5432
5433          return true;
5434        },
5435        requirementType: {
5436          '': 'string',
5437          step: 'string',
5438          base: 'number'
5439        },
5440        priority: 256
5441      },
5442      pattern: {
5443        validateString: function validateString(value, regexp) {
5444          if (!value) return true; // Builtin validators all accept empty strings, except `required` of course
5445
5446          return regexp.test(value);
5447        },
5448        requirementType: 'regexp',
5449        priority: 64
5450      },
5451      minlength: {
5452        validateString: function validateString(value, requirement) {
5453          if (!value) return true; // Builtin validators all accept empty strings, except `required` of course
5454
5455          return value.length >= requirement;
5456        },
5457        requirementType: 'integer',
5458        priority: 30
5459      },
5460      maxlength: {
5461        validateString: function validateString(value, requirement) {
5462          return value.length <= requirement;
5463        },
5464        requirementType: 'integer',
5465        priority: 30
5466      },
5467      length: {
5468        validateString: function validateString(value, min, max) {
5469          if (!value) return true; // Builtin validators all accept empty strings, except `required` of course
5470
5471          return value.length >= min && value.length <= max;
5472        },
5473        requirementType: ['integer', 'integer'],
5474        priority: 30
5475      },
5476      mincheck: {
5477        validateMultiple: function validateMultiple(values, requirement) {
5478          return values.length >= requirement;
5479        },
5480        requirementType: 'integer',
5481        priority: 30
5482      },
5483      maxcheck: {
5484        validateMultiple: function validateMultiple(values, requirement) {
5485          return values.length <= requirement;
5486        },
5487        requirementType: 'integer',
5488        priority: 30
5489      },
5490      check: {
5491        validateMultiple: function validateMultiple(values, min, max) {
5492          return values.length >= min && values.length <= max;
5493        },
5494        requirementType: ['integer', 'integer'],
5495        priority: 30
5496      },
5497      min: comparisonOperator(function (value, requirement) {
5498        return value >= requirement;
5499      }),
5500      max: comparisonOperator(function (value, requirement) {
5501        return value <= requirement;
5502      }),
5503      range: comparisonOperator(function (value, min, max) {
5504        return value >= min && value <= max;
5505      }),
5506      equalto: {
5507        validateString: function validateString(value, refOrValue) {
5508          if (!value) return true; // Builtin validators all accept empty strings, except `required` of course
5509
5510          var $reference = $(refOrValue);
5511          if ($reference.length) return value === $reference.val();else return value === refOrValue;
5512        },
5513        priority: 256
5514      },
5515      euvatin: {
5516        validateString: function validateString(value, refOrValue) {
5517          if (!value) {
5518            return true; // Builtin validators all accept empty strings, except `required` of course
5519          }
5520
5521          var re = /^[A-Z][A-Z][A-Za-z0-9 -]{2,}$/;
5522          return re.test(value);
5523        },
5524        priority: 30
5525      }
5526    }
5527  };
5528
5529  var UI = {};
5530
5531  var diffResults = function diffResults(newResult, oldResult, deep) {
5532    var added = [];
5533    var kept = [];
5534
5535    for (var i = 0; i < newResult.length; i++) {
5536      var found = false;
5537
5538      for (var j = 0; j < oldResult.length; j++) {
5539        if (newResult[i].assert.name === oldResult[j].assert.name) {
5540          found = true;
5541          break;
5542        }
5543      }
5544
5545      if (found) kept.push(newResult[i]);else added.push(newResult[i]);
5546    }
5547
5548    return {
5549      kept: kept,
5550      added: added,
5551      removed: !deep ? diffResults(oldResult, newResult, true).added : []
5552    };
5553  };
5554
5555  UI.Form = {
5556    _actualizeTriggers: function _actualizeTriggers() {
5557      var _this = this;
5558
5559      this.$element.on('submit.Parsley', function (evt) {
5560        _this.onSubmitValidate(evt);
5561      });
5562      this.$element.on('click.Parsley', Utils._SubmitSelector, function (evt) {
5563        _this.onSubmitButton(evt);
5564      }); // UI could be disabled
5565
5566      if (false === this.options.uiEnabled) return;
5567      this.element.setAttribute('novalidate', '');
5568    },
5569    focus: function focus() {
5570      this._focusedField = null;
5571      if (true === this.validationResult || 'none' === this.options.focus) return null;
5572
5573      for (var i = 0; i < this.fields.length; i++) {
5574        var field = this.fields[i];
5575
5576        if (true !== field.validationResult && field.validationResult.length > 0 && 'undefined' === typeof field.options.noFocus) {
5577          this._focusedField = field.$element;
5578          if ('first' === this.options.focus) break;
5579        }
5580      }
5581
5582      if (null === this._focusedField) return null;
5583      return this._focusedField.focus();
5584    },
5585    _destroyUI: function _destroyUI() {
5586      // Reset all event listeners
5587      this.$element.off('.Parsley');
5588    }
5589  };
5590  UI.Field = {
5591    _reflowUI: function _reflowUI() {
5592      this._buildUI(); // If this field doesn't have an active UI don't bother doing something
5593
5594
5595      if (!this._ui) return; // Diff between two validation results
5596
5597      var diff = diffResults(this.validationResult, this._ui.lastValidationResult); // Then store current validation result for next reflow
5598
5599      this._ui.lastValidationResult = this.validationResult; // Handle valid / invalid / none field class
5600
5601      this._manageStatusClass(); // Add, remove, updated errors messages
5602
5603
5604      this._manageErrorsMessages(diff); // Triggers impl
5605
5606
5607      this._actualizeTriggers(); // If field is not valid for the first time, bind keyup trigger to ease UX and quickly inform user
5608
5609
5610      if ((diff.kept.length || diff.added.length) && !this._failedOnce) {
5611        this._failedOnce = true;
5612
5613        this._actualizeTriggers();
5614      }
5615    },
5616    // Returns an array of field's error message(s)
5617    getErrorsMessages: function getErrorsMessages() {
5618      // No error message, field is valid
5619      if (true === this.validationResult) return [];
5620      var messages = [];
5621
5622      for (var i = 0; i < this.validationResult.length; i++) {
5623        messages.push(this.validationResult[i].errorMessage || this._getErrorMessage(this.validationResult[i].assert));
5624      }
5625
5626      return messages;
5627    },
5628    // It's a goal of Parsley that this method is no longer required [#1073]
5629    addError: function addError(name) {
5630      var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
5631          message = _ref.message,
5632          assert = _ref.assert,
5633          _ref$updateClass = _ref.updateClass,
5634          updateClass = _ref$updateClass === void 0 ? true : _ref$updateClass;
5635
5636      this._buildUI();
5637
5638      this._addError(name, {
5639        message: message,
5640        assert: assert
5641      });
5642
5643      if (updateClass) this._errorClass();
5644    },
5645    // It's a goal of Parsley that this method is no longer required [#1073]
5646    updateError: function updateError(name) {
5647      var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
5648          message = _ref2.message,
5649          assert = _ref2.assert,
5650          _ref2$updateClass = _ref2.updateClass,
5651          updateClass = _ref2$updateClass === void 0 ? true : _ref2$updateClass;
5652
5653      this._buildUI();
5654
5655      this._updateError(name, {
5656        message: message,
5657        assert: assert
5658      });
5659
5660      if (updateClass) this._errorClass();
5661    },
5662    // It's a goal of Parsley that this method is no longer required [#1073]
5663    removeError: function removeError(name) {
5664      var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
5665          _ref3$updateClass = _ref3.updateClass,
5666          updateClass = _ref3$updateClass === void 0 ? true : _ref3$updateClass;
5667
5668      this._buildUI();
5669
5670      this._removeError(name); // edge case possible here: remove a standard Parsley error that is still failing in this.validationResult
5671      // but highly improbable cuz' manually removing a well Parsley handled error makes no sense.
5672
5673
5674      if (updateClass) this._manageStatusClass();
5675    },
5676    _manageStatusClass: function _manageStatusClass() {
5677      if (this.hasConstraints() && this.needsValidation() && true === this.validationResult) this._successClass();else if (this.validationResult.length > 0) this._errorClass();else this._resetClass();
5678    },
5679    _manageErrorsMessages: function _manageErrorsMessages(diff) {
5680      if ('undefined' !== typeof this.options.errorsMessagesDisabled) return; // Case where we have errorMessage option that configure an unique field error message, regardless failing validators
5681
5682      if ('undefined' !== typeof this.options.errorMessage) {
5683        if (diff.added.length || diff.kept.length) {
5684          this._insertErrorWrapper();
5685
5686          if (0 === this._ui.$errorsWrapper.find('.parsley-custom-error-message').length) this._ui.$errorsWrapper.append($(this.options.errorTemplate).addClass('parsley-custom-error-message'));
5687
5688          this._ui.$errorClassHandler.attr('aria-describedby', this._ui.errorsWrapperId);
5689
5690          return this._ui.$errorsWrapper.addClass('filled').attr('aria-hidden', 'false').find('.parsley-custom-error-message').html(this.options.errorMessage);
5691        }
5692
5693        this._ui.$errorClassHandler.removeAttr('aria-describedby');
5694
5695        return this._ui.$errorsWrapper.removeClass('filled').attr('aria-hidden', 'true').find('.parsley-custom-error-message').remove();
5696      } // Show, hide, update failing constraints messages
5697
5698
5699      for (var i = 0; i < diff.removed.length; i++) {
5700        this._removeError(diff.removed[i].assert.name);
5701      }
5702
5703      for (i = 0; i < diff.added.length; i++) {
5704        this._addError(diff.added[i].assert.name, {
5705          message: diff.added[i].errorMessage,
5706          assert: diff.added[i].assert
5707        });
5708      }
5709
5710      for (i = 0; i < diff.kept.length; i++) {
5711        this._updateError(diff.kept[i].assert.name, {
5712          message: diff.kept[i].errorMessage,
5713          assert: diff.kept[i].assert
5714        });
5715      }
5716    },
5717    _addError: function _addError(name, _ref4) {
5718      var message = _ref4.message,
5719          assert = _ref4.assert;
5720
5721      this._insertErrorWrapper();
5722
5723      this._ui.$errorClassHandler.attr('aria-describedby', this._ui.errorsWrapperId);
5724
5725      this._ui.$errorsWrapper.addClass('filled').attr('aria-hidden', 'false').append($(this.options.errorTemplate).addClass('parsley-' + name).html(message || this._getErrorMessage(assert)));
5726    },
5727    _updateError: function _updateError(name, _ref5) {
5728      var message = _ref5.message,
5729          assert = _ref5.assert;
5730
5731      this._ui.$errorsWrapper.addClass('filled').find('.parsley-' + name).html(message || this._getErrorMessage(assert));
5732    },
5733    _removeError: function _removeError(name) {
5734      this._ui.$errorClassHandler.removeAttr('aria-describedby');
5735
5736      this._ui.$errorsWrapper.removeClass('filled').attr('aria-hidden', 'true').find('.parsley-' + name).remove();
5737    },
5738    _getErrorMessage: function _getErrorMessage(constraint) {
5739      var customConstraintErrorMessage = constraint.name + 'Message';
5740      if ('undefined' !== typeof this.options[customConstraintErrorMessage]) return window.Parsley.formatMessage(this.options[customConstraintErrorMessage], constraint.requirements);
5741      return window.Parsley.getErrorMessage(constraint);
5742    },
5743    _buildUI: function _buildUI() {
5744      // UI could be already built or disabled
5745      if (this._ui || false === this.options.uiEnabled) return;
5746      var _ui = {}; // Give field its Parsley id in DOM
5747
5748      this.element.setAttribute(this.options.namespace + 'id', this.__id__);
5749      /** Generate important UI elements and store them in this **/
5750      // $errorClassHandler is the $element that woul have parsley-error and parsley-success classes
5751
5752      _ui.$errorClassHandler = this._manageClassHandler(); // $errorsWrapper is a div that would contain the various field errors, it will be appended into $errorsContainer
5753
5754      _ui.errorsWrapperId = 'parsley-id-' + (this.options.multiple ? 'multiple-' + this.options.multiple : this.__id__);
5755      _ui.$errorsWrapper = $(this.options.errorsWrapper).attr('id', _ui.errorsWrapperId); // ValidationResult UI storage to detect what have changed bwt two validations, and update DOM accordingly
5756
5757      _ui.lastValidationResult = [];
5758      _ui.validationInformationVisible = false; // Store it in this for later
5759
5760      this._ui = _ui;
5761    },
5762    // Determine which element will have `parsley-error` and `parsley-success` classes
5763    _manageClassHandler: function _manageClassHandler() {
5764      // Class handled could also be determined by function given in Parsley options
5765      if ('string' === typeof this.options.classHandler && $(this.options.classHandler).length) return $(this.options.classHandler); // Class handled could also be determined by function given in Parsley options
5766
5767      var $handlerFunction = this.options.classHandler; // It might also be the function name of a global function
5768
5769      if ('string' === typeof this.options.classHandler && 'function' === typeof window[this.options.classHandler]) $handlerFunction = window[this.options.classHandler];
5770
5771      if ('function' === typeof $handlerFunction) {
5772        var $handler = $handlerFunction.call(this, this); // If this function returned a valid existing DOM element, go for it
5773
5774        if ('undefined' !== typeof $handler && $handler.length) return $handler;
5775      } else if ('object' === _typeof($handlerFunction) && $handlerFunction instanceof jQuery && $handlerFunction.length) {
5776        return $handlerFunction;
5777      } else if ($handlerFunction) {
5778        Utils.warn('The class handler `' + $handlerFunction + '` does not exist in DOM nor as a global JS function');
5779      }
5780
5781      return this._inputHolder();
5782    },
5783    _inputHolder: function _inputHolder() {
5784      // if simple element (input, texatrea, select...) it will perfectly host the classes and precede the error container
5785      if (!this.options.multiple || this.element.nodeName === 'SELECT') return this.$element; // But if multiple element (radio, checkbox), that would be their parent
5786
5787      return this.$element.parent();
5788    },
5789    _insertErrorWrapper: function _insertErrorWrapper() {
5790      var $errorsContainer = this.options.errorsContainer; // Nothing to do if already inserted
5791
5792      if (0 !== this._ui.$errorsWrapper.parent().length) return this._ui.$errorsWrapper.parent();
5793
5794      if ('string' === typeof $errorsContainer) {
5795        if ($($errorsContainer).length) return $($errorsContainer).append(this._ui.$errorsWrapper);else if ('function' === typeof window[$errorsContainer]) $errorsContainer = window[$errorsContainer];else Utils.warn('The errors container `' + $errorsContainer + '` does not exist in DOM nor as a global JS function');
5796      }
5797
5798      if ('function' === typeof $errorsContainer) $errorsContainer = $errorsContainer.call(this, this);
5799      if ('object' === _typeof($errorsContainer) && $errorsContainer.length) return $errorsContainer.append(this._ui.$errorsWrapper);
5800      return this._inputHolder().after(this._ui.$errorsWrapper);
5801    },
5802    _actualizeTriggers: function _actualizeTriggers() {
5803      var _this2 = this;
5804
5805      var $toBind = this._findRelated();
5806
5807      var trigger; // Remove Parsley events already bound on this field
5808
5809      $toBind.off('.Parsley');
5810      if (this._failedOnce) $toBind.on(Utils.namespaceEvents(this.options.triggerAfterFailure, 'Parsley'), function () {
5811        _this2._validateIfNeeded();
5812      });else if (trigger = Utils.namespaceEvents(this.options.trigger, 'Parsley')) {
5813        $toBind.on(trigger, function (event) {
5814          _this2._validateIfNeeded(event);
5815        });
5816      }
5817    },
5818    _validateIfNeeded: function _validateIfNeeded(event) {
5819      var _this3 = this;
5820
5821      // For keyup, keypress, keydown, input... events that could be a little bit obstrusive
5822      // do not validate if val length < min threshold on first validation. Once field have been validated once and info
5823      // about success or failure have been displayed, always validate with this trigger to reflect every yalidation change.
5824      if (event && /key|input/.test(event.type)) if (!(this._ui && this._ui.validationInformationVisible) && this.getValue().length <= this.options.validationThreshold) return;
5825
5826      if (this.options.debounce) {
5827        window.clearTimeout(this._debounced);
5828        this._debounced = window.setTimeout(function () {
5829          return _this3.validate();
5830        }, this.options.debounce);
5831      } else this.validate();
5832    },
5833    _resetUI: function _resetUI() {
5834      // Reset all event listeners
5835      this._failedOnce = false;
5836
5837      this._actualizeTriggers(); // Nothing to do if UI never initialized for this field
5838
5839
5840      if ('undefined' === typeof this._ui) return; // Reset all errors' li
5841
5842      this._ui.$errorsWrapper.removeClass('filled').children().remove(); // Reset validation class
5843
5844
5845      this._resetClass(); // Reset validation flags and last validation result
5846
5847
5848      this._ui.lastValidationResult = [];
5849      this._ui.validationInformationVisible = false;
5850    },
5851    _destroyUI: function _destroyUI() {
5852      this._resetUI();
5853
5854      if ('undefined' !== typeof this._ui) this._ui.$errorsWrapper.remove();
5855      delete this._ui;
5856    },
5857    _successClass: function _successClass() {
5858      this._ui.validationInformationVisible = true;
5859
5860      this._ui.$errorClassHandler.removeClass(this.options.errorClass).addClass(this.options.successClass);
5861    },
5862    _errorClass: function _errorClass() {
5863      this._ui.validationInformationVisible = true;
5864
5865      this._ui.$errorClassHandler.removeClass(this.options.successClass).addClass(this.options.errorClass);
5866    },
5867    _resetClass: function _resetClass() {
5868      this._ui.$errorClassHandler.removeClass(this.options.successClass).removeClass(this.options.errorClass);
5869    }
5870  };
5871
5872  var Form = function Form(element, domOptions, options) {
5873    this.__class__ = 'Form';
5874    this.element = element;
5875    this.$element = $(element);
5876    this.domOptions = domOptions;
5877    this.options = options;
5878    this.parent = window.Parsley;
5879    this.fields = [];
5880    this.validationResult = null;
5881  };
5882
5883  var statusMapping = {
5884    pending: null,
5885    resolved: true,
5886    rejected: false
5887  };
5888  Form.prototype = {
5889    onSubmitValidate: function onSubmitValidate(event) {
5890      var _this = this;
5891
5892      // This is a Parsley generated submit event, do not validate, do not prevent, simply exit and keep normal behavior
5893      if (true === event.parsley) return; // If we didn't come here through a submit button, use the first one in the form
5894
5895      var submitSource = this._submitSource || this.$element.find(Utils._SubmitSelector)[0];
5896      this._submitSource = null;
5897      this.$element.find('.parsley-synthetic-submit-button').prop('disabled', true);
5898      if (submitSource && null !== submitSource.getAttribute('formnovalidate')) return;
5899      window.Parsley._remoteCache = {};
5900      var promise = this.whenValidate({
5901        event: event
5902      });
5903
5904      if ('resolved' === promise.state() && false !== this._trigger('submit')) ; else {
5905        // Rejected or pending: cancel this submit
5906        event.stopImmediatePropagation();
5907        event.preventDefault();
5908        if ('pending' === promise.state()) promise.done(function () {
5909          _this._submit(submitSource);
5910        });
5911      }
5912    },
5913    onSubmitButton: function onSubmitButton(event) {
5914      this._submitSource = event.currentTarget;
5915    },
5916    // internal
5917    // _submit submits the form, this time without going through the validations.
5918    // Care must be taken to "fake" the actual submit button being clicked.
5919    _submit: function _submit(submitSource) {
5920      if (false === this._trigger('submit')) return; // Add submit button's data
5921
5922      if (submitSource) {
5923        var $synthetic = this.$element.find('.parsley-synthetic-submit-button').prop('disabled', false);
5924        if (0 === $synthetic.length) $synthetic = $('<input class="parsley-synthetic-submit-button" type="hidden">').appendTo(this.$element);
5925        $synthetic.attr({
5926          name: submitSource.getAttribute('name'),
5927          value: submitSource.getAttribute('value')
5928        });
5929      }
5930
5931      this.$element.trigger(_extends($.Event('submit'), {
5932        parsley: true
5933      }));
5934    },
5935    // Performs validation on fields while triggering events.
5936    // @returns `true` if all validations succeeds, `false`
5937    // if a failure is immediately detected, or `null`
5938    // if dependant on a promise.
5939    // Consider using `whenValidate` instead.
5940    validate: function validate(options) {
5941      if (arguments.length >= 1 && !$.isPlainObject(options)) {
5942        Utils.warnOnce('Calling validate on a parsley form without passing arguments as an object is deprecated.');
5943
5944        var _arguments = Array.prototype.slice.call(arguments),
5945            group = _arguments[0],
5946            force = _arguments[1],
5947            event = _arguments[2];
5948
5949        options = {
5950          group: group,
5951          force: force,
5952          event: event
5953        };
5954      }
5955
5956      return statusMapping[this.whenValidate(options).state()];
5957    },
5958    whenValidate: function whenValidate() {
5959      var _this2 = this,
5960          _Utils$all$done$fail$;
5961
5962      var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
5963          group = _ref.group,
5964          force = _ref.force,
5965          event = _ref.event;
5966
5967      this.submitEvent = event;
5968
5969      if (event) {
5970        this.submitEvent = _extends({}, event, {
5971          preventDefault: function preventDefault() {
5972            Utils.warnOnce("Using `this.submitEvent.preventDefault()` is deprecated; instead, call `this.validationResult = false`");
5973            _this2.validationResult = false;
5974          }
5975        });
5976      }
5977
5978      this.validationResult = true; // fire validate event to eventually modify things before every validation
5979
5980      this._trigger('validate'); // Refresh form DOM options and form's fields that could have changed
5981
5982
5983      this._refreshFields();
5984
5985      var promises = this._withoutReactualizingFormOptions(function () {
5986        return $.map(_this2.fields, function (field) {
5987          return field.whenValidate({
5988            force: force,
5989            group: group
5990          });
5991        });
5992      });
5993
5994      return (_Utils$all$done$fail$ = Utils.all(promises).done(function () {
5995        _this2._trigger('success');
5996      }).fail(function () {
5997        _this2.validationResult = false;
5998
5999        _this2.focus();
6000
6001        _this2._trigger('error');
6002      }).always(function () {
6003        _this2._trigger('validated');
6004      })).pipe.apply(_Utils$all$done$fail$, _toConsumableArray(this._pipeAccordingToValidationResult()));
6005    },
6006    // Iterate over refreshed fields, and stop on first failure.
6007    // Returns `true` if all fields are valid, `false` if a failure is detected
6008    // or `null` if the result depends on an unresolved promise.
6009    // Prefer using `whenValid` instead.
6010    isValid: function isValid(options) {
6011      if (arguments.length >= 1 && !$.isPlainObject(options)) {
6012        Utils.warnOnce('Calling isValid on a parsley form without passing arguments as an object is deprecated.');
6013
6014        var _arguments2 = Array.prototype.slice.call(arguments),
6015            group = _arguments2[0],
6016            force = _arguments2[1];
6017
6018        options = {
6019          group: group,
6020          force: force
6021        };
6022      }
6023
6024      return statusMapping[this.whenValid(options).state()];
6025    },
6026    // Iterate over refreshed fields and validate them.
6027    // Returns a promise.
6028    // A validation that immediately fails will interrupt the validations.
6029    whenValid: function whenValid() {
6030      var _this3 = this;
6031
6032      var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
6033          group = _ref2.group,
6034          force = _ref2.force;
6035
6036      this._refreshFields();
6037
6038      var promises = this._withoutReactualizingFormOptions(function () {
6039        return $.map(_this3.fields, function (field) {
6040          return field.whenValid({
6041            group: group,
6042            force: force
6043          });
6044        });
6045      });
6046
6047      return Utils.all(promises);
6048    },
6049    refresh: function refresh() {
6050      this._refreshFields();
6051
6052      return this;
6053    },
6054    // Reset UI
6055    reset: function reset() {
6056      // Form case: emit a reset event for each field
6057      for (var i = 0; i < this.fields.length; i++) {
6058        this.fields[i].reset();
6059      }
6060
6061      this._trigger('reset');
6062    },
6063    // Destroy Parsley instance (+ UI)
6064    destroy: function destroy() {
6065      // Field case: emit destroy event to clean UI and then destroy stored instance
6066      this._destroyUI(); // Form case: destroy all its fields and then destroy stored instance
6067
6068
6069      for (var i = 0; i < this.fields.length; i++) {
6070        this.fields[i].destroy();
6071      }
6072
6073      this.$element.removeData('Parsley');
6074
6075      this._trigger('destroy');
6076    },
6077    _refreshFields: function _refreshFields() {
6078      return this.actualizeOptions()._bindFields();
6079    },
6080    _bindFields: function _bindFields() {
6081      var _this4 = this;
6082
6083      var oldFields = this.fields;
6084      this.fields = [];
6085      this.fieldsMappedById = {};
6086
6087      this._withoutReactualizingFormOptions(function () {
6088        _this4.$element.find(_this4.options.inputs).not(_this4.options.excluded).not("[".concat(_this4.options.namespace, "excluded=true]")).each(function (_, element) {
6089          var fieldInstance = new window.Parsley.Factory(element, {}, _this4); // Only add valid and not excluded `Field` and `FieldMultiple` children
6090
6091          if ('Field' === fieldInstance.__class__ || 'FieldMultiple' === fieldInstance.__class__) {
6092            var uniqueId = fieldInstance.__class__ + '-' + fieldInstance.__id__;
6093
6094            if ('undefined' === typeof _this4.fieldsMappedById[uniqueId]) {
6095              _this4.fieldsMappedById[uniqueId] = fieldInstance;
6096
6097              _this4.fields.push(fieldInstance);
6098            }
6099          }
6100        });
6101
6102        $.each(Utils.difference(oldFields, _this4.fields), function (_, field) {
6103          field.reset();
6104        });
6105      });
6106
6107      return this;
6108    },
6109    // Internal only.
6110    // Looping on a form's fields to do validation or similar
6111    // will trigger reactualizing options on all of them, which
6112    // in turn will reactualize the form's options.
6113    // To avoid calling actualizeOptions so many times on the form
6114    // for nothing, _withoutReactualizingFormOptions temporarily disables
6115    // the method actualizeOptions on this form while `fn` is called.
6116    _withoutReactualizingFormOptions: function _withoutReactualizingFormOptions(fn) {
6117      var oldActualizeOptions = this.actualizeOptions;
6118
6119      this.actualizeOptions = function () {
6120        return this;
6121      };
6122
6123      var result = fn();
6124      this.actualizeOptions = oldActualizeOptions;
6125      return result;
6126    },
6127    // Internal only.
6128    // Shortcut to trigger an event
6129    // Returns true iff event is not interrupted and default not prevented.
6130    _trigger: function _trigger(eventName) {
6131      return this.trigger('form:' + eventName);
6132    }
6133  };
6134
6135  var Constraint = function Constraint(parsleyField, name, requirements, priority, isDomConstraint) {
6136    var validatorSpec = window.Parsley._validatorRegistry.validators[name];
6137    var validator = new Validator(validatorSpec);
6138    priority = priority || parsleyField.options[name + 'Priority'] || validator.priority;
6139    isDomConstraint = true === isDomConstraint;
6140
6141    _extends(this, {
6142      validator: validator,
6143      name: name,
6144      requirements: requirements,
6145      priority: priority,
6146      isDomConstraint: isDomConstraint
6147    });
6148
6149    this._parseRequirements(parsleyField.options);
6150  };
6151
6152  var capitalize = function capitalize(str) {
6153    var cap = str[0].toUpperCase();
6154    return cap + str.slice(1);
6155  };
6156
6157  Constraint.prototype = {
6158    validate: function validate(value, instance) {
6159      var _this$validator;
6160
6161      return (_this$validator = this.validator).validate.apply(_this$validator, [value].concat(_toConsumableArray(this.requirementList), [instance]));
6162    },
6163    _parseRequirements: function _parseRequirements(options) {
6164      var _this = this;
6165
6166      this.requirementList = this.validator.parseRequirements(this.requirements, function (key) {
6167        return options[_this.name + capitalize(key)];
6168      });
6169    }
6170  };
6171
6172  var Field = function Field(field, domOptions, options, parsleyFormInstance) {
6173    this.__class__ = 'Field';
6174    this.element = field;
6175    this.$element = $(field); // Set parent if we have one
6176
6177    if ('undefined' !== typeof parsleyFormInstance) {
6178      this.parent = parsleyFormInstance;
6179    }
6180
6181    this.options = options;
6182    this.domOptions = domOptions; // Initialize some properties
6183
6184    this.constraints = [];
6185    this.constraintsByName = {};
6186    this.validationResult = true; // Bind constraints
6187
6188    this._bindConstraints();
6189  };
6190
6191  var statusMapping$1 = {
6192    pending: null,
6193    resolved: true,
6194    rejected: false
6195  };
6196  Field.prototype = {
6197    // # Public API
6198    // Validate field and trigger some events for mainly `UI`
6199    // @returns `true`, an array of the validators that failed, or
6200    // `null` if validation is not finished. Prefer using whenValidate
6201    validate: function validate(options) {
6202      if (arguments.length >= 1 && !$.isPlainObject(options)) {
6203        Utils.warnOnce('Calling validate on a parsley field without passing arguments as an object is deprecated.');
6204        options = {
6205          options: options
6206        };
6207      }
6208
6209      var promise = this.whenValidate(options);
6210      if (!promise) // If excluded with `group` option
6211        return true;
6212
6213      switch (promise.state()) {
6214        case 'pending':
6215          return null;
6216
6217        case 'resolved':
6218          return true;
6219
6220        case 'rejected':
6221          return this.validationResult;
6222      }
6223    },
6224    // Validate field and trigger some events for mainly `UI`
6225    // @returns a promise that succeeds only when all validations do
6226    // or `undefined` if field is not in the given `group`.
6227    whenValidate: function whenValidate() {
6228      var _this$whenValid$alway,
6229          _this = this;
6230
6231      var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
6232          force = _ref.force,
6233          group = _ref.group;
6234
6235      // do not validate a field if not the same as given validation group
6236      this.refresh();
6237      if (group && !this._isInGroup(group)) return;
6238      this.value = this.getValue(); // Field Validate event. `this.value` could be altered for custom needs
6239
6240      this._trigger('validate');
6241
6242      return (_this$whenValid$alway = this.whenValid({
6243        force: force,
6244        value: this.value,
6245        _refreshed: true
6246      }).always(function () {
6247        _this._reflowUI();
6248      }).done(function () {
6249        _this._trigger('success');
6250      }).fail(function () {
6251        _this._trigger('error');
6252      }).always(function () {
6253        _this._trigger('validated');
6254      })).pipe.apply(_this$whenValid$alway, _toConsumableArray(this._pipeAccordingToValidationResult()));
6255    },
6256    hasConstraints: function hasConstraints() {
6257      return 0 !== this.constraints.length;
6258    },
6259    // An empty optional field does not need validation
6260    needsValidation: function needsValidation(value) {
6261      if ('undefined' === typeof value) value = this.getValue(); // If a field is empty and not required, it is valid
6262      // Except if `data-parsley-validate-if-empty` explicitely added, useful for some custom validators
6263
6264      if (!value.length && !this._isRequired() && 'undefined' === typeof this.options.validateIfEmpty) return false;
6265      return true;
6266    },
6267    _isInGroup: function _isInGroup(group) {
6268      if (Array.isArray(this.options.group)) return -1 !== $.inArray(group, this.options.group);
6269      return this.options.group === group;
6270    },
6271    // Just validate field. Do not trigger any event.
6272    // Returns `true` iff all constraints pass, `false` if there are failures,
6273    // or `null` if the result can not be determined yet (depends on a promise)
6274    // See also `whenValid`.
6275    isValid: function isValid(options) {
6276      if (arguments.length >= 1 && !$.isPlainObject(options)) {
6277        Utils.warnOnce('Calling isValid on a parsley field without passing arguments as an object is deprecated.');
6278
6279        var _arguments = Array.prototype.slice.call(arguments),
6280            force = _arguments[0],
6281            value = _arguments[1];
6282
6283        options = {
6284          force: force,
6285          value: value
6286        };
6287      }
6288
6289      var promise = this.whenValid(options);
6290      if (!promise) // Excluded via `group`
6291        return true;
6292      return statusMapping$1[promise.state()];
6293    },
6294    // Just validate field. Do not trigger any event.
6295    // @returns a promise that succeeds only when all validations do
6296    // or `undefined` if the field is not in the given `group`.
6297    // The argument `force` will force validation of empty fields.
6298    // If a `value` is given, it will be validated instead of the value of the input.
6299    whenValid: function whenValid() {
6300      var _this2 = this;
6301
6302      var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
6303          _ref2$force = _ref2.force,
6304          force = _ref2$force === void 0 ? false : _ref2$force,
6305          value = _ref2.value,
6306          group = _ref2.group,
6307          _refreshed = _ref2._refreshed;
6308
6309      // Recompute options and rebind constraints to have latest changes
6310      if (!_refreshed) this.refresh(); // do not validate a field if not the same as given validation group
6311
6312      if (group && !this._isInGroup(group)) return;
6313      this.validationResult = true; // A field without constraint is valid
6314
6315      if (!this.hasConstraints()) return $.when(); // Value could be passed as argument, needed to add more power to 'field:validate'
6316
6317      if ('undefined' === typeof value || null === value) value = this.getValue();
6318      if (!this.needsValidation(value) && true !== force) return $.when();
6319
6320      var groupedConstraints = this._getGroupedConstraints();
6321
6322      var promises = [];
6323      $.each(groupedConstraints, function (_, constraints) {
6324        // Process one group of constraints at a time, we validate the constraints
6325        // and combine the promises together.
6326        var promise = Utils.all($.map(constraints, function (constraint) {
6327          return _this2._validateConstraint(value, constraint);
6328        }));
6329        promises.push(promise);
6330        if (promise.state() === 'rejected') return false; // Interrupt processing if a group has already failed
6331      });
6332      return Utils.all(promises);
6333    },
6334    // @returns a promise
6335    _validateConstraint: function _validateConstraint(value, constraint) {
6336      var _this3 = this;
6337
6338      var result = constraint.validate(value, this); // Map false to a failed promise
6339
6340      if (false === result) result = $.Deferred().reject(); // Make sure we return a promise and that we record failures
6341
6342      return Utils.all([result]).fail(function (errorMessage) {
6343        if (!(_this3.validationResult instanceof Array)) _this3.validationResult = [];
6344
6345        _this3.validationResult.push({
6346          assert: constraint,
6347          errorMessage: 'string' === typeof errorMessage && errorMessage
6348        });
6349      });
6350    },
6351    // @returns Parsley field computed value that could be overrided or configured in DOM
6352    getValue: function getValue() {
6353      var value; // Value could be overriden in DOM or with explicit options
6354
6355      if ('function' === typeof this.options.value) value = this.options.value(this);else if ('undefined' !== typeof this.options.value) value = this.options.value;else value = this.$element.val(); // Handle wrong DOM or configurations
6356
6357      if ('undefined' === typeof value || null === value) return '';
6358      return this._handleWhitespace(value);
6359    },
6360    // Reset UI
6361    reset: function reset() {
6362      this._resetUI();
6363
6364      return this._trigger('reset');
6365    },
6366    // Destroy Parsley instance (+ UI)
6367    destroy: function destroy() {
6368      // Field case: emit destroy event to clean UI and then destroy stored instance
6369      this._destroyUI();
6370
6371      this.$element.removeData('Parsley');
6372      this.$element.removeData('FieldMultiple');
6373
6374      this._trigger('destroy');
6375    },
6376    // Actualize options and rebind constraints
6377    refresh: function refresh() {
6378      this._refreshConstraints();
6379
6380      return this;
6381    },
6382    _refreshConstraints: function _refreshConstraints() {
6383      return this.actualizeOptions()._bindConstraints();
6384    },
6385    refreshConstraints: function refreshConstraints() {
6386      Utils.warnOnce("Parsley's refreshConstraints is deprecated. Please use refresh");
6387      return this.refresh();
6388    },
6389
6390    /**
6391    * Add a new constraint to a field
6392    *
6393    * @param {String}   name
6394    * @param {Mixed}    requirements      optional
6395    * @param {Number}   priority          optional
6396    * @param {Boolean}  isDomConstraint   optional
6397    */
6398    addConstraint: function addConstraint(name, requirements, priority, isDomConstraint) {
6399      if (window.Parsley._validatorRegistry.validators[name]) {
6400        var constraint = new Constraint(this, name, requirements, priority, isDomConstraint); // if constraint already exist, delete it and push new version
6401
6402        if ('undefined' !== this.constraintsByName[constraint.name]) this.removeConstraint(constraint.name);
6403        this.constraints.push(constraint);
6404        this.constraintsByName[constraint.name] = constraint;
6405      }
6406
6407      return this;
6408    },
6409    // Remove a constraint
6410    removeConstraint: function removeConstraint(name) {
6411      for (var i = 0; i < this.constraints.length; i++) {
6412        if (name === this.constraints[i].name) {
6413          this.constraints.splice(i, 1);
6414          break;
6415        }
6416      }
6417
6418      delete this.constraintsByName[name];
6419      return this;
6420    },
6421    // Update a constraint (Remove + re-add)
6422    updateConstraint: function updateConstraint(name, parameters, priority) {
6423      return this.removeConstraint(name).addConstraint(name, parameters, priority);
6424    },
6425    // # Internals
6426    // Internal only.
6427    // Bind constraints from config + options + DOM
6428    _bindConstraints: function _bindConstraints() {
6429      var constraints = [];
6430      var constraintsByName = {}; // clean all existing DOM constraints to only keep javascript user constraints
6431
6432      for (var i = 0; i < this.constraints.length; i++) {
6433        if (false === this.constraints[i].isDomConstraint) {
6434          constraints.push(this.constraints[i]);
6435          constraintsByName[this.constraints[i].name] = this.constraints[i];
6436        }
6437      }
6438
6439      this.constraints = constraints;
6440      this.constraintsByName = constraintsByName; // then re-add Parsley DOM-API constraints
6441
6442      for (var name in this.options) {
6443        this.addConstraint(name, this.options[name], undefined, true);
6444      } // finally, bind special HTML5 constraints
6445
6446
6447      return this._bindHtml5Constraints();
6448    },
6449    // Internal only.
6450    // Bind specific HTML5 constraints to be HTML5 compliant
6451    _bindHtml5Constraints: function _bindHtml5Constraints() {
6452      // html5 required
6453      if (null !== this.element.getAttribute('required')) this.addConstraint('required', true, undefined, true); // html5 pattern
6454
6455      if (null !== this.element.getAttribute('pattern')) this.addConstraint('pattern', this.element.getAttribute('pattern'), undefined, true); // range
6456
6457      var min = this.element.getAttribute('min');
6458      var max = this.element.getAttribute('max');
6459      if (null !== min && null !== max) this.addConstraint('range', [min, max], undefined, true); // HTML5 min
6460      else if (null !== min) this.addConstraint('min', min, undefined, true); // HTML5 max
6461        else if (null !== max) this.addConstraint('max', max, undefined, true); // length
6462
6463      if (null !== this.element.getAttribute('minlength') && null !== this.element.getAttribute('maxlength')) this.addConstraint('length', [this.element.getAttribute('minlength'), this.element.getAttribute('maxlength')], undefined, true); // HTML5 minlength
6464      else if (null !== this.element.getAttribute('minlength')) this.addConstraint('minlength', this.element.getAttribute('minlength'), undefined, true); // HTML5 maxlength
6465        else if (null !== this.element.getAttribute('maxlength')) this.addConstraint('maxlength', this.element.getAttribute('maxlength'), undefined, true); // html5 types
6466
6467      var type = Utils.getType(this.element); // Small special case here for HTML5 number: integer validator if step attribute is undefined or an integer value, number otherwise
6468
6469      if ('number' === type) {
6470        return this.addConstraint('type', ['number', {
6471          step: this.element.getAttribute('step') || '1',
6472          base: min || this.element.getAttribute('value')
6473        }], undefined, true); // Regular other HTML5 supported types
6474      } else if (/^(email|url|range|date)$/i.test(type)) {
6475        return this.addConstraint('type', type, undefined, true);
6476      }
6477
6478      return this;
6479    },
6480    // Internal only.
6481    // Field is required if have required constraint without `false` value
6482    _isRequired: function _isRequired() {
6483      if ('undefined' === typeof this.constraintsByName.required) return false;
6484      return false !== this.constraintsByName.required.requirements;
6485    },
6486    // Internal only.
6487    // Shortcut to trigger an event
6488    _trigger: function _trigger(eventName) {
6489      return this.trigger('field:' + eventName);
6490    },
6491    // Internal only
6492    // Handles whitespace in a value
6493    // Use `data-parsley-whitespace="squish"` to auto squish input value
6494    // Use `data-parsley-whitespace="trim"` to auto trim input value
6495    _handleWhitespace: function _handleWhitespace(value) {
6496      if (true === this.options.trimValue) Utils.warnOnce('data-parsley-trim-value="true" is deprecated, please use data-parsley-whitespace="trim"');
6497      if ('squish' === this.options.whitespace) value = value.replace(/\s{2,}/g, ' ');
6498      if ('trim' === this.options.whitespace || 'squish' === this.options.whitespace || true === this.options.trimValue) value = Utils.trimString(value);
6499      return value;
6500    },
6501    _isDateInput: function _isDateInput() {
6502      var c = this.constraintsByName.type;
6503      return c && c.requirements === 'date';
6504    },
6505    // Internal only.
6506    // Returns the constraints, grouped by descending priority.
6507    // The result is thus an array of arrays of constraints.
6508    _getGroupedConstraints: function _getGroupedConstraints() {
6509      if (false === this.options.priorityEnabled) return [this.constraints];
6510      var groupedConstraints = [];
6511      var index = {}; // Create array unique of priorities
6512
6513      for (var i = 0; i < this.constraints.length; i++) {
6514        var p = this.constraints[i].priority;
6515        if (!index[p]) groupedConstraints.push(index[p] = []);
6516        index[p].push(this.constraints[i]);
6517      } // Sort them by priority DESC
6518
6519
6520      groupedConstraints.sort(function (a, b) {
6521        return b[0].priority - a[0].priority;
6522      });
6523      return groupedConstraints;
6524    }
6525  };
6526
6527  var Multiple = function Multiple() {
6528    this.__class__ = 'FieldMultiple';
6529  };
6530
6531  Multiple.prototype = {
6532    // Add new `$element` sibling for multiple field
6533    addElement: function addElement($element) {
6534      this.$elements.push($element);
6535      return this;
6536    },
6537    // See `Field._refreshConstraints()`
6538    _refreshConstraints: function _refreshConstraints() {
6539      var fieldConstraints;
6540      this.constraints = []; // Select multiple special treatment
6541
6542      if (this.element.nodeName === 'SELECT') {
6543        this.actualizeOptions()._bindConstraints();
6544
6545        return this;
6546      } // Gather all constraints for each input in the multiple group
6547
6548
6549      for (var i = 0; i < this.$elements.length; i++) {
6550        // Check if element have not been dynamically removed since last binding
6551        if (!$('html').has(this.$elements[i]).length) {
6552          this.$elements.splice(i, 1);
6553          continue;
6554        }
6555
6556        fieldConstraints = this.$elements[i].data('FieldMultiple')._refreshConstraints().constraints;
6557
6558        for (var j = 0; j < fieldConstraints.length; j++) {
6559          this.addConstraint(fieldConstraints[j].name, fieldConstraints[j].requirements, fieldConstraints[j].priority, fieldConstraints[j].isDomConstraint);
6560        }
6561      }
6562
6563      return this;
6564    },
6565    // See `Field.getValue()`
6566    getValue: function getValue() {
6567      // Value could be overriden in DOM
6568      if ('function' === typeof this.options.value) return this.options.value(this);else if ('undefined' !== typeof this.options.value) return this.options.value; // Radio input case
6569
6570      if (this.element.nodeName === 'INPUT') {
6571        var type = Utils.getType(this.element);
6572        if (type === 'radio') return this._findRelated().filter(':checked').val() || ''; // checkbox input case
6573
6574        if (type === 'checkbox') {
6575          var values = [];
6576
6577          this._findRelated().filter(':checked').each(function () {
6578            values.push($(this).val());
6579          });
6580
6581          return values;
6582        }
6583      } // Select multiple case
6584
6585
6586      if (this.element.nodeName === 'SELECT' && null === this.$element.val()) return []; // Default case that should never happen
6587
6588      return this.$element.val();
6589    },
6590    _init: function _init() {
6591      this.$elements = [this.$element];
6592      return this;
6593    }
6594  };
6595
6596  var Factory = function Factory(element, options, parsleyFormInstance) {
6597    this.element = element;
6598    this.$element = $(element); // If the element has already been bound, returns its saved Parsley instance
6599
6600    var savedparsleyFormInstance = this.$element.data('Parsley');
6601
6602    if (savedparsleyFormInstance) {
6603      // If the saved instance has been bound without a Form parent and there is one given in this call, add it
6604      if ('undefined' !== typeof parsleyFormInstance && savedparsleyFormInstance.parent === window.Parsley) {
6605        savedparsleyFormInstance.parent = parsleyFormInstance;
6606
6607        savedparsleyFormInstance._resetOptions(savedparsleyFormInstance.options);
6608      }
6609
6610      if ('object' === _typeof(options)) {
6611        _extends(savedparsleyFormInstance.options, options);
6612      }
6613
6614      return savedparsleyFormInstance;
6615    } // Parsley must be instantiated with a DOM element or jQuery $element
6616
6617
6618    if (!this.$element.length) throw new Error('You must bind Parsley on an existing element.');
6619    if ('undefined' !== typeof parsleyFormInstance && 'Form' !== parsleyFormInstance.__class__) throw new Error('Parent instance must be a Form instance');
6620    this.parent = parsleyFormInstance || window.Parsley;
6621    return this.init(options);
6622  };
6623
6624  Factory.prototype = {
6625    init: function init(options) {
6626      this.__class__ = 'Parsley';
6627      this.__version__ = '2.9.2';
6628      this.__id__ = Utils.generateID(); // Pre-compute options
6629
6630      this._resetOptions(options); // A Form instance is obviously a `<form>` element but also every node that is not an input and has the `data-parsley-validate` attribute
6631
6632
6633      if (this.element.nodeName === 'FORM' || Utils.checkAttr(this.element, this.options.namespace, 'validate') && !this.$element.is(this.options.inputs)) return this.bind('parsleyForm'); // Every other element is bound as a `Field` or `FieldMultiple`
6634
6635      return this.isMultiple() ? this.handleMultiple() : this.bind('parsleyField');
6636    },
6637    isMultiple: function isMultiple() {
6638      var type = Utils.getType(this.element);
6639      return type === 'radio' || type === 'checkbox' || this.element.nodeName === 'SELECT' && null !== this.element.getAttribute('multiple');
6640    },
6641    // Multiples fields are a real nightmare :(
6642    // Maybe some refactoring would be appreciated here...
6643    handleMultiple: function handleMultiple() {
6644      var _this = this;
6645
6646      var name;
6647      var parsleyMultipleInstance; // Handle multiple name
6648
6649      this.options.multiple = this.options.multiple || (name = this.element.getAttribute('name')) || this.element.getAttribute('id'); // Special select multiple input
6650
6651      if (this.element.nodeName === 'SELECT' && null !== this.element.getAttribute('multiple')) {
6652        this.options.multiple = this.options.multiple || this.__id__;
6653        return this.bind('parsleyFieldMultiple'); // Else for radio / checkboxes, we need a `name` or `data-parsley-multiple` to properly bind it
6654      } else if (!this.options.multiple) {
6655        Utils.warn('To be bound by Parsley, a radio, a checkbox and a multiple select input must have either a name or a multiple option.', this.$element);
6656        return this;
6657      } // Remove special chars
6658
6659
6660      this.options.multiple = this.options.multiple.replace(/(:|\.|\[|\]|\{|\}|\$)/g, ''); // Add proper `data-parsley-multiple` to siblings if we have a valid multiple name
6661
6662      if (name) {
6663        $('input[name="' + name + '"]').each(function (i, input) {
6664          var type = Utils.getType(input);
6665          if (type === 'radio' || type === 'checkbox') input.setAttribute(_this.options.namespace + 'multiple', _this.options.multiple);
6666        });
6667      } // Check here if we don't already have a related multiple instance saved
6668
6669
6670      var $previouslyRelated = this._findRelated();
6671
6672      for (var i = 0; i < $previouslyRelated.length; i++) {
6673        parsleyMultipleInstance = $($previouslyRelated.get(i)).data('Parsley');
6674
6675        if ('undefined' !== typeof parsleyMultipleInstance) {
6676          if (!this.$element.data('FieldMultiple')) {
6677            parsleyMultipleInstance.addElement(this.$element);
6678          }
6679
6680          break;
6681        }
6682      } // Create a secret Field instance for every multiple field. It will be stored in `data('FieldMultiple')`
6683      // And will be useful later to access classic `Field` stuff while being in a `FieldMultiple` instance
6684
6685
6686      this.bind('parsleyField', true);
6687      return parsleyMultipleInstance || this.bind('parsleyFieldMultiple');
6688    },
6689    // Return proper `Form`, `Field` or `FieldMultiple`
6690    bind: function bind(type, doNotStore) {
6691      var parsleyInstance;
6692
6693      switch (type) {
6694        case 'parsleyForm':
6695          parsleyInstance = $.extend(new Form(this.element, this.domOptions, this.options), new Base(), window.ParsleyExtend)._bindFields();
6696          break;
6697
6698        case 'parsleyField':
6699          parsleyInstance = $.extend(new Field(this.element, this.domOptions, this.options, this.parent), new Base(), window.ParsleyExtend);
6700          break;
6701
6702        case 'parsleyFieldMultiple':
6703          parsleyInstance = $.extend(new Field(this.element, this.domOptions, this.options, this.parent), new Multiple(), new Base(), window.ParsleyExtend)._init();
6704          break;
6705
6706        default:
6707          throw new Error(type + 'is not a supported Parsley type');
6708      }
6709
6710      if (this.options.multiple) Utils.setAttr(this.element, this.options.namespace, 'multiple', this.options.multiple);
6711
6712      if ('undefined' !== typeof doNotStore) {
6713        this.$element.data('FieldMultiple', parsleyInstance);
6714        return parsleyInstance;
6715      } // Store the freshly bound instance in a DOM element for later access using jQuery `data()`
6716
6717
6718      this.$element.data('Parsley', parsleyInstance); // Tell the world we have a new Form or Field instance!
6719
6720      parsleyInstance._actualizeTriggers();
6721
6722      parsleyInstance._trigger('init');
6723
6724      return parsleyInstance;
6725    }
6726  };
6727
6728  var vernums = $.fn.jquery.split('.');
6729
6730  if (parseInt(vernums[0]) <= 1 && parseInt(vernums[1]) < 8) {
6731    throw "The loaded version of jQuery is too old. Please upgrade to 1.8.x or better.";
6732  }
6733
6734  if (!vernums.forEach) {
6735    Utils.warn('Parsley requires ES5 to run properly. Please include https://github.com/es-shims/es5-shim');
6736  } // Inherit `on`, `off` & `trigger` to Parsley:
6737
6738
6739  var Parsley = _extends(new Base(), {
6740    element: document,
6741    $element: $(document),
6742    actualizeOptions: null,
6743    _resetOptions: null,
6744    Factory: Factory,
6745    version: '2.9.2'
6746  }); // Supplement Field and Form with Base
6747  // This way, the constructors will have access to those methods
6748
6749
6750  _extends(Field.prototype, UI.Field, Base.prototype);
6751
6752  _extends(Form.prototype, UI.Form, Base.prototype); // Inherit actualizeOptions and _resetOptions:
6753
6754
6755  _extends(Factory.prototype, Base.prototype); // ### jQuery API
6756  // `$('.elem').parsley(options)` or `$('.elem').psly(options)`
6757
6758
6759  $.fn.parsley = $.fn.psly = function (options) {
6760    if (this.length > 1) {
6761      var instances = [];
6762      this.each(function () {
6763        instances.push($(this).parsley(options));
6764      });
6765      return instances;
6766    } // Return undefined if applied to non existing DOM element
6767
6768
6769    if (this.length == 0) {
6770      return;
6771    }
6772
6773    return new Factory(this[0], options);
6774  }; // ### Field and Form extension
6775  // Ensure the extension is now defined if it wasn't previously
6776
6777
6778  if ('undefined' === typeof window.ParsleyExtend) window.ParsleyExtend = {}; // ### Parsley config
6779  // Inherit from ParsleyDefault, and copy over any existing values
6780
6781  Parsley.options = _extends(Utils.objectCreate(Defaults), window.ParsleyConfig);
6782  window.ParsleyConfig = Parsley.options; // Old way of accessing global options
6783  // ### Globals
6784
6785  window.Parsley = window.psly = Parsley;
6786  Parsley.Utils = Utils;
6787  window.ParsleyUtils = {};
6788  $.each(Utils, function (key, value) {
6789    if ('function' === typeof value) {
6790      window.ParsleyUtils[key] = function () {
6791        Utils.warnOnce('Accessing `window.ParsleyUtils` is deprecated. Use `window.Parsley.Utils` instead.');
6792        return Utils[key].apply(Utils, arguments);
6793      };
6794    }
6795  }); // ### Define methods that forward to the registry, and deprecate all access except through window.Parsley
6796
6797  var registry = window.Parsley._validatorRegistry = new ValidatorRegistry(window.ParsleyConfig.validators, window.ParsleyConfig.i18n);
6798  window.ParsleyValidator = {};
6799  $.each('setLocale addCatalog addMessage addMessages getErrorMessage formatMessage addValidator updateValidator removeValidator hasValidator'.split(' '), function (i, method) {
6800    window.Parsley[method] = function () {
6801      return registry[method].apply(registry, arguments);
6802    };
6803
6804    window.ParsleyValidator[method] = function () {
6805      var _window$Parsley;
6806
6807      Utils.warnOnce("Accessing the method '".concat(method, "' through Validator is deprecated. Simply call 'window.Parsley.").concat(method, "(...)'"));
6808      return (_window$Parsley = window.Parsley)[method].apply(_window$Parsley, arguments);
6809    };
6810  }); // ### UI
6811  // Deprecated global object
6812
6813  window.Parsley.UI = UI;
6814  window.ParsleyUI = {
6815    removeError: function removeError(instance, name, doNotUpdateClass) {
6816      var updateClass = true !== doNotUpdateClass;
6817      Utils.warnOnce("Accessing UI is deprecated. Call 'removeError' on the instance directly. Please comment in issue 1073 as to your need to call this method.");
6818      return instance.removeError(name, {
6819        updateClass: updateClass
6820      });
6821    },
6822    getErrorsMessages: function getErrorsMessages(instance) {
6823      Utils.warnOnce("Accessing UI is deprecated. Call 'getErrorsMessages' on the instance directly.");
6824      return instance.getErrorsMessages();
6825    }
6826  };
6827  $.each('addError updateError'.split(' '), function (i, method) {
6828    window.ParsleyUI[method] = function (instance, name, message, assert, doNotUpdateClass) {
6829      var updateClass = true !== doNotUpdateClass;
6830      Utils.warnOnce("Accessing UI is deprecated. Call '".concat(method, "' on the instance directly. Please comment in issue 1073 as to your need to call this method."));
6831      return instance[method](name, {
6832        message: message,
6833        assert: assert,
6834        updateClass: updateClass
6835      });
6836    };
6837  }); // ### PARSLEY auto-binding
6838  // Prevent it by setting `ParsleyConfig.autoBind` to `false`
6839
6840  if (false !== window.ParsleyConfig.autoBind) {
6841    $(function () {
6842      // Works only on `data-parsley-validate`.
6843      if ($('[data-parsley-validate]').length) $('[data-parsley-validate]').parsley();
6844    });
6845  }
6846
6847  var o = $({});
6848
6849  var deprecated = function deprecated() {
6850    Utils.warnOnce("Parsley's pubsub module is deprecated; use the 'on' and 'off' methods on parsley instances or window.Parsley");
6851  }; // Returns an event handler that calls `fn` with the arguments it expects
6852
6853
6854  function adapt(fn, context) {
6855    // Store to allow unbinding
6856    if (!fn.parsleyAdaptedCallback) {
6857      fn.parsleyAdaptedCallback = function () {
6858        var args = Array.prototype.slice.call(arguments, 0);
6859        args.unshift(this);
6860        fn.apply(context || o, args);
6861      };
6862    }
6863
6864    return fn.parsleyAdaptedCallback;
6865  }
6866
6867  var eventPrefix = 'parsley:'; // Converts 'parsley:form:validate' into 'form:validate'
6868
6869  function eventName(name) {
6870    if (name.lastIndexOf(eventPrefix, 0) === 0) return name.substr(eventPrefix.length);
6871    return name;
6872  } // $.listen is deprecated. Use Parsley.on instead.
6873
6874
6875  $.listen = function (name, callback) {
6876    var context;
6877    deprecated();
6878
6879    if ('object' === _typeof(arguments[1]) && 'function' === typeof arguments[2]) {
6880      context = arguments[1];
6881      callback = arguments[2];
6882    }
6883
6884    if ('function' !== typeof callback) throw new Error('Wrong parameters');
6885    window.Parsley.on(eventName(name), adapt(callback, context));
6886  };
6887
6888  $.listenTo = function (instance, name, fn) {
6889    deprecated();
6890    if (!(instance instanceof Field) && !(instance instanceof Form)) throw new Error('Must give Parsley instance');
6891    if ('string' !== typeof name || 'function' !== typeof fn) throw new Error('Wrong parameters');
6892    instance.on(eventName(name), adapt(fn));
6893  };
6894
6895  $.unsubscribe = function (name, fn) {
6896    deprecated();
6897    if ('string' !== typeof name || 'function' !== typeof fn) throw new Error('Wrong arguments');
6898    window.Parsley.off(eventName(name), fn.parsleyAdaptedCallback);
6899  };
6900
6901  $.unsubscribeTo = function (instance, name) {
6902    deprecated();
6903    if (!(instance instanceof Field) && !(instance instanceof Form)) throw new Error('Must give Parsley instance');
6904    instance.off(eventName(name));
6905  };
6906
6907  $.unsubscribeAll = function (name) {
6908    deprecated();
6909    window.Parsley.off(eventName(name));
6910    $('form,input,textarea,select').each(function () {
6911      var instance = $(this).data('Parsley');
6912
6913      if (instance) {
6914        instance.off(eventName(name));
6915      }
6916    });
6917  }; // $.emit is deprecated. Use jQuery events instead.
6918
6919
6920  $.emit = function (name, instance) {
6921    var _instance;
6922
6923    deprecated();
6924    var instanceGiven = instance instanceof Field || instance instanceof Form;
6925    var args = Array.prototype.slice.call(arguments, instanceGiven ? 2 : 1);
6926    args.unshift(eventName(name));
6927
6928    if (!instanceGiven) {
6929      instance = window.Parsley;
6930    }
6931
6932    (_instance = instance).trigger.apply(_instance, _toConsumableArray(args));
6933  };
6934
6935  $.extend(true, Parsley, {
6936    asyncValidators: {
6937      'default': {
6938        fn: function fn(xhr) {
6939          // By default, only status 2xx are deemed successful.
6940          // Note: we use status instead of state() because responses with status 200
6941          // but invalid messages (e.g. an empty body for content type set to JSON) will
6942          // result in state() === 'rejected'.
6943          return xhr.status >= 200 && xhr.status < 300;
6944        },
6945        url: false
6946      },
6947      reverse: {
6948        fn: function fn(xhr) {
6949          // If reverse option is set, a failing ajax request is considered successful
6950          return xhr.status < 200 || xhr.status >= 300;
6951        },
6952        url: false
6953      }
6954    },
6955    addAsyncValidator: function addAsyncValidator(name, fn, url, options) {
6956      Parsley.asyncValidators[name] = {
6957        fn: fn,
6958        url: url || false,
6959        options: options || {}
6960      };
6961      return this;
6962    }
6963  });
6964  Parsley.addValidator('remote', {
6965    requirementType: {
6966      '': 'string',
6967      'validator': 'string',
6968      'reverse': 'boolean',
6969      'options': 'object'
6970    },
6971    validateString: function validateString(value, url, options, instance) {
6972      var data = {};
6973      var ajaxOptions;
6974      var csr;
6975      var validator = options.validator || (true === options.reverse ? 'reverse' : 'default');
6976      if ('undefined' === typeof Parsley.asyncValidators[validator]) throw new Error('Calling an undefined async validator: `' + validator + '`');
6977      url = Parsley.asyncValidators[validator].url || url; // Fill current value
6978
6979      if (url.indexOf('{value}') > -1) {
6980        url = url.replace('{value}', encodeURIComponent(value));
6981      } else {
6982        data[instance.element.getAttribute('name') || instance.element.getAttribute('id')] = value;
6983      } // Merge options passed in from the function with the ones in the attribute
6984
6985
6986      var remoteOptions = $.extend(true, options.options || {}, Parsley.asyncValidators[validator].options); // All `$.ajax(options)` could be overridden or extended directly from DOM in `data-parsley-remote-options`
6987
6988      ajaxOptions = $.extend(true, {}, {
6989        url: url,
6990        data: data,
6991        type: 'GET'
6992      }, remoteOptions); // Generate store key based on ajax options
6993
6994      instance.trigger('field:ajaxoptions', instance, ajaxOptions);
6995      csr = $.param(ajaxOptions); // Initialise querry cache
6996
6997      if ('undefined' === typeof Parsley._remoteCache) Parsley._remoteCache = {}; // Try to retrieve stored xhr
6998
6999      var xhr = Parsley._remoteCache[csr] = Parsley._remoteCache[csr] || $.ajax(ajaxOptions);
7000
7001      var handleXhr = function handleXhr() {
7002        var result = Parsley.asyncValidators[validator].fn.call(instance, xhr, url, options);
7003        if (!result) // Map falsy results to rejected promise
7004          result = $.Deferred().reject();
7005        return $.when(result);
7006      };
7007
7008      return xhr.then(handleXhr, handleXhr);
7009    },
7010    priority: -1
7011  });
7012  Parsley.on('form:submit', function () {
7013    Parsley._remoteCache = {};
7014  });
7015
7016  Base.prototype.addAsyncValidator = function () {
7017    Utils.warnOnce('Accessing the method `addAsyncValidator` through an instance is deprecated. Simply call `Parsley.addAsyncValidator(...)`');
7018    return Parsley.addAsyncValidator.apply(Parsley, arguments);
7019  };
7020
7021  // This is included with the Parsley library itself,
7022  Parsley.addMessages('en', {
7023    defaultMessage: "This value seems to be invalid.",
7024    type: {
7025      email: "This value should be a valid email.",
7026      url: "This value should be a valid url.",
7027      number: "This value should be a valid number.",
7028      integer: "This value should be a valid integer.",
7029      digits: "This value should be digits.",
7030      alphanum: "This value should be alphanumeric."
7031    },
7032    notblank: "This value should not be blank.",
7033    required: "This value is required.",
7034    pattern: "This value seems to be invalid.",
7035    min: "This value should be greater than or equal to %s.",
7036    max: "This value should be lower than or equal to %s.",
7037    range: "This value should be between %s and %s.",
7038    minlength: "This value is too short. It should have %s characters or more.",
7039    maxlength: "This value is too long. It should have %s characters or fewer.",
7040    length: "This value length is invalid. It should be between %s and %s characters long.",
7041    mincheck: "You must select at least %s choices.",
7042    maxcheck: "You must select %s choices or fewer.",
7043    check: "You must select between %s and %s choices.",
7044    equalto: "This value should be the same.",
7045    euvatin: "It's not a valid VAT Identification Number."
7046  });
7047  Parsley.setLocale('en');
7048
7049  function InputEvent() {
7050    var _this = this;
7051
7052    var globals = window || global; // Slightly odd way construct our object. This way methods are force bound.
7053    // Used to test for duplicate library.
7054
7055    _extends(this, {
7056      // For browsers that do not support isTrusted, assumes event is native.
7057      isNativeEvent: function isNativeEvent(evt) {
7058        return evt.originalEvent && evt.originalEvent.isTrusted !== false;
7059      },
7060      fakeInputEvent: function fakeInputEvent(evt) {
7061        if (_this.isNativeEvent(evt)) {
7062          $(evt.target).trigger('input');
7063        }
7064      },
7065      misbehaves: function misbehaves(evt) {
7066        if (_this.isNativeEvent(evt)) {
7067          _this.behavesOk(evt);
7068
7069          $(document).on('change.inputevent', evt.data.selector, _this.fakeInputEvent);
7070
7071          _this.fakeInputEvent(evt);
7072        }
7073      },
7074      behavesOk: function behavesOk(evt) {
7075        if (_this.isNativeEvent(evt)) {
7076          $(document) // Simply unbinds the testing handler
7077          .off('input.inputevent', evt.data.selector, _this.behavesOk).off('change.inputevent', evt.data.selector, _this.misbehaves);
7078        }
7079      },
7080      // Bind the testing handlers
7081      install: function install() {
7082        if (globals.inputEventPatched) {
7083          return;
7084        }
7085
7086        globals.inputEventPatched = '0.0.3';
7087
7088        for (var _i = 0, _arr = ['select', 'input[type="checkbox"]', 'input[type="radio"]', 'input[type="file"]']; _i < _arr.length; _i++) {
7089          var selector = _arr[_i];
7090          $(document).on('input.inputevent', selector, {
7091            selector: selector
7092          }, _this.behavesOk).on('change.inputevent', selector, {
7093            selector: selector
7094          }, _this.misbehaves);
7095        }
7096      },
7097      uninstall: function uninstall() {
7098        delete globals.inputEventPatched;
7099        $(document).off('.inputevent');
7100      }
7101    });
vendor: 4,668 bytes, lines 7102-7213
7102  }
7103  var inputevent = new InputEvent();
7104
7105  inputevent.install();
7106
7107  return Parsley;
7108
7109})));
7110//# sourceMappingURL=parsley.js.map
7111
7112/**
7113 * FingerprintJS v5.1.0 - Copyright (c) FingerprintJS, Inc, 2026 (https://fingerprint.com)
7114 *
7115 * Licensed under MIT License
7116 *
7117 * Copyright (c) 2025 FingerprintJS, Inc
7118 *
7119 * Permission is hereby granted, free of charge, to any person obtaining a copy
7120 * of this software and associated documentation files (the "Software"), to deal
7121 * in the Software without restriction, including without limitation the rights
7122 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7123 * copies of the Software, and to permit persons to whom the Software is
7124 * furnished to do so, subject to the following conditions:
7125 *
7126 * The above copyright notice and this permission notice shall be included in all
7127 * copies or substantial portions of the Software.
7128 *
7129 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
7130 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7131 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
7132 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7133 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
7134 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
7135 * SOFTWARE.
7136 */
7137
7138var FingerprintJS = (function (exports) {
7139  'use strict';
7140
7141  var version = "5.1.0";
7142
7143  function wait(durationMs, resolveWith) {
7144      return new Promise((resolve) => setTimeout(resolve, durationMs, resolveWith));
7145  }
7146  /**
7147   * Allows asynchronous actions and microtasks to happen.
7148   */
7149  function releaseEventLoop() {
7150      // Don't use setTimeout because Chrome throttles it in some cases causing very long agent execution:
7151      // https://stackoverflow.com/a/6032591/1118709
7152      // https://github.com/chromium/chromium/commit/0295dd09496330f3a9103ef7e543fa9b6050409b
7153      // Reusing a MessageChannel object gives no noticeable benefits
7154      return new Promise((resolve) => {
7155          const channel = new MessageChannel();
7156          channel.port1.onmessage = () => resolve();
7157          channel.port2.postMessage(null);
7158      });
7159  }
7160  function requestIdleCallbackIfAvailable(fallbackTimeout, deadlineTimeout = Infinity) {
7161      const { requestIdleCallback } = window;
7162      if (requestIdleCallback) {
7163          // The function `requestIdleCallback` loses the binding to `window` here.
7164          // `globalThis` isn't always equal `window` (see https://github.com/fingerprintjs/fingerprintjs/issues/683).
7165          // Therefore, an error can occur. `call(window,` prevents the error.
7166          return new Promise((resolve) => requestIdleCallback.call(window, () => resolve(), { timeout: deadlineTimeout }));
7167      }
7168      else {
7169          return wait(Math.min(fallbackTimeout, deadlineTimeout));
7170      }
7171  }
7172  function isPromise(value) {
7173      return !!value && typeof value.then === 'function';
7174  }
7175  /**
7176   * Calls a maybe asynchronous function without creating microtasks when the function is synchronous.
7177   * Catches errors in both cases.
7178   *
7179   * If just you run a code like this:
7180   * ```
7181   * console.time('Action duration')
7182   * await action()
7183   * console.timeEnd('Action duration')
7184   * ```
7185   * The synchronous function time can be measured incorrectly because another microtask may run before the `await`
7186   * returns the control back to the code.
7187   */
7188  function awaitIfAsync(action, callback) {
7189      try {
7190          const returnedValue = action();
7191          if (isPromise(returnedValue)) {
7192              returnedValue.then((result) => callback(true, result), (error) => callback(false, error));
7193          }
7194          else {
7195              callback(true, returnedValue);
7196          }
7197      }
7198      catch (error) {
7199          callback(false, error);
7200      }
7201  }
7202  /**
7203   * If you run many synchronous tasks without using this function, the JS main loop will be busy and asynchronous tasks
7204   * (e.g. completing a network request, rendering the page) won't be able to happen.
7205   * This function allows running many synchronous tasks such way that asynchronous tasks can run too in background.
7206   */
7207  async function mapWithBreaks(items, callback, loopReleaseInterval = 16) {
7208      const results = Array(items.length);
7209      let lastLoopReleaseTime = Date.now();
7210      for (let i = 0; i < items.length; ++i) {
7211          results[i] = callback(items[i], i);
7212          const now = Date.now();
7213          if (now >= lastLoopReleaseTime + loopReleaseInterval) {
vendor: 72,509 bytes, lines 7214-8982
7214              lastLoopReleaseTime = now;
7215              await releaseEventLoop();
7216          }
7217      }
7218      return results;
7219  }
7220  /**
7221   * Makes the given promise never emit an unhandled promise rejection console warning.
7222   * The promise will still pass errors to the next promises.
7223   * Returns the input promise for convenience.
7224   *
7225   * Otherwise, promise emits a console warning unless it has a `catch` listener.
7226   */
7227  function suppressUnhandledRejectionWarning(promise) {
7228      promise.then(undefined, () => undefined);
7229      return promise;
7230  }
7231
7232  /*
7233   * This file contains functions to work with pure data only (no browser features, DOM, side effects, etc).
7234   */
7235  /**
7236   * Does the same as Array.prototype.includes but has better typing
7237   */
7238  function includes(haystack, needle) {
7239      for (let i = 0, l = haystack.length; i < l; ++i) {
7240          if (haystack[i] === needle) {
7241              return true;
7242          }
7243      }
7244      return false;
7245  }
7246  /**
7247   * Like `!includes()` but with proper typing
7248   */
7249  function excludes(haystack, needle) {
7250      return !includes(haystack, needle);
7251  }
7252  /**
7253   * Be careful, NaN can return
7254   */
7255  function toInt(value) {
7256      return parseInt(value);
7257  }
7258  /**
7259   * Be careful, NaN can return
7260   */
7261  function toFloat(value) {
7262      return parseFloat(value);
7263  }
7264  function replaceNaN(value, replacement) {
7265      return typeof value === 'number' && isNaN(value) ? replacement : value;
7266  }
7267  function countTruthy(values) {
7268      return values.reduce((sum, value) => sum + (value ? 1 : 0), 0);
7269  }
7270  function round(value, base = 1) {
7271      if (Math.abs(base) >= 1) {
7272          return Math.round(value / base) * base;
7273      }
7274      else {
7275          // Sometimes when a number is multiplied by a small number, precision is lost,
7276          // for example 1234 * 0.0001 === 0.12340000000000001, and it's more precise divide: 1234 / (1 / 0.0001) === 0.1234.
7277          const counterBase = 1 / base;
7278          return Math.round(value * counterBase) / counterBase;
7279      }
7280  }
7281  /**
7282   * Parses a CSS selector into tag name with HTML attributes.
7283   * Only single element selector are supported (without operators like space, +, >, etc).
7284   *
7285   * Multiple values can be returned for each attribute. You decide how to handle them.
7286   */
7287  function parseSimpleCssSelector(selector) {
7288      var _a, _b;
7289      const errorMessage = `Unexpected syntax '${selector}'`;
7290      const tagMatch = /^\s*([a-z-]*)(.*)$/i.exec(selector);
7291      const tag = tagMatch[1] || undefined;
7292      const attributes = {};
7293      const partsRegex = /([.:#][\w-]+|\[.+?\])/gi;
7294      const addAttribute = (name, value) => {
7295          attributes[name] = attributes[name] || [];
7296          attributes[name].push(value);
7297      };
7298      for (;;) {
7299          const match = partsRegex.exec(tagMatch[2]);
7300          if (!match) {
7301              break;
7302          }
7303          const part = match[0];
7304          switch (part[0]) {
7305              case '.':
7306                  addAttribute('class', part.slice(1));
7307                  break;
7308              case '#':
7309                  addAttribute('id', part.slice(1));
7310                  break;
7311              case '[': {
7312                  const attributeMatch = /^\[([\w-]+)([~|^$*]?=("(.*?)"|([\w-]+)))?(\s+[is])?\]$/.exec(part);
7313                  if (attributeMatch) {
7314                      addAttribute(attributeMatch[1], (_b = (_a = attributeMatch[4]) !== null && _a !== void 0 ? _a : attributeMatch[5]) !== null && _b !== void 0 ? _b : '');
7315                  }
7316                  else {
7317                      throw new Error(errorMessage);
7318                  }
7319                  break;
7320              }
7321              default:
7322                  throw new Error(errorMessage);
7323          }
7324      }
7325      return [tag, attributes];
7326  }
7327  /**
7328   * Converts a string to UTF8 bytes
7329   */
7330  function getUTF8Bytes(input) {
7331      // Benchmark: https://jsbench.me/b6klaaxgwq/1
7332      // If you want to just count bytes, see solutions at https://jsbench.me/ehklab415e/1
7333      const result = new Uint8Array(input.length);
7334      for (let i = 0; i < input.length; i++) {
7335          // `charCode` is faster than encoding, so we prefer that when it's possible
7336          const charCode = input.charCodeAt(i);
7337          // In case of non-ASCII symbols we use proper encoding
7338          if (charCode > 127) {
7339              return new TextEncoder().encode(input);
7340          }
7341          result[i] = charCode;
7342      }
7343      return result;
7344  }
7345
7346  /*
7347   * Based on https://github.com/karanlyons/murmurHash3.js/blob/a33d0723127e2e5415056c455f8aed2451ace208/murmurHash3.js
7348   */
7349  /**
7350   * Adds two 64-bit values (provided as tuples of 32-bit values)
7351   * and updates (mutates) first value to write the result
7352   */
7353  function x64Add(m, n) {
7354      const m0 = m[0] >>> 16, m1 = m[0] & 0xffff, m2 = m[1] >>> 16, m3 = m[1] & 0xffff;
7355      const n0 = n[0] >>> 16, n1 = n[0] & 0xffff, n2 = n[1] >>> 16, n3 = n[1] & 0xffff;
7356      let o0 = 0, o1 = 0, o2 = 0, o3 = 0;
7357      o3 += m3 + n3;
7358      o2 += o3 >>> 16;
7359      o3 &= 0xffff;
7360      o2 += m2 + n2;
7361      o1 += o2 >>> 16;
7362      o2 &= 0xffff;
7363      o1 += m1 + n1;
7364      o0 += o1 >>> 16;
7365      o1 &= 0xffff;
7366      o0 += m0 + n0;
7367      o0 &= 0xffff;
7368      m[0] = (o0 << 16) | o1;
7369      m[1] = (o2 << 16) | o3;
7370  }
7371  /**
7372   * Multiplies two 64-bit values (provided as tuples of 32-bit values)
7373   * and updates (mutates) first value to write the result
7374   */
7375  function x64Multiply(m, n) {
7376      const m0 = m[0] >>> 16, m1 = m[0] & 0xffff, m2 = m[1] >>> 16, m3 = m[1] & 0xffff;
7377      const n0 = n[0] >>> 16, n1 = n[0] & 0xffff, n2 = n[1] >>> 16, n3 = n[1] & 0xffff;
7378      let o0 = 0, o1 = 0, o2 = 0, o3 = 0;
7379      o3 += m3 * n3;
7380      o2 += o3 >>> 16;
7381      o3 &= 0xffff;
7382      o2 += m2 * n3;
7383      o1 += o2 >>> 16;
7384      o2 &= 0xffff;
7385      o2 += m3 * n2;
7386      o1 += o2 >>> 16;
7387      o2 &= 0xffff;
7388      o1 += m1 * n3;
7389      o0 += o1 >>> 16;
7390      o1 &= 0xffff;
7391      o1 += m2 * n2;
7392      o0 += o1 >>> 16;
7393      o1 &= 0xffff;
7394      o1 += m3 * n1;
7395      o0 += o1 >>> 16;
7396      o1 &= 0xffff;
7397      o0 += m0 * n3 + m1 * n2 + m2 * n1 + m3 * n0;
7398      o0 &= 0xffff;
7399      m[0] = (o0 << 16) | o1;
7400      m[1] = (o2 << 16) | o3;
7401  }
7402  /**
7403   * Provides left rotation of the given int64 value (provided as tuple of two int32)
7404   * by given number of bits. Result is written back to the value
7405   */
7406  function x64Rotl(m, bits) {
7407      const m0 = m[0];
7408      bits %= 64;
7409      if (bits === 32) {
7410          m[0] = m[1];
7411          m[1] = m0;
7412      }
7413      else if (bits < 32) {
7414          m[0] = (m0 << bits) | (m[1] >>> (32 - bits));
7415          m[1] = (m[1] << bits) | (m0 >>> (32 - bits));
7416      }
7417      else {
7418          bits -= 32;
7419          m[0] = (m[1] << bits) | (m0 >>> (32 - bits));
7420          m[1] = (m0 << bits) | (m[1] >>> (32 - bits));
7421      }
7422  }
7423  /**
7424   * Provides a left shift of the given int32 value (provided as tuple of [0, int32])
7425   * by given number of bits. Result is written back to the value
7426   */
7427  function x64LeftShift(m, bits) {
7428      bits %= 64;
7429      if (bits === 0) {
7430          return;
7431      }
7432      else if (bits < 32) {
7433          m[0] = m[1] >>> (32 - bits);
7434          m[1] = m[1] << bits;
7435      }
7436      else {
7437          m[0] = m[1] << (bits - 32);
7438          m[1] = 0;
7439      }
7440  }
7441  /**
7442   * Provides a XOR of the given int64 values(provided as tuple of two int32).
7443   * Result is written back to the first value
7444   */
7445  function x64Xor(m, n) {
7446      m[0] ^= n[0];
7447      m[1] ^= n[1];
7448  }
7449  const F1 = [0xff51afd7, 0xed558ccd];
7450  const F2 = [0xc4ceb9fe, 0x1a85ec53];
7451  /**
7452   * Calculates murmurHash3's final x64 mix of that block and writes result back to the input value.
7453   * (`[0, h[0] >>> 1]` is a 33 bit unsigned right shift. This is the
7454   * only place where we need to right shift 64bit ints.)
7455   */
7456  function x64Fmix(h) {
7457      const shifted = [0, h[0] >>> 1];
7458      x64Xor(h, shifted);
7459      x64Multiply(h, F1);
7460      shifted[1] = h[0] >>> 1;
7461      x64Xor(h, shifted);
7462      x64Multiply(h, F2);
7463      shifted[1] = h[0] >>> 1;
7464      x64Xor(h, shifted);
7465  }
7466  const C1 = [0x87c37b91, 0x114253d5];
7467  const C2 = [0x4cf5ad43, 0x2745937f];
7468  const M$1 = [0, 5];
7469  const N1 = [0, 0x52dce729];
7470  const N2 = [0, 0x38495ab5];
7471  /**
7472   * Given a string and an optional seed as an int, returns a 128 bit
7473   * hash using the x64 flavor of MurmurHash3, as an unsigned hex.
7474   * All internal functions mutates passed value to achieve minimal memory allocations and GC load
7475   *
7476   * Benchmark https://jsbench.me/p4lkpaoabi/1
7477   */
7478  function x64hash128(input, seed) {
7479      const key = getUTF8Bytes(input);
7480      seed = seed || 0;
7481      const length = [0, key.length];
7482      const remainder = length[1] % 16;
7483      const bytes = length[1] - remainder;
7484      const h1 = [0, seed];
7485      const h2 = [0, seed];
7486      const k1 = [0, 0];
7487      const k2 = [0, 0];
7488      let i;
7489      for (i = 0; i < bytes; i = i + 16) {
7490          k1[0] = key[i + 4] | (key[i + 5] << 8) | (key[i + 6] << 16) | (key[i + 7] << 24);
7491          k1[1] = key[i] | (key[i + 1] << 8) | (key[i + 2] << 16) | (key[i + 3] << 24);
7492          k2[0] = key[i + 12] | (key[i + 13] << 8) | (key[i + 14] << 16) | (key[i + 15] << 24);
7493          k2[1] = key[i + 8] | (key[i + 9] << 8) | (key[i + 10] << 16) | (key[i + 11] << 24);
7494          x64Multiply(k1, C1);
7495          x64Rotl(k1, 31);
7496          x64Multiply(k1, C2);
7497          x64Xor(h1, k1);
7498          x64Rotl(h1, 27);
7499          x64Add(h1, h2);
7500          x64Multiply(h1, M$1);
7501          x64Add(h1, N1);
7502          x64Multiply(k2, C2);
7503          x64Rotl(k2, 33);
7504          x64Multiply(k2, C1);
7505          x64Xor(h2, k2);
7506          x64Rotl(h2, 31);
7507          x64Add(h2, h1);
7508          x64Multiply(h2, M$1);
7509          x64Add(h2, N2);
7510      }
7511      k1[0] = 0;
7512      k1[1] = 0;
7513      k2[0] = 0;
7514      k2[1] = 0;
7515      const val = [0, 0];
7516      switch (remainder) {
7517          case 15:
7518              val[1] = key[i + 14];
7519              x64LeftShift(val, 48);
7520              x64Xor(k2, val);
7521          // fallthrough
7522          case 14:
7523              val[1] = key[i + 13];
7524              x64LeftShift(val, 40);
7525              x64Xor(k2, val);
7526          // fallthrough
7527          case 13:
7528              val[1] = key[i + 12];
7529              x64LeftShift(val, 32);
7530              x64Xor(k2, val);
7531          // fallthrough
7532          case 12:
7533              val[1] = key[i + 11];
7534              x64LeftShift(val, 24);
7535              x64Xor(k2, val);
7536          // fallthrough
7537          case 11:
7538              val[1] = key[i + 10];
7539              x64LeftShift(val, 16);
7540              x64Xor(k2, val);
7541          // fallthrough
7542          case 10:
7543              val[1] = key[i + 9];
7544              x64LeftShift(val, 8);
7545              x64Xor(k2, val);
7546          // fallthrough
7547          case 9:
7548              val[1] = key[i + 8];
7549              x64Xor(k2, val);
7550              x64Multiply(k2, C2);
7551              x64Rotl(k2, 33);
7552              x64Multiply(k2, C1);
7553              x64Xor(h2, k2);
7554          // fallthrough
7555          case 8:
7556              val[1] = key[i + 7];
7557              x64LeftShift(val, 56);
7558              x64Xor(k1, val);
7559          // fallthrough
7560          case 7:
7561              val[1] = key[i + 6];
7562              x64LeftShift(val, 48);
7563              x64Xor(k1, val);
7564          // fallthrough
7565          case 6:
7566              val[1] = key[i + 5];
7567              x64LeftShift(val, 40);
7568              x64Xor(k1, val);
7569          // fallthrough
7570          case 5:
7571              val[1] = key[i + 4];
7572              x64LeftShift(val, 32);
7573              x64Xor(k1, val);
7574          // fallthrough
7575          case 4:
7576              val[1] = key[i + 3];
7577              x64LeftShift(val, 24);
7578              x64Xor(k1, val);
7579          // fallthrough
7580          case 3:
7581              val[1] = key[i + 2];
7582              x64LeftShift(val, 16);
7583              x64Xor(k1, val);
7584          // fallthrough
7585          case 2:
7586              val[1] = key[i + 1];
7587              x64LeftShift(val, 8);
7588              x64Xor(k1, val);
7589          // fallthrough
7590          case 1:
7591              val[1] = key[i];
7592              x64Xor(k1, val);
7593              x64Multiply(k1, C1);
7594              x64Rotl(k1, 31);
7595              x64Multiply(k1, C2);
7596              x64Xor(h1, k1);
7597          // fallthrough
7598      }
7599      x64Xor(h1, length);
7600      x64Xor(h2, length);
7601      x64Add(h1, h2);
7602      x64Add(h2, h1);
7603      x64Fmix(h1);
7604      x64Fmix(h2);
7605      x64Add(h1, h2);
7606      x64Add(h2, h1);
7607      return (('00000000' + (h1[0] >>> 0).toString(16)).slice(-8) +
7608          ('00000000' + (h1[1] >>> 0).toString(16)).slice(-8) +
7609          ('00000000' + (h2[0] >>> 0).toString(16)).slice(-8) +
7610          ('00000000' + (h2[1] >>> 0).toString(16)).slice(-8));
7611  }
7612
7613  /**
7614   * Converts an error object to a plain object that can be used with `JSON.stringify`.
7615   * If you just run `JSON.stringify(error)`, you'll get `'{}'`.
7616   */
7617  function errorToObject(error) {
7618      var _a;
7619      return {
7620          name: error.name,
7621          message: error.message,
7622          stack: (_a = error.stack) === null || _a === void 0 ? void 0 : _a.split('\n'),
7623          // The fields are not enumerable, so TS is wrong saying that they will be overridden
7624          ...error,
7625      };
7626  }
7627  function isFunctionNative(func) {
7628      return /^function\s.*?\{\s*\[native code]\s*}$/.test(String(func));
7629  }
7630
7631  /* eslint-disable @typescript-eslint/no-explicit-any */
7632  function isFinalResultLoaded(loadResult) {
7633      return typeof loadResult !== 'function';
7634  }
7635  /**
7636   * Loads the given entropy source. Returns a function that gets an entropy component from the source.
7637   *
7638   * The result is returned synchronously to prevent `loadSources` from
7639   * waiting for one source to load before getting the components from the other sources.
7640   */
7641  function loadSource(source, sourceOptions) {
7642      const sourceLoadPromise = suppressUnhandledRejectionWarning(new Promise((resolveLoad) => {
7643          const loadStartTime = Date.now();
7644          // `awaitIfAsync` is used instead of just `await` in order to measure the duration of synchronous sources
7645          // correctly (other microtasks won't affect the duration).
7646          awaitIfAsync(source.bind(null, sourceOptions), (...loadArgs) => {
7647              const loadDuration = Date.now() - loadStartTime;
7648              // Source loading failed
7649              if (!loadArgs[0]) {
7650                  return resolveLoad(() => ({ error: loadArgs[1], duration: loadDuration }));
7651              }
7652              const loadResult = loadArgs[1];
7653              // Source loaded with the final result
7654              if (isFinalResultLoaded(loadResult)) {
7655                  return resolveLoad(() => ({ value: loadResult, duration: loadDuration }));
7656              }
7657              // Source loaded with "get" stage
7658              resolveLoad(() => new Promise((resolveGet) => {
7659                  const getStartTime = Date.now();
7660                  awaitIfAsync(loadResult, (...getArgs) => {
7661                      const duration = loadDuration + Date.now() - getStartTime;
7662                      // Source getting failed
7663                      if (!getArgs[0]) {
7664                          return resolveGet({ error: getArgs[1], duration });
7665                      }
7666                      // Source getting succeeded
7667                      resolveGet({ value: getArgs[1], duration });
7668                  });
7669              }));
7670          });
7671      }));
7672      return function getComponent() {
7673          return sourceLoadPromise.then((finalizeSource) => finalizeSource());
7674      };
7675  }
7676  /**
7677   * Loads the given entropy sources. Returns a function that collects the entropy components.
7678   *
7679   * The result is returned synchronously in order to allow start getting the components
7680   * before the sources are loaded completely.
7681   *
7682   * Warning for package users:
7683   * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk.
7684   */
7685  function loadSources(sources, sourceOptions, excludeSources, loopReleaseInterval) {
7686      const includedSources = Object.keys(sources).filter((sourceKey) => excludes(excludeSources, sourceKey));
7687      // Using `mapWithBreaks` allows asynchronous sources to complete between synchronous sources
7688      // and measure the duration correctly
7689      const sourceGettersPromise = suppressUnhandledRejectionWarning(mapWithBreaks(includedSources, (sourceKey) => loadSource(sources[sourceKey], sourceOptions), loopReleaseInterval));
7690      return async function getComponents() {
7691          const sourceGetters = await sourceGettersPromise;
7692          const componentPromises = await mapWithBreaks(sourceGetters, (sourceGetter) => suppressUnhandledRejectionWarning(sourceGetter()), loopReleaseInterval);
7693          const componentArray = await Promise.all(componentPromises);
7694          // Keeping the component keys order the same as the source keys order
7695          const components = {};
7696          for (let index = 0; index < includedSources.length; ++index) {
7697              components[includedSources[index]] = componentArray[index];
7698          }
7699          return components;
7700      };
7701  }
7702  /**
7703   * Modifies an entropy source by transforming its returned value with the given function.
7704   * Keeps the source properties: sync/async, 1/2 stages.
7705   *
7706   * Warning for package users:
7707   * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk.
7708   */
7709  function transformSource(source, transformValue) {
7710      const transformLoadResult = (loadResult) => {
7711          if (isFinalResultLoaded(loadResult)) {
7712              return transformValue(loadResult);
7713          }
7714          return () => {
7715              const getResult = loadResult();
7716              if (isPromise(getResult)) {
7717                  return getResult.then(transformValue);
7718              }
7719              return transformValue(getResult);
7720          };
7721      };
7722      return (options) => {
7723          const loadResult = source(options);
7724          if (isPromise(loadResult)) {
7725              return loadResult.then(transformLoadResult);
7726          }
7727          return transformLoadResult(loadResult);
7728      };
7729  }
7730
7731  /*
7732   * Functions to help with features that vary through browsers
7733   */
7734  /**
7735   * Checks whether the browser is based on Trident (the Internet Explorer engine) without using user-agent.
7736   *
7737   * Warning for package users:
7738   * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk.
7739   */
7740  function isTrident() {
7741      const w = window;
7742      const n = navigator;
7743      // The properties are checked to be in IE 10, IE 11 and not to be in other browsers in October 2020
7744      return (countTruthy([
7745          'MSCSSMatrix' in w,
7746          'msSetImmediate' in w,
7747          'msIndexedDB' in w,
7748          'msMaxTouchPoints' in n,
7749          'msPointerEnabled' in n,
7750      ]) >= 4);
7751  }
7752  /**
7753   * Checks whether the browser is based on EdgeHTML (the pre-Chromium Edge engine) without using user-agent.
7754   *
7755   * Warning for package users:
7756   * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk.
7757   */
7758  function isEdgeHTML() {
7759      // Based on research in October 2020
7760      const w = window;
7761      const n = navigator;
7762      return (countTruthy(['msWriteProfilerMark' in w, 'MSStream' in w, 'msLaunchUri' in n, 'msSaveBlob' in n]) >= 3 &&
7763          !isTrident());
7764  }
7765  /**
7766   * Checks whether the browser is based on Chromium without using user-agent.
7767   *
7768   * Warning for package users:
7769   * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk.
7770   */
7771  function isChromium() {
7772      // Based on research in October 2020. Tested to detect Chromium 42-86.
7773      const w = window;
7774      const n = navigator;
7775      return (countTruthy([
7776          'webkitPersistentStorage' in n,
7777          'webkitTemporaryStorage' in n,
7778          (n.vendor || '').indexOf('Google') === 0,
7779          'webkitResolveLocalFileSystemURL' in w,
7780          'BatteryManager' in w,
7781          'webkitMediaStream' in w,
7782          'webkitSpeechGrammar' in w,
7783      ]) >= 5);
7784  }
7785  /**
7786   * Checks whether the browser is based on mobile or desktop Safari without using user-agent.
7787   * All iOS browsers use WebKit (the Safari engine).
7788   *
7789   * Warning for package users:
7790   * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk.
7791   */
7792  function isWebKit() {
7793      // Based on research in August 2024
7794      const w = window;
7795      const n = navigator;
7796      return (countTruthy([
7797          'ApplePayError' in w,
7798          'CSSPrimitiveValue' in w,
7799          'Counter' in w,
7800          n.vendor.indexOf('Apple') === 0,
7801          'RGBColor' in w,
7802          'WebKitMediaKeys' in w,
7803      ]) >= 4);
7804  }
7805  /**
7806   * Checks whether this WebKit browser is a desktop browser.
7807   * It doesn't check that the browser is based on WebKit, there is a separate function for this.
7808   *
7809   * Warning for package users:
7810   * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk.
7811   */
7812  function isDesktopWebKit() {
7813      // Checked in Safari and DuckDuckGo
7814      const w = window;
7815      const { HTMLElement, Document } = w;
7816      return (countTruthy([
7817          'safari' in w,
7818          !('ongestureend' in w),
7819          !('TouchEvent' in w),
7820          !('orientation' in w),
7821          HTMLElement && !('autocapitalize' in HTMLElement.prototype),
7822          Document && 'pointerLockElement' in Document.prototype,
7823      ]) >= 4);
7824  }
7825  /**
7826   * Checks whether this WebKit browser is Safari.
7827   * It doesn't check that the browser is based on WebKit, there is a separate function for this.
7828   *
7829   * Warning! The function works properly only for Safari version 15.4 and newer.
7830   */
7831  function isSafariWebKit() {
7832      // Checked in Safari, Chrome, Firefox, Yandex, UC Browser, Opera, Edge and DuckDuckGo.
7833      // iOS Safari and Chrome were checked on iOS 11-18. DuckDuckGo was checked on iOS 17-18 and macOS 14-15.
7834      // Desktop Safari versions 12-18 were checked.
7835      // The other browsers were checked on iOS 17 and 18; there was no chance to check them on the other OS versions.
7836      const w = window;
7837      return (
7838      // Filters-out Chrome, Yandex, DuckDuckGo (macOS and iOS), Edge
7839      isFunctionNative(w.print) &&
7840          // Doesn't work in Safari < 15.4
7841          String(w.browser) === '[object WebPageNamespace]');
7842  }
7843  /**
7844   * Checks whether the browser is based on Gecko (Firefox engine) without using user-agent.
7845   *
7846   * Warning for package users:
7847   * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk.
7848   */
7849  function isGecko() {
7850      var _a, _b;
7851      const w = window;
7852      // Based on research in September 2020
7853      return (countTruthy([
7854          'buildID' in navigator,
7855          'MozAppearance' in ((_b = (_a = document.documentElement) === null || _a === void 0 ? void 0 : _a.style) !== null && _b !== void 0 ? _b : {}),
7856          'onmozfullscreenchange' in w,
7857          'mozInnerScreenX' in w,
7858          'CSSMozDocumentRule' in w,
7859          'CanvasCaptureMediaStream' in w,
7860      ]) >= 4);
7861  }
7862  /**
7863   * Checks whether the browser is based on Gecko version ≥120 (Firefox ≥120) without using user-agent.
7864   * It doesn't check that the browser is based on Gecko; there is a separate function for this.
7865   *
7866   * @see https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/120 Firefox 120 release notes
7867   */
7868  function isGecko120OrNewer() {
7869      // Checked in Firefox 119 vs. Firefox 120+
7870      const w = window;
7871      const n = navigator;
7872      const { CSS } = w;
7873      // We use a threshold of 3 out of 4 because `globalPrivacyControl` was added in Firefox 120 on desktop,
7874      // but only in Firefox 122 on mobile. Using >= 3 ensures detection works on both platforms for Firefox 120+.
7875      // @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/globalPrivacyControl#browser_compatibility
7876      return (countTruthy([
7877          // User Activation API - added in Firefox 120
7878          'userActivation' in n,
7879          // CSS light-dark() function - added in Firefox 120
7880          CSS.supports('color', 'light-dark(#000, #fff)'),
7881          // CSS lh unit - added in Firefox 120
7882          CSS.supports('height', '1lh'),
7883          // Global Privacy Control - added in Firefox 120 (desktop) / Firefox 122 (mobile)
7884          'globalPrivacyControl' in n,
7885      ]) >= 3);
7886  }
7887  /**
7888   * Checks whether the browser is based on Gecko version ≥143 (Firefox ≥143) without using user-agent.
7889   * It doesn't check that the browser is based on Gecko; there is a separate function for this.
7890   *
7891   * Firefox 143 shipped Phase 2 fingerprinting protections (screen resolution, processor count, touch points)
7892   * in Private Browsing and ETP Strict mode.
7893   *
7894   * @see https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/143 Firefox 143 release notes
7895   * @see https://bugzilla.mozilla.org/show_bug.cgi?id=1978414 Bug that shipped these protections
7896   */
7897  function isGecko143OrNewer() {
7898      // Checked in Firefox 142 vs. Firefox 143+
7899      const { CSS } = window;
7900      return (countTruthy([
7901          // CSS ::details-content pseudo-element - added in Firefox 143
7902          CSS.supports('selector(::details-content)'),
7903          // CSS ::before::marker nested pseudo-element - added in Firefox 143
7904          CSS.supports('selector(::before::marker)'),
7905          // CSS ::after::marker nested pseudo-element - added in Firefox 143
7906          CSS.supports('selector(::after::marker)'),
7907          // CompositionEvent.locale property - removed in Firefox 143 (Bug 1700969)
7908          !('locale' in CompositionEvent.prototype),
7909      ]) >= 3);
7910  }
7911  /**
7912   * Checks whether the browser is based on Chromium version ≥86 without using user-agent.
7913   * It doesn't check that the browser is based on Chromium, there is a separate function for this.
7914   */
7915  function isChromium86OrNewer() {
7916      // Checked in Chrome 85 vs Chrome 86 both on desktop and Android. Checked in macOS Chrome 128, Android Chrome 127.
7917      const w = window;
7918      return (countTruthy([
7919          !('MediaSettingsRange' in w),
7920          'RTCEncodedAudioFrame' in w,
7921          '' + w.Intl === '[object Intl]',
7922          '' + w.Reflect === '[object Reflect]',
7923      ]) >= 3);
7924  }
7925  /**
7926   * Checks whether the browser is based on Chromium version ≥122 without using user-agent.
7927   * It doesn't check that the browser is based on Chromium, there is a separate function for this.
7928   */
7929  function isChromium122OrNewer() {
7930      // Checked in Chrome 121 vs Chrome 122 and 129 both on desktop and Android
7931      const w = window;
7932      const { URLPattern } = w;
7933      return (countTruthy([
7934          'union' in Set.prototype,
7935          'Iterator' in w,
7936          URLPattern && 'hasRegExpGroups' in URLPattern.prototype,
7937          'RGB8' in WebGLRenderingContext.prototype,
7938      ]) >= 3);
7939  }
7940  /**
7941   * Checks whether the browser is based on WebKit version ≥606 (Safari ≥12) without using user-agent.
7942   * It doesn't check that the browser is based on WebKit, there is a separate function for this.
7943   *
7944   * @see https://en.wikipedia.org/wiki/Safari_version_history#Release_history Safari-WebKit versions map
7945   */
7946  function isWebKit606OrNewer() {
7947      // Checked in Safari 9–18
7948      const w = window;
7949      return (countTruthy([
7950          'DOMRectList' in w,
7951          'RTCPeerConnectionIceEvent' in w,
7952          'SVGGeometryElement' in w,
7953          'ontransitioncancel' in w,
7954      ]) >= 3);
7955  }
7956  /**
7957   * Checks whether the browser is based on WebKit version ≥616 (Safari ≥17) without using user-agent.
7958   * It doesn't check that the browser is based on WebKit, there is a separate function for this.
7959   *
7960   * @see https://developer.apple.com/documentation/safari-release-notes/safari-17-release-notes Safari 17 release notes
7961   * @see https://tauri.app/v1/references/webview-versions/#webkit-versions-in-safari Safari-WebKit versions map
7962   */
7963  function isWebKit616OrNewer() {
7964      const w = window;
7965      const n = navigator;
7966      const { CSS, HTMLButtonElement } = w;
7967      return (countTruthy([
7968          !('getStorageUpdates' in n),
7969          HTMLButtonElement && 'popover' in HTMLButtonElement.prototype,
7970          'CSSCounterStyleRule' in w,
7971          CSS.supports('font-size-adjust: ex-height 0.5'),
7972          CSS.supports('text-transform: full-width'),
7973      ]) >= 4);
7974  }
7975  /**
7976   * Checks whether the device is an iPad.
7977   * It doesn't check that the engine is WebKit and that the WebKit isn't desktop.
7978   */
7979  function isIPad() {
7980      // Checked on:
7981      // Safari on iPadOS (both mobile and desktop modes): 8, 11-18
7982      // Chrome on iPadOS (both mobile and desktop modes): 11-18
7983      // Safari on iOS (both mobile and desktop modes): 9-18
7984      // Chrome on iOS (both mobile and desktop modes): 9-18
7985      // Before iOS 13. Safari tampers the value in "request desktop site" mode since iOS 13.
7986      if (navigator.platform === 'iPad') {
7987          return true;
7988      }
7989      const s = screen;
7990      const screenRatio = s.width / s.height;
7991      return (countTruthy([
7992          // Since iOS 13. Doesn't work in Chrome on iPadOS <15, but works in desktop mode.
7993          'MediaSource' in window,
7994          // Since iOS 12. Doesn't work in Chrome on iPadOS.
7995          !!Element.prototype.webkitRequestFullscreen,
7996          // iPhone 4S that runs iOS 9 matches this, but it is not supported
7997          // Doesn't work in incognito mode of Safari ≥17 with split screen because of tracking prevention
7998          screenRatio > 0.65 && screenRatio < 1.53,
7999      ]) >= 2);
8000  }
8001  /**
8002   * Warning for package users:
8003   * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk.
8004   */
8005  function getFullscreenElement() {
8006      const d = document;
8007      return d.fullscreenElement || d.msFullscreenElement || d.mozFullScreenElement || d.webkitFullscreenElement || null;
8008  }
8009  function exitFullscreen() {
8010      const d = document;
8011      // `call` is required because the function throws an error without a proper "this" context
8012      return (d.exitFullscreen || d.msExitFullscreen || d.mozCancelFullScreen || d.webkitExitFullscreen).call(d);
8013  }
8014  /**
8015   * Checks whether the device runs on Android without using user-agent.
8016   *
8017   * Warning for package users:
8018   * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk.
8019   */
8020  function isAndroid() {
8021      const isItChromium = isChromium();
8022      const isItGecko = isGecko();
8023      const w = window;
8024      const n = navigator;
8025      const c = 'connection';
8026      // Chrome removes all words "Android" from `navigator` when desktop version is requested
8027      // Firefox keeps "Android" in `navigator.appVersion` when desktop version is requested
8028      if (isItChromium) {
8029          return (countTruthy([
8030              !('SharedWorker' in w),
8031              // `typechange` is deprecated, but it's still present on Android (tested on Chrome Mobile 117)
8032              // Removal proposal https://bugs.chromium.org/p/chromium/issues/detail?id=699892
8033              // Note: this expression returns true on ChromeOS, so additional detectors are required to avoid false-positives
8034              n[c] && 'ontypechange' in n[c],
8035              !('sinkId' in new Audio()),
8036          ]) >= 2);
8037      }
8038      else if (isItGecko) {
8039          return countTruthy(['onorientationchange' in w, 'orientation' in w, /android/i.test(n.appVersion)]) >= 2;
8040      }
8041      else {
8042          // Only 2 browser engines are presented on Android.
8043          // Actually, there is also Android 4.1 browser, but it's not worth detecting it at the moment.
8044          return false;
8045      }
8046  }
8047  /**
8048   * Checks whether the browser is Samsung Internet without using user-agent.
8049   * It doesn't check that the browser is based on Chromium, please use `isChromium` before using this function.
8050   *
8051   * Warning for package users:
8052   * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk.
8053   */
8054  function isSamsungInternet() {
8055      // Checked in Samsung Internet 21, 25 and 27
8056      const n = navigator;
8057      const w = window;
8058      const audioPrototype = Audio.prototype;
8059      const { visualViewport } = w;
8060      return (countTruthy([
8061          'srLatency' in audioPrototype,
8062          'srChannelCount' in audioPrototype,
8063          'devicePosture' in n,
8064          visualViewport && 'segments' in visualViewport,
8065          'getTextInformation' in Image.prototype, // Not available in Samsung Internet 21
8066      ]) >= 3);
8067  }
8068
8069  /**
8070   * A deep description: https://fingerprint.com/blog/audio-fingerprinting/
8071   * Inspired by and based on https://github.com/cozylife/audio-fingerprint
8072   *
8073   * A version of the entropy source with stabilization to make it suitable for static fingerprinting.
8074   * Audio signal is noised in private mode of Safari 17, so audio fingerprinting is skipped in Safari 17.
8075   */
8076  function getAudioFingerprint() {
8077      if (doesBrowserPerformAntifingerprinting()) {
8078          return -4 /* SpecialFingerprint.KnownForAntifingerprinting */;
8079      }
8080      return getUnstableAudioFingerprint();
8081  }
8082  /**
8083   * A version of the entropy source without stabilization.
8084   *
8085   * Warning for package users:
8086   * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk.
8087   */
8088  function getUnstableAudioFingerprint() {
8089      const w = window;
8090      const AudioContext = w.OfflineAudioContext || w.webkitOfflineAudioContext;
8091      if (!AudioContext) {
8092          return -2 /* SpecialFingerprint.NotSupported */;
8093      }
8094      // In some browsers, audio context always stays suspended unless the context is started in response to a user action
8095      // (e.g. a click or a tap). It prevents audio fingerprint from being taken at an arbitrary moment of time.
8096      // Such browsers are old and unpopular, so the audio fingerprinting is just skipped in them.
8097      // See a similar case explanation at https://stackoverflow.com/questions/46363048/onaudioprocess-not-called-on-ios11#46534088
8098      if (doesBrowserSuspendAudioContext()) {
8099          return -1 /* SpecialFingerprint.KnownForSuspending */;
8100      }
8101      const hashFromIndex = 4500;
8102      const hashToIndex = 5000;
8103      const context = new AudioContext(1, hashToIndex, 44100);
8104      const oscillator = context.createOscillator();
8105      oscillator.type = 'triangle';
8106      oscillator.frequency.value = 10000;
8107      const compressor = context.createDynamicsCompressor();
8108      compressor.threshold.value = -50;
8109      compressor.knee.value = 40;
8110      compressor.ratio.value = 12;
8111      compressor.attack.value = 0;
8112      compressor.release.value = 0.25;
8113      oscillator.connect(compressor);
8114      compressor.connect(context.destination);
8115      oscillator.start(0);
8116      const [renderPromise, finishRendering] = startRenderingAudio(context);
8117      // Suppresses the console error message in case when the fingerprint fails before requested
8118      const fingerprintPromise = suppressUnhandledRejectionWarning(renderPromise.then((buffer) => getHash(buffer.getChannelData(0).subarray(hashFromIndex)), (error) => {
8119          if (error.name === "timeout" /* InnerErrorName.Timeout */ || error.name === "suspended" /* InnerErrorName.Suspended */) {
8120              return -3 /* SpecialFingerprint.Timeout */;
8121          }
8122          throw error;
8123      }));
8124      return () => {
8125          finishRendering();
8126          return fingerprintPromise;
8127      };
8128  }
8129  /**
8130   * Checks if the current browser is known for always suspending audio context
8131   */
8132  function doesBrowserSuspendAudioContext() {
8133      // Mobile Safari 11 and older
8134      return isWebKit() && !isDesktopWebKit() && !isWebKit606OrNewer();
8135  }
8136  /**
8137   * Checks if the current browser is known for applying anti-fingerprinting measures in all or some critical modes
8138   */
8139  function doesBrowserPerformAntifingerprinting() {
8140      return (
8141      // Safari ≥17
8142      (isWebKit() && isWebKit616OrNewer() && isSafariWebKit()) ||
8143          // Samsung Internet ≥26
8144          (isChromium() && isSamsungInternet() && isChromium122OrNewer()));
8145  }
8146  /**
8147   * Starts rendering the audio context.
8148   * When the returned function is called, the render process starts finishing.
8149   */
8150  function startRenderingAudio(context) {
8151      const renderTryMaxCount = 3;
8152      const renderRetryDelay = 500;
8153      const runningMaxAwaitTime = 500;
8154      const runningSufficientTime = 5000;
8155      let finalize = () => undefined;
8156      const resultPromise = new Promise((resolve, reject) => {
8157          let isFinalized = false;
8158          let renderTryCount = 0;
8159          let startedRunningAt = 0;
8160          context.oncomplete = (event) => resolve(event.renderedBuffer);
8161          const startRunningTimeout = () => {
8162              setTimeout(() => reject(makeInnerError("timeout" /* InnerErrorName.Timeout */)), Math.min(runningMaxAwaitTime, startedRunningAt + runningSufficientTime - Date.now()));
8163          };
8164          const tryRender = () => {
8165              try {
8166                  const renderingPromise = context.startRendering();
8167                  // `context.startRendering` has two APIs: Promise and callback, we check that it's really a promise just in case
8168                  if (isPromise(renderingPromise)) {
8169                      // Suppresses all unhandled rejections in case of scheduled redundant retries after successful rendering
8170                      suppressUnhandledRejectionWarning(renderingPromise);
8171                  }
8172                  switch (context.state) {
8173                      case 'running':
8174                          startedRunningAt = Date.now();
8175                          if (isFinalized) {
8176                              startRunningTimeout();
8177                          }
8178                          break;
8179                      // Sometimes the audio context doesn't start after calling `startRendering` (in addition to the cases where
8180                      // audio context doesn't start at all). A known case is starting an audio context when the browser tab is in
8181                      // background on iPhone. Retries usually help in this case.
8182                      case 'suspended':
8183                          // The audio context can reject starting until the tab is in foreground. Long fingerprint duration
8184                          // in background isn't a problem, therefore the retry attempts don't count in background. It can lead to
8185                          // a situation when a fingerprint takes very long time and finishes successfully. FYI, the audio context
8186                          // can be suspended when `document.hidden === false` and start running after a retry.
8187                          if (!document.hidden) {
8188                              renderTryCount++;
8189                          }
8190                          if (isFinalized && renderTryCount >= renderTryMaxCount) {
8191                              reject(makeInnerError("suspended" /* InnerErrorName.Suspended */));
8192                          }
8193                          else {
8194                              setTimeout(tryRender, renderRetryDelay);
8195                          }
8196                          break;
8197                  }
8198              }
8199              catch (error) {
8200                  reject(error);
8201              }
8202          };
8203          tryRender();
8204          finalize = () => {
8205              if (!isFinalized) {
8206                  isFinalized = true;
8207                  if (startedRunningAt > 0) {
8208                      startRunningTimeout();
8209                  }
8210              }
8211          };
8212      });
8213      return [resultPromise, finalize];
8214  }
8215  function getHash(signal) {
8216      let hash = 0;
8217      for (let i = 0; i < signal.length; ++i) {
8218          hash += Math.abs(signal[i]);
8219      }
8220      return hash;
8221  }
8222  function makeInnerError(name) {
8223      const error = new Error(name);
8224      error.name = name;
8225      return error;
8226  }
8227
8228  /**
8229   * Creates and keeps an invisible iframe while the given function runs.
8230   * The given function is called when the iframe is loaded and has a body.
8231   * The iframe allows to measure DOM sizes inside itself.
8232   *
8233   * Notice: passing an initial HTML code doesn't work in IE.
8234   *
8235   * Warning for package users:
8236   * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk.
8237   */
8238  async function withIframe(action, initialHtml, domPollInterval = 50) {
8239      var _a, _b, _c;
8240      const d = document;
8241      // document.body can be null while the page is loading
8242      while (!d.body) {
8243          await wait(domPollInterval);
8244      }
8245      const iframe = d.createElement('iframe');
8246      try {
8247          await new Promise((_resolve, _reject) => {
8248              let isComplete = false;
8249              const resolve = () => {
8250                  isComplete = true;
8251                  _resolve();
8252              };
8253              const reject = (error) => {
8254                  isComplete = true;
8255                  _reject(error);
8256              };
8257              iframe.onload = resolve;
8258              iframe.onerror = reject;
8259              const { style } = iframe;
8260              style.setProperty('display', 'block', 'important'); // Required for browsers to calculate the layout
8261              style.position = 'absolute';
8262              style.top = '0';
8263              style.left = '0';
8264              style.visibility = 'hidden';
8265              if (initialHtml && 'srcdoc' in iframe) {
8266                  iframe.srcdoc = initialHtml;
8267              }
8268              else {
8269                  iframe.src = 'about:blank';
8270              }
8271              d.body.appendChild(iframe);
8272              // WebKit in WeChat doesn't fire the iframe's `onload` for some reason.
8273              // This code checks for the loading state manually.
8274              // See https://github.com/fingerprintjs/fingerprintjs/issues/645
8275              const checkReadyState = () => {
8276                  var _a, _b;
8277                  // The ready state may never become 'complete' in Firefox despite the 'load' event being fired.
8278                  // So an infinite setTimeout loop can happen without this check.
8279                  // See https://github.com/fingerprintjs/fingerprintjs/pull/716#issuecomment-986898796
8280                  if (isComplete) {
8281                      return;
8282                  }
8283                  // Make sure iframe.contentWindow and iframe.contentWindow.document are both loaded
8284                  // The contentWindow.document can miss in JSDOM (https://github.com/jsdom/jsdom).
8285                  if (((_b = (_a = iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.document) === null || _b === void 0 ? void 0 : _b.readyState) === 'complete') {
8286                      resolve();
8287                  }
8288                  else {
8289                      setTimeout(checkReadyState, 10);
8290                  }
8291              };
8292              checkReadyState();
8293          });
8294          while (!((_b = (_a = iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.document) === null || _b === void 0 ? void 0 : _b.body)) {
8295              await wait(domPollInterval);
8296          }
8297          return await action(iframe, iframe.contentWindow);
8298      }
8299      finally {
8300          (_c = iframe.parentNode) === null || _c === void 0 ? void 0 : _c.removeChild(iframe);
8301      }
8302  }
8303  /**
8304   * Creates a DOM element that matches the given selector.
8305   * Only single element selector are supported (without operators like space, +, >, etc).
8306   */
8307  function selectorToElement(selector) {
8308      const [tag, attributes] = parseSimpleCssSelector(selector);
8309      const element = document.createElement(tag !== null && tag !== void 0 ? tag : 'div');
8310      for (const name of Object.keys(attributes)) {
8311          const value = attributes[name].join(' ');
8312          // Changing the `style` attribute can cause a CSP error, therefore we change the `style.cssText` property.
8313          // https://github.com/fingerprintjs/fingerprintjs/issues/733
8314          if (name === 'style') {
8315              addStyleString(element.style, value);
8316          }
8317          else {
8318              element.setAttribute(name, value);
8319          }
8320      }
8321      return element;
8322  }
8323  /**
8324   * Adds CSS styles from a string in such a way that doesn't trigger a CSP warning (unsafe-inline or unsafe-eval)
8325   */
8326  function addStyleString(style, source) {
8327      // We don't use `style.cssText` because browsers must block it when no `unsafe-eval` CSP is presented: https://csplite.com/csp145/#w3c_note
8328      // Even though the browsers ignore this standard, we don't use `cssText` just in case.
8329      for (const property of source.split(';')) {
8330          const match = /^\s*([\w-]+)\s*:\s*(.+?)(\s*!([\w-]+))?\s*$/.exec(property);
8331          if (match) {
8332              const [, name, value, , priority] = match;
8333              style.setProperty(name, value, priority || ''); // The last argument can't be undefined in IE11
8334          }
8335      }
8336  }
8337  /**
8338   * Returns true if the code runs in an iframe, and any parent page's origin doesn't match the current origin
8339   */
8340  function isAnyParentCrossOrigin() {
8341      let currentWindow = window;
8342      for (;;) {
8343          const parentWindow = currentWindow.parent;
8344          if (!parentWindow || parentWindow === currentWindow) {
8345              return false; // The top page is reached
8346          }
8347          try {
8348              if (parentWindow.location.origin !== currentWindow.location.origin) {
8349                  return true;
8350              }
8351          }
8352          catch (error) {
8353              // The error is thrown when `origin` is accessed on `parentWindow.location` when the parent is cross-origin
8354              if (error instanceof Error && error.name === 'SecurityError') {
8355                  return true;
8356              }
8357              throw error;
8358          }
8359          currentWindow = parentWindow;
8360      }
8361  }
8362
8363  // We use m or w because these two characters take up the maximum width.
8364  // And we use a LLi so that the same matching fonts can get separated.
8365  const testString = 'mmMwWLliI0O&1';
8366  // We test using 48px font size, we may use any size. I guess larger the better.
8367  const textSize = '48px';
8368  // A font will be compared against all the three default fonts.
8369  // And if for any default fonts it doesn't match, then that font is available.
8370  const baseFonts = ['monospace', 'sans-serif', 'serif'];
8371  const fontList = [
8372      // This is android-specific font from "Roboto" family
8373      'sans-serif-thin',
8374      'ARNO PRO',
8375      'Agency FB',
8376      'Arabic Typesetting',
8377      'Arial Unicode MS',
8378      'AvantGarde Bk BT',
8379      'BankGothic Md BT',
8380      'Batang',
8381      'Bitstream Vera Sans Mono',
8382      'Calibri',
8383      'Century',
8384      'Century Gothic',
8385      'Clarendon',
8386      'EUROSTILE',
8387      'Franklin Gothic',
8388      'Futura Bk BT',
8389      'Futura Md BT',
8390      'GOTHAM',
8391      'Gill Sans',
8392      'HELV',
8393      'Haettenschweiler',
8394      'Helvetica Neue',
8395      'Humanst521 BT',
8396      'Leelawadee',
8397      'Letter Gothic',
8398      'Levenim MT',
8399      'Lucida Bright',
8400      'Lucida Sans',
8401      'Menlo',
8402      'MS Mincho',
8403      'MS Outlook',
8404      'MS Reference Specialty',
8405      'MS UI Gothic',
8406      'MT Extra',
8407      'MYRIAD PRO',
8408      'Marlett',
8409      'Meiryo UI',
8410      'Microsoft Uighur',
8411      'Minion Pro',
8412      'Monotype Corsiva',
8413      'PMingLiU',
8414      'Pristina',
8415      'SCRIPTINA',
8416      'Segoe UI Light',
8417      'Serifa',
8418      'SimHei',
8419      'Small Fonts',
8420      'Staccato222 BT',
8421      'TRAJAN PRO',
8422      'Univers CE 55 Medium',
8423      'Vrinda',
8424      'ZWAdobeF',
8425  ];
8426  // kudos to http://www.lalit.org/lab/javascript-css-font-detect/
8427  function getFonts() {
8428      // Running the script in an iframe makes it not affect the page look and not be affected by the page CSS. See:
8429      // https://github.com/fingerprintjs/fingerprintjs/issues/592
8430      // https://github.com/fingerprintjs/fingerprintjs/issues/628
8431      return withIframe(async (_, { document }) => {
8432          const holder = document.body;
8433          holder.style.fontSize = textSize;
8434          // div to load spans for the default fonts and the fonts to detect
8435          const spansContainer = document.createElement('div');
8436          spansContainer.style.setProperty('visibility', 'hidden', 'important');
8437          const defaultWidth = {};
8438          const defaultHeight = {};
8439          // creates a span where the fonts will be loaded
8440          const createSpan = (fontFamily) => {
8441              const span = document.createElement('span');
8442              const { style } = span;
8443              style.position = 'absolute';
8444              style.top = '0';
8445              style.left = '0';
8446              style.fontFamily = fontFamily;
8447              span.textContent = testString;
8448              spansContainer.appendChild(span);
8449              return span;
8450          };
8451          // creates a span and load the font to detect and a base font for fallback
8452          const createSpanWithFonts = (fontToDetect, baseFont) => {
8453              return createSpan(`'${fontToDetect}',${baseFont}`);
8454          };
8455          // creates spans for the base fonts and adds them to baseFontsDiv
8456          const initializeBaseFontsSpans = () => {
8457              return baseFonts.map(createSpan);
8458          };
8459          // creates spans for the fonts to detect and adds them to fontsDiv
8460          const initializeFontsSpans = () => {
8461              // Stores {fontName : [spans for that font]}
8462              const spans = {};
8463              for (const font of fontList) {
8464                  spans[font] = baseFonts.map((baseFont) => createSpanWithFonts(font, baseFont));
8465              }
8466              return spans;
8467          };
8468          // checks if a font is available
8469          const isFontAvailable = (fontSpans) => {
8470              return baseFonts.some((baseFont, baseFontIndex) => fontSpans[baseFontIndex].offsetWidth !== defaultWidth[baseFont] ||
8471                  fontSpans[baseFontIndex].offsetHeight !== defaultHeight[baseFont]);
8472          };
8473          // create spans for base fonts
8474          const baseFontsSpans = initializeBaseFontsSpans();
8475          // create spans for fonts to detect
8476          const fontsSpans = initializeFontsSpans();
8477          // add all the spans to the DOM
8478          holder.appendChild(spansContainer);
8479          // get the default width for the three base fonts
8480          for (let index = 0; index < baseFonts.length; index++) {
8481              defaultWidth[baseFonts[index]] = baseFontsSpans[index].offsetWidth; // width for the default font
8482              defaultHeight[baseFonts[index]] = baseFontsSpans[index].offsetHeight; // height for the default font
8483          }
8484          // check available fonts
8485          return fontList.filter((font) => isFontAvailable(fontsSpans[font]));
8486      });
8487  }
8488
8489  function getPlugins() {
8490      const rawPlugins = navigator.plugins;
8491      if (!rawPlugins) {
8492          return undefined;
8493      }
8494      const plugins = [];
8495      // Safari 10 doesn't support iterating navigator.plugins with for...of
8496      for (let i = 0; i < rawPlugins.length; ++i) {
8497          const plugin = rawPlugins[i];
8498          if (!plugin) {
8499              continue;
8500          }
8501          const mimeTypes = [];
8502          for (let j = 0; j < plugin.length; ++j) {
8503              const mimeType = plugin[j];
8504              mimeTypes.push({
8505                  type: mimeType.type,
8506                  suffixes: mimeType.suffixes,
8507              });
8508          }
8509          plugins.push({
8510              name: plugin.name,
8511              description: plugin.description,
8512              mimeTypes,
8513          });
8514      }
8515      return plugins;
8516  }
8517
8518  /**
8519   * A version of the entropy source with stabilization to make it suitable for static fingerprinting.
8520   *
8521   * Canvas image is noised in private mode of Safari 17, so image rendering is skipped in Safari 17.
8522   * Firefox 120+ randomizes canvas data in private browsing and strict ETP mode,
8523   * so image rendering is skipped in Firefox 120+.
8524   *
8525   * @see https://www.browserleaks.com/canvas#how-does-it-work
8526   * @see https://bugzilla.mozilla.org/show_bug.cgi?id=1816189 Firefox canvas randomization
8527   */
8528  function getCanvasFingerprint() {
8529      return getUnstableCanvasFingerprint(doesBrowserPerformAntiFingerprinting());
8530  }
8531  /**
8532   * A version of the entropy source without stabilization.
8533   *
8534   * Warning for package users:
8535   * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk.
8536   */
8537  function getUnstableCanvasFingerprint(skipImages) {
8538      let winding = false;
8539      let geometry;
8540      let text;
8541      const [canvas, context] = makeCanvasContext();
8542      if (!isSupported(canvas, context)) {
8543          geometry = text = "unsupported" /* ImageStatus.Unsupported */;
8544      }
8545      else {
8546          winding = doesSupportWinding(context);
8547          if (skipImages) {
8548              geometry = text = "skipped" /* ImageStatus.Skipped */;
8549          }
8550          else {
8551              [geometry, text] = renderImages(canvas, context);
8552          }
8553      }
8554      return { winding, geometry, text };
8555  }
8556  function makeCanvasContext() {
8557      const canvas = document.createElement('canvas');
8558      canvas.width = 1;
8559      canvas.height = 1;
8560      return [canvas, canvas.getContext('2d')];
8561  }
8562  function isSupported(canvas, context) {
8563      return !!(context && canvas.toDataURL);
8564  }
8565  function doesSupportWinding(context) {
8566      // https://web.archive.org/web/20170825024655/http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/
8567      // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/canvas/winding.js
8568      context.rect(0, 0, 10, 10);
8569      context.rect(2, 2, 6, 6);
8570      return !context.isPointInPath(5, 5, 'evenodd');
8571  }
8572  function renderImages(canvas, context) {
8573      renderTextImage(canvas, context);
8574      const textImage1 = canvasToString(canvas);
8575      const textImage2 = canvasToString(canvas); // It's slightly faster to double-encode the text image
8576      // Some browsers add a noise to the canvas: https://github.com/fingerprintjs/fingerprintjs/issues/791
8577      // The canvas is excluded from the fingerprint in this case
8578      if (textImage1 !== textImage2) {
8579          return ["unstable" /* ImageStatus.Unstable */, "unstable" /* ImageStatus.Unstable */];
8580      }
8581      // Text is unstable:
8582      // https://github.com/fingerprintjs/fingerprintjs/issues/583
8583      // https://github.com/fingerprintjs/fingerprintjs/issues/103
8584      // Therefore it's extracted into a separate image.
8585      renderGeometryImage(canvas, context);
8586      const geometryImage = canvasToString(canvas);
8587      return [geometryImage, textImage1];
8588  }
8589  function renderTextImage(canvas, context) {
8590      // Resizing the canvas cleans it
8591      canvas.width = 240;
8592      canvas.height = 60;
8593      context.textBaseline = 'alphabetic';
8594      context.fillStyle = '#f60';
8595      context.fillRect(100, 1, 62, 20);
8596      context.fillStyle = '#069';
8597      // It's important to use explicit built-in fonts in order to exclude the affect of font preferences
8598      // (there is a separate entropy source for them).
8599      context.font = '11pt "Times New Roman"';
8600      // The choice of emojis has a gigantic impact on rendering performance (especially in FF).
8601      // Some newer emojis cause it to slow down 50-200 times.
8602      // There must be no text to the right of the emoji, see https://github.com/fingerprintjs/fingerprintjs/issues/574
8603      // A bare emoji shouldn't be used because the canvas will change depending on the script encoding:
8604      // https://github.com/fingerprintjs/fingerprintjs/issues/66
8605      // Escape sequence shouldn't be used too because Terser will turn it into a bare unicode.
8606      const printedText = `Cwm fjordbank gly ${String.fromCharCode(55357, 56835) /* 😃 */}`;
8607      context.fillText(printedText, 2, 15);
8608      context.fillStyle = 'rgba(102, 204, 0, 0.2)';
8609      context.font = '18pt Arial';
8610      context.fillText(printedText, 4, 45);
8611  }
8612  function renderGeometryImage(canvas, context) {
8613      // Resizing the canvas cleans it
8614      canvas.width = 122;
8615      canvas.height = 110;
8616      // Canvas blending
8617      // https://web.archive.org/web/20170826194121/http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/
8618      // http://jsfiddle.net/NDYV8/16/
8619      context.globalCompositeOperation = 'multiply';
8620      for (const [color, x, y] of [
8621          ['#f2f', 40, 40],
8622          ['#2ff', 80, 40],
8623          ['#ff2', 60, 80],
8624      ]) {
8625          context.fillStyle = color;
8626          context.beginPath();
8627          context.arc(x, y, 40, 0, Math.PI * 2, true);
8628          context.closePath();
8629          context.fill();
8630      }
8631      // Canvas winding
8632      // https://web.archive.org/web/20130913061632/http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/
8633      // http://jsfiddle.net/NDYV8/19/
8634      context.fillStyle = '#f9c';
8635      context.arc(60, 60, 60, 0, Math.PI * 2, true);
8636      context.arc(60, 60, 20, 0, Math.PI * 2, true);
8637      context.fill('evenodd');
8638  }
8639  function canvasToString(canvas) {
8640      return canvas.toDataURL();
8641  }
8642  /**
8643   * Checks if the current browser is known for applying anti-fingerprinting measures in all or some critical modes:
8644   * - Safari 17+: noises canvas image in private mode
8645   * - Firefox 120+: randomizes canvas data in private browsing and strict ETP mode (CanvasRandomization)
8646   *
8647   * @see https://bugzilla.mozilla.org/show_bug.cgi?id=1816189 Firefox canvas randomization
8648   */
8649  function doesBrowserPerformAntiFingerprinting() {
8650      const isSafari17OrAbove = isWebKit() && isWebKit616OrNewer() && isSafariWebKit();
8651      const isFirefox120OrAbove = isGecko() && isGecko120OrNewer();
8652      return isSafari17OrAbove || isFirefox120OrAbove;
8653  }
8654
8655  /**
8656   * This is a crude and primitive touch screen detection. It's not possible to currently reliably detect the availability
8657   * of a touch screen with a JS, without actually subscribing to a touch event.
8658   *
8659   * @see http://www.stucox.com/blog/you-cant-detect-a-touchscreen/
8660   * @see https://github.com/Modernizr/Modernizr/issues/548
8661   */
8662  function getTouchSupport() {
8663      const n = navigator;
8664      let maxTouchPoints = 0;
8665      let touchEvent;
8666      if (n.maxTouchPoints !== undefined) {
8667          maxTouchPoints = toInt(n.maxTouchPoints);
8668      }
8669      else if (n.msMaxTouchPoints !== undefined) {
8670          maxTouchPoints = n.msMaxTouchPoints;
8671      }
8672      try {
8673          document.createEvent('TouchEvent');
8674          touchEvent = true;
8675      }
8676      catch (_a) {
8677          touchEvent = false;
8678      }
8679      const touchStart = 'ontouchstart' in window;
8680      return {
8681          maxTouchPoints,
8682          touchEvent,
8683          touchStart,
8684      };
8685  }
8686
8687  function getOsCpu() {
8688      return navigator.oscpu;
8689  }
8690
8691  function getLanguages() {
8692      const n = navigator;
8693      const result = [];
8694      const language = n.language || n.userLanguage || n.browserLanguage || n.systemLanguage;
8695      if (language !== undefined) {
8696          result.push([language]);
8697      }
8698      if (Array.isArray(n.languages)) {
8699          // Starting from Chromium 86, there is only a single value in `navigator.language` in Incognito mode:
8700          // the value of `navigator.language`. Therefore the value is ignored in this browser.
8701          if (!(isChromium() && isChromium86OrNewer())) {
8702              result.push(n.languages);
8703          }
8704      }
8705      else if (typeof n.languages === 'string') {
8706          const languages = n.languages;
8707          if (languages) {
8708              result.push(languages.split(','));
8709          }
8710      }
8711      return result;
8712  }
8713
8714  function getColorDepth() {
8715      return window.screen.colorDepth;
8716  }
8717
8718  function getDeviceMemory() {
8719      // `navigator.deviceMemory` is a string containing a number in some unidentified cases
8720      return replaceNaN(toFloat(navigator.deviceMemory), undefined);
8721  }
8722
8723  /**
8724   * A version of the entropy source with stabilization to make it suitable for static fingerprinting.
8725   * The window resolution is always the document size in private mode of Safari 17,
8726   * so the window resolution is not used in Safari 17.
8727   */
8728  function getScreenResolution() {
8729      if (isWebKit() && isWebKit616OrNewer() && isSafariWebKit()) {
8730          return undefined;
8731      }
8732      return getUnstableScreenResolution();
8733  }
8734  /**
8735   * A version of the entropy source without stabilization.
8736   *
8737   * Warning for package users:
8738   * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk.
8739   */
8740  function getUnstableScreenResolution() {
8741      const s = screen;
8742      // Some browsers return screen resolution as strings, e.g. "1200", instead of a number, e.g. 1200.
8743      // I suspect it's done by certain plugins that randomize browser properties to prevent fingerprinting.
8744      // Some browsers even return  screen resolution as not numbers.
8745      const parseDimension = (value) => replaceNaN(toInt(value), null);
8746      const dimensions = [parseDimension(s.width), parseDimension(s.height)];
8747      dimensions.sort().reverse();
8748      return dimensions;
8749  }
8750
8751  const screenFrameCheckInterval = 2500;
8752  const roundingPrecision = 10;
8753  // The type is readonly to protect from unwanted mutations
8754  let screenFrameBackup;
8755  let screenFrameSizeTimeoutId;
8756  /**
8757   * Starts watching the screen frame size. When a non-zero size appears, the size is saved and the watch is stopped.
8758   * Later, when `getScreenFrame` runs, it will return the saved non-zero size if the current size is null.
8759   *
8760   * This trick is required to mitigate the fact that the screen frame turns null in some cases.
8761   * See more on this at https://github.com/fingerprintjs/fingerprintjs/issues/568
8762   */
8763  function watchScreenFrame() {
8764      if (screenFrameSizeTimeoutId !== undefined) {
8765          return;
8766      }
8767      const checkScreenFrame = () => {
8768          const frameSize = getCurrentScreenFrame();
8769          if (isFrameSizeNull(frameSize)) {
8770              screenFrameSizeTimeoutId = setTimeout(checkScreenFrame, screenFrameCheckInterval);
8771          }
8772          else {
8773              screenFrameBackup = frameSize;
8774              screenFrameSizeTimeoutId = undefined;
8775          }
8776      };
8777      checkScreenFrame();
8778  }
8779  /**
8780   * A version of the entropy source without stabilization.
8781   *
8782   * Warning for package users:
8783   * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk.
8784   */
8785  function getUnstableScreenFrame() {
8786      watchScreenFrame();
8787      return async () => {
8788          let frameSize = getCurrentScreenFrame();
8789          if (isFrameSizeNull(frameSize)) {
8790              if (screenFrameBackup) {
8791                  return [...screenFrameBackup];
8792              }
8793              if (getFullscreenElement()) {
8794                  // Some browsers set the screen frame to zero when programmatic fullscreen is on.
8795                  // There is a chance of getting a non-zero frame after exiting the fullscreen.
8796                  // See more on this at https://github.com/fingerprintjs/fingerprintjs/issues/568
8797                  await exitFullscreen();
8798                  frameSize = getCurrentScreenFrame();
8799              }
8800          }
8801          if (!isFrameSizeNull(frameSize)) {
8802              screenFrameBackup = frameSize;
8803          }
8804          return frameSize;
8805      };
8806  }
8807  /**
8808   * A version of the entropy source with stabilization to make it suitable for static fingerprinting.
8809   *
8810   * Sometimes the available screen resolution changes a bit, e.g. 1900x1440 → 1900x1439. A possible reason: macOS Dock
8811   * shrinks to fit more icons when there is too little space. The rounding is used to mitigate the difference.
8812   *
8813   * The frame width is always 0 in the private mode of Safari 17, so the frame is not used in Safari 17.
8814   * Firefox 143+ spoofs screen frame (ScreenAvailRect) in private browsing and strict ETP mode,
8815   * so the frame is not used in Firefox 143+.
8816   *
8817   * @see https://bugzilla.mozilla.org/show_bug.cgi?id=1978414 Firefox screen resolution spoofing
8818   */
8819  function getScreenFrame() {
8820      const isSafari17OrAbove = isWebKit() && isWebKit616OrNewer() && isSafariWebKit();
8821      const isFirefox143OrAbove = isGecko() && isGecko143OrNewer();
8822      if (isSafari17OrAbove || isFirefox143OrAbove) {
8823          return () => Promise.resolve(undefined);
8824      }
8825      const screenFrameGetter = getUnstableScreenFrame();
8826      return async () => {
8827          const frameSize = await screenFrameGetter();
8828          const processSize = (sideSize) => (sideSize === null ? null : round(sideSize, roundingPrecision));
8829          // It might look like I don't know about `for` and `map`.
8830          // In fact, such code is used to avoid TypeScript issues without using `as`.
8831          return [processSize(frameSize[0]), processSize(frameSize[1]), processSize(frameSize[2]), processSize(frameSize[3])];
8832      };
8833  }
8834  function getCurrentScreenFrame() {
8835      const s = screen;
8836      // Some browsers return screen resolution as strings, e.g. "1200", instead of a number, e.g. 1200.
8837      // I suspect it's done by certain plugins that randomize browser properties to prevent fingerprinting.
8838      //
8839      // Some browsers (IE, Edge ≤18) don't provide `screen.availLeft` and `screen.availTop`. The property values are
8840      // replaced with 0 in such cases to not lose the entropy from `screen.availWidth` and `screen.availHeight`.
8841      return [
8842          replaceNaN(toFloat(s.availTop), null),
8843          replaceNaN(toFloat(s.width) - toFloat(s.availWidth) - replaceNaN(toFloat(s.availLeft), 0), null),
8844          replaceNaN(toFloat(s.height) - toFloat(s.availHeight) - replaceNaN(toFloat(s.availTop), 0), null),
8845          replaceNaN(toFloat(s.availLeft), null),
8846      ];
8847  }
8848  function isFrameSizeNull(frameSize) {
8849      for (let i = 0; i < 4; ++i) {
8850          if (frameSize[i]) {
8851              return false;
8852          }
8853      }
8854      return true;
8855  }
8856
8857  /**
8858   * A version of the entropy source with stabilization to make it suitable for static fingerprinting.
8859   *
8860   * Firefox 143+ spoofs hardwareConcurrency (NavigatorHWConcurrency) in private browsing and strict ETP mode
8861   * using tiered values: ≤4 cores -> 4, >4 cores -> 8. We apply the same tiering to stabilize the fingerprint
8862   * across all browsing modes, since the tiering function is idempotent.
8863   *
8864   * @see https://bugzilla.mozilla.org/show_bug.cgi?id=1978414 Firefox processor count spoofing
8865   * @see https://searchfox.org/firefox-main/source/dom/workers/RuntimeService.cpp#2057:~:text=if%20(MOZ_UNLIKELY(aRFPTiered))%20%7B
8866   */
8867  function getHardwareConcurrency() {
8868      const value = getUnstableHardwareConcurrency();
8869      if (value !== undefined && isGecko() && isGecko143OrNewer()) {
8870          return value >= 8 ? 8 : 4;
8871      }
8872      return value;
8873  }
8874  /**
8875   * A version of the entropy source without stabilization.
8876   *
8877   * Warning for package users:
8878   * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk.
8879   */
8880  function getUnstableHardwareConcurrency() {
8881      // sometimes hardware concurrency is a string
8882      return replaceNaN(toInt(navigator.hardwareConcurrency), undefined);
8883  }
8884
8885  function getTimezone() {
8886      var _a;
8887      const DateTimeFormat = (_a = window.Intl) === null || _a === void 0 ? void 0 : _a.DateTimeFormat;
8888      if (DateTimeFormat) {
8889          const timezone = new DateTimeFormat().resolvedOptions().timeZone;
8890          if (timezone) {
8891              return timezone;
8892          }
8893      }
8894      // For browsers that don't support timezone names
8895      // The minus is intentional because the JS offset is opposite to the real offset
8896      const offset = -getTimezoneOffset();
8897      return `UTC${offset >= 0 ? '+' : ''}${offset}`;
8898  }
8899  function getTimezoneOffset() {
8900      const currentYear = new Date().getFullYear();
8901      // The timezone offset may change over time due to daylight saving time (DST) shifts.
8902      // The non-DST timezone offset is used as the result timezone offset.
8903      // Since the DST season differs in the northern and the southern hemispheres,
8904      // both January and July timezones offsets are considered.
8905      return Math.max(
8906      // `getTimezoneOffset` returns a number as a string in some unidentified cases
8907      toFloat(new Date(currentYear, 0, 1).getTimezoneOffset()), toFloat(new Date(currentYear, 6, 1).getTimezoneOffset()));
8908  }
8909
8910  function getSessionStorage() {
8911      try {
8912          return !!window.sessionStorage;
8913      }
8914      catch (error) {
8915          /* SecurityError when referencing it means it exists */
8916          return true;
8917      }
8918  }
8919
8920  // https://bugzilla.mozilla.org/show_bug.cgi?id=781447
8921  function getLocalStorage() {
8922      try {
8923          return !!window.localStorage;
8924      }
8925      catch (e) {
8926          /* SecurityError when referencing it means it exists */
8927          return true;
8928      }
8929  }
8930
8931  function getIndexedDB() {
8932      // IE and Edge don't allow accessing indexedDB in private mode, therefore IE and Edge will have different
8933      // visitor identifier in normal and private modes.
8934      if (isTrident() || isEdgeHTML()) {
8935          return undefined;
8936      }
8937      try {
8938          return !!window.indexedDB;
8939      }
8940      catch (e) {
8941          /* SecurityError when referencing it means it exists */
8942          return true;
8943      }
8944  }
8945
8946  function getOpenDatabase() {
8947      return !!window.openDatabase;
8948  }
8949
8950  function getCpuClass() {
8951      return navigator.cpuClass;
8952  }
8953
8954  function getPlatform() {
8955      // Android Chrome 86 and 87 and Android Firefox 80 and 84 don't mock the platform value when desktop mode is requested
8956      const { platform } = navigator;
8957      // iOS mocks the platform value when desktop version is requested: https://github.com/fingerprintjs/fingerprintjs/issues/514
8958      // iPad uses desktop mode by default since iOS 13
8959      // The value is 'MacIntel' on M1 Macs
8960      // The value is 'iPhone' on iPod Touch
8961      if (platform === 'MacIntel') {
8962          if (isWebKit() && !isDesktopWebKit()) {
8963              return isIPad() ? 'iPad' : 'iPhone';
8964          }
8965      }
8966      return platform;
8967  }
8968
8969  function getVendor() {
8970      return navigator.vendor || '';
8971  }
8972
8973  /**
8974   * Checks for browser-specific (not engine specific) global variables to tell browsers with the same engine apart.
8975   * Only somewhat popular browsers are considered.
8976   */
8977  function getVendorFlavors() {
8978      const flavors = [];
8979      for (const key of [
8980          // Blink and some browsers on iOS
8981          'chrome',
8982          // Safari on macOS
8983          'safari',
8984          // Chrome on iOS (checked in 85 on 13 and 87 on 14)
8985          '__crWeb',
8986          '__gCrWeb',
8987          // Yandex Browser on iOS, macOS and Android (checked in 21.2 on iOS 14, macOS and Android)
8988          'yandex',
8989          // Yandex Browser on iOS (checked in 21.2 on 14)
8990          '__yb',
8991          '__ybro',
8992          // Firefox on iOS (checked in 32 on 14)
8993          '__firefox__',
8994          // Edge on iOS (checked in 46 on 14)
8995          '__edgeTrackingPreventionStatistics',
8996          'webkit',
8997          // Opera Touch on iOS (checked in 2.6 on 14)
8998          'oprt',
8999          // Samsung Internet on Android (checked in 11.1)
9000          'samsungAr',
9001          // UC Browser on Android (checked in 12.10 and 13.0)
9002          'ucweb',
9003          'UCShellJava',
9004          // Puffin on Android (checked in 9.0)
9005          'puffinDevice',
9006          // UC on iOS and Opera on Android have no specific global variables
9007          // Edge for Android isn't checked
9008      ]) {
9009          const value = window[key];
9010          if (value && typeof value === 'object') {
9011              flavors.push(key);
9012          }
9013      }
9014      return flavors.sort();
9015  }
9016
9017  /**
9018   * navigator.cookieEnabled cannot detect custom or nuanced cookie blocking configurations. For example, when blocking
9019   * cookies via the Advanced Privacy Settings in IE9, it always returns true. And there have been issues in the past with
9020   * site-specific exceptions. Don't rely on it.
9021   *
9022   * @see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cookies.js Taken from here
9023   */
9024  function areCookiesEnabled() {
9025      const d = document;
9026      // Taken from here: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cookies.js
9027      // navigator.cookieEnabled cannot detect custom or nuanced cookie blocking configurations. For example, when blocking
9028      // cookies via the Advanced Privacy Settings in IE9, it always returns true. And there have been issues in the past
9029      // with site-specific exceptions. Don't rely on it.
9030      // try..catch because some in situations `document.cookie` is exposed but throws a
9031      // SecurityError if you try to access it; e.g. documents created from data URIs
9032      // or in sandboxed iframes (depending on flags/context)
vendor: 8,553 bytes, lines 9033-9228
9033      try {
9034          // Create cookie
9035          d.cookie = 'cookietest=1; SameSite=Strict;';
9036          const result = d.cookie.indexOf('cookietest=') !== -1;
9037          // Delete cookie
9038          d.cookie = 'cookietest=1; SameSite=Strict; expires=Thu, 01-Jan-1970 00:00:01 GMT';
9039          return result;
9040      }
9041      catch (e) {
9042          return false;
9043      }
9044  }
9045
9046  /**
9047   * Only single element selector are supported (no operators like space, +, >, etc).
9048   * `embed` and `position: fixed;` will be considered as blocked anyway because it always has no offsetParent.
9049   * Avoid `iframe` and anything with `[src=]` because they produce excess HTTP requests.
9050   *
9051   * The "inappropriate" selectors are obfuscated. See https://github.com/fingerprintjs/fingerprintjs/issues/734.
9052   * A function is used instead of a plain object to help tree-shaking.
9053   *
9054   * The function code is generated automatically. See docs/content_blockers.md to learn how to make the list.
9055   */
9056  function getFilters() {
9057      const fromB64 = atob; // Just for better minification
9058      return {
9059          abpIndo: [
9060              '#Iklan-Melayang',
9061              '#Kolom-Iklan-728',
9062              '#SidebarIklan-wrapper',
9063              '[title="ALIENBOLA" i]',
9064              fromB64('I0JveC1CYW5uZXItYWRz'),
9065          ],
9066          abpvn: ['.quangcao', '#mobileCatfish', fromB64('LmNsb3NlLWFkcw=='), '[id^="bn_bottom_fixed_"]', '#pmadv'],
9067          adBlockFinland: [
9068              '.mainostila',
9069              fromB64('LnNwb25zb3JpdA=='),
9070              '.ylamainos',
9071              fromB64('YVtocmVmKj0iL2NsaWNrdGhyZ2guYXNwPyJd'),
9072              fromB64('YVtocmVmXj0iaHR0cHM6Ly9hcHAucmVhZHBlYWsuY29tL2FkcyJd'),
9073          ],
9074          adBlockPersian: [
9075              '#navbar_notice_50',
9076              '.kadr',
9077              'TABLE[width="140px"]',
9078              '#divAgahi',
9079              fromB64('YVtocmVmXj0iaHR0cDovL2cxLnYuZndtcm0ubmV0L2FkLyJd'),
9080          ],
9081          adBlockWarningRemoval: [
9082              '#adblock-honeypot',
9083              '.adblocker-root',
9084              '.wp_adblock_detect',
9085              fromB64('LmhlYWRlci1ibG9ja2VkLWFk'),
9086              fromB64('I2FkX2Jsb2NrZXI='),
9087          ],
9088          adGuardAnnoyances: [
9089              '.hs-sosyal',
9090              '#cookieconsentdiv',
9091              'div[class^="app_gdpr"]',
9092              '.as-oil',
9093              '[data-cypress="soft-push-notification-modal"]',
9094          ],
9095          adGuardBase: [
9096              '.BetterJsPopOverlay',
9097              fromB64('I2FkXzMwMFgyNTA='),
9098              fromB64('I2Jhbm5lcmZsb2F0MjI='),
9099              fromB64('I2NhbXBhaWduLWJhbm5lcg=='),
9100              fromB64('I0FkLUNvbnRlbnQ='),
9101          ],
9102          adGuardChinese: [
9103              fromB64('LlppX2FkX2FfSA=='),
9104              fromB64('YVtocmVmKj0iLmh0aGJldDM0LmNvbSJd'),
9105              '#widget-quan',
9106              fromB64('YVtocmVmKj0iLzg0OTkyMDIwLnh5eiJd'),
9107              fromB64('YVtocmVmKj0iLjE5NTZobC5jb20vIl0='),
9108          ],
9109          adGuardFrench: [
9110              '#pavePub',
9111              fromB64('LmFkLWRlc2t0b3AtcmVjdGFuZ2xl'),
9112              '.mobile_adhesion',
9113              '.widgetadv',
9114              fromB64('LmFkc19iYW4='),
9115          ],
9116          adGuardGerman: ['aside[data-portal-id="leaderboard"]'],
9117          adGuardJapanese: [
9118              '#kauli_yad_1',
9119              fromB64('YVtocmVmXj0iaHR0cDovL2FkMi50cmFmZmljZ2F0ZS5uZXQvIl0='),
9120              fromB64('Ll9wb3BJbl9pbmZpbml0ZV9hZA=='),
9121              fromB64('LmFkZ29vZ2xl'),
9122              fromB64('Ll9faXNib29zdFJldHVybkFk'),
9123          ],
9124          adGuardMobile: [
9125              fromB64('YW1wLWF1dG8tYWRz'),
9126              fromB64('LmFtcF9hZA=='),
9127              'amp-embed[type="24smi"]',
9128              '#mgid_iframe1',
9129              fromB64('I2FkX2ludmlld19hcmVh'),
9130          ],
9131          adGuardRussian: [
9132              fromB64('YVtocmVmXj0iaHR0cHM6Ly9hZC5sZXRtZWFkcy5jb20vIl0='),
9133              fromB64('LnJlY2xhbWE='),
9134              'div[id^="smi2adblock"]',
9135              fromB64('ZGl2W2lkXj0iQWRGb3hfYmFubmVyXyJd'),
9136              '#psyduckpockeball',
9137          ],
9138          adGuardSocial: [
9139              fromB64('YVtocmVmXj0iLy93d3cuc3R1bWJsZXVwb24uY29tL3N1Ym1pdD91cmw9Il0='),
9140              fromB64('YVtocmVmXj0iLy90ZWxlZ3JhbS5tZS9zaGFyZS91cmw/Il0='),
9141              '.etsy-tweet',
9142              '#inlineShare',
9143              '.popup-social',
9144          ],
9145          adGuardSpanishPortuguese: ['#barraPublicidade', '#Publicidade', '#publiEspecial', '#queTooltip', '.cnt-publi'],
9146          adGuardTrackingProtection: [
9147              '#qoo-counter',
9148              fromB64('YVtocmVmXj0iaHR0cDovL2NsaWNrLmhvdGxvZy5ydS8iXQ=='),
9149              fromB64('YVtocmVmXj0iaHR0cDovL2hpdGNvdW50ZXIucnUvdG9wL3N0YXQucGhwIl0='),
9150              fromB64('YVtocmVmXj0iaHR0cDovL3RvcC5tYWlsLnJ1L2p1bXAiXQ=='),
9151              '#top100counter',
9152          ],
9153          adGuardTurkish: [
9154              '#backkapat',
9155              fromB64('I3Jla2xhbWk='),
9156              fromB64('YVtocmVmXj0iaHR0cDovL2Fkc2Vydi5vbnRlay5jb20udHIvIl0='),
9157              fromB64('YVtocmVmXj0iaHR0cDovL2l6bGVuemkuY29tL2NhbXBhaWduLyJd'),
9158              fromB64('YVtocmVmXj0iaHR0cDovL3d3dy5pbnN0YWxsYWRzLm5ldC8iXQ=='),
9159          ],
9160          bulgarian: [fromB64('dGQjZnJlZW5ldF90YWJsZV9hZHM='), '#ea_intext_div', '.lapni-pop-over', '#xenium_hot_offers'],
9161          easyList: [
9162              '.yb-floorad',
9163              fromB64('LndpZGdldF9wb19hZHNfd2lkZ2V0'),
9164              fromB64('LnRyYWZmaWNqdW5reS1hZA=='),
9165              '.textad_headline',
9166              fromB64('LnNwb25zb3JlZC10ZXh0LWxpbmtz'),
9167          ],
9168          easyListChina: [
9169              fromB64('LmFwcGd1aWRlLXdyYXBbb25jbGljayo9ImJjZWJvcy5jb20iXQ=='),
9170              fromB64('LmZyb250cGFnZUFkdk0='),
9171              '#taotaole',
9172              '#aafoot.top_box',
9173              '.cfa_popup',
9174          ],
9175          easyListCookie: [
9176              '.ezmob-footer',
9177              '.cc-CookieWarning',
9178              '[data-cookie-number]',
9179              fromB64('LmF3LWNvb2tpZS1iYW5uZXI='),
9180              '.sygnal24-gdpr-modal-wrap',
9181          ],
9182          easyListCzechSlovak: [
9183              '#onlajny-stickers',
9184              fromB64('I3Jla2xhbW5pLWJveA=='),
9185              fromB64('LnJla2xhbWEtbWVnYWJvYXJk'),
9186              '.sklik',
9187              fromB64('W2lkXj0ic2tsaWtSZWtsYW1hIl0='),
9188          ],
9189          easyListDutch: [
9190              fromB64('I2FkdmVydGVudGll'),
9191              fromB64('I3ZpcEFkbWFya3RCYW5uZXJCbG9jaw=='),
9192              '.adstekst',
9193              fromB64('YVtocmVmXj0iaHR0cHM6Ly94bHR1YmUubmwvY2xpY2svIl0='),
9194              '#semilo-lrectangle',
9195          ],
9196          easyListGermany: [
9197              '#SSpotIMPopSlider',
9198              fromB64('LnNwb25zb3JsaW5rZ3J1ZW4='),
9199              fromB64('I3dlcmJ1bmdza3k='),
9200              fromB64('I3Jla2xhbWUtcmVjaHRzLW1pdHRl'),
9201              fromB64('YVtocmVmXj0iaHR0cHM6Ly9iZDc0Mi5jb20vIl0='),
9202          ],
9203          easyListItaly: [
9204              fromB64('LmJveF9hZHZfYW5udW5jaQ=='),
9205              '.sb-box-pubbliredazionale',
9206              fromB64('YVtocmVmXj0iaHR0cDovL2FmZmlsaWF6aW9uaWFkcy5zbmFpLml0LyJd'),
9207              fromB64('YVtocmVmXj0iaHR0cHM6Ly9hZHNlcnZlci5odG1sLml0LyJd'),
9208              fromB64('YVtocmVmXj0iaHR0cHM6Ly9hZmZpbGlhemlvbmlhZHMuc25haS5pdC8iXQ=='),
9209          ],
9210          easyListLithuania: [
9211              fromB64('LnJla2xhbW9zX3RhcnBhcw=='),
9212              fromB64('LnJla2xhbW9zX251b3JvZG9z'),
9213              fromB64('aW1nW2FsdD0iUmVrbGFtaW5pcyBza3lkZWxpcyJd'),
9214              fromB64('aW1nW2FsdD0iRGVkaWt1b3RpLmx0IHNlcnZlcmlhaSJd'),
9215              fromB64('aW1nW2FsdD0iSG9zdGluZ2FzIFNlcnZlcmlhaS5sdCJd'),
9216          ],
9217          estonian: [fromB64('QVtocmVmKj0iaHR0cDovL3BheTRyZXN1bHRzMjQuZXUiXQ==')],
9218          fanboyAnnoyances: ['#ac-lre-player', '.navigate-to-top', '#subscribe_popup', '.newsletter_holder', '#back-top'],
9219          fanboyAntiFacebook: ['.util-bar-module-firefly-visible'],
9220          fanboyEnhancedTrackers: [
9221              '.open.pushModal',
9222              '#issuem-leaky-paywall-articles-zero-remaining-nag',
9223              '#sovrn_container',
9224              'div[class$="-hide"][zoompage-fontsize][style="display: block;"]',
9225              '.BlockNag__Card',
9226          ],
9227          fanboySocial: ['#FollowUs', '#meteored_share', '#social_follow', '.article-sharer', '.community__social-desc'],
9228          frellwitSwedish: [
9229              fromB64('YVtocmVmKj0iY2FzaW5vcHJvLnNlIl1bdGFyZ2V0PSJfYmxhbmsiXQ=='),
9230              fromB64('YVtocmVmKj0iZG9rdG9yLXNlLm9uZWxpbmsubWUiXQ=='),
9231              'article.category-samarbete',
9232              fromB64('ZGl2LmhvbGlkQWRz'),
9233              'ul.adsmodern',
9234          ],
9235          greekAdBlock: [
9236              fromB64('QVtocmVmKj0iYWRtYW4ub3RlbmV0LmdyL2NsaWNrPyJd'),
9237              fromB64('QVtocmVmKj0iaHR0cDovL2F4aWFiYW5uZXJzLmV4b2R1cy5nci8iXQ=='),
9238              fromB64('QVtocmVmKj0iaHR0cDovL2ludGVyYWN0aXZlLmZvcnRobmV0LmdyL2NsaWNrPyJd'),
9239              'DIV.agores300',
9240              'TABLE.advright',
9241          ],
9242          hungarian: [
9243              '#cemp_doboz',
9244              '.optimonk-iframe-container',
9245              fromB64('LmFkX19tYWlu'),
9246              fromB64('W2NsYXNzKj0iR29vZ2xlQWRzIl0='),
9247              '#hirdetesek_box',
9248          ],
9249          iDontCareAboutCookies: [
9250              '.alert-info[data-block-track*="CookieNotice"]',
9251              '.ModuleTemplateCookieIndicator',
9252              '.o--cookies--container',
9253              '#cookies-policy-sticky',
9254              '#stickyCookieBar',
9255          ],
9256          icelandicAbp: [fromB64('QVtocmVmXj0iL2ZyYW1ld29yay9yZXNvdXJjZXMvZm9ybXMvYWRzLmFzcHgiXQ==')],
9257          latvian: [
9258              fromB64('YVtocmVmPSJodHRwOi8vd3d3LnNhbGlkemluaS5sdi8iXVtzdHlsZT0iZGlzcGxheTogYmxvY2s7IHdpZHRoOiAxMjBweDsgaGVpZ2h0O' +
9259                  'iA0MHB4OyBvdmVyZmxvdzogaGlkZGVuOyBwb3NpdGlvbjogcmVsYXRpdmU7Il0='),
9260              fromB64('YVtocmVmPSJodHRwOi8vd3d3LnNhbGlkemluaS5sdi8iXVtzdHlsZT0iZGlzcGxheTogYmxvY2s7IHdpZHRoOiA4OHB4OyBoZWlnaHQ6I' +
9261                  'DMxcHg7IG92ZXJmbG93OiBoaWRkZW47IHBvc2l0aW9uOiByZWxhdGl2ZTsiXQ=='),
9262          ],
9263          listKr: [
9264              fromB64('YVtocmVmKj0iLy9hZC5wbGFuYnBsdXMuY28ua3IvIl0='),
9265              fromB64('I2xpdmVyZUFkV3JhcHBlcg=='),
9266              fromB64('YVtocmVmKj0iLy9hZHYuaW1hZHJlcC5jby5rci8iXQ=='),
9267              fromB64('aW5zLmZhc3R2aWV3LWFk'),
9268              '.revenue_unit_item.dable',
vendor: 15,412 bytes, lines 9269-9661
9269          ],
9270          listeAr: [
9271              fromB64('LmdlbWluaUxCMUFk'),
9272              '.right-and-left-sponsers',
9273              fromB64('YVtocmVmKj0iLmFmbGFtLmluZm8iXQ=='),
9274              fromB64('YVtocmVmKj0iYm9vcmFxLm9yZyJd'),
9275              fromB64('YVtocmVmKj0iZHViaXp6bGUuY29tL2FyLz91dG1fc291cmNlPSJd'),
9276          ],
9277          listeFr: [
9278              fromB64('YVtocmVmXj0iaHR0cDovL3Byb21vLnZhZG9yLmNvbS8iXQ=='),
9279              fromB64('I2FkY29udGFpbmVyX3JlY2hlcmNoZQ=='),
9280              fromB64('YVtocmVmKj0id2Vib3JhbWEuZnIvZmNnaS1iaW4vIl0='),
9281              '.site-pub-interstitiel',
9282              'div[id^="crt-"][data-criteo-id]',
9283          ],
9284          officialPolish: [
9285              '#ceneo-placeholder-ceneo-12',
9286              fromB64('W2hyZWZePSJodHRwczovL2FmZi5zZW5kaHViLnBsLyJd'),
9287              fromB64('YVtocmVmXj0iaHR0cDovL2Fkdm1hbmFnZXIudGVjaGZ1bi5wbC9yZWRpcmVjdC8iXQ=='),
9288              fromB64('YVtocmVmXj0iaHR0cDovL3d3dy50cml6ZXIucGwvP3V0bV9zb3VyY2UiXQ=='),
9289              fromB64('ZGl2I3NrYXBpZWNfYWQ='),
9290          ],
9291          ro: [
9292              fromB64('YVtocmVmXj0iLy9hZmZ0cmsuYWx0ZXgucm8vQ291bnRlci9DbGljayJd'),
9293              fromB64('YVtocmVmXj0iaHR0cHM6Ly9ibGFja2ZyaWRheXNhbGVzLnJvL3Ryay9zaG9wLyJd'),
9294              fromB64('YVtocmVmXj0iaHR0cHM6Ly9ldmVudC4ycGVyZm9ybWFudC5jb20vZXZlbnRzL2NsaWNrIl0='),
9295              fromB64('YVtocmVmXj0iaHR0cHM6Ly9sLnByb2ZpdHNoYXJlLnJvLyJd'),
9296              'a[href^="/url/"]',
9297          ],
9298          ruAd: [
9299              fromB64('YVtocmVmKj0iLy9mZWJyYXJlLnJ1LyJd'),
9300              fromB64('YVtocmVmKj0iLy91dGltZy5ydS8iXQ=='),
9301              fromB64('YVtocmVmKj0iOi8vY2hpa2lkaWtpLnJ1Il0='),
9302              '#pgeldiz',
9303              '.yandex-rtb-block',
9304          ],
9305          thaiAds: [
9306              'a[href*=macau-uta-popup]',
9307              fromB64('I2Fkcy1nb29nbGUtbWlkZGxlX3JlY3RhbmdsZS1ncm91cA=='),
9308              fromB64('LmFkczMwMHM='),
9309              '.bumq',
9310              '.img-kosana',
9311          ],
9312          webAnnoyancesUltralist: [
9313              '#mod-social-share-2',
9314              '#social-tools',
9315              fromB64('LmN0cGwtZnVsbGJhbm5lcg=='),
9316              '.zergnet-recommend',
9317              '.yt.btn-link.btn-md.btn',
9318          ],
9319      };
9320  }
9321  /**
9322   * The order of the returned array means nothing (it's always sorted alphabetically).
9323   *
9324   * Notice that the source is slightly unstable.
9325   * Safari provides a 2-taps way to disable all content blockers on a page temporarily.
9326   * Also content blockers can be disabled permanently for a domain, but it requires 4 taps.
9327   * So empty array shouldn't be treated as "no blockers", it should be treated as "no signal".
9328   * If you are a website owner, don't make your visitors want to disable content blockers.
9329   */
9330  async function getDomBlockers({ debug } = {}) {
9331      if (!isApplicable()) {
9332          return undefined;
9333      }
9334      const filters = getFilters();
9335      const filterNames = Object.keys(filters);
9336      const allSelectors = [].concat(...filterNames.map((filterName) => filters[filterName]));
9337      const blockedSelectors = await getBlockedSelectors(allSelectors);
9338      if (debug) {
9339          printDebug(filters, blockedSelectors);
9340      }
9341      const activeBlockers = filterNames.filter((filterName) => {
9342          const selectors = filters[filterName];
9343          const blockedCount = countTruthy(selectors.map((selector) => blockedSelectors[selector]));
9344          return blockedCount > selectors.length * 0.6;
9345      });
9346      activeBlockers.sort();
9347      return activeBlockers;
9348  }
9349  function isApplicable() {
9350      // Safari (desktop and mobile) and all Android browsers keep content blockers in both regular and private mode
9351      return isWebKit() || isAndroid();
9352  }
9353  async function getBlockedSelectors(selectors) {
9354      var _a;
9355      const d = document;
9356      const root = d.createElement('div');
9357      const elements = new Array(selectors.length);
9358      const blockedSelectors = {}; // Set() isn't used just in case somebody need older browser support
9359      forceShow(root);
9360      // First create all elements that can be blocked. If the DOM steps below are done in a single cycle,
9361      // browser will alternate tree modification and layout reading, that is very slow.
9362      for (let i = 0; i < selectors.length; ++i) {
9363          const element = selectorToElement(selectors[i]);
9364          if (element.tagName === 'DIALOG') {
9365              element.show();
9366          }
9367          const holder = d.createElement('div'); // Protects from unwanted effects of `+` and `~` selectors of filters
9368          forceShow(holder);
9369          holder.appendChild(element);
9370          root.appendChild(holder);
9371          elements[i] = element;
9372      }
9373      // document.body can be null while the page is loading
9374      while (!d.body) {
9375          await wait(50);
9376      }
9377      d.body.appendChild(root);
9378      try {
9379          // Then check which of the elements are blocked
9380          for (let i = 0; i < selectors.length; ++i) {
9381              if (!elements[i].offsetParent) {
9382                  blockedSelectors[selectors[i]] = true;
9383              }
9384          }
9385      }
9386      finally {
9387          // Then remove the elements
9388          (_a = root.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(root);
9389      }
9390      return blockedSelectors;
9391  }
9392  function forceShow(element) {
9393      element.style.setProperty('visibility', 'hidden', 'important');
9394      element.style.setProperty('display', 'block', 'important');
9395  }
9396  function printDebug(filters, blockedSelectors) {
9397      let message = 'DOM blockers debug:\n```';
9398      for (const filterName of Object.keys(filters)) {
9399          message += `\n${filterName}:`;
9400          for (const selector of filters[filterName]) {
9401              message += `\n  ${blockedSelectors[selector] ? '🚫' : '➡️'} ${selector}`;
9402          }
9403      }
9404      // console.log is ok here because it's under a debug clause
9405      // eslint-disable-next-line no-console
9406      console.log(`${message}\n\`\`\``);
9407  }
9408
9409  /**
9410   * @see https://developer.mozilla.org/en-US/docs/Web/CSS/@media/color-gamut
9411   */
9412  function getColorGamut() {
9413      // rec2020 includes p3 and p3 includes srgb
9414      for (const gamut of ['rec2020', 'p3', 'srgb']) {
9415          if (matchMedia(`(color-gamut: ${gamut})`).matches) {
9416              return gamut;
9417          }
9418      }
9419      return undefined;
9420  }
9421
9422  /**
9423   * @see https://developer.mozilla.org/en-US/docs/Web/CSS/@media/inverted-colors
9424   */
9425  function areColorsInverted() {
9426      if (doesMatch$5('inverted')) {
9427          return true;
9428      }
9429      if (doesMatch$5('none')) {
9430          return false;
9431      }
9432      return undefined;
9433  }
9434  function doesMatch$5(value) {
9435      return matchMedia(`(inverted-colors: ${value})`).matches;
9436  }
9437
9438  /**
9439   * @see https://developer.mozilla.org/en-US/docs/Web/CSS/@media/forced-colors
9440   */
9441  function areColorsForced() {
9442      if (doesMatch$4('active')) {
9443          return true;
9444      }
9445      if (doesMatch$4('none')) {
9446          return false;
9447      }
9448      return undefined;
9449  }
9450  function doesMatch$4(value) {
9451      return matchMedia(`(forced-colors: ${value})`).matches;
9452  }
9453
9454  const maxValueToCheck = 100;
9455  /**
9456   * If the display is monochrome (e.g. black&white), the value will be ≥0 and will mean the number of bits per pixel.
9457   * If the display is not monochrome, the returned value will be 0.
9458   * If the browser doesn't support this feature, the returned value will be undefined.
9459   *
9460   * @see https://developer.mozilla.org/en-US/docs/Web/CSS/@media/monochrome
9461   */
9462  function getMonochromeDepth() {
9463      if (!matchMedia('(min-monochrome: 0)').matches) {
9464          // The media feature isn't supported by the browser
9465          return undefined;
9466      }
9467      // A variation of binary search algorithm can be used here.
9468      // But since expected values are very small (≤10), there is no sense in adding the complexity.
9469      for (let i = 0; i <= maxValueToCheck; ++i) {
9470          if (matchMedia(`(max-monochrome: ${i})`).matches) {
9471              return i;
9472          }
9473      }
9474      throw new Error('Too high value');
9475  }
9476
9477  /**
9478   * @see https://www.w3.org/TR/mediaqueries-5/#prefers-contrast
9479   * @see https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-contrast
9480   */
9481  function getContrastPreference() {
9482      if (doesMatch$3('no-preference')) {
9483          return 0 /* ContrastPreference.None */;
9484      }
9485      // The sources contradict on the keywords. Probably 'high' and 'low' will never be implemented.
9486      // Need to check it when all browsers implement the feature.
9487      if (doesMatch$3('high') || doesMatch$3('more')) {
9488          return 1 /* ContrastPreference.More */;
9489      }
9490      if (doesMatch$3('low') || doesMatch$3('less')) {
9491          return -1 /* ContrastPreference.Less */;
9492      }
9493      if (doesMatch$3('forced')) {
9494          return 10 /* ContrastPreference.ForcedColors */;
9495      }
9496      return undefined;
9497  }
9498  function doesMatch$3(value) {
9499      return matchMedia(`(prefers-contrast: ${value})`).matches;
9500  }
9501
9502  /**
9503   * @see https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion
9504   */
9505  function isMotionReduced() {
9506      if (doesMatch$2('reduce')) {
9507          return true;
9508      }
9509      if (doesMatch$2('no-preference')) {
9510          return false;
9511      }
9512      return undefined;
9513  }
9514  function doesMatch$2(value) {
9515      return matchMedia(`(prefers-reduced-motion: ${value})`).matches;
9516  }
9517
9518  /**
9519   * @see https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-transparency
9520   */
9521  function isTransparencyReduced() {
9522      if (doesMatch$1('reduce')) {
9523          return true;
9524      }
9525      if (doesMatch$1('no-preference')) {
9526          return false;
9527      }
9528      return undefined;
9529  }
9530  function doesMatch$1(value) {
9531      return matchMedia(`(prefers-reduced-transparency: ${value})`).matches;
9532  }
9533
9534  /**
9535   * @see https://www.w3.org/TR/mediaqueries-5/#dynamic-range
9536   */
9537  function isHDR() {
9538      if (doesMatch('high')) {
9539          return true;
9540      }
9541      if (doesMatch('standard')) {
9542          return false;
9543      }
9544      return undefined;
9545  }
9546  function doesMatch(value) {
9547      return matchMedia(`(dynamic-range: ${value})`).matches;
9548  }
9549
9550  const M = Math; // To reduce the minified code size
9551  const fallbackFn = () => 0;
9552  /**
9553   * @see https://gitlab.torproject.org/legacy/trac/-/issues/13018
9554   * @see https://bugzilla.mozilla.org/show_bug.cgi?id=531915
9555   */
9556  function getMathFingerprint() {
9557      // Native operations
9558      const acos = M.acos || fallbackFn;
9559      const acosh = M.acosh || fallbackFn;
9560      const asin = M.asin || fallbackFn;
9561      const asinh = M.asinh || fallbackFn;
9562      const atanh = M.atanh || fallbackFn;
9563      const atan = M.atan || fallbackFn;
9564      const sin = M.sin || fallbackFn;
9565      const sinh = M.sinh || fallbackFn;
9566      const cos = M.cos || fallbackFn;
9567      const cosh = M.cosh || fallbackFn;
9568      const tan = M.tan || fallbackFn;
9569      const tanh = M.tanh || fallbackFn;
9570      const exp = M.exp || fallbackFn;
9571      const expm1 = M.expm1 || fallbackFn;
9572      const log1p = M.log1p || fallbackFn;
9573      // Operation polyfills
9574      const powPI = (value) => M.pow(M.PI, value);
9575      const acoshPf = (value) => M.log(value + M.sqrt(value * value - 1));
9576      const asinhPf = (value) => M.log(value + M.sqrt(value * value + 1));
9577      const atanhPf = (value) => M.log((1 + value) / (1 - value)) / 2;
9578      const sinhPf = (value) => M.exp(value) - 1 / M.exp(value) / 2;
9579      const coshPf = (value) => (M.exp(value) + 1 / M.exp(value)) / 2;
9580      const expm1Pf = (value) => M.exp(value) - 1;
9581      const tanhPf = (value) => (M.exp(2 * value) - 1) / (M.exp(2 * value) + 1);
9582      const log1pPf = (value) => M.log(1 + value);
9583      // Note: constant values are empirical
9584      return {
9585          acos: acos(0.123124234234234242),
9586          acosh: acosh(1e308),
9587          acoshPf: acoshPf(1e154),
9588          asin: asin(0.123124234234234242),
9589          asinh: asinh(1),
9590          asinhPf: asinhPf(1),
9591          atanh: atanh(0.5),
9592          atanhPf: atanhPf(0.5),
9593          atan: atan(0.5),
9594          sin: sin(-1e300),
9595          sinh: sinh(1),
9596          sinhPf: sinhPf(1),
9597          cos: cos(10.000000000123),
9598          cosh: cosh(1),
9599          coshPf: coshPf(1),
9600          tan: tan(-1e300),
9601          tanh: tanh(1),
9602          tanhPf: tanhPf(1),
9603          exp: exp(1),
9604          expm1: expm1(1),
9605          expm1Pf: expm1Pf(1),
9606          log1p: log1p(10),
9607          log1pPf: log1pPf(10),
9608          powPI: powPI(-100),
9609      };
9610  }
9611
9612  /**
9613   * We use m or w because these two characters take up the maximum width.
9614   * Also there are a couple of ligatures.
9615   */
9616  const defaultText = 'mmMwWLliI0fiflO&1';
9617  /**
9618   * Settings of text blocks to measure. The keys are random but persistent words.
9619   */
9620  const presets = {
9621      /**
9622       * The default font. User can change it in desktop Chrome, desktop Firefox, IE 11,
9623       * Android Chrome (but only when the size is ≥ than the default) and Android Firefox.
9624       */
9625      default: [],
9626      /** OS font on macOS. User can change its size and weight. Applies after Safari restart. */
9627      apple: [{ font: '-apple-system-body' }],
9628      /** User can change it in desktop Chrome and desktop Firefox. */
9629      serif: [{ fontFamily: 'serif' }],
9630      /** User can change it in desktop Chrome and desktop Firefox. */
9631      sans: [{ fontFamily: 'sans-serif' }],
9632      /** User can change it in desktop Chrome and desktop Firefox. */
9633      mono: [{ fontFamily: 'monospace' }],
9634      /**
9635       * Check the smallest allowed font size. User can change it in desktop Chrome, desktop Firefox and desktop Safari.
9636       * The height can be 0 in Chrome on a retina display.
9637       */
9638      min: [{ fontSize: '1px' }],
9639      /** Tells one OS from another in desktop Chrome. */
9640      system: [{ fontFamily: 'system-ui' }],
9641  };
9642  /**
9643   * The result is a dictionary of the width of the text samples.
9644   * Heights aren't included because they give no extra entropy and are unstable.
9645   *
9646   * The result is very stable in IE 11, Edge 18 and Safari 14.
9647   * The result changes when the OS pixel density changes in Chromium 87. The real pixel density is required to solve,
9648   * but seems like it's impossible: https://stackoverflow.com/q/1713771/1118709.
9649   * The "min" and the "mono" (only on Windows) value may change when the page is zoomed in Firefox 87.
9650   */
9651  function getFontPreferences() {
9652      return withNaturalFonts((document, container) => {
9653          const elements = {};
9654          const sizes = {};
9655          // First create all elements to measure. If the DOM steps below are done in a single cycle,
9656          // browser will alternate tree modification and layout reading, that is very slow.
9657          for (const key of Object.keys(presets)) {
9658              const [style = {}, text = defaultText] = presets[key];
9659              const element = document.createElement('span');
9660              element.textContent = text;
9661              element.style.whiteSpace = 'nowrap';
vendor: 6,570 bytes, lines 9662-9780
9662              for (const name of Object.keys(style)) {
9663                  const value = style[name];
9664                  if (value !== undefined) {
9665                      element.style[name] = value;
9666                  }
9667              }
9668              elements[key] = element;
9669              container.append(document.createElement('br'), element);
9670          }
9671          // Then measure the created elements
9672          for (const key of Object.keys(presets)) {
9673              sizes[key] = elements[key].getBoundingClientRect().width;
9674          }
9675          return sizes;
9676      });
9677  }
9678  /**
9679   * Creates a DOM environment that provides the most natural font available, including Android OS font.
9680   * Measurements of the elements are zoom-independent.
9681   * Don't put a content to measure inside an absolutely positioned element.
9682   */
9683  function withNaturalFonts(action, containerWidthPx = 4000) {
9684      /*
9685       * Requirements for Android Chrome to apply the system font size to a text inside an iframe:
9686       * - The iframe mustn't have a `display: none;` style;
9687       * - The text mustn't be positioned absolutely;
9688       * - The text block must be wide enough.
9689       *   2560px on some devices in portrait orientation for the biggest font size option (32px);
9690       * - There must be much enough text to form a few lines (I don't know the exact numbers);
9691       * - The text must have the `text-size-adjust: none` style. Otherwise the text will scale in "Desktop site" mode;
9692       *
9693       * Requirements for Android Firefox to apply the system font size to a text inside an iframe:
9694       * - The iframe document must have a header: `<meta name="viewport" content="width=device-width, initial-scale=1" />`.
9695       *   The only way to set it is to use the `srcdoc` attribute of the iframe;
9696       * - The iframe content must get loaded before adding extra content with JavaScript;
9697       *
9698       * https://example.com as the iframe target always inherits Android font settings so it can be used as a reference.
9699       *
9700       * Observations on how page zoom affects the measurements:
9701       * - macOS Safari 11.1, 12.1, 13.1, 14.0: zoom reset + offsetWidth = 100% reliable;
9702       * - macOS Safari 11.1, 12.1, 13.1, 14.0: zoom reset + getBoundingClientRect = 100% reliable;
9703       * - macOS Safari 14.0: offsetWidth = 5% fluctuation;
9704       * - macOS Safari 14.0: getBoundingClientRect = 5% fluctuation;
9705       * - iOS Safari 9, 10, 11.0, 12.0: haven't found a way to zoom a page (pinch doesn't change layout);
9706       * - iOS Safari 13.1, 14.0: zoom reset + offsetWidth = 100% reliable;
9707       * - iOS Safari 13.1, 14.0: zoom reset + getBoundingClientRect = 100% reliable;
9708       * - iOS Safari 14.0: offsetWidth = 100% reliable;
9709       * - iOS Safari 14.0: getBoundingClientRect = 100% reliable;
9710       * - Chrome 42, 65, 80, 87: zoom 1/devicePixelRatio + offsetWidth = 1px fluctuation;
9711       * - Chrome 42, 65, 80, 87: zoom 1/devicePixelRatio + getBoundingClientRect = 100% reliable;
9712       * - Chrome 87: offsetWidth = 1px fluctuation;
9713       * - Chrome 87: getBoundingClientRect = 0.7px fluctuation;
9714       * - Firefox 48, 51: offsetWidth = 10% fluctuation;
9715       * - Firefox 48, 51: getBoundingClientRect = 10% fluctuation;
9716       * - Firefox 52, 53, 57, 62, 66, 67, 68, 71, 75, 80, 84: offsetWidth = width 100% reliable, height 10% fluctuation;
9717       * - Firefox 52, 53, 57, 62, 66, 67, 68, 71, 75, 80, 84: getBoundingClientRect = width 100% reliable, height 10%
9718       *   fluctuation;
9719       * - Android Chrome 86: haven't found a way to zoom a page (pinch doesn't change layout);
9720       * - Android Firefox 84: font size in accessibility settings changes all the CSS sizes, but offsetWidth and
9721       *   getBoundingClientRect keep measuring with regular units, so the size reflects the font size setting and doesn't
9722       *   fluctuate;
9723       * - IE 11, Edge 18: zoom 1/devicePixelRatio + offsetWidth = 100% reliable;
9724       * - IE 11, Edge 18: zoom 1/devicePixelRatio + getBoundingClientRect = reflects the zoom level;
9725       * - IE 11, Edge 18: offsetWidth = 100% reliable;
9726       * - IE 11, Edge 18: getBoundingClientRect = 100% reliable;
9727       */
9728      return withIframe((_, iframeWindow) => {
9729          const iframeDocument = iframeWindow.document;
9730          const iframeBody = iframeDocument.body;
9731          const bodyStyle = iframeBody.style;
9732          bodyStyle.width = `${containerWidthPx}px`;
9733          bodyStyle.webkitTextSizeAdjust = bodyStyle.textSizeAdjust = 'none';
9734          // See the big comment above
9735          if (isChromium()) {
9736              iframeBody.style.zoom = `${1 / iframeWindow.devicePixelRatio}`;
9737          }
9738          else if (isWebKit()) {
9739              iframeBody.style.zoom = 'reset';
9740          }
9741          // See the big comment above
9742          const linesOfText = iframeDocument.createElement('div');
9743          linesOfText.textContent = [...Array((containerWidthPx / 20) << 0)].map(() => 'word').join(' ');
9744          iframeBody.appendChild(linesOfText);
9745          return action(iframeDocument, iframeBody);
9746      }, '<!doctype html><html><head><meta name="viewport" content="width=device-width, initial-scale=1">');
9747  }
9748
9749  function isPdfViewerEnabled() {
9750      return navigator.pdfViewerEnabled;
9751  }
9752
9753  /**
9754   * Unlike most other architectures, on x86/x86-64 when floating-point instructions
9755   * have no NaN arguments, but produce NaN output, the output NaN has sign bit set.
9756   * We use it to distinguish x86/x86-64 from other architectures, by doing subtraction
9757   * of two infinities (must produce NaN per IEEE 754 standard).
9758   *
9759   * See https://codebrowser.bddppq.com/pytorch/pytorch/third_party/XNNPACK/src/init.c.html#79
9760   */
9761  function getArchitecture() {
9762      const f = new Float32Array(1);
9763      const u8 = new Uint8Array(f.buffer);
9764      f[0] = Infinity;
9765      f[0] = f[0] - f[0];
9766      return u8[3];
9767  }
9768
9769  /**
9770   * The return type is a union instead of the enum, because it's too challenging to embed the const enum into another
9771   * project. Turning it into a union is a simple and an elegant solution.
9772   */
9773  function getApplePayState() {
9774      const { ApplePaySession } = window;
9775      if (typeof (ApplePaySession === null || ApplePaySession === void 0 ? void 0 : ApplePaySession.canMakePayments) !== 'function') {
9776          return -1 /* ApplePayState.NoAPI */;
9777      }
9778      if (willPrintConsoleError()) {
9779          return -3 /* ApplePayState.NotAvailableInFrame */;
9780      }
vendor: 24,098 bytes, lines 9781-10311
9781      try {
9782          return ApplePaySession.canMakePayments() ? 1 /* ApplePayState.Enabled */ : 0 /* ApplePayState.Disabled */;
9783      }
9784      catch (error) {
9785          return getStateFromError(error);
9786      }
9787  }
9788  /**
9789   * Starting from Safari 15 calling `ApplePaySession.canMakePayments()` produces this error message when FingerprintJS
9790   * runs in an iframe with a cross-origin parent page, and the iframe on that page has no allow="payment *" attribute:
9791   *   Feature policy 'Payment' check failed for element with origin 'https://example.com' and allow attribute ''.
9792   * This function checks whether the error message is expected.
9793   *
9794   * We check for cross-origin parents, which is prone to false-positive results. Instead, we should check for allowed
9795   * feature/permission, but we can't because none of these API works in Safari yet:
9796   *   navigator.permissions.query({ name: ‘payment' })
9797   *   navigator.permissions.query({ name: ‘payment-handler' })
9798   *   document.featurePolicy
9799   */
9800  const willPrintConsoleError = isAnyParentCrossOrigin;
9801  function getStateFromError(error) {
9802      // See full expected error messages in the test
9803      if (error instanceof Error && error.name === 'InvalidAccessError' && /\bfrom\b.*\binsecure\b/i.test(error.message)) {
9804          return -2 /* ApplePayState.NotAvailableInInsecureContext */;
9805      }
9806      throw error;
9807  }
9808
9809  /**
9810   * Checks whether the Safari's Privacy Preserving Ad Measurement setting is on.
9811   * The setting is on when the value is not undefined.
9812   * A.k.a. private click measurement, privacy-preserving ad attribution.
9813   *
9814   * Unfortunately, it doesn't work in mobile Safari.
9815   * Probably, it will start working in mobile Safari or stop working in desktop Safari later.
9816   * We've found no way to detect the setting state in mobile Safari. Help wanted.
9817   *
9818   * @see https://webkit.org/blog/11529/introducing-private-click-measurement-pcm/
9819   * @see https://developer.apple.com/videos/play/wwdc2021/10033
9820   */
9821  function getPrivateClickMeasurement() {
9822      var _a;
9823      const link = document.createElement('a');
9824      const sourceId = (_a = link.attributionSourceId) !== null && _a !== void 0 ? _a : link.attributionsourceid;
9825      return sourceId === undefined ? undefined : String(sourceId);
9826  }
9827
9828  /** WebGl context is not available */
9829  const STATUS_NO_GL_CONTEXT = -1;
9830  /** WebGL context `getParameter` method is not a function */
9831  const STATUS_GET_PARAMETER_NOT_A_FUNCTION = -2;
9832  const validContextParameters = new Set([
9833      10752, 2849, 2884, 2885, 2886, 2928, 2929, 2930, 2931, 2932, 2960, 2961, 2962, 2963, 2964, 2965, 2966, 2967, 2968,
9834      2978, 3024, 3042, 3088, 3089, 3106, 3107, 32773, 32777, 32777, 32823, 32824, 32936, 32937, 32938, 32939, 32968, 32969,
9835      32970, 32971, 3317, 33170, 3333, 3379, 3386, 33901, 33902, 34016, 34024, 34076, 3408, 3410, 3411, 3412, 3413, 3414,
9836      3415, 34467, 34816, 34817, 34818, 34819, 34877, 34921, 34930, 35660, 35661, 35724, 35738, 35739, 36003, 36004, 36005,
9837      36347, 36348, 36349, 37440, 37441, 37443, 7936, 7937, 7938,
9838      // SAMPLE_ALPHA_TO_COVERAGE (32926) and SAMPLE_COVERAGE (32928) are excluded because they trigger a console warning
9839      // in IE, Chrome ≤ 59 and Safari ≤ 13 and give no entropy.
9840  ]);
9841  const validExtensionParams = new Set([
9842      34047,
9843      35723,
9844      36063,
9845      34852,
9846      34853,
9847      34854,
9848      34229,
9849      36392,
9850      36795,
9851      38449, // MAX_VIEWS_OVR
9852  ]);
9853  const shaderTypes = ['FRAGMENT_SHADER', 'VERTEX_SHADER'];
9854  const precisionTypes = ['LOW_FLOAT', 'MEDIUM_FLOAT', 'HIGH_FLOAT', 'LOW_INT', 'MEDIUM_INT', 'HIGH_INT'];
9855  const rendererInfoExtensionName = 'WEBGL_debug_renderer_info';
9856  const polygonModeExtensionName = 'WEBGL_polygon_mode';
9857  /**
9858   * Gets the basic and simple WebGL parameters
9859   */
9860  function getWebGlBasics({ cache }) {
9861      var _a, _b, _c, _d, _e, _f;
9862      const gl = getWebGLContext(cache);
9863      if (!gl) {
9864          return STATUS_NO_GL_CONTEXT;
9865      }
9866      if (!isValidParameterGetter(gl)) {
9867          return STATUS_GET_PARAMETER_NOT_A_FUNCTION;
9868      }
9869      const debugExtension = shouldAvoidDebugRendererInfo() ? null : gl.getExtension(rendererInfoExtensionName);
9870      return {
9871          version: ((_a = gl.getParameter(gl.VERSION)) === null || _a === void 0 ? void 0 : _a.toString()) || '',
9872          vendor: ((_b = gl.getParameter(gl.VENDOR)) === null || _b === void 0 ? void 0 : _b.toString()) || '',
9873          vendorUnmasked: debugExtension ? (_c = gl.getParameter(debugExtension.UNMASKED_VENDOR_WEBGL)) === null || _c === void 0 ? void 0 : _c.toString() : '',
9874          renderer: ((_d = gl.getParameter(gl.RENDERER)) === null || _d === void 0 ? void 0 : _d.toString()) || '',
9875          rendererUnmasked: debugExtension ? (_e = gl.getParameter(debugExtension.UNMASKED_RENDERER_WEBGL)) === null || _e === void 0 ? void 0 : _e.toString() : '',
9876          shadingLanguageVersion: ((_f = gl.getParameter(gl.SHADING_LANGUAGE_VERSION)) === null || _f === void 0 ? void 0 : _f.toString()) || '',
9877      };
9878  }
9879  /**
9880   * Gets the advanced and massive WebGL parameters and extensions
9881   */
9882  function getWebGlExtensions({ cache }) {
9883      const gl = getWebGLContext(cache);
9884      if (!gl) {
9885          return STATUS_NO_GL_CONTEXT;
9886      }
9887      if (!isValidParameterGetter(gl)) {
9888          return STATUS_GET_PARAMETER_NOT_A_FUNCTION;
9889      }
9890      const extensions = gl.getSupportedExtensions();
9891      const contextAttributes = gl.getContextAttributes();
9892      const unsupportedExtensions = [];
9893      // Features
9894      const attributes = [];
9895      const parameters = [];
9896      const extensionParameters = [];
9897      const shaderPrecisions = [];
9898      // Context attributes
9899      if (contextAttributes) {
9900          for (const attributeName of Object.keys(contextAttributes)) {
9901              attributes.push(`${attributeName}=${contextAttributes[attributeName]}`);
9902          }
9903      }
9904      // Context parameters
9905      const constants = getConstantsFromPrototype(gl);
9906      for (const constant of constants) {
9907          const code = gl[constant];
9908          parameters.push(`${constant}=${code}${validContextParameters.has(code) ? `=${gl.getParameter(code)}` : ''}`);
9909      }
9910      // Extension parameters
9911      if (extensions) {
9912          for (const name of extensions) {
9913              if ((name === rendererInfoExtensionName && shouldAvoidDebugRendererInfo()) ||
9914                  (name === polygonModeExtensionName && shouldAvoidPolygonModeExtensions())) {
9915                  continue;
9916              }
9917              const extension = gl.getExtension(name);
9918              if (!extension) {
9919                  unsupportedExtensions.push(name);
9920                  continue;
9921              }
9922              for (const constant of getConstantsFromPrototype(extension)) {
9923                  const code = extension[constant];
9924                  extensionParameters.push(`${constant}=${code}${validExtensionParams.has(code) ? `=${gl.getParameter(code)}` : ''}`);
9925              }
9926          }
9927      }
9928      // Shader precision
9929      for (const shaderType of shaderTypes) {
9930          for (const precisionType of precisionTypes) {
9931              const shaderPrecision = getShaderPrecision(gl, shaderType, precisionType);
9932              shaderPrecisions.push(`${shaderType}.${precisionType}=${shaderPrecision.join(',')}`);
9933          }
9934      }
9935      // Postprocess
9936      extensionParameters.sort();
9937      parameters.sort();
9938      return {
9939          contextAttributes: attributes,
9940          parameters: parameters,
9941          shaderPrecisions: shaderPrecisions,
9942          extensions: extensions,
9943          extensionParameters: extensionParameters,
9944          unsupportedExtensions,
9945      };
9946  }
9947  /**
9948   * This function usually takes the most time to execute in all the sources, therefore we cache its result.
9949   *
9950   * Warning for package users:
9951   * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk.
9952   */
9953  function getWebGLContext(cache) {
9954      if (cache.webgl) {
9955          return cache.webgl.context;
9956      }
9957      const canvas = document.createElement('canvas');
9958      let context;
9959      canvas.addEventListener('webglCreateContextError', () => (context = undefined));
9960      for (const type of ['webgl', 'experimental-webgl']) {
9961          try {
9962              context = canvas.getContext(type);
9963          }
9964          catch (_a) {
9965              // Ok, continue
9966          }
9967          if (context) {
9968              break;
9969          }
9970      }
9971      cache.webgl = { context };
9972      return context;
9973  }
9974  /**
9975   * https://developer.mozilla.org/en-US/docs/Web/API/WebGLShaderPrecisionFormat
9976   * https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/getShaderPrecisionFormat
9977   * https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.12
9978   */
9979  function getShaderPrecision(gl, shaderType, precisionType) {
9980      const shaderPrecision = gl.getShaderPrecisionFormat(gl[shaderType], gl[precisionType]);
9981      return shaderPrecision ? [shaderPrecision.rangeMin, shaderPrecision.rangeMax, shaderPrecision.precision] : [];
9982  }
9983  function getConstantsFromPrototype(obj) {
9984      // eslint-disable-next-line @typescript-eslint/no-explicit-any
9985      const keys = Object.keys(obj.__proto__);
9986      return keys.filter(isConstantLike);
9987  }
9988  function isConstantLike(key) {
9989      return typeof key === 'string' && !key.match(/[^A-Z0-9_x]/);
9990  }
9991  /**
9992   * Some browsers print a console warning when the WEBGL_debug_renderer_info extension is requested.
9993   * JS Agent aims to avoid printing messages to console, so we avoid this extension in that browsers.
9994   */
9995  function shouldAvoidDebugRendererInfo() {
9996      return isGecko();
9997  }
9998  /**
9999   * Some browsers print a console warning when the WEBGL_polygon_mode extension is requested.
10000   * JS Agent aims to avoid printing messages to console, so we avoid this extension in that browsers.
10001   */
10002  function shouldAvoidPolygonModeExtensions() {
10003      return isChromium() || isWebKit();
10004  }
10005  /**
10006   * Some unknown browsers have no `getParameter` method
10007   */
10008  function isValidParameterGetter(gl) {
10009      return typeof gl.getParameter === 'function';
10010  }
10011
10012  function getAudioContextBaseLatency() {
10013      // The signal emits warning in Chrome and Firefox, therefore it is enabled on Safari where it doesn't produce warning
10014      // and on Android where it's less visible
10015      const isAllowedPlatform = isAndroid() || isWebKit();
10016      if (!isAllowedPlatform) {
10017          return -2 /* SpecialFingerprint.Disabled */;
10018      }
10019      if (!window.AudioContext) {
10020          return -1 /* SpecialFingerprint.NotSupported */;
10021      }
10022      const latency = new AudioContext().baseLatency;
10023      if (latency === null || latency === undefined) {
10024          return -1 /* SpecialFingerprint.NotSupported */;
10025      }
10026      if (!isFinite(latency)) {
10027          return -3 /* SpecialFingerprint.NotFinite */;
10028      }
10029      return latency;
10030  }
10031
10032  /**
10033   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/resolvedOptions
10034   *
10035   * The return type is a union instead of a const enum due to the difficulty of embedding const enums in other projects.
10036   * This makes integration simpler and more elegant.
10037   */
10038  function getDateTimeLocale() {
10039      if (!window.Intl) {
10040          return -1 /* Status.IntlAPINotSupported */;
10041      }
10042      const DateTimeFormat = window.Intl.DateTimeFormat;
10043      if (!DateTimeFormat) {
10044          return -2 /* Status.DateTimeFormatNotSupported */;
10045      }
10046      const locale = DateTimeFormat().resolvedOptions().locale;
10047      if (!locale && locale !== '') {
10048          return -3 /* Status.LocaleNotAvailable */;
10049      }
10050      return locale;
10051  }
10052
10053  /**
10054   * The list of entropy sources used to make visitor identifiers.
10055   *
10056   * This value isn't restricted by Semantic Versioning, i.e. it may be changed without bumping minor or major version of
10057   * this package.
10058   *
10059   * Note: Rollup and Webpack are smart enough to remove unused properties of this object during tree-shaking, so there is
10060   * no need to export the sources individually.
10061   */
10062  const sources = {
10063      // READ FIRST:
10064      // See https://github.com/fingerprintjs/fingerprintjs/blob/master/contributing.md#how-to-add-an-entropy-source
10065      // to learn how entropy source works and how to make your own.
10066      // The sources run in this exact order.
10067      // The asynchronous sources are at the start to run in parallel with other sources.
10068      fonts: getFonts,
10069      domBlockers: getDomBlockers,
10070      fontPreferences: getFontPreferences,
10071      audio: getAudioFingerprint,
10072      screenFrame: getScreenFrame,
10073      canvas: getCanvasFingerprint,
10074      osCpu: getOsCpu,
10075      languages: getLanguages,
10076      colorDepth: getColorDepth,
10077      deviceMemory: getDeviceMemory,
10078      screenResolution: getScreenResolution,
10079      hardwareConcurrency: getHardwareConcurrency,
10080      timezone: getTimezone,
10081      sessionStorage: getSessionStorage,
10082      localStorage: getLocalStorage,
10083      indexedDB: getIndexedDB,
10084      openDatabase: getOpenDatabase,
10085      cpuClass: getCpuClass,
10086      platform: getPlatform,
10087      plugins: getPlugins,
10088      touchSupport: getTouchSupport,
10089      vendor: getVendor,
10090      vendorFlavors: getVendorFlavors,
10091      cookiesEnabled: areCookiesEnabled,
10092      colorGamut: getColorGamut,
10093      invertedColors: areColorsInverted,
10094      forcedColors: areColorsForced,
10095      monochrome: getMonochromeDepth,
10096      contrast: getContrastPreference,
10097      reducedMotion: isMotionReduced,
10098      reducedTransparency: isTransparencyReduced,
10099      hdr: isHDR,
10100      math: getMathFingerprint,
10101      pdfViewerEnabled: isPdfViewerEnabled,
10102      architecture: getArchitecture,
10103      applePay: getApplePayState,
10104      privateClickMeasurement: getPrivateClickMeasurement,
10105      audioBaseLatency: getAudioContextBaseLatency,
10106      dateTimeLocale: getDateTimeLocale,
10107      // Some sources can affect other sources (e.g. WebGL can affect canvas), so it's important to run these sources
10108      // after other sources.
10109      webGlBasics: getWebGlBasics,
10110      webGlExtensions: getWebGlExtensions,
10111  };
10112  /**
10113   * Loads the built-in entropy sources.
10114   * Returns a function that collects the entropy components to make the visitor identifier.
10115   */
10116  function loadBuiltinSources(options) {
10117      return loadSources(sources, options, []);
10118  }
10119
10120  const commentTemplate = '$ if upgrade to Pro: https://fingerprint.com/github/?utm_source=oss&utm_medium=referral&utm_campaign=confidence_score';
10121  function getConfidence(components) {
10122      const openConfidenceScore = getOpenConfidenceScore(components);
10123      const proConfidenceScore = deriveProConfidenceScore(openConfidenceScore);
10124      return { score: openConfidenceScore, comment: commentTemplate.replace(/\$/g, `${proConfidenceScore}`) };
10125  }
10126  function getOpenConfidenceScore(components) {
10127      // In order to calculate the true probability of the visitor identifier being correct, we need to know the number of
10128      // website visitors (the higher the number, the less the probability because the fingerprint entropy is limited).
10129      // JS agent doesn't know the number of visitors, so we can only do an approximate assessment.
10130      if (isAndroid()) {
10131          return 0.4;
10132      }
10133      // Safari (mobile and desktop)
10134      if (isWebKit()) {
10135          return isDesktopWebKit() && !(isWebKit616OrNewer() && isSafariWebKit()) ? 0.5 : 0.3;
10136      }
10137      const platform = 'value' in components.platform ? components.platform.value : '';
10138      // Windows
10139      if (/^Win/.test(platform)) {
10140          // The score is greater than on macOS because of the higher variety of devices running Windows.
10141          // Chrome provides more entropy than Firefox according too
10142          // https://netmarketshare.com/browser-market-share.aspx?options=%7B%22filter%22%3A%7B%22%24and%22%3A%5B%7B%22platform%22%3A%7B%22%24in%22%3A%5B%22Windows%22%5D%7D%7D%5D%7D%2C%22dateLabel%22%3A%22Trend%22%2C%22attributes%22%3A%22share%22%2C%22group%22%3A%22browser%22%2C%22sort%22%3A%7B%22share%22%3A-1%7D%2C%22id%22%3A%22browsersDesktop%22%2C%22dateInterval%22%3A%22Monthly%22%2C%22dateStart%22%3A%222019-11%22%2C%22dateEnd%22%3A%222020-10%22%2C%22segments%22%3A%22-1000%22%7D
10143          // So we assign the same score to them.
10144          return 0.6;
10145      }
10146      // macOS
10147      if (/^Mac/.test(platform)) {
10148          // Chrome provides more entropy than Safari and Safari provides more entropy than Firefox.
10149          // Chrome is more popular than Safari and Safari is more popular than Firefox according to
10150          // https://netmarketshare.com/browser-market-share.aspx?options=%7B%22filter%22%3A%7B%22%24and%22%3A%5B%7B%22platform%22%3A%7B%22%24in%22%3A%5B%22Mac%20OS%22%5D%7D%7D%5D%7D%2C%22dateLabel%22%3A%22Trend%22%2C%22attributes%22%3A%22share%22%2C%22group%22%3A%22browser%22%2C%22sort%22%3A%7B%22share%22%3A-1%7D%2C%22id%22%3A%22browsersDesktop%22%2C%22dateInterval%22%3A%22Monthly%22%2C%22dateStart%22%3A%222019-11%22%2C%22dateEnd%22%3A%222020-10%22%2C%22segments%22%3A%22-1000%22%7D
10151          // So we assign the same score to them.
10152          return 0.5;
10153      }
10154      // Another platform, e.g. a desktop Linux. It's rare, so it should be pretty unique.
10155      return 0.7;
10156  }
10157  function deriveProConfidenceScore(openConfidenceScore) {
10158      return round(0.99 + 0.01 * openConfidenceScore, 0.0001);
10159  }
10160
10161  function componentsToCanonicalString(components) {
10162      let result = '';
10163      for (const componentKey of Object.keys(components).sort()) {
10164          const component = components[componentKey];
10165          const value = 'error' in component ? 'error' : JSON.stringify(component.value);
10166          result += `${result ? '|' : ''}${componentKey.replace(/([:|\\])/g, '\\$1')}:${value}`;
10167      }
10168      return result;
10169  }
10170  function componentsToDebugString(components) {
10171      return JSON.stringify(components, (_key, value) => {
10172          if (value instanceof Error) {
10173              return errorToObject(value);
10174          }
10175          return value;
10176      }, 2);
10177  }
10178  function hashComponents(components) {
10179      return x64hash128(componentsToCanonicalString(components));
10180  }
10181  /**
10182   * Makes a GetResult implementation that calculates the visitor id hash on demand.
10183   * Designed for optimisation.
10184   */
10185  function makeLazyGetResult(components) {
10186      let visitorIdCache;
10187      // This function runs very fast, so there is no need to make it lazy
10188      const confidence = getConfidence(components);
10189      // A plain class isn't used because its getters and setters aren't enumerable.
10190      return {
10191          get visitorId() {
10192              if (visitorIdCache === undefined) {
10193                  visitorIdCache = hashComponents(this.components);
10194              }
10195              return visitorIdCache;
10196          },
10197          set visitorId(visitorId) {
10198              visitorIdCache = visitorId;
10199          },
10200          confidence,
10201          components,
10202          version,
10203      };
10204  }
10205  /**
10206   * A delay is required to ensure consistent entropy components.
10207   * See https://github.com/fingerprintjs/fingerprintjs/issues/254
10208   * and https://github.com/fingerprintjs/fingerprintjs/issues/307
10209   * and https://github.com/fingerprintjs/fingerprintjs/commit/945633e7c5f67ae38eb0fea37349712f0e669b18
10210   */
10211  function prepareForSources(delayFallback = 50) {
10212      // A proper deadline is unknown. Let it be twice the fallback timeout so that both cases have the same average time.
10213      return requestIdleCallbackIfAvailable(delayFallback, delayFallback * 2);
10214  }
10215  /**
10216   * The function isn't exported from the index file to not allow to call it without `load()`.
10217   * The hiding gives more freedom for future non-breaking updates.
10218   *
10219   * A factory function is used instead of a class to shorten the attribute names in the minified code.
10220   * Native private class fields could've been used, but TypeScript doesn't allow them with `"target": "es5"`.
10221   */
10222  function makeAgent(getComponents, debug) {
10223      const creationTime = Date.now();
10224      return {
10225          async get(options) {
10226              const startTime = Date.now();
10227              const components = await getComponents();
10228              const result = makeLazyGetResult(components);
10229              if (debug || (options === null || options === void 0 ? void 0 : options.debug)) {
10230                  // console.log is ok here because it's under a debug clause
10231                  // eslint-disable-next-line no-console
10232                  console.log(`Copy the text below to get the debug data:
10233
10234\`\`\`
10235version: ${result.version}
10236userAgent: ${navigator.userAgent}
10237timeBetweenLoadAndGet: ${startTime - creationTime}
10238visitorId: ${result.visitorId}
10239components: ${componentsToDebugString(components)}
10240\`\`\``);
10241              }
10242              return result;
10243          },
10244      };
10245  }
10246  /**
10247   * Sends an unpersonalized AJAX request to collect installation statistics
10248   */
10249  function monitor() {
10250      // The FingerprintJS CDN (https://github.com/fingerprintjs/cdn) replaces `window.__fpjs_d_m` with `true`
10251      if (window.__fpjs_d_m || Math.random() >= 0.001) {
10252          return;
10253      }
10254      try {
10255          const request = new XMLHttpRequest();
10256          request.open('get', `https://m1.openfpcdn.io/fingerprintjs/v${version}/npm-monitoring`, true);
10257          request.send();
10258      }
10259      catch (error) {
10260          // console.error is ok here because it's an unexpected error handler
10261          // eslint-disable-next-line no-console
10262          console.error(error);
10263      }
10264  }
10265  /**
10266   * Builds an instance of Agent and waits a delay required for a proper operation.
10267   */
10268  async function load(options = {}) {
10269      const { delayFallback, debug, monitoring = true } = options;
10270      if (monitoring) {
10271          monitor();
10272      }
10273      await prepareForSources(delayFallback);
10274      const getComponents = loadBuiltinSources({ cache: {}, debug });
10275      return makeAgent(getComponents, debug);
10276  }
10277
10278  // The default export is a syntax sugar (`import * as FP from '...' → import FP from '...'`).
10279  // It should contain all the public exported values.
10280  var index = { load, hashComponents, componentsToDebugString };
10281  // The exports below are for private usage. They may change unexpectedly. Use them at your own risk.
10282  /** Not documented, out of Semantic Versioning, usage is at your own risk */
10283  const murmurX64Hash128 = x64hash128;
10284
10285  exports.componentsToDebugString = componentsToDebugString;
10286  exports.default = index;
10287  exports.getFullscreenElement = getFullscreenElement;
10288  exports.getUnstableAudioFingerprint = getUnstableAudioFingerprint;
10289  exports.getUnstableCanvasFingerprint = getUnstableCanvasFingerprint;
10290  exports.getUnstableHardwareConcurrency = getUnstableHardwareConcurrency;
10291  exports.getUnstableScreenFrame = getUnstableScreenFrame;
10292  exports.getUnstableScreenResolution = getUnstableScreenResolution;
10293  exports.getWebGLContext = getWebGLContext;
10294  exports.hashComponents = hashComponents;
10295  exports.isAndroid = isAndroid;
10296  exports.isChromium = isChromium;
10297  exports.isDesktopWebKit = isDesktopWebKit;
10298  exports.isEdgeHTML = isEdgeHTML;
10299  exports.isGecko = isGecko;
10300  exports.isSamsungInternet = isSamsungInternet;
10301  exports.isTrident = isTrident;
10302  exports.isWebKit = isWebKit;
10303  exports.load = load;
10304  exports.loadSources = loadSources;
10305  exports.murmurX64Hash128 = murmurX64Hash128;
10306  exports.prepareForSources = prepareForSources;
10307  exports.sources = sources;
10308  exports.transformSource = transformSource;
10309  exports.withIframe = withIframe;
10310
10311  Object.defineProperty(exports, '__esModule', { value: true });
10312
10313  return exports;
10314
10315})({});
10316
10317"use strict"
10318var VetrinaMotoriUtils = {
10319    UniqueList: function (originalList) {
10320        var result = new Array();
10321
10322        $(originalList).each(function (index, item) {
10323            var exist = $.grep(result, function (it) {
10324                return it.MacroModelID == item.MacroModelID;
10325            });
10326            if ($(exist).length === 0) {
10327                result.push(item);
10328            }
10329        });
10330
10331        return result;
10332    },
10333    constants: {
10334        MyMotoLoginUrl: "/Views/IFrame/Access/NetworkLogin.aspx?fs=v",
10335        MyMotoMenuAutoUrl: "/Views/IFrame/IUserMenuAuto.aspx?fs=v",
10336        MyMotoSubscriptionPrivateUrl: "/MyAuto/Views/IFrame/Subscription/Privato.aspx?fs=v",
10337        MyMotoSubscriptionUrl: "/Views/IFrame/Subscription/NetworkSubscription.aspx?fs=v",
10338        MyMotoSubscriptionDealerUrl: "/Views/IFrame/Subscription/NetworkSubscriptionDealer.aspx?fs=v",
10339        MyMotoMenuUrl: "/Views/IFrame/INetworkUserMenuVM.aspx?fs=v",
10340        MyMotoMobileMenuUrl: "/Views/IFrame/Mobile/INetworkUserMenuVM.aspx?fs=v",
10341        MyMotoCheckAuthentication: "/Views/Handlers/Authentication/CheckAuthentication.ashx",
10342        LoginMode: 'Network'
10343    },
10344    TabNavigation: function () {
10345        //navigazione tab in tutto il sito
10346        var e = function (e) {
10347            $(".js-tab-content").each(function (i, t) {
10348                t.getAttribute("data-group") !== e && e || t.classList.remove("js-tab-initial", "js-tab-selected")
10349            })
10350        };
10351
10352        $(".js-tab-header").each(function (i, t) {
10353            t.addEventListener("click", function (event) {
10354                if ($(t).parents("#search-box-market").length == 0) {
10355                    event.preventDefault();
10356                }
10357                var n = t.getAttribute("data-target"),
10358                    r = document.getElementById(n),
10359                    o = t.getAttribute("data-group");
10360                $.cookie("sitenav-data-target", n);
10361                $.cookie("sitenav-data-group", o);
10362                e(o), r && (r.className += " js-tab-selected"), $(".js-tab-header").each(function (index, e) {
10363                    e.getAttribute("data-group") === o && e.classList.remove("js-tab-selected")
10364                }), t.classList.add("js-tab-selected")
10365            })
10366        });
10367
10368        // var navTarget = $.cookie("sitenav-data-target");
10369        // var navGroup = $.cookie("sitenav-data-group");
10370        // if (navTarget && navGroup) {
10371        //     //$('*[data-customerID="22"]');
10372        //     var clickedElements = $(".js-tab-header[data-target='"+ navTarget+"'][data-group='"+navGroup+"'] ");
10373        //     if (clickedElements.length > 0) {
10374        //         clickedElements[0].click();
10375        //     }
10376        // }
10377    },
10378    ShowNumber: function () {
10379        $(".phone-button").on("click", function (e) {
10380            var btn = this;
10381            $(btn).parent().addClass("show-number");
10382            var span = $(btn).parent().children(".phone-number");
10383            var uID = $(span).data('userinfo');
10384
10385            $.ajax({
10386                url: '/ajax/putdata',
10387                type: 'POST',
10388                headers: {'__RequestVerificationToken': $('[name=__RequestVerificationToken]').val()},
10389                data: {methodName: 'account/getauthcodebuyserid', jsonParameters: JSON.stringify({UserID: uID})}
10390            })
10391                .done(function (data) {
10392                    if (data.Success) {
10393                        var jsonData = JSON.parse(data["Result"]["ResultSet"]);
10394                        var phone = jsonData[0]["Phone"];
10395                        $(span).text(phone);
10396                    }
10397                });
10398        });
10399    },
10400    ShowAllButton: function () {
10401        $(".btn-show-all").each(function (i, e) {
10402            $(e).on("click", function (e) {
10403                $("#sidebarcarousel-otherbrands").toggle();
10404                $(this).toggleClass("opened");
10405            });
10406        });
10407    },
10408    GeoSearch: function () {
10409        var $body = $("body"),
10410            input = $("#geocomplete"),
10411            input_lat = $("#latitude"),
10412            input_lng = $("#longitude"),
10413            service = "/geo/place-suggest",
10414            event_name = "GetSearchChanged";
10415
10416        console.log("GeoSearch init");
10417
10418        input.autocomplete({
10419            source: service,
10420            minLength: 1,
10421            select: function (event, ui) {
10422
10423                input_lng.val(ui.item.Longitude);
10424
10425                input_lat.val(ui.item.Latitude);
10426
10427                $body.trigger(event_name);
10428            }
10429        });
10430
10431
10432        // clear underlaying values when needed
10433        input.keyup(function () {
10434
10435            if (input.val() === "") {
10436                input_lng.val("");
10437                input_lat.val("");
10438
10439                $body.trigger(event_name);
10440            }
10441
10442        });
10443    },
10444    IsLoggedIn: function () {
10445        return userAuthorized;
10446    },
10447    SetupLogin: function () {
10448        $("#btn-auth-text").click(function (e) {
10449            //console.log("click");
10450            e.preventDefault();
10451            e.stopPropagation();
10452            $(".login-panel").empty();
10453            $(".login-panel").append($('<iframe/>', {
10454                src: env.MyMotoUrl + VetrinaMotoriUtils.constants.MyMotoLoginUrl + '&ru=' + envCompounded.LoginRedirectUrl,
10455                seamless: '',
10456                width: '100%',
10457                height: '100%'
10458            }));
10459            $(".login-panel").css("display", "block");
10460        });
10461        $("#btn-auth-text-mobile").click(function (e) {
10462            e.preventDefault();
10463            e.stopPropagation();
10464            $(".login-panel").empty();
10465            $(".login-panel").append($('<iframe/>', {
10466                src: env.MyMotoUrl + VetrinaMotoriUtils.constants.MyMotoLoginUrl + '&ru=' + envCompounded.LoginRedirectUrl,
10467                seamless: '',
10468                width: '100%',
10469                border: '0',
10470                height: '100%'
10471            }));
10472            $(".login-panel").css("display", "block");
10473        });
10474    },
10475    SetupSubscription: function () {
10476        $("#btn-register-text-mobile").click(function (e) {
10477            e.preventDefault();
10478            e.stopPropagation();
10479            $(".login-panel").empty();
10480            $(".login-panel").append($('<iframe/>', {
10481                src: env.MyMotoUrl + VetrinaMotoriUtils.constants.MyMotoSubscriptionUrl + '&ru=' + envCompounded.LoginRedirectUrl,
10482                seamless: '',
10483                width: '100%',
10484                height: '100%'
10485            }));
10486            $(".login-panel").css("display", "block");
10487        });
10488
10489        $("#btn-dealer").click(function (e) {
10490            e.preventDefault();
10491            e.stopPropagation();
10492            $(".login-panel").empty();
10493            $(".login-panel").append($('<iframe/>', {
10494                src: env.MyMotoUrl + VetrinaMotoriUtils.constants.MyMotoSubscriptionDealerUrl + '&ru=' + envCompounded.LoginRedirectUrl,
10495                seamless: '',
10496                width: '100%',
10497                height: '100%'
10498            }));
10499            $(".login-panel").css("display", "block");
10500        });
10501
10502        $("#btn-dealer-mobile").click(function (e) {
10503            e.preventDefault();
10504            e.stopPropagation();
10505            $(".login-panel").empty();
10506            $(".login-panel").append($('<iframe/>', {
10507                src: env.MyMotoUrl + VetrinaMotoriUtils.constants.MyMotoSubscriptionDealerUrl + '&ru=' + envCompounded.LoginRedirectUrl,
10508                seamless: '',
10509                width: '100%',
10510                border: '0',
10511                height: '100%'
10512            }));
10513            $(".login-panel").css("display", "block");
10514        });
10515    },
10516    SetupDashboard: function () {
10517        $("#btn-auth-logged").click(function (e) {
10518            e.preventDefault();
10519            e.stopPropagation();
10520
10521            if (window.matchMedia("(min-width: 1024px)").matches) {
10522                if ($(".logged-panel-desktop").hasClass('opened')) {
10523                    $(".logged-panel-desktop").css("display", "none");
10524                    $(".logged-panel-desktop").removeClass("opened");
10525                } else {
10526                    $(".logged-panel-desktop").empty();
10527                    $(".logged-panel-desktop").append($('<iframe/>', {
10528                        src: env.MyMotoUrl + VetrinaMotoriUtils.constants.MyMotoMenuUrl + '&ru=' + envCompounded.LogoutRedirectUrl,
10529                        seamless: '',
10530                        width: '100%',
10531                        scrolling: 'no',
10532                        height: '340'
10533                    }));
10534                    $(".logged-panel-desktop").css("display", "block");
10535                    $(".logged-panel-desktop").addClass("opened");
10536                }
10537
10538            } else {
10539                $(".sidemenudx-logged-mobile-wrapper").empty();
10540                $(".sidemenudx-logged-mobile-wrapper").append($('<iframe/>', {
10541                    src: env.MyMotoUrl + VetrinaMotoriUtils.constants.MyMotoMobileMenuUrl + '&ru=' + envCompounded.LogoutRedirectUrl,
10542                    seamless: ''
10543                }));
10544                $(".sidemenudx-login-wrapper").hide();
10545                $('#btn-auth-logged').toggleClass('clicked');
10546                $('.sidemenudx-overlay').toggleClass('show');
10547                $('div.sidemenudx-mobileacc').toggleClass('show');
10548                $('body').toggleClass('overflow');
10549                $('html').toggleClass('freeze-body');
10550                $(".sidemenudx-logged-mobile-wrapper").show();
10551            }
10552        });
10553    },
10554    CheckNetworkLogin: function () {
10555        $.ajax({
10556            async: false,
10557            type: 'GET',
10558            crossDomain: true,
10559            dataType: 'jsonp',
10560            url: env.MyMotoUrl + VetrinaMotoriUtils.constants.MyMotoCheckAuthentication,
10561            done: function (result) {
10562            },
10563            success: function (response) {
10564                if (response["Result"] == "0") {
10565                    $.ajax({
10566                        type: 'POST',
10567                        url: env.PortalUrl + '/Account/VMLogout',
10568                        success: function (data) {
10569                            document.location.reload();
10570                        }
10571                    });
10572                }
10573            },
10574            fail: function (response) {
10575                alert('Si è verificato un errore: ' + response);
10576            }
10577        });
10578    },
10579    WarmUp: function () {
10580    },
10581    ScrollIntoView: function () {
10582        var btnScrollTo = $("[data-scroll-to-view]");
10583        $(btnScrollTo).each(function (i, item) {
10584            $(item).on("click", function (e) {
10585                var theView = $(item).attr("data-scroll-to-view");
10586                var offset = $(theView).offset();
10587
10588                $("html, body").animate({
10589                    scrollTop: offset.top - $('#content').offset().top + $('#content').scrollTop() - $("header").height() - 10,
10590                    scrolLeft: offset.left
10591                });
10592            });
10593        });
10594    },
10595    FormSearchBy: function (vehictype) {
10596        $("a[data-group='sidebarsearch']").on('click', function (evt, ui) {
10597
10598            var vm = ko.dataFor(document.getElementById("form-search"));
10599            if (vm) {
10600                ko.cleanNode(document.getElementById("form-search"));
10601                $("#form-search").remove();
10602            }
10603
10604            var divToFill = "#" + $(this).data("target"),
10605                url = $(this).data("url"),
10606                vehicle = $(this).data("vehicle");
10607
10608            $.get(url, function (data) {
10609                $(divToFill).empty();
10610                $(divToFill).html(data);
10611                $(divToFill).addClass("js-tab-selected");
10612
10613                var path = $("#search-bind").attr('src');
10614                if (vehicle === 'car') {
10615                    path = path.replace('UsedNewCommercials', 'UsedNewCars');
10616                    path = path.replace('UsedNewMotorbikes', 'UsedNewCars');
10617                } else if (vehicle === 'motorbike') {
10618                    path = path.replace('UsedNewCommercials', 'UsedNewMotorbikes');
10619                    path = path.replace('UsedNewCars', 'UsedNewMotorbikes');
10620                } else if (vehicle === 'commercial') {
10621                    path = path.replace('UsedNewCars', 'UsedNewCommercials');
10622                    path = path.replace('UsedNewMotorbikes', 'UsedNewCommercials');
10623                }
10624
10625                $("#search-bind").remove();
10626
10627                $('<script>').attr({
10628                    src: path,
10629                    id: 'search-bind'
10630                }).appendTo('body');
10631            });
10632            $("a[data-group='sidebarsearch']").unbind('click');
10633        });
10634    },
10635    ImageGallery: function () {
10636        //image gllery nel dettaglio modalità originale
10637        var slideCount = $(".gallery-images-list li").length;
10638
10639        var myObject = {
10640            title: $(".detail-info > h1").html(),
10641            images: []
10642        };
10643
10644        var list = $(".gallery-images-list li");
10645        $(list).each(function (index, item) {
10646            var fullimg = $(item).attr("data-fullimg");
10647            var bckimge = $(item).first('span').css("background-image").slice(4, -1).replace(/"/g, "");
10648
10649            myObject.images.push({
10650                id: index,
10651                full: fullimg,
10652                bck: bckimge,
10653                elem: item
10654            });
10655        });
10656
10657        function movePrevious() {
10658            var currentimg = $(".gallery-current-image").css("background-image").slice(4, -1).replace(/"/g, "");
10659            var current = $.grep(myObject.images, function (e) {
10660                return e.full == currentimg
10661            })[0];
10662
10663            var previd = current.id - 1;
10664            if (previd < 0) {
10665                previd = slideCount - 1;
10666            }
10667
10668            $(current.elem).removeClass("active");
10669            var find = $.grep(myObject.images, function (e) {
10670                return e.id == previd
10671            })[0];
10672            $(".gallery-current-image").css("background-image", "url(" + find.full + ")");
10673            $(find.elem).addClass("active");
10674        };
10675
10676        function moveNext() {
10677            var currentimg = $(".gallery-current-image").css("background-image").slice(4, -1).replace(/"/g, "");
10678            var current = $.grep(myObject.images, function (e) {
10679                return e.full == currentimg
10680            })[0];
10681
10682            var nextid = current.id + 1;
10683            if (nextid === slideCount) {
10684                nextid = 0;
10685            }
10686
10687            $(current.elem).removeClass("active");
10688            var find = $.grep(myObject.images, function (e) {
10689                return e.id == nextid
10690            })[0];
10691            $(".gallery-current-image").css("background-image", "url(" + find.full + ")");
10692            $(find.elem).addClass("active");
10693        };
10694
10695        $(".gallery-previous").on('click', function () {
10696            movePrevious();
10697        });
10698        $(".gallery-next").on('click', function () {
10699            moveNext();
10700        });
10701
10702        $(".gallery-images-list li").each(function (index, item) {
10703            $(item).on("click", function (e) {
10704                var url = $(item).attr("data-fullimg");
10705                $(".gallery-current-image").css("background-image", "url(" + url + ")");
10706
10707                $(".gallery-images-list li.active").removeClass('active');
10708                $(item).addClass("active");
10709            });
10710        })
10711    },
10712    SetBackPage: function () {
10713        $(window).on("beforeunload", function () {
10714            localStorage.setItem("currentsearch", document.location);
10715            localStorage.setItem("currentpage", document.location.hash);
10716        });
10717    },
10718    ManageFavorites: function (action, current, type) {
10719        var pm = new Object();
10720        pm.AdID = current;
10721        var params = {methodName: 'account/' + type + '/favorite/' + action, jsonParameters: JSON.stringify(pm)};
10722
10723        $.ajax({
10724            url: '/ajax/putdata',
10725            headers: {'__RequestVerificationToken': $('[name=__RequestVerificationToken]').val()},
10726            type: 'POST',
10727            data: params
10728        })
10729            .done(function (data) {
10730                console.log(data);
10731            });
10732    },
10733    ManageFavoritsOnCookie: function (action, current, type) {
10734        var arCookies;
10735        var valcookies = $.cookie("favorites");
10736        if (typeof valcookies === 'undefined' || !valcookies) {
10737            arCookies = [{'action': action, 'ID': current, 'type': type}];
10738        } else {
10739            arCookies = $.grep($.parseJSON(valcookies), function (o) {
10740                return (o !== undefined && o.ID !== current)
10741            });
10742            $.cookie("favorites", null);
10743            arCookies.push({'action': action, 'ID': current, 'type': type});
10744        }
10745        $.cookie("favorites", JSON.stringify(arCookies));
10746    },
10747    DrawCaptcha: function () {
10748        grecaptcha.render('captchaContainer', {
10749            'sitekey': '6Lf7DD0UAAAAALDgnPdeUtLWQFTmrs8w4v1SpD--'
10750        });
10751    },
10752    FormSendToFriend: function () {
10753        var self = this;
10754        self.SenderName = ko.observable().extend({required: {message: "Inserisci il tupo nome"}});
10755        self.SenderEmail = ko.observable().extend({required: {message: "inserisci la tua mail"}});
10756        self.ContactEmail = ko.observable().extend({required: true});//.extend({ required: {message:"Inserisci il nome del tuo amico"} });
10757        self.Message = ko.observable('Ciao,\r\n\tsu Vetrina Motori ho trovato questo annuncio che potrebbe interessarti.').extend({required: true}); //.extend({ required: true });
10758        self.Url = ko.computed(function () {
10759            return document.location.href;
10760        });
10761        self.Load = function () {
10762            $.ajax({
10763                url: '/ajax/putdata',
10764                headers: {'__RequestVerificationToken': $('[name=__RequestVerificationToken]').val()},
10765                type: 'POST',
10766                data: {methodName: 'account/getuserinfo', jsonParameters: JSON.stringify({Page: 1})},
10767                dataType: 'JSON'
10768            })
10769                .done(function (data) {
10770                    if (data.Success) {
10771                        var userInfo = $.parseJSON(data.Result.ResultSet)[0];
10772                        self.SenderName(userInfo.Name);
10773                        self.SenderEmail(userInfo.Email);
10774                    }
10775                });
10776        };
10777        self.Submit = function () {
10778            var apiBrandParam = new Object();
10779            apiBrandParam.ContactEmail = self.ContactEmail();
10780            apiBrandParam.SenderName = self.SenderName();
10781            apiBrandParam.SenderEmail = self.SenderEmail();
10782            apiBrandParam.Message = self.Message();
10783            apiBrandParam.UrlToShare = self.Url();
10784            $.ajax({
10785                url: '/ajax/putdata',
10786                headers: {'__RequestVerificationToken': $('[name=__RequestVerificationToken]').val()},
10787                type: 'POST',
10788                data: {methodName: 'common/sendtofriend', jsonParameters: JSON.stringify(apiBrandParam)},
10789                dataType: 'JSON'
10790            })
10791                .done(function (data) {
10792
10793                    $('#myModalSendToFriend').modal({
10794                        backdrop: true
10795                    });
10796
10797                    //alert("Annuncio inviato correttamente");
10798
10799                    // $("#send-to-friend").hide();
10800                    // $("#send-to-friend-success").show();
10801                    //window.location.reload();
10802                });
10803        };
10804        self.PerformValdation = function () {
10805            ko.validation.group(self).showAllMessages();
10806
10807            if (self.errors().length > 0) {
10808                console.log("Prevent something")
10809            } else {
10810                console.log("Good to go!")
10811            }
10812        };
10813    },
10814    ReportFraud: function () {
10815        var self = this;
10816        self.SenderName = ko.observable().extend({required: true});
10817        self.SenderEmail = ko.observable().extend({required: true});
10818        self.Message = ko.observable('').extend({required: true});
10819        self.Subject = ko.computed(function () {
10820            return document.getElementsByTagName("title")[0].innerHTML;
10821        });
10822        self.AcceptCondition = ko.observable(false).extend({required: true}),
10823            self.ChangeCondition = function () {
10824                self.AcceptCondition(!self.AcceptCondition());
10825                if (self.AcceptCondition()) {
10826                    $("#reportfraud-privacy-confirmation").hide();
10827                }
10828                //console.log(self.AcceptCondition());
10829            },
10830            self.VehicType = ko.computed(function () {
10831                var currentUl = document.location.href;
10832                var segmntsUrl = currentUl.split('/');
10833
10834                segmntsUrl = segmntsUrl.filter(function (n) {
10835                    return n !== "";
10836                });
10837                var lastSegment = segmntsUrl[segmntsUrl.length - 1];
10838                var segmentsLstSegment = lastSegment.split('-');
10839                segmentsLstSegment = segmentsLstSegment.filter(function (n) {
10840                    return n !== "";
10841                });
10842                var lasSegOfLast = segmentsLstSegment[segmentsLstSegment.length - 1];
10843
10844                if (lasSegOfLast.match("^au")) {
10845                    return "car";
10846                } else if (lasSegOfLast.match("^an")) {
10847                    return "car";
10848                } else if (lasSegOfLast.match("^ae")) {
10849                    return "car";
10850                } else if (lasSegOfLast.match("^cn")) {
10851                    return "car";
10852                } else if (lasSegOfLast.match("^cu")) {
10853                    return "car";
10854                } else if (lasSegOfLast.match("^mn")) {
10855                    return "motorbike";
10856                } else if (lasSegOfLast.match("^mu")) {
10857                    return "motorbike";
10858                } else if (lasSegOfLast.match("^me")) {
10859                    return "motorbike";
10860                }
10861                return "unknown";
10862            });
10863        self.AdID = ko.pureComputed(function () {
10864            //return $($("span.value")[0]).text();
10865
10866            var segmntsUrl = document.location.href.split('/').filter(function (n) {
10867                return n !== "";
10868            });
10869            var lastSegment = segmntsUrl[segmntsUrl.length - 1];
10870            var segmentsLstSegment = lastSegment.split('-').filter(function (n) {
10871                return n !== "";
10872            });
10873            var lasSegOfLast = segmentsLstSegment[segmentsLstSegment.length - 1];
10874
10875            var adId = lasSegOfLast
10876                .replace(/au/g, '')
10877                .replace(/an/g, '')
10878                .replace(/ae/g, '')
10879                .replace(/mu/g, '')
10880                .replace(/mn/g, '')
10881                .replace(/me/g, '')
10882                .replace(/cn/g, '')
10883                .replace(/cu/g, '');
10884
10885            return adId;
10886        });
10887        self.Load = function () {
10888            $.ajax({
10889                url: '/ajax/putdata',
10890                headers: {'__RequestVerificationToken': $('[name=__RequestVerificationToken]').val()},
10891                type: 'POST',
10892                data: {methodName: 'account/getuserinfo', jsonParameters: JSON.stringify({Page: 1})},
10893                dataType: 'JSON'
10894            })
10895                .done(function (data) {
10896                    if (data.Success) {
10897                        var userInfo = $.parseJSON(data.Result.ResultSet)[0];
10898                        self.SenderName(userInfo.Name);
10899                        self.SenderEmail(userInfo.Email);
10900                    }
10901                });
10902        };
10903        self.Submit = function () {
10904            if (!self.AcceptCondition()) {
10905                $("#reportfraud-privacy-confirmation").show();
10906                return false;
10907            } else {
10908                var apiParam = new Object();
10909                apiParam.AdID = self.AdID();
10910                apiParam.SenderPropsParameters = new Object();
10911                apiParam.SenderPropsParameters.CoreConsens = self.AcceptCondition();
10912                apiParam.SenderPropsParameters.ContactName = self.SenderName();
10913                apiParam.SenderPropsParameters.ContactEmail = self.SenderEmail();
10914                apiParam.SenderPropsParameters.ContactMessage = self.Message();
10915
10916                $.ajax({
10917                    url: '/ajax/putdata',
10918                    headers: {'__RequestVerificationToken': $('[name=__RequestVerificationToken]').val()},
10919                    type: 'POST',
10920                    data: {
10921                        methodName: 'market/' + self.VehicType() + '/reportfraud',
10922                        jsonParameters: JSON.stringify(apiParam)
10923                    },
10924                    dataType: 'JSON'
10925                })
10926                    .done(function (data) {
10927                        $('#myModal').modal({
10928                            backdrop: true
10929                        });
10930
10931                        //alert("Message sent successfully");
10932                        // $("#send-to-friend").hide();
10933                        // $("#send-to-friend-success").show();
10934                        //window.location.reload();
10935                    });
10936            }
10937        };
10938    },
10939    ContactDealer: function () {
10940        var self = this;
10941
10942        self.SenderName = ko.observable().extend({required: true});
10943        self.SenderEmail = ko.observable().extend({required: true});
10944        self.SenderPhone = ko.observable().extend({required: true});
10945        self.Message = ko.observable('').extend({required: true});
10946        self.DealerName = ko.computed(function () {
10947            var dealername = $(".seller-info-top > h2").text();
10948            return dealername;
10949        });
10950        self.AdID = ko.pureComputed(function () {
10951            // return $($("span.value")[0]).text();            
10952            var segmntsUrl = document.location.href.split('/').filter(function (n) {
10953                return n !== "";
10954            });
10955            var lastSegment = segmntsUrl[segmntsUrl.length - 1];
10956            var segmentsLstSegment = lastSegment.split('-').filter(function (n) {
10957                return n !== "";
10958            });
10959            var lasSegOfLast = segmentsLstSegment[segmentsLstSegment.length - 1];
10960
10961            var adId = lasSegOfLast
10962                .replace(/au/g, '')
10963                .replace(/an/g, '')
10964                .replace(/ae/g, '')
10965                .replace(/mu/g, '')
10966                .replace(/mn/g, '')
10967                .replace(/me/g, '')
10968                .replace(/cn/g, '')
10969                .replace(/cu/g, '');
10970
10971            return adId;
10972        });
10973        self.Privacy1 = ko.observable();
10974        self.Privacy2 = ko.observable();
10975        self.CheckPrivacy1 = ko.computed({
10976            read: function () {
10977                return self.Privacy1;
10978            },
10979            write: function (value) {
10980                //console.log(value);
10981                $("#id_privacy1").prop("checked", !$("#id_privacy1").prop("checked"));
10982
10983                self.Privacy1($("#id_privacy1").prop("checked"));
10984            }
10985        });
10986        self.CheckPrivacy2 = ko.computed({
10987            read: function () {
10988                return self.Privacy2;
10989            },
10990            write: function (value) {
10991                //console.log(value);
10992                $("#id_privacy2").prop("checked", !$("#id_privacy2").prop("checked"));
10993
10994                self.Privacy2($("#id_privacy2").prop("checked"));
10995            }
10996        });
10997        self.VehicType = ko.computed(function () {
10998            var currentUl = document.location.href;
10999            var segmntsUrl = currentUl.split('/');
11000
11001            segmntsUrl = segmntsUrl.filter(function (n) {
11002                return n !== "";
11003            });
11004            var lastSegment = segmntsUrl[segmntsUrl.length - 1];
11005            var segmentsLstSegment = lastSegment.split('-');
11006            segmentsLstSegment = segmentsLstSegment.filter(function (n) {
11007                return n !== "";
11008            });
11009            var lasSegOfLast = segmentsLstSegment[segmentsLstSegment.length - 1];
11010
11011            if (lasSegOfLast.match("^da")) {
11012                return "car";
11013            } else if (lasSegOfLast.match("^dm")) {
11014                return "motorbike";
11015            }
11016            return "unknown";
11017        });
11018        self.Load = function () {
11019            $.ajax({
11020                url: '/ajax/putdata',
11021                headers: {'__RequestVerificationToken': $('[name=__RequestVerificationToken]').val()},
11022                type: 'POST',
11023                data: {methodName: 'account/getuserinfo', jsonParameters: JSON.stringify({Page: 1})},
11024                dataType: 'JSON'
11025            })
11026                .done(function (data) {
11027                    if (data.Success) {
11028                        var userInfo = $.parseJSON(data.Result.ResultSet)[0];
11029                        self.SenderName(userInfo.Name);
11030                        self.SenderEmail(userInfo.Email);
11031                        self.SenderPhone(userInfo.Phone);
11032                    }
11033                });
11034        };
11035        self.Submit = function () {
11036            if (!self.Privacy1() || !self.Privacy2()) {
11037                alert("E' necessario accettare le condizioni della privacy");
11038                return;
11039            }
11040            var apiMessage = new Object();
11041            apiMessage.AdID = self.AdID();
11042            apiMessage.SenderPropsParameters = new Object();
11043            apiMessage.SenderPropsParameters.SenderFullName = self.SenderName();
11044            apiMessage.SenderPropsParameters.SenderEmail = self.SenderEmail();
11045            apiMessage.SenderPropsParameters.SenderPhoneNumber = self.SenderPhone();
11046            apiMessage.SenderPropsParameters.SenderMessage = self.Message();
11047
11048            $.ajax({
11049                url: '/ajax/putdata',
11050                headers: {'__RequestVerificationToken': $('[name=__RequestVerificationToken]').val()},
11051                type: 'POST',
11052                data: {
11053                    methodName: 'market/' + self.VehicType() + '/contactseller',
11054                    jsonParameters: JSON.stringify(apiMessage)
11055                },
11056                dataType: 'JSON'
11057            })
11058                .done(function (data) {
11059                    $('html,body').animate({
11060                        scrollTop: $("#vehicle-contact-seller").offset().top
11061                    }, 500);
11062                    //alert("Messaggio inviato correttamente");
11063                    // window.location.reload();
11064                });
11065        }
11066    },
11067    //Modal Form - BEGIN
11068    OpenModalWindow: function (url) {
11069        $.ajax({
11070            url: url,
11071            type: 'GET',
11072        }).done(function (response) {
11073            $("div#update-form").empty();
11074            $("div#update-form").append(response);
11075            $("#modalShared").removeClass("reportfraud");
11076
11077            $(".modal-container").attr("id", "modalShared");
11078            if (url.toString().indexOf("sendtofriendform") >= 0) {
11079                var sendTofriendForm = ko.validatedObservable(new VetrinaMotoriUtils.FormSendToFriend());
11080                ko.applyBindings(sendTofriendForm, document.getElementById("send-to-friend"));
11081                sendTofriendForm().Load();
11082                $("#myModalLabel").empty().text("Invia ad un amico");
11083            }
11084            if (url.toString().indexOf("reportfraudform") >= 0) {
11085                var reportFraudFrm = new VetrinaMotoriUtils.ReportFraud();
11086                ko.applyBindings(reportFraudFrm, document.getElementById("report-fraud"));
11087                reportFraudFrm.Load();
11088                $("#myModalLabel").empty().text("Segnala una truffa");
11089                $("#modalShared").addClass("reportfraud");
11090            }
11091            if (url.toString().indexOf("SendeMessageFormDealer") >= 0) {
11092                var contactDealer = new VetrinaMotoriUtils.ContactDealer();
11093                ko.applyBindings(contactDealer, document.getElementById("contact-dealer"));
11094                contactDealer.Load();
11095                $("#myModalLabel").empty().text("Contatta il Concessionario");
11096            }
11097            $("#modalShared").modal({show: true});
11098        });
11099    },
11100    MenuNavigation: function (itemsel) {
11101        $('.vehicle-categories .vehicle-categories-bottom ul > li.selected').removeClass("selected");
11102        $(itemsel).addClass("selected");
11103    },
11104    StickyButton: function () {
11105        //STICKY FILTER - BEGIN
11106        var win = $(window),
11107            canvas = $(".sidebar-search-form .js-tab-content.js-tab-selected").first(), //contenitore sticky bar
11108            css = {
11109                fixedbottom: "col-fixedbottom"
11110            };
11111
11112        //container-fluid vehicle-filters vehicle-filters-category
11113
11114        //attivo check sticky solo se ho il canvas contenitore
11115        if ($(canvas).length > 0) {
11116            setupBottomOptions();
11117
11118            function setupBottomOptions() {
11119                var checker = canvas.find(".bottom-checker")[0],
11120                    opts = canvas.find(".btn-container"),
11121                    min_h = 100; //minimum content visible area for becoming sticky
11122
11123                // search button always visibile
11124                win.on("scroll", function () {
11125                    defineFixedStatus();
11126                });
11127                win.resize(function () {
11128                    defineFixedStatus();
11129                });
11130
11131
11132                // check iniziale
11133                defineFixedStatus();
11134
11135                function defineFixedStatus() {
11136
11137                    //se ho elemento e container
11138                    if ($(checker).length > 0 && $(opts).length) {
11139
11140                        //rimuovo sticky se:
11141                        //- sono su mobile
11142                        //- la fine sotto del contenitore è nel viewport
11143                        //- la fine sotto è più in alto
11144                        //- l'altezza della porzione visibile del canvas è < dell'altezza minima richiesta
11145                        if (isMobileView() || isElementInViewport(checker)
11146                            || isElementUpperThanCurrentView(checker)
11147                            || isVisibleContainerSmallerThanMinimumHeight(opts, canvas, min_h)) {
11148                            canvas.removeClass(css.fixedbottom);
11149                            opts.css("left", "0px");
11150                        } else {
11151                            var left = canvas.offset().left;
11152                            opts.css("left", left + "px");
11153                            canvas.addClass(css.fixedbottom);
11154                        }
11155                    }
11156                }
11157
11158            }
11159
11160            //se l'elemento cercato è nel viewport
11161            function isElementInViewport(el) {
11162
11163                var rect = el.getBoundingClientRect();
11164                return (
11165                    rect.top >= 0 &&
11166                    rect.left >= 0 &&
11167                    rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
11168                    rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
11169                );
11170            }
11171
11172            //se sono in dimensioni tablet o mobile
11173            function isMobileView() {
11174                return ($('.vmq-md').css('display') != 'block') && ($('.vmq-lg').css('display') != 'block') && ($('.vmq-xl').css('display') != 'block') && ($('.vmq-xxl').css('display') != 'block')
11175            }
11176
11177            //se l'elemento non è ancora entrato nel viewport
11178            function isElementUpperThanCurrentView(el) {
11179
11180                var scroll = win.scrollTop();
11181                var el_top = $(el).offset().top;
11182                return el_top < scroll;
11183            }
11184
11185            //Se l'altezza della porzione visibile del canvas è < dell'altezza del contenitore
11186            function isVisibleContainerSmallerThanMinimumHeight(el, container, h) {
11187
11188                var cont_top = $(container).offset().top - win.scrollTop();
11189                var cont_visiblepart = (window.innerHeight || document.documentElement.clientHeight) - cont_top;
11190                cont_visiblepart = (cont_visiblepart < 0) ? 0 : cont_visiblepart;
11191                var el_h = $(el).height() + h;
11192
11193                //console.log ("el_h: " + el_h + " - cont_visiblepart: " + cont_visiblepart + (el_h>cont_visiblepart));
11194
11195                return (el_h > cont_visiblepart);
11196            }
11197
11198            //STICKY FILTER - END
11199        }
11200    },
11201    OrderDataEvt: function () {
11202        var url = window.location.pathname;
11203        url = url.replace(/pag-(\d+).*$/g, "");
11204
11205        var re = new RegExp("([?&])orderby=.*?(&|$)", "i");
11206        var src = window.location.search;
11207        var separator = src.indexOf('?') !== -1 ? "&" : "?";
11208        var selected = $("#orderby option:selected").val();
11209
11210        if (src.match(re)) {
11211            src = src.replace(re, '$1' + "orderby=" + selected + '$2');
11212        } else {
11213            src = src + separator + "orderby=" + selected;
11214            src = src.replace("&&", "&");
11215            //url = url.slice(0, -1) + separator + "orderby=" + self.OrderData.SelectedOrder();
11216        }
11217        window.location.href = url + src;
11218    },
11219    SelectedOrderDefault: function () {
11220        var url = window.location.pathname;
11221        url = url.replace(/pag-(\d+).*$/g, "");
11222
11223        var re = new RegExp("([?&])orderby=.*?(&|$)", "i");
11224        var src = window.location.search;
11225        var separator = src.indexOf('?') !== -1 ? "&" : "?";
11226
11227        if (src.match(re)) {
11228            //src = src.replace(re, '$1' + "orderby=" + self.OrderData.SelectedOrder() + '$2');
11229            var orderval = src.match(re)[0].replace("orderby=", "");
11230            orderval = orderval.replace("?", "");
11231            orderval = orderval.replace("&", "");
11232            $("#orderby").val(orderval);
11233        } else {
11234            $("#orderby").val("relevance-asc");
11235        }
11236    },
11237    BgText: function () {
11238        if ($(".filters-bg > img").length) {
11239            var el = $(".filters-bg > img");
11240            var txel = $(".bg-text");
11241            var io = el.offset();
11242            if ($(window).width() > 1024) {
11243                io.left += el.width() * 0.41 + el.width() * 0.58 / 2 - txel.width() / 2;
11244            } else {
11245                io.left += el.width() / 2 - txel.width() / 2;
11246            }
11247            io.top += el.height() / 2 - txel.height() / 2;
11248
11249            $(".bg-text").offset(io);
11250        }
11251    },
11252    IsCar: function () {
11253        var sUrl = window.location.href;
11254        return (sUrl.indexOf('ricerca-auto') >= 0 || sUrl.indexOf('auto-nuove') >= 0 || sUrl.indexOf('auto-usate') >= 0 || sUrl.indexOf('auto-epoca') >= 0);
11255    },
11256    IsMotorbike: function () {
11257        var sUrl = window.location.href;
11258        return (sUrl.indexOf('ricerca-moto') >= 0 || sUrl.indexOf('moto-nuove') >= 0 || sUrl.indexOf('moto-usate') >= 0 || sUrl.indexOf('moto-epoca') >= 0);
11259    },
11260    SetFavorite: function (current) {
11261        /*when is not logged in*/
11262        if (!userAuthorized) {
11263            $(window).on("message", function (evt) {
11264                if (evt.originalEvent.data === "postLoginActionReload") {
11265                    VetrinaMotoriUtils.Favorite(current);
11266                }
11267            });
11268            $(".login-panel").empty();
11269            $(".login-panel").append($('<iframe/>', {
11270                src: env.MyMotoUrl + VetrinaMotoriUtils.constants.MyMotoLoginUrl + '&ru=' + env.PortalUrl,
11271                seamless: '',
11272                width: '100%',
11273                height: '100%'
11274            }));
11275            $(".login-panel").css("display", "block");
11276        } else {
11277            /*when is logged in*/
11278            VetrinaMotoriUtils.Favorite(current);
11279        }
11280    },
11281    Favorite: function (current) {
11282        /*set element added*/
11283        $("#" + current + "-favorite").toggleClass("added");
11284        var action = '';
11285        var e = $("#" + current + "-favorite").attr("class").indexOf("added") > -1;
11286
11287        if (e) {
11288            action = 'create';
11289        } else {
11290            action = 'delete';
11291        }
11292
11293        var vehicType = 'car';
11294        if (VetrinaMotoriUtils.IsMotorbike()) {
11295            vehicType = 'motorbike'
11296        }
11297        var currentAd = $("#" + current + "-favorite");
11298        VetrinaMotoriUtils.ManageFavorites(action, current, vehicType);
11299    },
11300    Distance: function (lon1, lat1, lon2, lat2) {
11301        var R = 6371; // Radius of the earth in km
11302        var dLat = VetrinaMotoriUtils.toRad(lat2 - lat1);  // Javascript functions in radians
11303        var dLon = VetrinaMotoriUtils.toRad(lon2 - lon1);
11304        var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
11305            Math.cos(VetrinaMotoriUtils.toRad(lat1)) * Math.cos(VetrinaMotoriUtils.toRad(lat2)) *
11306            Math.sin(dLon / 2) * Math.sin(dLon / 2);
11307        var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
11308        var d = R * c; // Distance in km
11309        return d;
11310    },
11311    toRad: function (value) {
11312        return value * Math.PI / 180;
11313    }
11314};
11315
11316function CategoryClassByUrl(url) {
11317    if (url) {
11318        if (url.indexOf("segmento-") < 0) {
11319            url = '/segmento-' + url.substring(1);
11320        }
11321        return 'vehicle-type vehicle-type-' + url.substring(1);
11322    }
11323    return 'vehicle-type vehicle-type-segmento-berlina';
11324}
11325
11326
11327(function ($, sr) {
11328
11329    // debouncing function from John Hann
11330    // http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
11331    var debounce = function (func, threshold, execAsap) {
11332        var timeout;
11333
11334        return function debounced() {
11335            var obj = this, args = arguments;
11336
11337            function delayed() {
11338                if (!execAsap)
11339                    func.apply(obj, args);
11340                timeout = null;
11341            };
11342
11343            if (timeout)
11344                clearTimeout(timeout);
11345            else if (execAsap)
11346                func.apply(obj, args);
11347
11348            timeout = setTimeout(delayed, threshold || 100);
11349        };
11350    }
11351    // smartresize
11352    jQuery.fn[sr] = function (fn) {
11353        return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr);
11354    };
11355
11356})(jQuery, 'smartresize');
11357
11358
11359//Logged Menu - BEGIN
11360AdjustLoggedMenuSize = function () {
11361
11362    //se il logged menu è aperto
11363    if ($("#account-dropdown").hasClass("opened")) {
11364
11365        //setto l'altezza di #account-dropdown uguale al contenuto
11366        $("#account-dropdown").css("height", $("#account-dropdown .auth-content").css("height"));
11367
11368        //se l'altezza del menu più la distanza dal top è SUPERIORE all'altezza dello schermo, quindi il menu esce
11369        //  setto l'altezza del menu = altezza schermo
11370        if ($("#account-dropdown").height() + parseInt($("#account-dropdown").css("top"), 10) > $(window).height()) {
11371            $("#account-dropdown").height($(window).height() - parseInt($("#account-dropdown").css("top"), 10));
11372            $("#account-dropdown .auth-content").height($("#account-dropdown").height() - parseInt($("#account-dropdown .auth-header").css("height")) - 2 * parseInt($("#account-dropdown .auth-content").css("padding-top"), 10));
11373        }
11374
11375        //se screenwidth è xxs o xxxs && l'altezza del menu è inferiore all'altezza dello schermo,
11376        //setto l'altezza del menu = altezza schermo
11377        if ((($('.vmq-xxs').css('display') === 'block') || ($('.vmq-xxxs').css('display') === 'block'))
11378            && ($("#account-dropdown").height() + parseInt($("#account-dropdown").css("top"), 10) < $(window).height())) {
11379            $("#account-dropdown").height($(window).height());
11380        }
11381    }
11382}
11383
11384CheckLoggedMenuResize = function () {
11385
11386    var changedheight = (vh != $(window).height());
11387    var changedwidth = (vw != $(window).width());
11388
11389    //se faccio resize:
11390    //solo se cambia la larghezza nascondo logged menu e risetto l'altezza iniziale
11391    if (changedwidth) {
11392        $("#auth-backdrop, #account-dropdown").removeClass("opened");
11393        $("#account-dropdown").height("initial");
11394    }
11395
11396    if (changedheight) {
11397        AdjustLoggedMenuSize();
11398    }
11399
11400}
11401
11402ToggleLoggedMenu = function (e) {
11403    console.log("toggle");
11404
11405    e.preventDefault();
11406    e.stopPropagation();
11407
11408    //accendo/spengo #auth-backdrop e #account-dropdown
11409    $("#auth-backdrop, #account-dropdown").toggleClass("opened");
11410    AdjustLoggedMenuSize();
11411}
11412//Logged Menu - END
11413
11414
11415//Toggle Search Form on Mobile - BEGIN
11416ToggleMobileSearch = function (e) {
11417    $('.sidebar-search-form').addClass("displayform");
11418    $('.sidebar-search-form').length > 0 && $("html, body").animate({
11419        scrollTop: (+$('.sidebar-search-form').offset().top - $('#content').offset().top + $('#content').scrollTop() - $("header").height() - 10),
11420    }, 600);
11421}
11422//Toggle Search Form on Mobile - END
11423
11424CheckMobileSearch = function () {
11425    $(window).smartresize(function () {
11426        if (
11427            ($('.vmq-md').css('display') === 'block') || ($('.vmq-lg').css('display') === 'block') || ($('.vmq-xl').css('display') === 'block') || ($('.vmq-xxl').css('display') === 'block')
11428        ) {
11429            $('.sidebar-search-form').removeClass("displayform");
11430        }
11431    });
11432}
11433
11434
11435//ScrollTop - BEGIN
11436InitScrollTop = function () {
11437    $(window).scroll(function () {
11438        if ($(this).scrollTop() > 100) {
11439            $('.scroll-top').fadeIn();
11440        } else {
11441            $('.scroll-top').fadeOut();
11442        }
11443    });
11444
11445    $('.scroll-top').click(function () {
11446        $("html, body").animate({
11447            scrollTop: 0
11448        }, 600);
11449        return false;
11450    });
11451}
11452//Scrolltop - END
11453
11454
11455// Check list layout - List vs Grid - BEGIN
11456CheckListingGrid = function () {
11457    $(window).smartresize(function () {
11458
11459        /* $(window).on('resize', function () { versione originale */
11460        //se i selettori lista/griglia sono nascosti e c'è un elemento
11461        //con classe "ul.grid-view", lo cambio in "ul.list-view"
11462        if (($(".list-header-selectors .listgrid-selector").css("display") == 'none') && ($("div[class$='-listing'] > ul").hasClass("grid-view"))) {
11463
11464            //setto il listing in formato list-view
11465            $("div[class$='-listing'] > ul").addClass('list-view').removeClass('grid-view');
11466
11467            //spengo il selettore grid-view e riaccendo il selettore list-view
11468            $(".list-header-selectors div[class$='-listing-selectors'] a").toggleClass("selected");
11469        }
11470    });
11471}
11472// Check list layout - List vs Grid - END
11473
11474SetExpandableText = function () {
11475    //  expandable_mh - expandable text max-height
11476    //  use multiple of '#expandable-text p' line-height that is 25px
11477    var expandable_mh = 250;
11478    $('#expandable-text p:first').data("orig-height", $('#expandable-text p').height());
11479
11480    if (($('.vmq-xxxs').css('display') === 'block') || ($('.vmq-xxs').css('display') === 'block')) {
11481        expandable_mh = 250
11482    } else if (($('.vmq-xs').css('display') === 'block') || ($('.vmq-sm').css('display') === 'block')) {
11483        expandable_mh = 375
11484    } else {
11485        expandable_mh = 375
11486    }
11487    ;
11488
11489    if ($('#expandable-text p').height() > expandable_mh) {
11490        $('#expandable-text p').css("max-height", expandable_mh + "px")
11491        $('.btn-show-all').css("display", "block")
11492    }
11493    // Expandable text - END
11494}
11495
11496/* CLOSE ALL OPENED SIDEBAR CAROUSEL TABS - BEGIN */
11497
11498function collapseOpenCarousels() {
11499    $(".sidebar-carousel .tab-dropdown.opened .tab-content.collapse").each(function (j) {
11500        $(this).collapse("hide");
11501        $(this).parent().removeClass("opened");
11502    });
11503}
11504
11505/* CLOSE ALL OPENED SIDEBAR CAROUSEL TABS - END */
11506
11507
11508function collapseCarouselsOnMobile() {
11509    //se sono su mobile
11510    if (
11511        ($('.vmq-md').css('display') != 'block') && ($('.vmq-lg').css('display') != 'block') && ($('.vmq-xl').css('display') != 'block') && ($('.vmq-xxl').css('display') != 'block')
11512    ) {
11513        collapseOpenCarousels();
11514    }
11515}
11516
11517
11518/* OPEN ALL SIDEBAR CAROUSEL TABS WITH DATa-STATUS-START=OPENED - BEGIN */
11519function showCarousels() {
11520    $(".sidebar-carousel .tab-dropdown[data-status-start='opened'] .tab-content.collapse").each(function (j) {
11521        $(this).collapse("show");
11522        $(this).parent().addClass("opened");
11523    })
11524}
11525
11526/* OPEN ALL SIDEBAR CAROUSEL TABS WITH DATa-STATUS-START=OPENED - END */
11527
11528
11529/* ONLY ON DESKTOP, OPEN ALL SIDEBAR CAROUSEL TABS WITH DATa-STATUS-START=OPENED - BEGIN */
11530function showCarouselsOnDesktop() {
11531    //se sono su desktop
11532    if (
11533        ($('.vmq-md').css('display') === 'block') || ($('.vmq-lg').css('display') === 'block') || ($('.vmq-xl').css('display') === 'block') || ($('.vmq-xxl').css('display') === 'block')
11534    ) {
11535        showCarousels();
11536    }
11537}
11538
11539/* ONLY ON DESKTOP, OPEN ALL SIDEBAR CAROUSEL TABS WITH DATa-STATUS-START=OPENED - END */
11540
11541
11542function CheckSidebarCarousel() {
11543    //al load aggiungo classe collapse a tutti i tab della sidebar - così senza js sono tutti aperti
11544    $(".sidebar-carousel .tab-dropdown .tab-content").addClass("collapse");
11545
11546    //se sono su desktop apro i carousel che hanno data-status-start='opened'
11547    $(function () {
11548        collapseCarouselsOnMobile();
11549        showCarouselsOnDesktop()
11550    });
11551    //al termine del resize chiudo i carousel se sono su mobile e li apro se sono su desktop
11552    $(window).smartresize(function () {
11553        collapseCarouselsOnMobile();
11554        showCarouselsOnDesktop();
11555    });
11556}
11557
11558
11559if (navigator.userAgent.match(/IEMobile\/10\.0/)) {
11560    var msViewportStyle = document.createElement("style");
11561    msViewportStyle.appendChild(
11562        document.createTextNode(
11563            "@-ms-viewport{width:auto!important}"
11564        )
11565    );
11566    document.getElementsByTagName("head")[0].appendChild(msViewportStyle);
11567}
11568
11569//Check versione desktop vs mobile
11570var desktopVersion = (!(/Android|iPhone|iPad|iPod|BlackBerry|Windows Phone/i).test(navigator.userAgent || navigator.vendor || window.opera)) ? true : false;
11571
11572//check dimensione larghezza e altezza dello schermo del device (NB non dell viewport)
11573//NB: racchiuso in IIFE per non esporre w/d/e/g/x/y nello scope globale: un 'd' globale
11574//collideva con un 'let/const/class d' di script terzi/consent -> SyntaxError che uccideva app.js.
11575var img_size = 'm'; //default (resta globale, usato dalla gallery)
11576(function () {
11577    var w = window, d = document, e = d.documentElement, g = d.getElementsByTagName('body')[0],
11578        x = w.innerWidth || e.clientWidth || g.clientWidth,
11579        y = w.innerHeight || e.clientHeight || g.clientHeight,
11580        max_W_H = Math.max(x, y, 320); //MAX larghezza/altezza schermo. Minimo: 320px
11581    if (max_W_H > 600) {
11582        img_size = 'l';
11583    }
11584})();
11585
11586//TURN ON MAIN MENU TOP HEADER - BEGIN
11587SetTopNav = function () {
11588    //console.log( $("body").attr("class"))
11589    var body = $("body");
11590    if ($(body).attr("class")) {
11591        var sct = {
11592            main: $(body).attr("class").split("-")[0], //home-car-motorbike ecc
11593            sub: $(body).attr("class").split("-")[1],   //new-used-vintage ecc
11594            subsub: $(body).attr("class").split("-")[2],  //list-detail
11595            target: ""
11596        }
11597        //console.log(sct.main +" -- "+ sct.sub +" -- "+ sct.subsub)
11598        if (sct.main === 'home') {
11599            //burger
11600            $('.sidemenu-nav ul.main li a[data-section="' + sct.main + '"]').addClass("selected");
11601        }
11602        if (sct.main === 'car' || sct.main === 'motorbike' || sct.main === 'commercial' || sct.main === 'dealership') {
11603            //top menu
11604            $('.top-menu-vm ul li a[data-section="' + sct.main + "-" + sct.sub + '"]').parent().addClass("active");
11605            //burger
11606            $('.sidemenu-nav ul.main li a[data-section="' + sct.main + "-" + sct.sub + '"]').addClass("selected");
11607        }
11608    }
11609}
11610//TURN ON MAIN MENU TOP HEADER - END
11611
11612
11613//Check Mouse Presence - BEGIN
11614detectHover = function () {
11615    document.removeEventListener('mousemove', detectHover, false);
11616    currentMousePos = event.clientX;
11617    if (currentMousePos) {
11618        $(body).addClass("hasMouse");
11619    }
11620};
11621
11622//Check mouse presence - END
11623
11624$(function () {
11625
11626    SetTopNav();
11627
11628    //Sidebar - setto l'altezza di tutti gli ul in tab che hanno l'attributo "data-show" valorizzato
11629    $('.sidebar-carousel').find("[data-show]").each(function (i) {
11630        var itemsToShow = $(this).data("show");
11631        var t = 0;
11632        $(this).find(".tab-content ul li").slice(0, itemsToShow).each(function (index) {
11633            t = t + $(this).outerHeight(true);
11634            if (index == itemsToShow - 1) {
11635                t = t - parseInt($(this).css("margin-bottom"));
11636            }
11637
11638        });
11639        $(this).find(".tab-content ul").height(t).css('overflow', 'hidden');
11640    });
11641
11642    $("#src-car, #src-motorbike, #src-commercials").click(function (e) {
11643        e.preventDefault();
11644        e.stopPropagation();
11645    });
11646
11647    // BURGER MENU - BEGIN
11648    $('.sidemenu-burger, .sidemenu-overlay').click(function () {
11649        $('.sidemenu-burger').toggleClass('clicked');
11650        $('.sidemenu-overlay').toggleClass('show');
11651        $('nav').toggleClass('show');
11652        $('body').toggleClass('sidemenu-overflow');
11653    });
11654    // BURGER MENU - END
11655
11656
11657    //Sidebar - tasto Show all -  click su "Mostra tutto"
11658    $('.sidebar-carousel .btn-show-all').on('click', function (e) {
11659        $(e.target).closest("[data-show]").find("ul").removeAttr("style");
11660        $(e.target).css('display', 'none');
11661    })
11662
11663    // Sidebar - tab dropdown - gestione apertura e icona
11664    //apre il tab
11665    $(".tab-dropdown.opened .tab-content.collapse").addClass("in");
11666    //gestisce l'icona del tab header
11667    $('.tab-dropdown .tab-header .btn').on('click', function (e) {
11668        $(e.target).closest('.tab-dropdown').toggleClass("opened");
11669    });
11670
11671
11672    //listview, GalleryView
11673    $('.vehicle-listing-selectors a').on('click', function (e) {
11674        var t = $(e.target).closest("[data-type]").attr("data-type");
11675        $(e.target).toggleClass("selected").siblings().toggleClass("selected");
11676        $(".items-listing > ul").removeClass().addClass(t);
11677    });
11678
11679    $(".share-mail").click(function (e) {
11680        VetrinaMotoriUtils.OpenModalWindow('/market/sendtofriendform');
11681    });
11682    $("#btn-report-fraud").click(function (e) {
11683        VetrinaMotoriUtils.OpenModalWindow('/market/reportfraudform');
11684    });
11685
11686    // $("#btn-contact-seller").click(function (e) { 
11687    //     VetrinaMotoriUtils.OpenModalWindow('/market/contactsellerform?adID=4CE3A1'); 
11688    // });
11689
11690    $("#auth-backdrop, #account-dropdown .auth-header button.close").click(function (e) {
11691        ToggleLoggedMenu(e);
11692    });
11693    //scrollTop
11694    InitScrollTop();
11695
11696
11697    //Check della Grid view - la disabilita sulle small viewports
11698    CheckListingGrid();
11699
11700    vh = $(window).height();
11701    vw = $(window).width();
11702
11703    //Check dimensione logged menu sul resize
11704    $(window).resize(function () {
11705        CheckLoggedMenuResize();
11706    });
11707
11708
11709    //GALLERY CAROUSEL - BEGIN
11710    var $glr = $("#gallery");
11711
11712    //Carieamento immagini
11713    //Se la gallery deve ricaricare alcune immagini ad alta risoluzione
11714    if (img_size == 'l') {
11715
11716        $glr.find(".item img").each(function () {
11717            //Se l'immagine è da sostituire con l'immagine ad alta risoluzione
11718            if ($(this).attr('src') != $(this).data('src-fullsize')) {
11719                $(this).attr('src', $(this).data('src-fullsize'));
11720            }
11721        });
11722    }
11723
11724    //Inizialmente creo e stoppo il carousel
11725    $glr.carousel("pause");
11726    var blockclick = false; //lo scroll è fermo, consento il click
11727
11728    //precedente
11729    $glr.find(".left").click(function (e) {
11730        e.preventDefault();
11731        e.stopPropagation();
11732        $("#gallery").carousel("prev");
11733    });
11734
11735    //successivo
11736    $glr.find(".right").click(function (e) {
11737        e.preventDefault();
11738        e.stopPropagation();
11739        $("#gallery").carousel("next");
11740    });
11741
11742    //click su thumbnail
11743    $(".carousel-thumbs li").click(function (e) {
11744        e.preventDefault();
11745        e.stopPropagation();
11746        //Se ho cliccato a scroll fermo
11747        if (blockclick == false) {
11748            var t = $(this).parent().data("carousel-target");   // li → ul
11749            var i = $(this).data("carousel-slideto");           // li
11750            var j = $(t + " .item.active").index(); //recupero item attivo
11751            if (i != j) {
11752                $(t).carousel(i);
11753            }
11754        }
11755    });
11756
11757    $glr.on('slide.bs.carousel', function (e) {
11758        blockclick = true; //blocco il click
11759    });
11760
11761    //finito lo slide, riallineo il thumb attivo e consento il click
11762    $glr.on('slid.bs.carousel', function (e) {
11763        blockclick = false;
11764        var i = $glr.find(".item.active").index();
11765        $(".carousel-thumbs li.active").removeClass("active");
11766        $('.carousel-thumbs li[data-carousel-slideto="' + i + '"]').addClass("active");
11767        $glr.carousel("pause");
11768    });
11769
11770    //Swipe support
11771    $glr.swipe({
11772        swipe: function (event, direction, distance, duration, fingerCount, fingerData) {
11773            if (direction == 'left') $(this).carousel('next');
11774            if (direction == 'right') $(this).carousel('prev');
11775        },
11776        allowPageScroll: "vertical"
11777    });
11778    //GALLERY CAROUSEL - END
11779
11780
11781    //SEARCH LINK - TOGGLE SEARCH ON mobile
11782    $("header .search-link").click(function (e) {
11783        ToggleMobileSearch(e);
11784    });
11785    CheckMobileSearch();
11786
11787    // Expandable text - BEGIN
11788    SetExpandableText();
11789    $('#show-text').click(function (e) {
11790        e.preventDefault();
11791        e.stopPropagation();
11792        $('#expandable-text p').css("max-height", "none");
11793        $('.expandable-text .btn-show-all').css("display", "none");
11794    });
11795
11796    CheckSidebarCarousel();
11797
11798    //menage menu tabs everywhere
11799    VetrinaMotoriUtils
11800        .TabNavigation();
11801
11802    //menage login box
11803    VetrinaMotoriUtils
11804        .WarmUp();
11805
11806    $("#src-car, #src-motorbike, #src-commercials").click(function (e) {
11807        e.preventDefault();
11808        e.stopPropagation();
11809    });
11810
11811    //LOGIN MENU - SOLO DA MOBILE
11812    $('#btn-auth-dropdown-mobile, .sidemenudx-overlay, .close-overlay').click(function (e) {
11813        e.stopPropagation();
11814        e.preventDefault();
11815        $('#btn-auth-dropdown-mobile').toggleClass('clicked');
11816        $('.sidemenudx-overlay').toggleClass('show');
11817        $('div.sidemenudx-mobileacc').toggleClass('show');
11818        $('body').toggleClass('overflow');
11819        $('html').toggleClass('freeze-body');
11820    });
11821
11822    window.addEventListener("message", function (event) {
11823        switch (event.data) {
11824            case "closeMyMotoOverlay":
11825                $(".login-panel").html("").css("display", "none");
11826                break;
11827            case "postLoginActionReload":
11828                document.location.reload();
11829                break;
11830        }
11831    });
11832
11833    //network login from mymoto
11834    VetrinaMotoriUtils.SetupLogin();
11835    //subscription from mymoto
11836    VetrinaMotoriUtils.SetupSubscription();
11837    //mymoto dashboard
11838    VetrinaMotoriUtils.SetupDashboard();
11839
11840    VetrinaMotoriUtils.SelectedOrderDefault();
11841
11842    $("#orderby").on("change", function (e) {
11843        VetrinaMotoriUtils.OrderDataEvt();
11844    });
11845
11846    VetrinaMotoriUtils.BgText();
11847    $(window).resize(VetrinaMotoriUtils.BgText);
11848});
11849(function ($) {
11850    "use strict"
11851
11852    const COOKIE_NAME = 'CrmFp';
11853
11854    $(function () {
11855        saveFingerprint();
11856    })
11857
11858
11859    async function saveFingerprint() {
11860
11861        if (Cookies.get(COOKIE_NAME)) return;
11862
11863        const fp = await FingerprintJS.load();
11864        const result = await fp.get();
11865
11866        Cookies.set(COOKIE_NAME, result.visitorId, {expires: 365});
11867    }
11868
11869})(jQuery);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.