PageSourceSearch

https://cpfairyland.com/_static/??-eJytlMFOxDAMRH+ItBQtqjggvsWbeqm…/tLetl91KacF9eb/F1GF+Gp8Pz4zB+AsjQSOc=

js cpfairyland.com collected 2026-10-09 05:15:45 UTC 1,268,109 bytes, 38,176 lines download raw bytes

1/*! This file is auto-generated */
2window.wp=window.wp||{},function(r){var t="undefined"==typeof _wpUtilSettings?{}:_wpUtilSettings;wp.template=_.memoize(function(a){var n,s={evaluate:/<#([\s\S]+?)#>/g,interpolate:/\{\{\{([\s\S]+?)\}\}\}/g,escape:/\{\{([^\}]+?)\}\}(?!\})/g,variable:"data"};return function(t){var e=document.querySelector("script#tmpl-"+a);if(e)return(n=n||_.template(r(e).html(),s))(t);throw new Error("Template not found: #tmpl-"+a)}}),wp.ajax={settings:t.ajax||{},post:function(t,e){return wp.ajax.send({data:_.isObject(t)?t:_.extend(e||{},{action:t})})},send:function(n,t){var e,a;return _.isObject(n)?t=n:(t=t||{}).data=_.extend(t.data||{},{action:n}),t=_.defaults(t||{},{type:"POST",url:wp.ajax.settings.url,context:this}),(e=(a=r.Deferred(function(a){t.success&&a.done(t.success),t.error&&a.fail(t.error),delete t.success,delete t.error,a.jqXHR=r.ajax(t).done(function(t){var e;"1"!==t&&1!==t||(t={success:!0}),_.isObject(t)&&!_.isUndefined(t.success)?(e=this,a.done(function(){n&&n.data&&"query-attachments"===n.data.action&&a.jqXHR.hasOwnProperty("getResponseHeader")&&a.jqXHR.getResponseHeader("X-WP-Total")?e.totalAttachments=parseInt(a.jqXHR.getResponseHeader("X-WP-Total"),10):e.totalAttachments=0}),a[t.success?"resolveWith":"rejectWith"](this,[t.data])):a.rejectWith(this,[t])}).fail(function(){a.rejectWith(this,arguments)})})).promise()).abort=function(){return a.jqXHR.abort(),this},e}}}(jQuery);;
3/*!
4 * jQuery UI Touch Punch 0.2.3
5 *
6 * Copyright 2011–2014, Dave Furfero
7 * Dual licensed under the MIT or GPL Version 2 licenses.
8 *
9 * Depends:
10 *  jquery.ui.widget.js
11 *  jquery.ui.mouse.js
12 */
13!function(t){if(t.support.touch="ontouchend"in document,t.support.touch){var o,e=t.ui.mouse.prototype,u=e._mouseInit,n=e._mouseDestroy;e._touchStart=function(t){!o&&this._mouseCapture(t.originalEvent.changedTouches[0])&&(o=!0,this._touchMoved=!1,c(t,"mouseover"),c(t,"mousemove"),c(t,"mousedown"))},e._touchMove=function(t){o&&(this._touchMoved=!0,c(t,"mousemove"))},e._touchEnd=function(t){o&&(c(t,"mouseup"),c(t,"mouseout"),this._touchMoved||c(t,"click"),o=!1)},e._mouseInit=function(){this.element.on({touchstart:t.proxy(this,"_touchStart"),touchmove:t.proxy(this,"_touchMove"),touchend:t.proxy(this,"_touchEnd")}),u.call(this)},e._mouseDestroy=function(){this.element.off({touchstart:t.proxy(this,"_touchStart"),touchmove:t.proxy(this,"_touchMove"),touchend:t.proxy(this,"_touchEnd")}),n.call(this)}}function c(t,o){if(!(t.originalEvent.touches.length>1)){t.preventDefault();var e=t.originalEvent.changedTouches[0],u=document.createEvent("MouseEvents");u.initMouseEvent(o,!0,!0,window,1,e.screenX,e.screenY,e.clientX,e.clientY,!1,!1,!1,!1,0,null),t.target.dispatchEvent(u)}}}(jQuery);;
14/**
15 * The following API is shared on both frontend and backend.
16 *
17 * No external libraries are loaded in this file.
18 *
19 * 1. Polyfills
20 * 2. themeComplete plugin JS API
21 * 3. themeComplete jQuery extensions
22 */
23/*jshint bitwise: false*/
24/**
25 * 1. Polyfills
26 *
27 * The following are required methods for
28 * the String, Number and Object prototype
29 */
30( function() {
31	'use strict';
32
33	/**
34	 * String.prototype.isNumeric()
35	 *
36	 * Determines whether a string contains a numeric value
37	 *
38	 * https://stackoverflow.com/questions/18082/validate-decimal-numbers-in-javascript-isnumeric
39	 */
40	if ( ! String.prototype.isNumeric ) {
41		Object.defineProperty( String.prototype, 'isNumeric', {
42			value: function() {
43				return ! isNaN( parseFloat( this ) ) && isFinite( this );
44			}
45		} );
46	}
47}() );
48
49// jQuery Mask Plugin v1.14.15
50// github.com/igorescobar/jQuery-Mask-Plugin
51window.jQuery.jMaskGlobals = {
52	maskElements: '.tc-extra-product-options input'
53};
54
55/**
56 * 2. ThemeComplete plugin JS API
57 */
58
59( function( $ ) {
60	'use strict';
61
62	$.epoAPI = {};
63	$.epoAPI.error = false;
64	$.epoAPI.math = {};
65	$.epoAPI.dom = {};
66	$.epoAPI.util = {};
67	$.epoAPI.locale = {};
68	$.epoAPI.template = {};
69
70	$.epoAPI.math.toFloat = function( s, d ) {
71		var n;
72
73		if ( ! ( typeof s === 'string' || typeof s === 'number' ) || isNaN( s ) ) {
74			return 0;
75		}
76		n = parseFloat( s );
77		if ( isNaN( n ) ) {
78			if ( d !== undefined ) {
79				return d;
80			}
81			return s;
82		}
83
84		return n;
85	};
86
87	$.epoAPI.math.toInt = function( s, d ) {
88		var n;
89
90		if ( ! ( typeof s === 'string' || typeof s === 'number' ) || isNaN( s ) || s === '' ) {
91			return 0;
92		}
93		n = parseInt( s, 10 );
94		if ( isNaN( n ) ) {
95			if ( d !== undefined ) {
96				return d;
97			}
98			return s;
99		}
100
101		return n;
102	};
103
104	/**
105	 * Rounds a number
106	 * modified from http://locutus.io/php/math/round/
107	 *
108	 * @param {any}    value     the value to round.
109	 * @param {int}    precision the precision.
110	 * @param {string} mode      the rounding mode.
111	 */
112	$.epoAPI.math.round = function( value, precision, mode ) {
113		var m;
114		var f;
115		var isHalf;
116		var n;
117		var sgn; // helper variables
118
119		// making sure precision is integer
120		precision = parseInt( precision, 10 );
121		if ( ! Number.isFinite( precision ) ) {
122			precision = 0;
123		}
124		m = Math.pow( 10, precision );
125		value = value * m;
126
127		// sign of the number
128		if ( typeof value === 'number' ) {
129			if ( value ) {
130				if ( value < 0 ) {
131					sgn = -1;
132				} else {
133					sgn = 1;
134				}
135			} else {
136				sgn = 0;
137			}
138		} else {
139			sgn = 0;
140		}
141
142		isHalf = value % 1 === 0.5 * sgn;
143		f = Math.floor( value );
144
145		if ( isHalf ) {
146			switch ( mode ) {
147				case 'PHP_ROUND_HALF_DOWN':
148					// rounds .5 toward zero
149					if ( sgn < 0 ) {
150						n = 1;
151					} else {
152						n = 0;
153					}
154					value = f + n;
155					break;
156				case 'PHP_ROUND_HALF_EVEN':
157					// rouds .5 towards the next even integer
158					value = f + ( ( f % 2 ) * sgn );
159					break;
160				case 'PHP_ROUND_HALF_ODD':
161					// rounds .5 towards the next odd integer
162					n = f % 2;
163					if ( n ) {
164						n = 0;
165					} else {
166						n = 1;
167					}
168					value = f + n;
169					break;
170				default:
171					// rounds .5 away from zero
172					if ( sgn > 0 ) {
173						n = 1;
174					} else {
175						n = 0;
176					}
177					value = f + n;
178			}
179		}
180
181		if ( ! isHalf ) {
182			value = Math.round( value );
183		}
184
185		return value / m;
186	};
187
188	// https://locutus.io/php/misc/uniqid/index.html
189	$.epoAPI.math.uniqueid = function( prefix, moreEntropy ) {
190		var retId;
191		var _formatSeed = function( seed, reqWidth ) {
192			seed = parseInt( seed, 10 ).toString( 16 ); // to hex str
193			if ( reqWidth < seed.length ) {
194				// so long we split
195				return seed.slice( seed.length - reqWidth );
196			}
197			if ( reqWidth > seed.length ) {
198				// so short we pad
199				return new Array( 1 + ( reqWidth - seed.length ) ).join( '0' ) + seed;
200			}
201			return seed;
202		};
203		var radom;
204
205		if ( prefix === undefined ) {
206			prefix = '';
207		}
208
209		$.epoAPI.php = $.epoAPI.php || {};
210
211		if ( ! $.epoAPI.php.uniqidSeed ) {
212			// init seed with big random int
213			$.epoAPI.php.uniqidSeed = Math.floor( Math.random() * 0x75bcd15 );
214		}
215		$.epoAPI.php.uniqidSeed += 1;
216
217		// start with prefix, add current milliseconds hex string
218		retId = prefix;
219		retId += _formatSeed( parseInt( Date.now() / 1000, 10 ), 8 );
220		// add seed hex string
221		retId += _formatSeed( $.epoAPI.php.uniqidSeed, 5 );
222		if ( moreEntropy ) {
223			// for more entropy we add a float lower to 10
224			radom = Math.random() * 10;
225			retId += radom.toFixed( 8 ).toString();
226		}
227
228		return retId;
229	};
230
231	$.epoAPI.dom.id = function( id ) {
232		if ( typeof id === 'undefined' ) {
233			return id;
234		}
235		if ( ! ( typeof id === 'string' || typeof id === 'number' ) || ( typeof id === 'number' && isNaN( id ) ) ) {
236			return id.toString();
237		}
238		return id.toString().replace( /(%|:|\.|\[|\]|,|=)/g, '\\$1' );
239	};
240
241	$.epoAPI.dom.scroll = function() {
242		var scrollLeft;
243		var scrollTop;
244
245		if ( window.pageYOffset ) {
246			scrollTop = window.pageYOffset;
247			scrollLeft = window.pageXOffset;
248		} else if ( document.documentElement && document.documentElement.scrollTop ) {
249			scrollTop = document.documentElement.scrollTop;
250			scrollLeft = document.documentElement.scrollLeft;
251		} else if ( document.body ) {
252			scrollTop = document.body.scrollTop;
253			scrollLeft = document.body.scrollLeft;
254		}
255
256		return { left: scrollLeft, top: scrollTop };
257	};
258
259	$.epoAPI.dom.size = function() {
260		var totalDocumentHeight;
261		var totalDocumentWidth;
262		var fullHeight;
263		var fullWidth;
264		var visibleWidth;
265		var visibleHeight;
266
267		if ( window.innerHeight && window.scrollMaxY ) {
268			totalDocumentWidth = window.innerWidth + window.scrollMaxX;
269			totalDocumentHeight = window.innerHeight + window.scrollMaxY;
270		} else if ( document.body.scrollHeight > document.body.offsetHeight ) {
271			totalDocumentWidth = document.body.scrollWidth;
272			totalDocumentHeight = document.body.scrollHeight;
273		} else {
274			totalDocumentWidth = document.body.offsetWidth;
275			totalDocumentHeight = document.body.offsetHeight;
276		}
277
278		if ( window.innerHeight ) {
279			if ( document.documentElement.clientWidth ) {
280				visibleWidth = document.documentElement.clientWidth;
281			} else {
282				visibleWidth = window.innerWidth;
283			}
284			visibleHeight = window.innerHeight;
285		} else if ( document.documentElement && document.documentElement.clientHeight ) {
286			visibleWidth = document.documentElement.clientWidth;
287			visibleHeight = document.documentElement.clientHeight;
288		} else if ( document.body ) {
289			visibleWidth = document.body.clientWidth;
290			visibleHeight = document.body.clientHeight;
291		}
292		if ( totalDocumentHeight < visibleHeight ) {
293			fullHeight = visibleHeight;
294		} else {
295			fullHeight = totalDocumentHeight;
296		}
297		if ( totalDocumentWidth < visibleWidth ) {
298			fullWidth = visibleWidth;
299		} else {
300			fullWidth = totalDocumentWidth;
301		}
302
303		return {
304			fullWidth: fullWidth,
305			fullHeight: fullHeight,
306
307			visibleWidth: visibleWidth,
308			visibleHeight: visibleHeight,
309
310			totalWidth: totalDocumentWidth,
311			totalHeight: totalDocumentHeight
312		};
313	};
314
315	$.epoAPI.util.escapeSelector = ( function() {
316		/* original escape string
317		 *  /([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g;
318		 */
319		var selectorEscape = /([!"$%&'()*+,/:;<=>?@[\]^`{|}~])/g;
320		return function( selector ) {
321			return selector.replace( selectorEscape, '\\$1' );
322		};
323	}() );
324
325	$.epoAPI.util.parseParams = function( string, decode ) {
326		if ( typeof string !== 'string' || string.split === undefined ) {
327			return [];
328		}
329		return string
330			.split( '&' )
331			.map( function( value ) {
332				var obj = {};
333
334				if ( decode === true ) {
335					value = decodeURIComponent( value.replace( /\+/g, '%20' ) );
336				}
337
338				value = value.split( '=' ).map( function( v ) {
339					var a = v.split( '?' );
340
341					if ( a.length > 1 ) {
342						return a[ 1 ];
343					}
344					return v;
345				} );
346
347				if ( value.length > 1 ) {
348					obj[ value[ 0 ] ] = value[ 1 ];
349				}
350
351				return obj;
352			} )
353			.filter( function( n ) {
354				return n !== null;
355			} )
356			.reduce( function( current, next ) {
357				return Object.assign( {}, current, next );
358			}, {} );
359	};
360
361	// jQuery trim is deprecated, provide a trim method based on String.prototype.trim
362	$.epoAPI.util.trim = function( str ) {
363		if ( typeof str === 'string' ) {
364			// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim#Polyfill
365			return str.replace( /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '' );
366		}
367		return str;
368	};
369
370	$.epoAPI.util.parseJSON = function( s ) {
371		var parsedjson;
372		var JSON = window.JSON;
373
374		try {
375			parsedjson = JSON.parse( s + '' );
376			if ( parsedjson && typeof parsedjson === 'object' && parsedjson !== null ) {
377				return parsedjson;
378			}
379		} catch ( err ) {
380			$.epoAPI.error = err;
381			return false;
382		}
383
384		return false;
385	};
386
387	$.epoAPI.util.decodeHTML = function( html ) {
388		var txt = document.createElement( 'textarea' );
389		txt.innerHTML = html;
390		return txt.value;
391	};
392
393	$.epoAPI.util.getStorage = function( type ) {
394		var storage;
395		var x;
396		try {
397			storage = window[ type ];
398			x = '__storage_test__';
399			storage.setItem( x, x );
400			storage.removeItem( x );
401			return storage;
402		} catch ( e ) {
403			return false;
404		}
405	};
406
407	$.epoAPI.util.basename = function( path ) {
408		return path.replace( /.*\//, '' );
409	};
410
411	$.epoAPI.util.getType = function( obj ) {
412		// Get the type string using `toString.call()`
413		var type = Object.prototype.toString.call( obj ).match( /\[object (\w+)]/ )[ 1 ].toLowerCase();
414
415		// If it's a plain object, make sure it's not a class instance
416		if ( type === 'object' && Object.getPrototypeOf( obj ) === Object.prototype ) {
417			return 'object'; // Plain object ({} or new Object())
418		}
419		if ( type === 'object' ) {
420			type = 'other';
421		}
422
423		// Return the type for arrays and other types directly from the match result
424		return type;
425	};
426
427	// https://developer.mozilla.org/en-US/docs/Web/API/structuredClone
428	// https://medium.com/javascript-in-plain-english/how-to-deep-copy-objects-and-arrays-in-javascript-7c911359b089
429	$.epoAPI.util.deepCopyArray = function( inObject ) {
430		var outObject;
431		var value;
432
433		if ( window.structuredClone !== undefined ) {
434			return window.structuredClone( inObject );
435		}
436
437		if ( typeof inObject !== 'object' || inObject === null ) {
438			return inObject; // Return the value if inObject is not an object
439		}
440
441		// Create an array or object to hold the values
442		outObject = Array.isArray( inObject ) ? [] : {};
443
444		Object.keys( inObject ).forEach( function( key ) {
445			if ( inObject ) {
446				value = inObject[ key ];
447
448				// Recursively (deep) copy for nested objects, including arrays
449				outObject[ key ] = typeof value === 'object' && value !== null ? $.epoAPI.util.deepCopyArray( value ) : value;
450			}
451		} );
452
453		return outObject;
454	};
455
456	$.epoAPI.util.isEmpty = function( obj, customEmptyValues ) {
457		var emptyValues = customEmptyValues || [ undefined, null, false, 0, '', '0' ];
458		var isEmptyValue =
459			emptyValues.filter( function( item ) {
460				return obj === item;
461			} ).length === 1;
462		var isEmptyObject = false;
463
464		if ( obj && typeof obj === 'object' ) {
465			isEmptyObject =
466				Object.keys( obj ).filter( function( key ) {
467					return Object.prototype.hasOwnProperty.call( obj, key );
468				} ).length === 0;
469			return isEmptyObject;
470		}
471
472		return isEmptyValue || isEmptyObject;
473	};
474
475	$.epoAPI.util.isEmptyValue = function( obj ) {
476		return $.epoAPI.util.isEmpty( obj, [ undefined, null, false ] );
477	};
478
479	$.epoAPI.util.clenValue = function( value, retIfEmpty ) {
480		if ( $.epoAPI.util.isEmptyValue( value ) ) {
481			return retIfEmpty || '';
482		}
483		return value;
484	};
485
486	$.epoAPI.locale.getSystemDecimalSeparator = function() {
487		var n = 1.1;
488
489		// This gets null on languages that don't return 1 in a number format.
490		n = /^1(.+)1$/.exec( n.toLocaleString() );
491
492		if ( n ) {
493			n = n[ 1 ];
494		} else {
495			n = ',';
496		}
497
498		return n;
499	};
500
501	$.epoAPI.template.html = function( template, obj ) {
502		var $template_html = template( obj );
503
504		$template_html = $template_html.replace( '/*<![CDATA[*/', '' );
505		$template_html = $template_html.replace( '/*]]>*/', '' );
506
507		return $template_html;
508	};
509
510	$.epoAPI.filters = {};
511
512	$.epoAPI.addFilter = function( $tag, $function_to_add, $priority, $accepted_args ) {
513		var $idx;
514
515		$priority = parseInt( $priority, 10 );
516		if ( isNaN( $priority ) ) {
517			$priority = 10;
518		}
519		$accepted_args = parseInt( $accepted_args, 10 );
520		if ( isNaN( $accepted_args ) ) {
521			$accepted_args = 1;
522		}
523		$idx = $function_to_add + '_' + $priority;
524		if ( ! $.epoAPI.filters[ $tag ] ) {
525			$.epoAPI.filters[ $tag ] = {};
526		}
527		if ( ! $.epoAPI.filters[ $tag ][ $priority ] ) {
528			$.epoAPI.filters[ $tag ][ $priority ] = {};
529		}
530		$.epoAPI.filters[ $tag ][ $priority ][ $idx ] = {
531			func: $function_to_add,
532			accepted_args: $accepted_args
533		};
534
535		return true;
536	};
537
538	$.epoAPI.removeFilter = function( $tag, $function_to_remove, $priority ) {
539		var $idx;
540		$priority = parseInt( $priority, 10 );
541		if ( isNaN( $priority ) ) {
542			$priority = 10;
543		}
544		$idx = $function_to_remove + '_' + $priority;
545
546		if ( $.epoAPI.filters[ $tag ] && $.epoAPI.filters[ $tag ][ $priority ] && $.epoAPI.filters[ $tag ][ $priority ][ $idx ] ) {
547			delete $.epoAPI.filters[ $tag ][ $priority ][ $idx ];
548			return true;
549		}
550
551		return false;
552	};
553
554	$.epoAPI.applyFilter = function( $tag, $value ) {
555		var $args = $.makeArray( arguments );
556		var priorities;
557
558		$args.splice( 0, 1 );
559
560		if ( ! $.epoAPI.filters[ $tag ] ) {
561			return $value;
562		}
563
564		priorities = $.epoAPI.filters[ $tag ];
565		$.each( priorities, function( i, el ) {
566			$.each( el, function( i2, el2 ) {
567				var func = el2.func;
568
569				if ( func instanceof Function ) {
570					$value = func.apply( null, $args );
571				} else if ( window[ func ] && window[ func ] instanceof Function ) {
572					$value = window[ func ].apply( null, $args );
573				}
574			} );
575		} );
576
577		return $value;
578	};
579
580	// Backwards compatibility (to be removed in the future)
581	$.tc_add_filter = $.epoAPI.addFilter;
582	$.tc_remove_filter = $.epoAPI.removeFilter;
583	$.tc_apply_filters = $.epoAPI.applyFilter;
584}( window.jQuery ) );
585
586/*
587 * Formatting functions
588 *
589 * Code modifiled from accounting.js
590 * http://openexchangerates.github.io/accounting.js/
591 */
592
593( function( $ ) {
594	'use strict';
595
596	/**
597	 * Check and normalise the value of precision (must be positive integer)
598	 *
599	 * @param {any} val  the value to check.
600	 * @param {any} base the value to return is check fails.
601	 */
602	function checkPrecision( val, base ) {
603		val = Math.round( Math.abs( val ) );
604		return isNaN( val ) ? base : val;
605	}
606
607	/**
608	 * Takes a string/array of strings, removes all formatting/cruft
609	 * and returns the raw float value.
610	 *
611	 * @param {any} value   the value to unformat.
612	 * @param {any} decimal the decimal point.
613	 */
614	function unformat( value, decimal ) {
615		var regex;
616		var unformatted;
617
618		// Recursively unformat arrays:
619		if ( Array.isArray( value ) ) {
620			return value.map( value, function( val ) {
621				return unformat( val, decimal );
622			} );
623		}
624
625		// Fails silently (need decent errors):
626		value = value || 0;
627
628		// Return the value as-is if it's already a number:
629		if ( typeof value === 'number' ) {
630			return value;
631		}
632
633		// Default decimal point comes from settings, but could be set to eg. "," in opts:
634		decimal = decimal || '.';
635
636		// Build regex to strip out everything except digits, decimal point and minus sign:
637		regex = new RegExp( '[^0-9-' + decimal + ']', [ 'g' ] );
638		unformatted = parseFloat(
639			( '' + value )
640				.replace( regex, '' ) // strip out any cruft
641				.replace( decimal, '.' ) // make sure decimal point is standard
642		);
643
644		// This will fail silently which may cause trouble, let's wait and see:
645		return ! isNaN( unformatted ) ? unformatted : 0;
646	}
647
648	/**
649	 * Implementation of toFixed() that treats floats more like decimals
650	 *
651	 * Fixes binary rounding issues (eg. (0.615).toFixed(2) === "0.61") that present
652	 * problems for accounting- and finance-related software.
653	 *
654	 * @param {any} value     the value to convert.
655	 * @param {int} precision the precision.
656	 */
657	function toFixed( value, precision ) {
658		var exponentialForm;
659		var rounded;
660		var finalResult;
661
662		if ( ! Number.isFinite( value ) ) {
663			return '-';
664		}
665
666		precision = checkPrecision( precision, 2 );
667
668		exponentialForm = Number( unformat( value ) * Math.pow( 10, precision ) );
669		rounded = Math.round( exponentialForm );
670		finalResult = Number( rounded / Math.pow( 10, precision ) ).toFixed( precision );
671		return finalResult;
672	}
673
674	/**
675	 * Format into currency or a number, with comma-separated thousands and custom precision/decimal places
676	 *
677	 * opts = {
678	 *     symbol: "$",    // currency symbol is '$'
679	 *     format: "%s%v", // controls output: %s = symbol, %v = value (can be object, see docs)
680	 *     decimal: ".",   // decimal point separator
681	 *     thousand: ",",  // thousands separator
682	 *     precision: 2,   // decimal places
683	 * }
684	 *
685	 * @param {any}    number the value to convert.
686	 * @param {Object} opts   the options for formatting.
687	 */
688	function format( number, opts ) {
689		var formats;
690		var useFormat;
691		var negative;
692		var base;
693		var mod;
694
695		// Resursively format arrays:
696		if ( Array.isArray( number ) ) {
697			return number.map( function( val ) {
698				return format( val, opts );
699			} );
700		}
701
702		// Clean up number:
703		number = unformat( number );
704
705		// Do not format if opts are not correct or missing
706		if ( ! opts ) {
707			return number;
708		}
709
710		// Clean up precision
711		opts.precision = checkPrecision( opts.precision );
712
713		// Format currency
714		if ( opts.format && opts.symbol ) {
715			// Check format (returns object with pos, neg and zero):
716			formats = {
717				pos: opts.format,
718				neg: opts.format.replace( '-', '' ).replace( '%v', '-%v' ),
719				zero: opts.format
720			};
721
722			// Choose which format to use for this value:
723			useFormat = number > 0 ? formats.pos : number < 0 ? formats.neg : formats.zero;
724
725			// Return with currency symbol added:
726			opts.opts = {
727				precision: opts.precision,
728				thousand: opts.thousand,
729				decimal: opts.decimal
730			};
731
732			number = useFormat.replace( '%s', opts.symbol ).replace( '%v', format( Math.abs( number ), opts.opts ) );
733
734			// Format number
735		} else {
736			if ( ! Number.isFinite( number ) ) {
737				return '-';
738			}
739			// Do some calc:
740			negative = number < 0 ? '-' : '';
741			base = parseInt( toFixed( Math.abs( number || 0 ), opts.precision ), 10 ) + '';
742			mod = base.length > 3 ? base.length % 3 : 0;
743
744			// Format the number:
745			number = negative + ( mod ? base.substring( 0, mod ) + opts.thousand : '' ) + base.substring( mod ).replace( /(\d{3})(?=\d)/g, '$1' + opts.thousand ) + ( opts.precision ? opts.decimal + toFixed( Math.abs( number ), opts.precision ).split( '.' )[ 1 ] : '' );
746		}
747
748		return number;
749	}
750
751	$.epoAPI.math.unformat = unformat;
752	$.epoAPI.math.format = format;
753	$.epoAPI.math.toFixed = toFixed;
754}( window.jQuery ) );
755
756/**
757 * 3. ThemeComplete jQuery extensions
758 *
759 * @param {Object} $ The jQuery object.
760 */
761( function( $ ) {
762	'use strict';
763
764	var expo;
765	var rCRLF = /\r?\n/g;
766	var rcheckableType = /^(?:checkbox|radio)$/i;
767	var rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i;
768	var rsubmittable = /^(?:input|select|textarea|keygen)/i;
769
770	// Extend jQuery easing
771	if ( ! $.easing.easeInExpo ) {
772		expo = function( p ) {
773			return Math.pow( p, 6 );
774		};
775		$.easing.easeInExpo = expo;
776		$.easing.easeOutExpo = function( p ) {
777			return 1 - expo( 1 - p );
778		};
779		$.easing.easeInOutExpo = function( p ) {
780			return p < 0.5 ? expo( p * 2 ) / 2 : 1 - ( expo( ( p * -2 ) + 2 ) / 2 );
781		};
782	}
783
784	// Add jQuery plugins
785	$.fn.extend( {
786		// Create checkbox switch
787		tmcheckboxes: function() {
788			var tm_settings_wrap_checkbox = this.find( ":checkbox:not('.tm-default-checkbox')" ).not( '.wp-tab-panel :checkbox, .tm-weekdays-picker-wrap :checkbox, .tm-months-picker-wrap :checkbox' );
789			tm_settings_wrap_checkbox.wrap( '<label class="tm-epo-switch-wrapper-label"></label>' );
790			tm_settings_wrap_checkbox.wrap( '<span class="tm-epo-switch-wrapper tc"></span>' );
791			tm_settings_wrap_checkbox.after( '<span class="tc-label tm-epo-switch tc"></span>' );
792			return this;
793		},
794
795		// Encode a set of HTML elements as an array of names and values.
796		// Elements do not need to be inside a form
797		tcSerializeArray: function() {
798			return this.find( ':input' )
799				.filter( function() {
800					var type = this.type;
801
802					// Use .is( ":disabled" ) so that fieldset[disabled] works
803					return this.name && ! $( this ).is( ':disabled' ) && rsubmittable.test( this.nodeName ) && ! rsubmitterTypes.test( type ) && ( this.checked || ! rcheckableType.test( type ) );
804				} )
805				.map( function( i, elem ) {
806					var val = $( this ).val();
807
808					if ( val === null ) {
809						return null;
810					}
811
812					if ( Array.isArray( val ) ) {
813						return $.map( val, function( thisval ) {
814							return { name: elem.name, value: thisval.replace( rCRLF, '\r\n' ) };
815						} );
816					}
817
818					return { name: elem.name, value: val.replace( rCRLF, '\r\n' ) };
819				} )
820				.get();
821		},
822
823		// convert element to a valid JSON object
824		tcSerializeObject: function() {
825			var o = {};
826			var a = this.tcSerializeArray();
827
828			$.each( a, function() {
829				var name = this.name;
830				var value = this.value || '';
831
832				if ( name.endsWith( '[]' ) ) {
833					if ( ! o[ name ] ) {
834						o[ name ] = [];
835					}
836					o[ name ].push( value );
837				} else {
838					o[ name ] = value;
839				}
840			} );
841
842			return o;
843		},
844
845		// Scroll container to a specific element
846		tcScrollTo: function( obj, duration, offset ) {
847			var element = this;
848
849			obj = $( obj );
850			if ( obj.length === 0 ) {
851				return this;
852			}
853			if ( ! duration ) {
854				duration = 0;
855			}
856			if ( ! offset ) {
857				offset = 0;
858			}
859			if ( element[ 0 ].self === window ) {
860				element = $( 'html, body' );
861			} else {
862				if ( element.find( '.woodmart-scroll-content' ).length ) {
863					element = element.find( '.woodmart-scroll-content' );
864				}
865				if ( ! element.offset() ) {
866					element = $( 'html, body' );
867				} else {
868					offset = offset + ( element.scrollTop() - element.offset().top );
869				}
870			}
871
872			return element.animate(
873				{
874					scrollTop: $( obj ).offset().top + offset
875				},
876				duration
877			);
878		},
879
880		/**
881		 * Textarea and select clone() bug workaround | Spencer Tipping
882		 * Licensed under the terms of the MIT source code license
883		 * https://github.com/spencertipping/jquery.fix.clone/blob/master/jquery.fix.clone.js
884		 */
885		tcClone: function() {
886			var i;
887			var l;
888			var j;
889			var m;
890			var result = $.fn.clone.apply( this, arguments );
891			var textareas = this.find( 'textarea' ).add( this.filter( 'textarea' ) );
892			var resultTextareas = result.find( 'textarea' ).add( result.filter( 'textarea' ) );
893			var selects = this.find( 'select' ).add( this.filter( 'select' ) );
894			var resultSelects = result.find( 'select' ).add( result.filter( 'select' ) );
895
896			for ( i = 0, l = textareas.length; i < l; i += 1 ) {
897				$( resultTextareas[ i ] ).val( $( textareas[ i ] ).val() );
898			}
899			for ( i = 0, l = selects.length; i < l; i += 1 ) {
900				for ( j = 0, m = selects[ i ].options.length; j < m; j += 1 ) {
901					if ( selects[ i ].options[ j ].selected === true ) {
902						resultSelects[ i ].options[ j ].selected = true;
903					}
904				}
905			}
906
907			return result;
908		}
909	} );
910}( window.jQuery ) );
911;
912/**
913 * jquery.tcfloatbox.js
914 *
915 * @param {Object} $ The jQuery object
916 * @version: v2.0
917 * @author: ThemeComplete
918 *
919 * Created by ThemeComplete
920 *
921 * Copyright (c) 2025 themeComplete http://themecomplete.com
922 */
923( function( $ ) {
924	'use strict';
925
926	var FloatBox = function( dom, options ) {
927		this.element = $( dom );
928
929		this.settings = $.extend( {}, $.fn.tcFloatBox.defaults, options );
930		this.settings.type = '<' + this.settings.type + '>';
931
932		if ( this.element.length === 1 ) {
933			this.init();
934			return this;
935		}
936
937		return false;
938	};
939
940	FloatBox.prototype = {
941		constructor: FloatBox,
942
943		destroy: function() {
944			var settings = this.settings;
945
946			if ( this.instance !== undefined ) {
947				$.fn.tcFloatBox.instances.splice( this.instance, 1 );
948
949				delete this.instance;
950
951				if ( settings.hideelements ) {
952					$( 'embed, object, select' ).css( {
953						visibility: 'visible'
954					} );
955				}
956
957				if ( settings._ovl ) {
958					settings._ovl.off();
959				}
960
961				$( settings.floatboxIDMain ).remove();
962
963				this.element.removeData( 'tcfloatbox' );
964
965				$( window ).off( 'scroll.tcfloatbox' + this.instance );
966				$( window ).off( 'resize.tcfloatbox' + this.instance );
967			}
968
969			return this;
970		},
971
972		hide: function() {
973			var settings = this.settings;
974
975			if ( settings.hideelements ) {
976				$( 'embed, object, select' ).css( {
977					visibility: 'visible'
978				} );
979			}
980			if ( settings.showoverlay === true ) {
981				if ( settings._ovl ) {
982					settings._ovl.removeClass( 'fl-overlay' );
983				}
984			}
985
986			$( settings.floatboxID )
987				.addClass( 'tc-closing' )
988				.removeClass( settings.animateIn )
989				.addClass( settings.animateOut );
990			$( settings.floatboxID ).animate(
991				{
992					opacity: 0
993				},
994				settings.closefadeouttime,
995				function() {
996					$( settings.floatboxIDMain ).hide();
997					$( settings.floatboxID )
998						.removeClass( 'tc-closing' )
999						.addClass( 'tc-closed' );
1000				}
1001			);
1002
1003			$( window ).off( 'scroll.tcfloatbox' + this.instance );
1004			$( window ).off( 'resize.tcfloatbox' + this.instance );
1005		},
1006
1007		show: function() {
1008			var settings = this.settings;
1009
1010			if ( this.element.length === 1 ) {
1011				if ( this.instance === undefined ) {
1012					this.init();
1013				}
1014
1015				if ( settings.hideelements ) {
1016					$( 'embed, object, select' ).css( {
1017						visibility: 'hidden'
1018					} );
1019				}
1020
1021				if ( settings.showoverlay === true ) {
1022					if ( ! settings._ovl ) {
1023						settings._ovl = settings.main;
1024						settings._ovl.css(
1025							{
1026								zIndex: parseInt( settings.zIndex, 10 ) - 1
1027							}
1028						);
1029						if ( ! settings.ismodal ) {
1030							if ( settings.cancelEvent || settings.unique ) {
1031								settings._ovl.on(
1032									'click',
1033									function( e ) {
1034										if ( e.target === $( settings.floatboxID )[ 0 ] || $( e.target ).closest( settings.floatboxID ).length ) {
1035											return;
1036										}
1037										this.applyCancelEvent.call( this, this );
1038									}.bind( this )
1039								);
1040							} else {
1041								settings._ovl.on(
1042									'click',
1043									function( e ) {
1044										if ( e.target === $( settings.floatboxID )[ 0 ] || $( e.target ).closest( settings.floatboxID ).length ) {
1045											return;
1046										}
1047										settings.cancelfunc.call( this, this );
1048									}.bind( this )
1049								);
1050							}
1051						}
1052					}
1053					settings._ovl.addClass( 'fl-overlay' );
1054				}
1055
1056				if ( settings.showfunc ) {
1057					settings.showfunc.call();
1058				}
1059
1060				$( settings.floatboxID )
1061					.removeClass( 'tc-closing tc-closed' )
1062					.addClass(
1063						settings.animationBaseClass + ' ' + settings.animateIn
1064					);
1065
1066				$( settings.floatboxID ).addClass( 'flasho-center' );
1067			}
1068		},
1069
1070		applyCancelEvent: function() {
1071			var settings = this.settings;
1072
1073			if ( settings.cancelEvent === true ) {
1074				this.destroy();
1075			} else if ( typeof settings.cancelEvent === 'function' ) {
1076				settings.cancelEvent.call( this, this );
1077			}
1078		},
1079
1080		applyCancelEventFromKey: function( e ) {
1081			if ( e.which === 27 ) {
1082				this.applyCancelEvent();
1083			}
1084		},
1085
1086		applyUpdateEvent: function() {
1087			var settings = this.settings;
1088
1089			if ( typeof settings.updateEvent === 'function' ) {
1090				settings.updateEvent.call( this, this );
1091			}
1092		},
1093
1094		applyUpdateEventFromKey: function( e ) {
1095			if ( e.which === 13 ) {
1096				this.applyUpdateEvent();
1097			}
1098		},
1099
1100		init: function() {
1101			var settings = this.settings;
1102			var main;
1103
1104			if ( this.element.length === 1 ) {
1105				// Instance initialization
1106				if ( $.fn.tcFloatBox.instances.length > 0 ) {
1107					settings.zIndex =
1108						parseInt(
1109							$.fn.tcFloatBox.instances[
1110								$.fn.tcFloatBox.instances.length - 1
1111							].zIndex,
1112							10
1113						) + 100;
1114				}
1115				this.instance = $.fn.tcFloatBox.instances.length;
1116				$.fn.tcFloatBox.instances.push( settings );
1117
1118				settings.id = settings.id + this.instance;
1119				settings.floatboxID = '#' + $.epoAPI.dom.id( settings.id );
1120
1121				if ( settings.idMain === settings.id ) {
1122					settings.idMain = 'main-' + settings.idMain;
1123				}
1124
1125				settings.idMain = settings.idMain + this.instance;
1126				settings.floatboxIDMain = '#' + $.epoAPI.dom.id( settings.idMain );
1127
1128				this.hide();
1129
1130				main = $( '<div>' )
1131					.attr( 'id', settings.idMain )
1132					.addClass( settings.classnameMain );
1133
1134				$( settings.type )
1135					.attr( 'id', settings.id )
1136					.addClass( settings.classname )
1137					.html( settings.data )
1138					.appendTo( main );
1139
1140				main.appendTo( this.element );
1141
1142				settings.main = main;
1143
1144				if ( settings.leger ) {
1145					$( settings.floatboxIDMain ).css( {
1146						width: settings.width,
1147						height: settings.height
1148					} );
1149				} else {
1150					$( settings.floatboxID ).css( {
1151						width: settings.width,
1152						height: settings.height
1153					} );
1154				}
1155
1156				if ( settings.minWidth ) {
1157					$( settings.floatboxID ).css( {
1158						'min-width': settings.minWidth
1159					} );
1160				}
1161
1162				if ( settings.minHeight ) {
1163					$( settings.floatboxID ).css( {
1164						'min-height': settings.minHeight
1165					} );
1166				}
1167
1168				if ( settings.maxWidth ) {
1169					$( settings.floatboxID ).css( {
1170						'max-width': settings.maxWidth
1171					} );
1172				}
1173
1174				if ( settings.maxHeight ) {
1175					$( settings.floatboxID ).css( {
1176						'max-height': settings.maxHeight
1177					} );
1178				}
1179
1180				$( settings.floatboxIDMain ).css( {
1181					'z-index': settings.zIndex
1182				} );
1183
1184				this.cancelfunc = settings.cancelfunc;
1185
1186				if ( settings.cancelEvent && settings.cancelClass ) {
1187					$( settings.floatboxID )
1188						.find( settings.cancelClass )
1189						.on( 'click', this.applyCancelEvent.bind( this ) );
1190					if ( settings.isconfirm ) {
1191						$( document )
1192							.off( 'keyup.escape-' + settings.floatboxID )
1193							.on(
1194								'keyup.escape-' + settings.floatboxID,
1195								this.applyCancelEventFromKey.bind( this )
1196							);
1197					}
1198				}
1199
1200				if ( settings.updateEvent && settings.updateClass ) {
1201					$( settings.floatboxID )
1202						.find( settings.updateClass )
1203						.on( 'click', this.applyUpdateEvent.bind( this ) );
1204					if ( settings.isconfirm ) {
1205						$( document )
1206							.off( 'keyup.enter-' + settings.floatboxID )
1207							.on(
1208								'keyup.enter-' + settings.floatboxID,
1209								this.applyUpdateEventFromKey.bind( this )
1210							);
1211					}
1212				}
1213
1214				this.show();
1215			}
1216		}
1217	};
1218
1219	$.fn.tcFloatBox = function( option ) {
1220		var methodReturn;
1221		var targets = $( this );
1222		var data = targets.data( 'tcfloatbox' );
1223		var options;
1224		var ret;
1225
1226		if ( typeof option === 'object' ) {
1227			options = option;
1228		} else {
1229			options = {};
1230		}
1231
1232		if ( ! data ) {
1233			data = new FloatBox( this, options );
1234			targets.data( 'tcfloatbox', data );
1235		}
1236
1237		if ( typeof option === 'string' ) {
1238			methodReturn = data[ option ].apply( data, [] );
1239		}
1240
1241		if ( methodReturn === undefined ) {
1242			ret = targets;
1243		} else {
1244			ret = methodReturn;
1245		}
1246
1247		return ret;
1248	};
1249
1250	$.fn.tcFloatBox.defaults = {
1251		idMain: 'floatbox',
1252		classnameMain: 'floatbox',
1253		leger: false,
1254		id: 'flasho',
1255		classname: 'flasho',
1256		type: 'div',
1257		data: '',
1258		width: 'auto',
1259		height: 'auto',
1260		minWidth: 0,
1261		minHeight: 0,
1262		maxWidth: 0,
1263		maxHeight: 0,
1264		closefadeouttime: 1000,
1265		animationBaseClass: 'tm-animated',
1266		animateIn: 'fadein',
1267		animateOut: 'fadeout',
1268		fps: 4,
1269		hideelements: false,
1270		showoverlay: true,
1271		zIndex: 100100,
1272		ismodal: false,
1273		cancelfunc: FloatBox.prototype.hide,
1274		showfunc: null,
1275		cancelEvent: true,
1276		cancelClass: '.floatbox-cancel',
1277		updateEvent: false,
1278		updateClass: false,
1279		unique: true,
1280		isconfirm: false
1281	};
1282
1283	$.fn.tcFloatBox.instances = [];
1284
1285	$.fn.tcFloatBox.Constructor = FloatBox;
1286
1287	$.tcFloatBox = function( options ) {
1288		var target = $( 'body' );
1289		var data = false;
1290		var hasAtLeastOneNonToolTip = target
1291			.map( function() {
1292				return $( this ).data( 'tcfloatbox' ) || '';
1293			} )
1294			.get()
1295			.some( function( value ) {
1296				return value === '';
1297			} );
1298		if ( hasAtLeastOneNonToolTip || options.unique ) {
1299			data = new FloatBox( target, options );
1300			target.data( 'tcfloatbox', data );
1301		} else {
1302			data = target.data( 'tcfloatbox' );
1303			data.init();
1304		}
1305		return data;
1306	};
1307}( window.jQuery ) );
1308;
1309/**
1310 * jquery.tctooltip.js
1311 *
1312 * @param {Window} window - The window object representing the browser window.
1313 * @param {jQuery} $      - The jQuery object.
1314 * @version: v1.1
1315 * @author: themeComplete
1316 *
1317 * Created by themeComplete
1318 *
1319 * Copyright (c) 2021 themeComplete http://themecomplete.com
1320 */
1321( function( window, $ ) {
1322	'use strict';
1323
1324	var ToolTip = function( dom, options ) {
1325		this.targets = $( dom );
1326
1327		this.settings = $.extend( {}, $.fn.tcToolTip.defaults, options );
1328
1329		if ( this.targets.length > 0 ) {
1330			this.init();
1331			return this;
1332		}
1333
1334		return false;
1335	};
1336
1337	var TMEPOJS;
1338
1339	// document ready
1340	$( function() {
1341		TMEPOJS = window.TMEPOJS || { tm_epo_global_tooltip_max_width: '340px' };
1342	} );
1343
1344	ToolTip.prototype = {
1345		constructor: ToolTip,
1346
1347		removeTooltip: function( target, tooltip ) {
1348			var settings = this.settings;
1349
1350			if ( target.data( 'is_moving' ) ) {
1351				return;
1352			}
1353
1354			tooltip.removeClass( settings.fadin ).addClass( settings.fadeout );
1355
1356			tooltip.animate(
1357				{
1358					opacity: 0
1359				},
1360				settings.speed,
1361				function() {
1362					$( this ).remove();
1363				}
1364			);
1365
1366			if ( target.data( 'tmtip-title' ) && target.data( 'tm-tip-html' ) === undefined && ! target.attr( 'data-tm-tooltip-html' ) ) {
1367				target.attr( 'title', target.data( 'tmtip-title' ) );
1368			}
1369
1370			$( window ).off( 'scroll.tcToolTip resize.tcToolTip' );
1371
1372			if ( settings.onetime ) {
1373				this.destroy();
1374			}
1375		},
1376
1377		initTooltip: function( target, tooltip, nofx ) {
1378			var settings = this.settings;
1379			var tip;
1380			var scroll;
1381			var pos_left;
1382			var pos_top;
1383			var pos_from_top;
1384			var original_pos_left;
1385
1386			if ( target && tooltip && target.length === 1 && tooltip.length === 1 && target.data( 'tm-has-tm-tip' ) === 1 ) {
1387				if ( nofx === 1 ) {
1388					if ( target.data( 'tm-tip-html' ) !== undefined ) {
1389						tip = target.data( 'tm-tip-html' );
1390					} else if ( target.attr( 'data-tm-tooltip-html' ) ) {
1391						tip = target.attr( 'data-tm-tooltip-html' );
1392					} else {
1393						tip = target.attr( 'title' );
1394					}
1395
1396					tooltip.html( tip );
1397					target.data( 'is_moving', true );
1398				}
1399
1400				tooltip.find( 'aside' ).hide();
1401
1402				if ( TMEPOJS.tm_epo_global_tooltip_max_width === '' ) {
1403					// 50: average scrollbar width. Needed to avoid flickering width issues on mobile.
1404					if ( $( window ).width() <= tooltip.outerWidth() * 1.2 ) {
1405						tooltip.css( 'max-width', ( $( window ).width() / 1.2 ) + 'px' );
1406					} else {
1407						tooltip.css( 'max-width', '340px' );
1408					}
1409				} else {
1410					if ( TMEPOJS.tm_epo_global_tooltip_max_width.isNumeric() ) {
1411						TMEPOJS.tm_epo_global_tooltip_max_width = TMEPOJS.tm_epo_global_tooltip_max_width + 'px';
1412					}
1413					tooltip.css( 'max-width', TMEPOJS.tm_epo_global_tooltip_max_width );
1414				}
1415
1416				tooltip.find( 'aside' ).show();
1417
1418				tooltip.css( {
1419					left: '',
1420					right: '',
1421					top: ''
1422				} );
1423
1424				scroll = $.epoAPI.dom.scroll();
1425				pos_left = target.offset().left + ( target.outerWidth() / 2 ) - ( tooltip.outerWidth() / 2 );
1426				original_pos_left = pos_left;
1427
1428				if ( pos_left < 0 ) {
1429					pos_left = target.offset().left + ( target.outerWidth() / 2 ) - 20;
1430					tooltip.addClass( 'left' );
1431				} else {
1432					tooltip.removeClass( 'left' );
1433				}
1434				if ( original_pos_left >= 0 && pos_left + tooltip.outerWidth() > $( window ).width() ) {
1435					pos_left = target.offset().left - tooltip.outerWidth() + ( target.outerWidth() / 2 ) + 20;
1436					if ( pos_left < 0 ) {
1437						pos_left = pos_left - 10;
1438						tooltip.css( 'max-width', 'calc(' + tooltip.css( 'max-width' ) + ' - ' + Math.abs( pos_left ) + 'px)' );
1439						pos_left = 10;
1440					}
1441					tooltip.addClass( 'right' );
1442				} else {
1443					tooltip.removeClass( 'right' );
1444				}
1445
1446				tooltip.css( {
1447					left: pos_left,
1448					right: 'auto',
1449					top: pos_top
1450				} );
1451				pos_top = target.offset().top - tooltip.outerHeight() - 10;
1452				pos_from_top = target.offset().top - scroll.top - tooltip.outerHeight() - 10;
1453
1454				if ( pos_top < 0 || pos_from_top < 0 ) {
1455					pos_top = target.offset().top + target.outerHeight() + 10;
1456					tooltip.addClass( 'top' );
1457					tooltip.removeClass( 'bottom' );
1458				} else {
1459					tooltip.removeClass( 'top' );
1460					tooltip.addClass( 'bottom' );
1461				}
1462
1463				$( window ).trigger( 'tm_tooltip_show' );
1464
1465				if ( nofx ) {
1466					tooltip.css( {
1467						left: pos_left,
1468						top: pos_top
1469					} );
1470					target.data( 'is_moving', false );
1471				} else {
1472					tooltip
1473						.css( {
1474							left: pos_left,
1475							top: pos_top
1476						} )
1477						.removeClass( settings.fadeout )
1478						.addClass( settings.fadin );
1479				}
1480			}
1481		},
1482
1483		show: function( target ) {
1484			var tooltip;
1485			var tip;
1486			var img;
1487			var settings = this.settings;
1488
1489			if ( target.data( 'is_moving' ) ) {
1490				return;
1491			}
1492
1493			if ( target.data( 'tm-has-tm-tip' ) === 1 ) {
1494				if ( target.data( 'tm-tip-html' ) !== undefined ) {
1495					tip = target.data( 'tm-tip-html' );
1496					if ( target.attr( 'title' ) ) {
1497						target.data( 'tmtip-title', target.attr( 'title' ) );
1498					}
1499					target.removeAttr( 'title' );
1500				} else if ( target.attr( 'data-tm-tooltip-html' ) ) {
1501					tip = target.attr( 'data-tm-tooltip-html' );
1502					if ( target.attr( 'title' ) ) {
1503						target.data( 'tmtip-title', target.attr( 'title' ) );
1504					}
1505					target.removeAttr( 'title' );
1506				} else {
1507					tip = target.attr( 'title' );
1508				}
1509
1510				if ( tip !== undefined ) {
1511					$( '#tm-tooltip' ).remove();
1512					if ( ! settings.tipclass ) {
1513						settings.tipclass = '';
1514					} else {
1515						settings.tipclass = ' ' + settings.tipclass;
1516					}
1517					tooltip = $( '<div id="tm-tooltip" class="tm-tip tm-animated' + settings.tipclass + '"></div>' );
1518					tooltip.css( 'opacity', 0 ).html( tip ).appendTo( 'body' );
1519
1520					img = tooltip.find( 'img' );
1521					if ( img.length > 0 ) {
1522						img.on( 'load', this.initTooltip.bind( this, target, tooltip ) );
1523					}
1524
1525					this.initTooltip( target, tooltip );
1526
1527					$( window ).on( 'scroll.tcToolTip resize.tcToolTip', this.initTooltip.bind( this, target, tooltip ) );
1528
1529					target.data( 'is_moving', false );
1530
1531					target.on( 'tmmovetooltip', this.initTooltip.bind( this, target, tooltip, 1 ) );
1532					target.on( 'mouseleave.tc tmhidetooltip', this.removeTooltip.bind( this, target, tooltip ) );
1533
1534					target.closest( 'label' ).on( 'mouseleave.tc tmhidetooltip', this.removeTooltip.bind( this, target, tooltip ) );
1535
1536					tooltip.on( 'click', this.removeTooltip.bind( this, target, tooltip ) );
1537				}
1538			}
1539
1540			return false;
1541		},
1542
1543		destroy: function() {
1544			if ( this.targets.length > 0 ) {
1545				this.targets.toArray().forEach( function( element ) {
1546					var target = $( element );
1547					target.closest( 'off' ).on( 'mouseleave.tc tmhidetooltip' );
1548					target.off( 'tc-tooltip-html-changed tmmovetooltip tmhidetooltip mouseenter.tc mouseleave.tc tmshowtooltip.tc' );
1549					target.removeData( 'tmtip-title' );
1550					target.removeData( 'tm-tip-html' );
1551				} );
1552				this.targets.removeData( 'tctooltip tm-has-tm-tip is_moving' );
1553			}
1554		},
1555
1556		init: function() {
1557			var that = this;
1558			var settings = this.settings;
1559
1560			if ( this.targets.length > 0 ) {
1561				this.targets.toArray().forEach( function( element ) {
1562					var target;
1563					var is_swatch;
1564					var is_swatch_desc;
1565					var is_swatch_lbl_desc;
1566					var is_swatch_img;
1567					var is_swatch_img_lbl;
1568					var is_swatch_img_desc;
1569					var is_swatch_img_lbl_desc;
1570					var tip;
1571					var label;
1572					var desc;
1573					var descHTML;
1574					var get_img_src;
1575					var findlabel;
1576					var is_hide_label;
1577					var findlabelText;
1578
1579					target = $( element );
1580					tip = settings.tip || undefined;
1581
1582					if ( target.data( 'tm-has-tm-tip' ) === undefined ) {
1583						is_swatch = target.attr( 'data-tm-tooltip-swatch' );
1584						is_swatch_desc = target.attr( 'data-tm-tooltip-swatch-desc' );
1585						is_swatch_lbl_desc = target.attr( 'data-tm-tooltip-swatch-lbl-desc' );
1586						is_swatch_img = target.attr( 'data-tm-tooltip-swatch-img' );
1587						is_swatch_img_lbl = target.attr( 'data-tm-tooltip-swatch-img-lbl' );
1588						is_swatch_img_desc = target.attr( 'data-tm-tooltip-swatch-img-desc' );
1589						is_swatch_img_lbl_desc = target.attr( 'data-tm-tooltip-swatch-img-lbl-desc' );
1590
1591						target.data( 'tm-has-tm-tip', 1 );
1592
1593						if ( target.attr( 'data-original' ) !== undefined ) {
1594							get_img_src = target.attr( 'data-original' );
1595						} else if ( target.attr( 'src' ) !== undefined ) {
1596							if ( target.attr( 'data-src' ) !== undefined ) {
1597								get_img_src = target.attr( 'data-src' );
1598							} else {
1599								get_img_src = target.attr( 'src' );
1600							}
1601						} else {
1602							get_img_src = target[ 0 ].src;
1603						}
1604
1605						label = target.closest( '.tmcp-field-wrap' );
1606						if ( label.is( '.tc-epo-element-product-holder' ) ) {
1607							label = target.closest( '.cpf-element' );
1608						}
1609						if ( label.length === 0 ) {
1610							label = target.closest( '.cpf-element' );
1611						}
1612						// If the target is inside a product element and there is a closest section
1613						if ( label.is( '.cpf-type-product' ) && target.closest( '.tm-epo-element-label' ).length === 0 && target.closest( '.cpf-section' ).length ) {
1614							label = target.closest( '.cpf-section' ).find( '.tc-section-inner-wrap .tm-section-description.tm-description' );
1615						}
1616						if ( label.length === 0 ) {
1617							label = target.closest( '.cpf-section' ).find( '.tc-section-inner-wrap .tm-section-description.tm-description' );
1618						}
1619						findlabel = label.find( '.tm-tip-html' );
1620						if ( findlabel.length === 0 ) {
1621							findlabel = label.find( '.checkbox-image-label,.checkbox-image-label-inline,.radio-image-label,.radio-image-label-inline' );
1622						}
1623
1624						if ( findlabel.length === 0 ) {
1625							findlabel = label.next( '.checkbox-image-label,.checkbox-image-label-inline,.radio-image-label,.radio-image-label-inline,.tm-tip-html' );
1626						}
1627						if ( findlabel.length === 0 && label.is( '.tm-description' ) ) {
1628							findlabel = label;
1629						}
1630						label = findlabel;
1631
1632						findlabel = $( label );
1633						findlabelText = findlabel.find( '.tc-label-text' );
1634						if ( ! findlabelText.length ) {
1635							findlabelText = findlabel;
1636						}
1637
1638						is_hide_label = target.attr( 'data-tm-hide-label' ) === 'yes' || target.attr( 'data-tm-hide-label' ) === undefined || findlabel.is( '.tm-tip-html' );
1639
1640						descHTML = '';
1641						desc = target.closest( '.tmcp-field-wrap' );
1642						desc = desc.find( '[data-tm-tooltip-html]' );
1643						if ( desc.length === 0 ) {
1644							desc = target.closest( '.tmcp-field-wrap' ).find( '.tc-inline-description' );
1645							if ( desc.length > 0 ) {
1646								descHTML = desc.html();
1647							}
1648						} else {
1649							descHTML = desc.attr( 'data-tm-tooltip-html' );
1650						}
1651
1652						if ( tip === undefined ) {
1653							if ( is_swatch ) {
1654								tip = findlabelText.html();
1655							} else if ( is_swatch_desc && descHTML !== '' ) {
1656								tip = '<aside>' + descHTML + '</aside>';
1657							} else if ( is_swatch_lbl_desc && ( findlabelText.html() !== '' || descHTML !== '' ) ) {
1658								tip = '<aside>' + findlabelText.html() + '</aside><aside>' + descHTML + '</aside>';
1659							} else if ( is_swatch_img && get_img_src !== '' ) {
1660								tip = '<img src="' + get_img_src + '">';
1661							} else if ( is_swatch_img_lbl && ( findlabelText.html() !== '' || get_img_src !== '' ) ) {
1662								tip = '<img src="' + get_img_src + '"><aside>' + findlabelText.html() + '</aside>';
1663							} else if ( is_swatch_img_desc && ( get_img_src !== '' || descHTML !== '' ) ) {
1664								tip = '<img src="' + get_img_src + '"><aside>' + descHTML + '</aside>';
1665							} else if ( is_swatch_img_lbl_desc && ( findlabelText.html() !== '' || get_img_src !== '' || descHTML !== '' ) ) {
1666								tip = '<img src="' + get_img_src + '"><aside>' + findlabelText.html() + '</aside><aside>' + descHTML + '</aside>';
1667							}
1668
1669							if ( tip !== undefined ) {
1670								target.data( 'tm-tip-html', tip );
1671								if ( is_hide_label ) {
1672									findlabel.find( '.tm-tooltip' ).remove();
1673									findlabelText.hide();
1674								}
1675							}
1676							if ( tip === undefined ) {
1677								// The following two methods are here for dynamic tooltip support
1678								if ( target.attr( 'data-tm-tooltip-html' ) ) {
1679									tip = target.attr( 'data-tm-tooltip-html' );
1680								} else {
1681									tip = target.attr( 'title' );
1682								}
1683							}
1684						} else {
1685							target.data( 'tm-tip-html', tip );
1686						}
1687
1688						target.on( 'tc-tooltip-html-changed', function() {
1689							if ( target.attr( 'data-tm-tooltip-html' ) ) {
1690								target.show();
1691							} else {
1692								target.hide();
1693							}
1694						} );
1695
1696						if ( target.is( 'img' ) ) {
1697							target.closest( 'label' ).on( 'mouseenter tmshowtooltip', that.show.bind( that, target ) );
1698						}
1699						target.on( 'mouseenter.tc tmshowtooltip.tc', that.show.bind( that, target ) );
1700
1701						if ( settings.trigger ) {
1702							that.show( target );
1703						}
1704					}
1705				} );
1706			}
1707		}
1708	};
1709
1710	$.fn.tcToolTip = function( option ) {
1711		var methodReturn;
1712		var targets = $( this );
1713		var data;
1714		var options;
1715		var ret;
1716		var hasAtLeastOneNonToolTip = targets
1717			.map( function() {
1718				return $( this ).data( 'tctooltip' ) || '';
1719			} )
1720			.get()
1721			.some( function( value ) {
1722				return value === '';
1723			} );
1724
1725		if ( typeof option === 'object' ) {
1726			options = option;
1727		} else {
1728			options = {};
1729		}
1730
1731		if ( hasAtLeastOneNonToolTip ) {
1732			data = new ToolTip( this, options );
1733			targets.data( 'tctooltip', data );
1734		}
1735
1736		if ( typeof option === 'string' ) {
1737			data = targets.data( 'tctooltip' );
1738			methodReturn = data[ option ].apply( data, [] );
1739		}
1740
1741		if ( methodReturn === undefined ) {
1742			ret = targets;
1743		} else {
1744			ret = methodReturn;
1745		}
1746
1747		return ret;
1748	};
1749
1750	$.fn.tcToolTip.defaults = {
1751		fadin: 'fadein',
1752		fadeout: 'fadeout',
1753		speed: 1500
1754	};
1755
1756	$.fn.tcToolTip.instances = [];
1757
1758	$.fn.tcToolTip.Constructor = ToolTip;
1759
1760	$.tcToolTip = function( targets, options ) {
1761		var data = false;
1762		var hasAtLeastOneNonToolTip;
1763
1764		targets = targets || $( '.tm-tooltip' );
1765		hasAtLeastOneNonToolTip = targets
1766			.map( function() {
1767				return $( this ).data( 'tctooltip' ) || '';
1768			} )
1769			.get()
1770			.some( function( value ) {
1771				return value === '';
1772			} );
1773		if ( hasAtLeastOneNonToolTip ) {
1774			data = new ToolTip( targets, options );
1775			targets.data( 'tctooltip', data );
1776		}
1777
1778		return data;
1779	};
1780}( window, window.jQuery ) );
1781;
1782/*!
1783 * jQuery Validation Plugin v1.19.3
1784 *
1785 * https://jqueryvalidation.org/
1786 *
1787 * Copyright (c) 2021 Jörn Zaefferer
1788 * Released under the MIT license
1789 *
1790 * Renamed methods for compatibility
1791 * reformatted code by ThemeComplete
1792 */
1793( function( factory ) {
1794	'use strict';
1795	if ( typeof window.define === 'function' && window.define.amd ) {
1796		window.define( [ 'jquery' ], factory );
1797	} else if ( typeof window.module === 'object' && window.module.exports ) {
1798		window.module.exports = factory( window.require( 'jquery' ) );
1799	} else {
1800		factory( window.jQuery );
1801	}
1802}( function( $ ) {
1803	'use strict';
1804	var pendingRequests = {};
1805	var ajax;
1806	var trim;
1807
1808	$.extend( $.fn, {
1809		// https://jqueryvalidation.org/validate/
1810		tc_validate: function( options ) {
1811			var tc_validator;
1812			// If nothing is selected, return nothing; can't chain anyway
1813			if ( ! this.length ) {
1814				return;
1815			}
1816
1817			// Check if a tc_validator for this form was already created
1818			tc_validator = $.data( this[ 0 ], 'tc_validator' );
1819			if ( tc_validator ) {
1820				return tc_validator;
1821			}
1822
1823			// Add novalidate tag if HTML5.
1824			this.attr( 'novalidate', 'novalidate' );
1825
1826			tc_validator = new $.tc_validator( options, this[ 0 ] );
1827			$.data( this[ 0 ], 'tc_validator', tc_validator );
1828
1829			if ( tc_validator.settings.onsubmit ) {
1830				this.on( 'click.tc_validate', ':submit', function( event ) {
1831					// Track the used submit button to properly handle scripted
1832					// submits later.
1833					tc_validator.submitButton = event.currentTarget;
1834
1835					// Allow suppressing validation by adding a cancel class to the submit button
1836					if ( $( this ).hasClass( 'cancel' ) ) {
1837						tc_validator.cancelSubmit = true;
1838					}
1839
1840					// Allow suppressing validation by adding the html5 formnovalidate attribute to the submit button
1841					if ( $( this ).attr( 'formnovalidate' ) !== undefined ) {
1842						tc_validator.cancelSubmit = true;
1843					}
1844				} );
1845
1846				// Validate the form on submit
1847				this.on( 'submit.tc_validate', function( event ) {
1848					if ( tc_validator.settings.debug ) {
1849						// Prevent form submit to be able to see console output
1850						event.preventDefault();
1851					}
1852
1853					function handle() {
1854						var hidden;
1855						var result;
1856
1857						// Insert a hidden input as a replacement for the missing submit button
1858						// The hidden input is inserted in two cases:
1859						//   - A user defined a `submitHandler`
1860						//   - There was a pending request due to `remote` method and `stopRequest()`
1861						//     was called to submit the form in case it's valid
1862						if ( tc_validator.submitButton && ( tc_validator.settings.submitHandler || tc_validator.formSubmitted ) ) {
1863							hidden = $( "<input type='hidden'>" ).attr( 'name', tc_validator.submitButton.name ).val( $( tc_validator.submitButton ).val() ).appendTo( tc_validator.currentForm );
1864						}
1865
1866						if ( tc_validator.settings.submitHandler && ! tc_validator.settings.debug ) {
1867							result = tc_validator.settings.submitHandler.call( tc_validator, tc_validator.currentForm, event );
1868							if ( hidden ) {
1869								// And clean up afterwards; thanks to no-block-scope, hidden can be referenced
1870								hidden.remove();
1871							}
1872							if ( result !== undefined ) {
1873								return result;
1874							}
1875							return false;
1876						}
1877						return true;
1878					}
1879
1880					// Prevent submit for invalid forms or custom submit handlers
1881					if ( tc_validator.cancelSubmit ) {
1882						tc_validator.cancelSubmit = false;
1883						return handle();
1884					}
1885					if ( tc_validator.form() ) {
1886						if ( tc_validator.pendingRequest ) {
1887							tc_validator.formSubmitted = true;
1888							return false;
1889						}
1890						return handle();
1891					}
1892					tc_validator.focusInvalid();
1893					return false;
vendor: 5,155 bytes, lines 1894-2076
1894				} );
1895			}
1896
1897			return tc_validator;
1898		},
1899
1900		// https://jqueryvalidation.org/valid/
1901		tc_valid: function() {
1902			var valid;
1903			var tc_validator;
1904			var errorList;
1905
1906			if ( $( this[ 0 ] ).is( 'form' ) ) {
1907				valid = this.tc_validate().form();
1908			} else {
1909				errorList = [];
1910				valid = true;
1911				tc_validator = $( this[ 0 ].form ).tc_validate();
1912				this.each( function() {
1913					valid = tc_validator.element( this ) && valid;
1914					if ( ! valid ) {
1915						errorList = errorList.concat( tc_validator.errorList );
1916					}
1917				} );
1918				tc_validator.errorList = errorList;
1919			}
1920			return valid;
1921		},
1922
1923		// https://jqueryvalidation.org/rules/
1924		tc_rules: function( command, argument ) {
1925			var element = this[ 0 ];
1926			var isContentEditable = typeof this.attr( 'contenteditable' ) !== 'undefined' && this.attr( 'contenteditable' ) !== 'false';
1927			var settings;
1928			var staticRules;
1929			var existingRules;
1930			var data;
1931			var param;
1932			var filtered;
1933
1934			// If nothing is selected, return empty object; can't chain anyway
1935			if ( element === null ) {
1936				return;
1937			}
1938
1939			if ( ! element.form && isContentEditable ) {
1940				element.form = this.closest( 'form' )[ 0 ];
1941				element.name = this.attr( 'name' );
1942			}
1943
1944			if ( element.form === null ) {
1945				return;
1946			}
1947
1948			if ( command ) {
1949				settings = $.data( element.form, 'tc_validator' ).settings;
1950				staticRules = settings.rules;
1951				existingRules = $.tc_validator.staticRules( element );
1952				switch ( command ) {
1953					case 'add':
1954						$.extend( existingRules, $.tc_validator.normalizeRule( argument ) );
1955
1956						// Remove messages from rules, but allow them to be set separately
1957						delete existingRules.messages;
1958						staticRules[ element.name ] = existingRules;
1959						if ( argument.messages ) {
1960							settings.messages[ element.name ] = $.extend( settings.messages[ element.name ], argument.messages );
1961						}
1962						break;
1963					case 'remove':
1964						if ( ! argument ) {
1965							delete staticRules[ element.name ];
1966							return existingRules;
1967						}
1968						filtered = {};
1969						$.each( argument.split( /\s/ ), function( index, method ) {
1970							filtered[ method ] = existingRules[ method ];
1971							delete existingRules[ method ];
1972						} );
1973						return filtered;
1974				}
1975			}
1976
1977			data = $.tc_validator.normalizeRules( $.extend( {}, $.tc_validator.classRules( element ), $.tc_validator.attributeRules( element ), $.tc_validator.dataRules( element ), $.tc_validator.staticRules( element ) ), element );
1978
1979			// Make sure required is at front
1980			if ( data.required ) {
1981				param = data.required;
1982				delete data.required;
1983				data = $.extend( { required: param }, data );
1984			}
1985
1986			// Make sure remote is at back
1987			if ( data.remote ) {
1988				param = data.remote;
1989				delete data.remote;
1990				data = $.extend( data, { remote: param } );
1991			}
1992
1993			return data;
1994		}
1995	} );
1996
1997	// JQuery trim is deprecated, provide a trim method based on String.prototype.trim
1998	trim = function( str ) {
1999		// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim#Polyfill
2000		return str.replace( /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '' );
2001	};
2002	// Custom selectors
2003	$.extend( $.expr.pseudos || $.expr[ ':' ], {
2004		// https://jqueryvalidation.org/blank-selector/
2005		blank: function( a ) {
2006			return ! trim( '' + $( a ).val() );
2007		},
2008
2009		// https://jqueryvalidation.org/filled-selector/
2010		filled: function( a ) {
2011			var val = $( a ).val();
2012			return val !== null && !! trim( '' + val );
2013		},
2014
2015		// https://jqueryvalidation.org/unchecked-selector/
2016		unchecked: function( a ) {
2017			return ! $( a ).prop( 'checked' );
2018		}
2019	} );
2020
2021	// Constructor for tc_validator
2022	$.tc_validator = function( options, form ) {
2023		this.settings = $.extend( true, {}, $.tc_validator.defaults, options );
2024		this.currentForm = form;
2025		this.init();
2026	};
2027
2028	// https://jqueryvalidation.org/jQuery.tc_validator.format/
2029	$.tc_validator.format = function( source, params ) {
2030		if ( arguments.length === 1 ) {
2031			return function() {
2032				var args = $.makeArray( arguments );
2033				args.unshift( source );
2034				return $.tc_validator.format.apply( this, args );
2035			};
2036		}
2037		if ( params === undefined ) {
2038			return source;
2039		}
2040		if ( arguments.length > 2 && params.constructor !== Array ) {
2041			params = $.makeArray( arguments ).slice( 1 );
2042		}
2043		if ( params.constructor !== Array ) {
2044			params = [ params ];
2045		}
2046		$.each( params, function( i, n ) {
2047			source = source.replace( new RegExp( '\\{' + i + '\\}', 'g' ), function() {
2048				return n;
2049			} );
2050		} );
2051		return source;
2052	};
2053
2054	$.extend( $.tc_validator, {
2055		defaults: {
2056			messages: {},
2057			groups: {},
2058			rules: {},
2059			errorClass: 'error',
2060			pendingClass: 'pending',
2061			validClass: 'valid',
2062			errorElement: 'label',
2063			focusCleanup: false,
2064			focusInvalid: true,
2065			errorContainer: $( [] ),
2066			errorLabelContainer: $( [] ),
2067			onsubmit: true,
2068			ignore: ':hidden',
2069			ignoreTitle: false,
2070			onfocusin: function( element ) {
2071				this.lastActive = element;
2072
2073				// Hide error label and remove error class on focus if enabled
2074				if ( this.settings.focusCleanup ) {
2075					if ( this.settings.unhighlight ) {
2076						this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.val
2076idClass );
2077					}
2078					this.hideThese( this.errorsFor( element ) );
2079				}
2080			},
2081			onfocusout: function( element ) {
2082				if ( ! this.checkable( element ) && ( element.name in this.submitted || ! this.optional( element ) ) ) {
2083					this.element( element );
2084				}
2085			},
2086			onkeyup: function( element, event ) {
2087				// Avoid revalidate the field when pressing one of the following keys
2088				// Shift       => 16
2089				// Ctrl        => 17
2090				// Alt         => 18
2091				// Caps lock   => 20
2092				// End         => 35
2093				// Home        => 36
2094				// Left arrow  => 37
2095				// Up arrow    => 38
2096				// Right arrow => 39
2097				// Down arrow  => 40
2098				// Insert      => 45
2099				// Num lock    => 144
2100				// AltGr key   => 225
2101				var excludedKeys = [ 16, 17, 18, 20, 35, 36, 37, 38, 39, 40, 45, 144, 225 ];
2102
2103				if ( ( event.which === 9 && this.elementValue( element ) === '' ) || $.inArray( event.keyCode, excludedKeys ) !== -1 ) {
2104
2105				} else if ( element.name in this.submitted || element.name in this.invalid ) {
2106					this.element( element );
2107				}
2108			},
2109			onclick: function( element ) {
2110				// Click on selects, radiobuttons and checkboxes
2111				if ( element.name in this.submitted ) {
2112					this.element( element );
2113
2114					// Or option elements, check parent select in that case
2115				} else if ( element.parentNode.name in this.submitted ) {
2116					this.element( element.parentNode );
2117				}
2118			},
2119			highlight: function( element, errorClass, validClass ) {
2120				if ( element.type === 'radio' ) {
2121					this.findByName( element.name ).addClass( errorClass ).removeClass( validClass );
2122				} else {
2123					$( element ).addClass( errorClass ).removeClass( validClass );
2124				}
2125			},
2126			unhighlight: function( element, errorClass, validClass ) {
2127				if ( element.type === 'radio' ) {
2128					this.findByName( element.name ).removeClass( errorClass ).addClass( validClass );
2129				} else {
2130					$( element ).removeClass( errorClass ).addClass( validClass );
2131				}
2132			}
2133		},
2134
2135		// https://jqueryvalidation.org/jQuery.tc_validator.setDefaults/
2136		setDefaults: function( settings ) {
2137			$.extend( $.tc_validator.defaults, settings );
2138		},
2139
2140		messages: {
2141			required: 'This field is required.',
2142			remote: 'Please fix this field.',
2143			email: 'Please enter a valid email address.',
2144			url: 'Please enter a valid URL.',
2145			date: 'Please enter a valid date.',
2146			dateISO: 'Please enter a valid date (ISO).',
2147			number: 'Please enter a valid number.',
2148			digits: 'Please enter only digits.',
2149			equalTo: 'Please enter the same value again.',
2150			maxlength: $.tc_validator.format( 'Please enter no more than {0} characters.' ),
2151			maxlengthsingle: $.tc_validator.format( 'Please enter no more than {0} character.' ),
2152			minlength: $.tc_validator.format( 'Please enter at least {0} characters.' ),
2153			minlengthsingle: $.tc_validator.format( 'Please enter at least {0} character.' ),
2154			rangelength: $.tc_validator.format( 'Please enter a value between {0} and {1} characters long.' ),
2155			range: $.tc_validator.format( 'Please enter a value between {0} and {1}.' ),
2156			max: $.tc_validator.format( 'Please enter a value less than or equal to {0}.' ),
2157			min: $.tc_validator.format( 'Please enter a value greater than or equal to {0}.' ),
2158			step: $.tc_validator.format( 'Please enter a multiple of {0}.' )
2159		},
2160
2161		autoCreateRanges: false,
2162
2163		prototype: {
2164			init: function() {
2165				var currentForm = this.currentForm;
2166				var groups = {};
2167				var rules;
2168
2169				this.labelContainer = $( this.settings.errorLabelContainer );
2170				this.errorContext = ( this.labelContainer.length && this.labelContainer ) || $( this.currentForm );
2171				this.containers = $( this.settings.errorContainer ).add( this.settings.errorLabelContainer );
2172				this.submitted = {};
2173				this.valueCache = {};
2174				this.pendingRequest = 0;
2175				this.pending = {};
2176				this.invalid = {};
2177				this.reset();
2178				this.groups = {};
2179
2180				$.each( this.settings.groups, function( key, value ) {
2181					if ( typeof value === 'string' ) {
2182						value = value.split( /\s/ );
2183					}
2184					$.each( value, function( index, name ) {
2185						groups[ name ] = key;
2186					} );
2187				} );
2188				rules = this.settings.rules;
2189				$.each( rules, function( key, value ) {
2190					rules[ key ] = $.tc_validator.normalizeRule( value );
2191				} );
2192
2193				function delegate( event ) {
2194					var isContentEditable = typeof $( event.data.that ).attr( 'contenteditable' ) !== 'undefined' && $( event.data.that ).attr( 'contenteditable' ) !== 'false';
2195					var tc_validator;
2196					var eventType;
2197					var settings;
2198					// Set form expando on contenteditable
2199					if ( ! event.data.that.form && isContentEditable ) {
2200						event.data.that.form = $( event.data.that ).closest( 'form' )[ 0 ];
2201						event.data.that.name = $( event.data.that ).attr( 'name' );
2202					}
2203
2204					// Ignore the element if it belongs to another form. This will happen mainly
2205					// when setting the `form` attribute of an input to the id of another form.
2206					if ( currentForm !== event.data.that.form ) {
2207						return;
2208					}
2209
2210					tc_validator = $.data( event.data.that.form, 'tc_validator' );
2211					eventType = 'on' + event.type.replace( /^validate/, '' );
2212					settings = tc_validator.settings;
2213					if ( settings[ eventType ] && ! $( event.data.that ).is( settings.ignore ) ) {
2214						settings[ eventType ].call( tc_validator, event.data.that, event );
2215					}
2216				}
2217
2218				$( this.currentForm )
2219					.on(
2220						'focusin.tc_validate focusout.tc_validate keyup.tc_validate',
2221						":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], " +
2222							"[type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], " +
2223							"[type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], " +
2224							"[type='radio'], [type='checkbox'], [contenteditable], [type='button']",
2225						{ that: this },
2226						delegate
2227					)
2228
2229					// Support: Chrome, oldIE
2230					// "select" is provided as event.target when clicking a option
2231					.on( 'click.tc_validate', "select, option, [type='radio'], [type='checkbox']", { that: this }, delegate );
2232
2233				if ( this.settings.invalidHandler ) {
2234					$( this.currentForm ).on( 'invalid-form.tc_validate', this.settings.invalidHandler );
2235				}
2236			},
2237
2238			// https://jqueryvalidation.org/tc_validator.form/
2239			form: function() {
2240				this.checkForm();
2241				$.extend( this.submitted, this.errorMap );
2242				this.invalid = $.extend( {}, this.errorMap );
2243				if ( ! this.tc_valid() ) {
2244					$( this.currentForm ).triggerHandler( 'invalid-form', [ this ] );
2245				}
2246				this.showErrors();
2247				return this.tc_valid();
2248			},
2249
2250			checkForm: function() {
2251				var i;
2252				var elements;
2253				this.prepareForm();
2254				for ( i = 0, elements = this.elements(); elements[ i ]; i += 1 ) {
2255					this.currentElements = elements;
2256					this.check( elements[ i ] );
2257				}
2258				return this.tc_valid();
2259			},
2260
2261			// https://jqueryvalidation.org/tc_validator.element/
2262			element: function( element ) {
2263				var cleanElement = this.clean( element ),
2264					checkElement = this.validationTargetFor( cleanElement ),
2265					v = this,
2266					result = true,
2267					rs,
2268					group;
2269
2270				if ( checkElement === undefined ) {
2271					delete this.invalid[ cleanElement.name ];
2272				} else {
2273					this.prepareElement( checkElement );
2274					this.currentElements = $( checkElement );
2275
2276					// If this element is grouped, then validate all group elements already
2277					// containing a value
2278					group = this.groups[ checkElement.name ];
2279					if ( group ) {
2280						$.each( this.groups, function( name, testgroup ) {
2281							if ( testgroup === group && name !== checkElement.name ) {
2282								cleanElement = v.validationTargetFor( v.clean( v.findByName( name ) ) );
2283								if ( cleanElement && cleanElement.name in v.invalid ) {
2284									v.currentElements.push( cleanElement );
2285									result = v.check( cleanElement ) && result;
2286								}
2287							}
2288						} );
2289					}
2290
2291					rs = this.check( checkElement ) !== false;
2292					result = result && rs;
2293					if ( rs ) {
2294						this.invalid[ checkElement.name ] = false;
2295					} else {
2296						this.invalid[ checkElement.name ] = true;
2297					}
2298
2299					if ( ! this.numberOfInvalids() ) {
2300						// Hide error containers on last error
2301						this.toHide = this.toHide.add( this.containers );
2302					}
2303					this.showErrors();
2304
2305					// Add aria-invalid status for screen readers
2306					$( element ).attr( 'aria-invalid', ! rs );
2307				}
2308
2309				return result;
2310			},
2311
2312			// https://jqueryvalidation.org/tc_validator.showErrors/
2313			showErrors: function( errors ) {
2314				var tc_validator;
2315				if ( errors ) {
2316					tc_validator = this;
2317
2318					// Add items to error list and map
2319					$.extend( this.errorMap, errors );
2320					this.errorList = $.map( this.errorMap, function( message, name ) {
2321						return {
2322							message: message,
2323							element: tc_validator.findByName( name )[ 0 ]
2324						};
2325					} );
2326
2327					// Remove items from success list
2328					this.successList = $.grep( this.successList, function( element ) {
2329						return ! ( element.name in errors );
2330					} );
2331				}
2332				if ( this.settings.showErrors ) {
2333					this.settings.showErrors.call( this, this.errorMap, this.errorList );
2334				} else {
2335					this.defaultShowErrors();
2336				}
2337			},
2338
2339			// https://jqueryvalidation.org/tc_validator.resetForm/
2340			resetForm: function() {
2341				var elements;
2342				if ( $.fn.resetForm ) {
2343					$( this.currentForm ).resetForm();
2344				}
2345				this.invalid = {};
2346				this.submitted = {};
2347				this.prepareForm();
2348				this.hideErrors();
2349				elements = this.elements().removeData( 'previousValue' ).removeAttr( 'aria-invalid' );
2350
2351				this.resetElements( elements );
2352			},
2353
2354			resetElements: function( elements ) {
2355				var i;
2356
2357				if ( this.settings.unhighlight ) {
2358					for ( i = 0; elements[ i ]; i += 1 ) {
2359						this.settings.unhighlight.call( this, elements[ i ], this.settings.errorClass, '' );
2360						this.findByName( elements[ i ].name ).removeClass( this.settings.val
2360idClass );
2361					}
2362				} else {
2363					elements.removeClass( this.settings.errorClass ).removeClass( this.settings.validClass );
2364				}
2365			},
2366
2367			numberOfInvalids: function() {
2368				return this.objectLength( this.invalid );
2369			},
2370
2371			objectLength: function( obj ) {
2372				/* jshint unused: false */
2373				var count = 0,
2374					i;
2375				for ( i in obj ) {
2376					// This check allows counting elements with empty error
2377					// message as invalid elements
2378					if ( obj[ i ] !== undefined && obj[ i ] !== null && obj[ i ] !== false ) {
2379						count += 1;
2380					}
2381				}
2382				return count;
2383			},
2384
2385			hideErrors: function() {
2386				this.hideThese( this.toHide );
2387			},
2388
2389			hideThese: function( errors ) {
2390				errors.not( this.containers ).text( '' );
2391				this.addWrapper( errors ).hide();
2392			},
2393
2394			tc_valid: function() {
2395				return this.size() === 0;
2396			},
2397
2398			size: function() {
2399				return this.errorList.length;
2400			},
2401
2402			focusInvalid: function() {
2403				if ( this.settings.focusInvalid ) {
2404					try {
2405						$( this.findLastActive() || ( this.errorList.length && this.errorList[ 0 ].element ) || [] )
2406							.filter( ':visible' )
2407							.trigger( 'focus' )
2408
2409							// Manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find
2410							.trigger( 'focusin' );
2411					} catch ( e ) {
2412						// Ignore IE throwing errors when focusing hidden elements
2413					}
2414				}
2415			},
2416
2417			findLastActive: function() {
2418				var lastActive = this.lastActive;
2419				return (
2420					lastActive &&
2421					$.grep( this.errorList, function( n ) {
2422						return n.element.name === lastActive.name;
2423					} ).length === 1 &&
2424					lastActive
2425				);
2426			},
2427
2428			elements: function() {
2429				var tc_validator = this,
2430					rulesCache = {};
2431
2432				// Select all valid inputs inside the form (no submit or reset buttons)
2433				return $( this.currentForm )
2434					.find( 'input, select, textarea, [contenteditable]' )
2435					.not( ':submit, :reset, :image, :disabled' )
2436					.not( this.settings.ignore )
2437					.filter( function() {
2438						var name = this.name || $( this ).attr( 'name' ); // For contenteditable
2439						var isContentEditable = typeof $( this ).attr( 'contenteditable' ) !== 'undefined' && $( this ).attr( 'contenteditable' ) !== 'false';
2440
2441						// Set form expando on contenteditable
2442						if ( isContentEditable ) {
2443							this.form = $( this ).closest( 'form' )[ 0 ];
2444							this.name = name;
2445						}
2446
2447						// Ignore elements that belong to other/nested forms
2448						if ( this.form !== tc_validator.currentForm ) {
2449							return false;
2450						}
2451
2452						// Select only the first element for each name, and only those with rules specified
2453						if ( name in rulesCache || ! tc_validator.objectLength( $( this ).tc_rules() ) ) {
2454							return false;
2455						}
2456
2457						rulesCache[ name ] = true;
2458						return true;
2459					} );
2460			},
2461
2462			clean: function( selector ) {
2463				return $( selector )[ 0 ];
2464			},
2465
2466			errors: function() {
2467				var errorClass = this.settings.errorClass.split( ' ' ).join( '.' );
2468				return $( this.settings.errorElement + '.' + errorClass, this.errorContext );
2469			},
2470
2471			resetInternals: function() {
2472				this.successList = [];
2473				this.errorList = [];
2474				this.errorMap = {};
2475				this.toShow = $( [] );
2476				this.toHide = $( [] );
2477			},
2478
2479			reset: function() {
2480				this.resetInternals();
2481				this.currentElements = $( [] );
2482			},
2483
2484			prepareForm: function() {
2485				this.reset();
2486				this.toHide = this.errors().add( this.containers );
2487			},
2488
2489			prepareElement: function( element ) {
2490				this.reset();
2491				this.toHide = this.errorsFor( element );
2492			},
2493
2494			elementValue: function( element ) {
2495				var $element = $( element ),
2496					type = element.type,
2497					isContentEditable = typeof $element.attr( 'contenteditable' ) !== 'undefined' && $element.attr( 'contenteditable' ) !== 'false',
2498					val,
2499					idx;
2500
2501				if ( type === 'radio' || type === 'checkbox' ) {
2502					return this.findByName( element.name ).filter( ':checked' ).val();
2503				} else if ( type === 'number' && typeof element.validity !== 'undefined' ) {
2504					return element.validity.badInput ? 'NaN' : $element.val();
2505				}
2506
2507				if ( isContentEditable ) {
2508					val = $element.text();
2509				} else {
2510					val = $element.val();
2511				}
2512
2513				if ( type === 'file' ) {
2514					// Modern browser (chrome & safari)
2515					if ( val.substring( 0, 12 ) === 'C:\\fakepath\\' ) {
2516						return val.substring( 12 );
2517					}
2518
2519					// Legacy browsers
2520					// Unix-based path
2521					idx = val.lastIndexOf( '/' );
2522					if ( idx >= 0 ) {
2523						return val.substring( idx + 1 );
2524					}
2525
2526					// Windows-based path
2527					idx = val.lastIndexOf( '\\' );
2528					if ( idx >= 0 ) {
2529						return val.substring( idx + 1 );
2530					}
2531
2532					// Just the file name
2533					return val;
2534				}
2535
2536				if ( typeof val === 'string' ) {
2537					return val.replace( /\r/g, '' );
2538				}
2539				return val;
2540			},
2541
2542			check: function( element ) {
2543				var rules;
2544				var rulesCount;
2545				var dependencyMismatch;
2546				var val;
2547				var result;
2548				var method;
2549				var rule;
2550				var normalizer;
2551
2552				element = this.validationTargetFor( this.clean( element ) );
2553
2554				rules = $( element ).tc_rules();
2555				rulesCount = $.map( rules, function( n, i ) {
2556					return i;
2557				} ).length;
2558				dependencyMismatch = false;
2559				val = this.elementValue( element );
2560
2561				// Prioritize the local normalizer defined for this element over the global one
2562				// if the former exists, otherwise user the global one in case it exists.
2563				if ( typeof rules.normalizer === 'function' ) {
2564					normalizer = rules.normalizer;
2565				} else if ( typeof this.settings.normalizer === 'function' ) {
2566					normalizer = this.settings.normalizer;
2567				}
2568
2569				// If normalizer is defined, then call it to retreive the changed value instead
2570				// of using the real one.
2571				// Note that `this` in the normalizer is `element`.
2572				if ( normalizer ) {
2573					val = normalizer.call( element, val );
2574
2575					// Delete the normalizer from rules to avoid treating it as a pre-defined method.
2576					delete rules.normalizer;
2577				}
2578
2579				for ( method in rules ) {
2580					if ( method ) {
2581						rule = { method: method, parameters: rules[ method ] };
2582						try {
2583							result = $.tc_validator.methods[ method ].call( this, val, element, rule.parameters );
2584
2585							// If a method indicates that the field is optional and therefore valid,
2586							// don't mark it as valid when there are no other rules
2587							if ( result === 'dependency-mismatch' && rulesCount === 1 ) {
2588								dependencyMismatch = true;
2589								continue;
2590							}
2591							dependencyMismatch = false;
2592
2593							if ( result === 'pending' ) {
2594								this.toHide = this.toHide.not( this.errorsFor( element ) );
2595								return;
2596							}
2597
2598							if ( ! result ) {
2599								this.formatAndAdd( element, rule );
2600								return false;
2601							}
2602						} catch ( e ) {
2603							if ( e instanceof TypeError ) {
2604								e.message += '.  Exception occurred when checking element ' + element.id + ", check the '" + rule.method + "' method.";
2605							}
2606
2607							throw e;
2608						}
2609					}
2610				}
2611				if ( dependencyMismatch ) {
2612					return;
2613				}
2614				if ( this.objectLength( rules ) ) {
2615					this.successList.push( element );
2616				}
2617				return true;
2618			},
2619
2620			// Return the custom message for the given element and validation method
2621			// specified in the element's HTML5 data attribute
2622			// return the generic message if present and no method specific message is present
2623			customDataMessage: function( element, method ) {
2624				return $( element ).data( 'msg' + method.charAt( 0 ).toUpperCase() + method.substring( 1 ).toLowerCase() ) || $( element ).data( 'msg' );
2625			},
2626
2627			// Return the custom message for the given element name and validation method
2628			customMessage: function( name, method ) {
2629				var m = this.settings.messages[ name ];
2630				return m && ( m.constructor === String ? m : m[ method ] );
2631			},
2632
2633			// Return the first defined argument, allowing empty strings
2634			findDefined: function() {
2635				var i;
2636				for ( i = 0; i < arguments.length; i += 1 ) {
2637					if ( arguments[ i ] !== undefined ) {
2638						return arguments[ i ];
2639					}
2640				}
2641				return undefined;
2642			},
2643
2644			// The second parameter 'rule' used to be a string, and extended to an object literal
2645			// of the following form:
2646			// rule = {
2647			//     method: "method name",
2648			//     parameters: "the given method parameters"
2649			// }
2650			//
2651			// The old behavior still supported, kept to maintain backward compatibility with
2652			// old code, and will be removed in the next major release.
2653			defaultMessage: function( element, rule ) {
2654				var message;
2655				var theregex;
2656				var singleCheck = '';
2657
2658				if ( typeof rule === 'string' ) {
2659					rule = { method: rule };
2660				}
2661
2662				if ( rule.method === 'minlength' || rule.method === 'maxlength' ) {
2663					if ( rule.parameters === 1 || rule.parameters === '1' ) {
2664						singleCheck = 'single';
2665					}
2666				}
2667
2668				message = this.findDefined(
2669					this.customMessage( element.name, rule.method + singleCheck ),
2670					this.customDataMessage( element, rule.method + singleCheck ),
2671
2672					// 'title' is never undefined, so handle empty string as undefined
2673					( ! this.settings.ignoreTitle && element.title ) || undefined,
2674					$.tc_validator.messages[ rule.method + singleCheck ],
2675					'<strong>Warning: No message defined for ' + element.name + '</strong>'
2676				);
2677				theregex = /\$?\{(\d+)\}/g;
2678				if ( typeof message === 'function' ) {
2679					message = message.call( this, rule.parameters, element );
2680				} else if ( theregex.test( message ) ) {
2681					message = $.tc_validator.format( message.replace( theregex, '{$1}' ), rule.parameters );
2682				}
2683
2684				return message;
2685			},
2686
2687			formatAndAdd: function( element, rule ) {
2688				var message = this.defaultMessage( element, rule );
2689
2690				this.errorList.push( {
2691					message: message,
2692					element: element,
2693					method: rule.method
2694				} );
2695
2696				this.errorMap[ element.name ] = message;
2697				this.submitted[ element.name ] = message;
2698			},
2699
2700			addWrapper: function( toToggle ) {
2701				if ( this.settings.wrapper ) {
2702					toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );
2703				}
2704				return toToggle;
2705			},
2706
2707			defaultShowErrors: function() {
2708				var i, elements, error;
2709				for ( i = 0; this.errorList[ i ]; i += 1 ) {
2710					error = this.errorList[ i ];
2711					if ( this.settings.highlight ) {
2712						this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.val
2712idClass );
2713					}
2714					this.showLabel( error.element, error.message );
2715				}
2716				if ( this.errorList.length ) {
2717					this.toShow = this.toShow.add( this.containers );
2718				}
2719				if ( this.settings.success ) {
2720					for ( i = 0; this.successList[ i ]; i += 1 ) {
2721						this.showLabel( this.successList[ i ] );
2722					}
2723				}
2724				if ( this.settings.unhighlight ) {
2725					for ( i = 0, elements = this.validElements(); elements[ i ]; i += 1 ) {
2726						this.settings.unhighlight.call( this, elements[ i ], this.settings.errorClass, this.settings.validClass );
2727					}
2728				}
2729				this.toHide = this.toHide.not( this.toShow );
2730				this.hideErrors();
2731				this.addWrapper( this.toShow ).show();
2732			},
2733
2734			validElements: function() {
2735				return this.currentElements.not( this.invalidElements() );
2736			},
2737
2738			invalidElements: function() {
2739				return $( this.errorList ).map( function() {
2740					return this.element;
2741				} );
2742			},
2743
2744			showLabel: function( element, message ) {
2745				var place,
2746					group,
2747					errorID,
2748					v,
2749					error = this.errorsFor( element ),
2750					elementID = this.idOrName( element ),
2751					describedBy = $( element ).attr( 'aria-describedby' );
2752
2753				if ( error.length ) {
2754					// Refresh error/success class
2755					error.removeClass( this.settings.validClass ).addClass( this.settings.errorClass );
2756
2757					// Replace message on existing label
2758					error.html( message );
2759				} else {
2760					// Create error element
2761					error = $( '<' + this.settings.errorElement + '>' )
2762						.attr( 'id', elementID + '-error' )
2763						.addClass( this.settings.errorClass )
2764						.html( message || '' );
2765
2766					// Maintain reference to the element to be placed into the DOM
2767					place = error;
2768					if ( this.settings.wrapper ) {
2769						// Make sure the element is visible, even in IE
2770						// actually showing the wrapped element is handled elsewhere
2771						place = error
2772							.hide()
2773							.show()
2774							.wrap( '<' + this.settings.wrapper + '/>' )
2775							.parent();
2776					}
2777					if ( this.labelContainer.length ) {
2778						this.labelContainer.append( place );
2779					} else if ( this.settings.errorPlacement ) {
2780						this.settings.errorPlacement.call( this, place, $( element ) );
2781					} else {
2782						place.insertAfter( element );
2783					}
2784
2785					// Link error back to the element
2786					if ( error.is( 'label' ) ) {
2787						// If the error is a label, then associate using 'for'
2788						error.attr( 'for', elementID );
2789
2790						// If the element is not a child of an associated label, then it's necessary
2791						// to explicitly apply aria-describedby
2792					} else if ( error.parents( "label[for='" + this.escapeCssMeta( elementID ) + "']" ).length === 0 ) {
2793						errorID = error.attr( 'id' );
2794
2795						// Respect existing non-error aria-describedby
2796						if ( ! describedBy ) {
2797							describedBy = errorID;
2798						} else if ( ! describedBy.match( new RegExp( '\\b' + this.escapeCssMeta( errorID ) + '\\b' ) ) ) {
2799							// Add to end of list if not already present
2800							describedBy += ' ' + errorID;
2801						}
2802						$( element ).attr( 'aria-describedby', describedBy );
2803
2804						// If this element is grouped, then assign to all elements in the same group
2805						group = this.groups[ element.name ];
2806						if ( group ) {
2807							v = this;
2808							$.each( v.groups, function( name, testgroup ) {
2809								if ( testgroup === group ) {
2810									$( "[name='" + v.escapeCssMeta( name ) + "']", v.currentForm ).attr( 'aria-describedby', error.attr( 'id' ) );
2811								}
2812							} );
2813						}
2814					}
2815				}
2816				if ( ! message && this.settings.success ) {
2817					error.text( '' );
2818					if ( typeof this.settings.success === 'string' ) {
2819						error.addClass( this.settings.success );
2820					} else {
2821						this.settings.success( error, element );
2822					}
2823				}
2824				this.toShow = this.toShow.add( error );
2825			},
2826
2827			errorsFor: function( element ) {
2828				var name = this.escapeCssMeta( this.idOrName( element ) ),
2829					describer = $( element ).attr( 'aria-describedby' ),
2830					selector = "label[for='" + name + "'], label[for='" + name + "'] *";
2831
2832				// 'aria-describedby' should directly reference the error element
2833				if ( describer ) {
2834					selector = selector + ', #' + this.escapeCssMeta( describer ).replace( /\s+/g, ', #' );
2835				}
2836
2837				return this.errors().filter( selector );
2838			},
2839
2840			// See https://api.jquery.com/category/selectors/, for CSS
2841			// meta-characters that should be escaped in order to be used with JQuery
2842			// as a literal part of a name/id or any selector.
2843			escapeCssMeta: function( string ) {
2844				// eslint-disable-next-line no-useless-escape
2845				return string.replace( /([\\!"#$%&'()*+,.\/:;<=>?@\[\]\^`{|}~])/g, '\\$1' );
2846			},
2847
2848			idOrName: function( element ) {
2849				return this.groups[ element.name ] || ( this.checkable( element ) ? element.name : element.i
2849d || element.name );
2850			},
2851
2852			validationTargetFor: function( element ) {
2853				// If radio/checkbox, validate first element in group instead
2854				if ( this.checkable( element ) ) {
2855					element = this.findByName( element.name );
2856				}
2857
2858				// Always apply ignore filter
2859				return $( element ).not( this.settings.ignore )[ 0 ];
2860			},
2861
2862			checkable: function( element ) {
2863				return /radio|checkbox/i.test( element.type );
2864			},
2865
2866			findByName: function( name ) {
2867				return $( this.currentForm ).find( "[name='" + this.escapeCssMeta( name ) + "']" );
2868			},
2869
2870			getLength: function( value, element ) {
2871				switch ( element.nodeName.toLowerCase() ) {
2872					case 'select':
2873						return $( 'option:selected', element ).length;
2874					case 'input':
2875						if ( this.checkable( element ) ) {
2876							return this.findByName( element.name ).filter( ':checked' ).length;
2877						}
2878				}
2879				return value.length;
2880			},
2881
2882			depend: function( param, element ) {
2883				return this.dependTypes[ typeof param ] ? this.dependTypes[ typeof param ]( param, element ) : true;
2884			},
2885
2886			dependTypes: {
2887				boolean: function( param ) {
2888					return param;
2889				},
2890				string: function( param, element ) {
2891					return !! $( param, element.form ).length;
2892				},
2893				function: function( param, element ) {
2894					return param( element );
2895				}
2896			},
2897
2898			optional: function( element ) {
2899				var val = this.elementValue( element );
2900				return ! $.tc_validator.methods.required.call( this, val, element ) && 'dependency-mismatch';
2901			},
2902
2903			startRequest: function( element ) {
2904				if ( ! this.pending[ element.name ] ) {
2905					this.pendingRequest += 1;
2906					$( element ).addClass( this.settings.pendingClass );
2907					this.pending[ element.name ] = true;
2908				}
2909			},
2910
2911			stopRequest: function( element, valid ) {
2912				this.pendingRequest -= 1;
2913
2914				// Sometimes synchronization fails, make sure pendingRequest is never < 0
2915				if ( this.pendingRequest < 0 ) {
2916					this.pendingRequest = 0;
2917				}
2918				delete this.pending[ element.name ];
2919				$( element ).removeClass( this.settings.pendingClass );
2920				if ( valid && this.pendingRequest === 0 && this.formSubmitted && this.form() ) {
2921					$( this.currentForm ).trigger( 'submit' );
2922
2923					// Remove the hidden input that was used as a replacement for the
2924					// missing submit button. The hidden input is added by `handle()`
2925					// to ensure that the value of the used submit button is passed on
2926					// for scripted submits triggered by this method
2927					if ( this.submitButton ) {
2928						$( "input:hidden[name='" + this.submitButton.name + "']", this.currentForm ).remove();
2929					}
2930
2931					this.formSubmitted = false;
2932				} else if ( ! valid && this.pendingRequest === 0 && this.formSubmitted ) {
2933					$( this.currentForm ).triggerHandler( 'invalid-form', [ this ] );
2934					this.formSubmitted = false;
2935				}
2936			},
2937
2938			previousValue: function( element, method ) {
2939				method = ( typeof method === 'string' && method ) || 'remote';
2940
2941				return (
2942					$.data( element, 'previousValue' ) ||
2943					$.data( element, 'previousValue', {
2944						old: null,
2945						valid: true,
2946						message: this.defaultMessage( element, { method: method } )
2947					} )
2948				);
2949			},
2950
2951			// Cleans up all forms and elements, removes tc_validator-specific events
2952			destroy: function() {
2953				this.resetForm();
2954
2955				$( this.currentForm )
2956					.off( '.tc_validate' )
2957					.removeData( 'tc_validator' )
2958					.find( '.tc_validate-equalTo-blur' )
2959					.off( '.tc_validate-equalTo' )
2960					.removeClass( 'validate-equalTo-blur' )
2961					.find( '.tc_validate-lessThan-blur' )
2962					.off( '.tc_validate-lessThan' )
2963					.removeClass( 'validate-lessThan-blur' )
2964					.find( '.tc_validate-lessThanEqual-blur' )
2965					.off( '.tc_validate-lessThanEqual' )
2966					.removeClass( 'validate-lessThanEqual-blur' )
2967					.find( '.tc_validate-greaterThanEqual-blur' )
2968					.off( '.tc_validate-greaterThanEqual' )
2969					.removeClass( 'validate-greaterThanEqual-blur' )
2970					.find( '.tc_validate-greaterThan-blur' )
2971					.off( '.tc_validate-greaterThan' )
2972					.removeClass( 'validate-greaterThan-blur' );
2973			}
2974		},
2975
2976		classRuleSettings: {
2977			required: { required: true },
2978			email: { email: true },
2979			url: { url: true },
2980			date: { date: true },
2981			dateISO: { dateISO: true },
2982			number: { number: true },
2983			digits: { digits: true },
2984			creditcard: { creditcard: true }
2985		},
2986
2987		addClassRules: function( className, rules ) {
2988			if ( className.constructor === String ) {
2989				this.classRuleSettings[ className ] = rules;
2990			} else {
2991				$.extend( this.classRuleSettings, className );
2992			}
2993		},
2994
2995		classRules: function( element ) {
2996			var rules = {},
2997				classes = $( element ).attr( 'class' );
2998
2999			if ( classes ) {
3000				$.each( classes.split( ' ' ), function() {
3001					if ( this in $.tc_validator.classRuleSettings ) {
3002						$.extend( rules, $.tc_validator.classRuleSettings[ this ] );
3003					}
3004				} );
3005			}
3006			return rules;
3007		},
3008
3009		normalizeAttributeRule: function( rules, type, method, value ) {
3010			// Convert the value to a number for number inputs, and for text for backwards compability
3011			// allows type="date" and others to be compared as strings
3012			if ( /min|max|step/.test( method ) && ( type === null || /number|range|text/.test( type ) ) ) {
3013				value = Number( value );
3014
vendor: 4,278 bytes, lines 3015-3166
3015				// Support Opera Mini, which returns NaN for undefined minlength
3016				if ( isNaN( value ) ) {
3017					value = undefined;
3018				}
3019			}
3020
3021			if ( value || value === 0 ) {
3022				rules[ method ] = value;
3023			} else if ( type === method && type !== 'range' ) {
3024				// Exception: the jquery validate 'range' method
3025				// does not test for the html5 'range' type
3026				rules[ method ] = true;
3027			}
3028		},
3029
3030		attributeRules: function( element ) {
3031			var rules = {},
3032				$element = $( element ),
3033				type = element.getAttribute( 'type' ),
3034				method,
3035				value;
3036
3037			for ( method in $.tc_validator.methods ) {
3038				if ( method ) {
3039					// Support for <input required> in both html5 and older browsers
3040					if ( method === 'required' ) {
3041						value = element.getAttribute( method );
3042
3043						// Some browsers return an empty string for the required attribute
3044						// and non-HTML5 browsers might have required="" markup
3045						if ( value === '' ) {
3046							value = true;
3047						}
3048
3049						// Force non-HTML5 browsers to return bool
3050						value = !! value;
3051					} else {
3052						value = $element.attr( method );
3053					}
3054
3055					this.normalizeAttributeRule( rules, type, method, value );
3056				}
3057			}
3058
3059			// 'maxlength' may be returned as -1, 2147483647 ( IE ) and 524288 ( safari ) for text inputs
3060			if ( rules.maxlength && /-1|2147483647|524288/.test( rules.maxlength ) ) {
3061				delete rules.maxlength;
3062			}
3063
3064			return rules;
3065		},
3066
3067		dataRules: function( element ) {
3068			var rules = {},
3069				$element = $( element ),
3070				type = element.getAttribute( 'type' ),
3071				method,
3072				value;
3073
3074			for ( method in $.tc_validator.methods ) {
3075				if ( method ) {
3076					value = $element.data( 'rule' + method.charAt( 0 ).toUpperCase() + method.substring( 1 ).toLowerCase() );
3077
3078					// Cast empty attributes like `data-rule-required` to `true`
3079					if ( value === '' ) {
3080						value = true;
3081					}
3082
3083					this.normalizeAttributeRule( rules, type, method, value );
3084				}
3085			}
3086			return rules;
3087		},
3088
3089		staticRules: function( element ) {
3090			var rules = {},
3091				tc_validator = $.data( element.form, 'tc_validator' );
3092
3093			if ( tc_validator.settings.rules ) {
3094				rules = $.tc_validator.normalizeRule( tc_validator.settings.rules[ element.name ] ) || {};
3095			}
3096			return rules;
3097		},
3098
3099		normalizeRules: function( rules, element ) {
3100			var keepRule;
3101			// Handle dependency check
3102			$.each( rules, function( prop, val ) {
3103				// Ignore rule when param is explicitly false, eg. required:false
3104				if ( val === false ) {
3105					delete rules[ prop ];
3106					return;
3107				}
3108				if ( val.param || val.depends ) {
3109					keepRule = true;
3110					switch ( typeof val.depends ) {
3111						case 'string':
3112							keepRule = !! $( val.depends, element.form ).length;
3113							break;
3114						case 'function':
3115							keepRule = val.depends.call( element, element );
3116							break;
3117					}
3118					if ( keepRule ) {
3119						rules[ prop ] = val.param !== undefined ? val.param : true;
3120					} else {
3121						$.data( element.form, 'tc_validator' ).resetElements( $( element ) );
3122						delete rules[ prop ];
3123					}
3124				}
3125			} );
3126
3127			// Evaluate parameters
3128			$.each( rules, function( rule, parameter ) {
3129				rules[ rule ] = typeof parameter === 'function' && rule !== 'normalizer' ? parameter( element ) : parameter;
3130			} );
3131
3132			// Clean number parameters
3133			$.each( [ 'minlength', 'maxlength' ], function() {
3134				if ( rules[ this ] ) {
3135					rules[ this ] = Number( rules[ this ] );
3136				}
3137			} );
3138			$.each( [ 'rangelength', 'range' ], function() {
3139				var parts;
3140				if ( rules[ this ] ) {
3141					if ( Array.isArray( rules[ this ] ) ) {
3142						rules[ this ] = [ Number( rules[ this ][ 0 ] ), Number( rules[ this ][ 1 ] ) ];
3143					} else if ( typeof rules[ this ] === 'string' ) {
3144						parts = rules[ this ].replace( /[[\]]/g, '' ).split( /[\s,]+/ );
3145						rules[ this ] = [ Number( parts[ 0 ] ), Number( parts[ 1 ] ) ];
3146					}
3147				}
3148			} );
3149
3150			if ( $.tc_validator.autoCreateRanges ) {
3151				// Auto-create ranges
3152				if ( rules.min !== null && rules.max !== null ) {
3153					rules.range = [ rules.min, rules.max ];
3154					delete rules.min;
3155					delete rules.max;
3156				}
3157				if ( rules.minlength !== null && rules.maxlength !== null ) {
3158					rules.rangelength = [ rules.minlength, rules.maxlength ];
3159					delete rules.minlength;
3160					delete rules.maxlength;
3161				}
3162			}
3163
3164			return rules;
3165		},
3166
3167		// Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}
3168		normalizeRule: function( data ) {
3169			var transformed;
3170			if ( typeof data === 'string' ) {
3171				transformed = {};
3172				$.each( data.split( /\s/ ), function() {
3173					transformed[ this ] = true;
3174				} );
3175				data = transformed;
3176			}
3177			return data;
3178		},
3179
3180		// https://jqueryvalidation.org/jQuery.tc_validator.addMethod/
3181		addMethod: function( name, method, message ) {
3182			$.tc_validator.methods[ name ] = method;
3183			$.tc_validator.messages[ name ] = message !== undefined ? message : $.tc_validator.messages[ name ];
3184			if ( method.length < 3 ) {
3185				$.tc_validator.addClassRules( name, $.tc_validator.normalizeRule( name ) );
3186			}
3187		},
3188
3189		// https://jqueryvalidation.org/jQuery.tc_validator.methods/
3190		methods: {
3191			// https://jqueryvalidation.org/required-method/
3192			required: function( value, element, param ) {
3193				var val;
3194				// Check if dependency is met
3195				if ( ! this.depend( param, element ) ) {
3196					return 'dependency-mismatch';
3197				}
3198				if ( element.nodeName.toLowerCase() === 'select' ) {
3199					// Could be an array for select-multiple or a string, both are fine this way
3200					val = $( element ).val();
3201					return val && val.length > 0;
3202				}
3203				if ( this.checkable( element ) ) {
3204					return this.getLength( value, element ) > 0;
3205				}
3206				return value !== undefined && value !== null && value.length > 0;
3207			},
3208
3209			// https://jqueryvalidation.org/email-method/
3210			email: function( value, element ) {
3211				// From https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address
3212				// Retrieved 2014-01-14
3213				// If you have a problem with this implementation, report a bug against the above spec
3214				// Or use custom methods to implement your own email validation
3215				// eslint-disable-next-line no-useless-escape
3216				return this.optional( element ) || /^[a-zA-Z0-9.!#$%&'*+\/=?\^_`{|}~\-]+@[a-zA-Z0-9](?:[a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$/.test( value );
3217			},
3218
3219			// https://jqueryvalidation.org/url-method/
3220			url: function( value, element ) {
3221				// Copyright (c) 2010-2013 Diego Perini, MIT licensed
3222				// https://gist.github.com/dperini/729294
3223				// see also https://mathiasbynens.be/demo/url-regex
3224				// modified to allow protocol-relative URLs
3225				return (
3226					this.optional( element ) ||
3227					// eslint-disable-next-line no-useless-escape
3228					/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[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]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(
3229						value
3230					)
3231				);
3232			},
3233			// https://jqueryvalidation.org/date-method/
3234			date: function( value, element ) {
3235				/* This method is deprecated and will be removed in version 2.0.0. Please don't use it, since it relies on the Date constructor, which behaves very differently across browsers and locales. Use dateISO instead or one of the locale specific methods (in localizations/ and additional-methods.js). */
3236				return this.optional( element ) || ! /Invalid|NaN/.test( new Date( value ).toString() );
3237			},
3238
3239			// https://jqueryvalidation.org/dateISO-method/
3240			dateISO: function( value, element ) {
3241				// eslint-disable-next-line no-useless-escape
3242				return this.optional( element ) || /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test( value );
3243			},
3244
3245			// https://jqueryvalidation.org/number-method/
3246			number: function( value, element ) {
3247				return this.optional( element ) || /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test( value );
3248			},
3249
3250			// https://jqueryvalidation.org/digits-method/
3251			digits: function( value, element ) {
3252				return this.optional( element ) || /^\d+$/.test( value );
3253			},
3254
3255			// https://jqueryvalidation.org/minlength-method/
3256			minlength: function( value, element, param ) {
3257				var length = Array.isArray( value ) ? value.length : this.getLength( value, element );
3258				return this.optional( element ) || length >= param;
3259			},
3260
3261			// https://jqueryvalidation.org/maxlength-method/
3262			maxlength: function( value, element, param ) {
3263				var length = Array.isArray( value ) ? value.length : this.getLength( value, element );
3264				return this.optional( element ) || length <= param;
3265			},
3266
3267			// https://jqueryvalidation.org/rangelength-method/
3268			rangelength: function( value, element, param ) {
3269				var length = Array.isArray( value ) ? value.length : this.getLength( value, element );
3270				return this.optional( element ) || ( length >= param[ 0 ] && length <= param[ 1 ] );
3271			},
3272
3273			// https://jqueryvalidation.org/min-method/
3274			min: function( value, element, param ) {
3275				return this.optional( element ) || value >= param;
3276			},
3277
3278			// https://jqueryvalidation.org/max-method/
3279			max: function( value, element, param ) {
3280				return this.optional( element ) || value <= param;
3281			},
3282
3283			// https://jqueryvalidation.org/range-method/
3284			range: function( value, element, param ) {
3285				return this.optional( element ) || ( value >= param[ 0 ] && value <= param[ 1 ] );
3286			},
3287
3288			// https://jqueryvalidation.org/step-method/
3289			step: function( value, element, param ) {
3290				var type = $( element ).attr( 'type' ),
3291					errorMessage = 'Step attribute on input type ' + type + ' is not supported.',
3292					supportedTypes = [ 'text', 'number', 'range' ],
3293					re = new RegExp( '\\b' + type + '\\b' ),
3294					notSupported = type && ! re.test( supportedTypes.join() ),
3295					decimalPlaces = function( num ) {
3296						var match = ( '' + num ).match( /(?:\.(\d+))?$/ );
3297						if ( ! match ) {
3298							return 0;
3299						}
3300
3301						// Number of digits right of decimal point.
3302						return match[ 1 ] ? match[ 1 ].length : 0;
3303					},
3304					decimals,
3305					toInt = function( num ) {
3306						return Math.round( num * Math.pow( 10, decimals ) );
3307					},
3308					valid = true;
3309
3310				// Works only for text, number and range input types
3311				// TO-DO find a way to support input types date, datetime, datetime-local, month, time and week
3312				if ( notSupported ) {
3313					throw new Error( errorMessage );
3314				}
3315
3316				decimals = decimalPlaces( param );
3317
3318				// Value can't have too many decimals
3319				if ( decimalPlaces( value ) > decimals || toInt( value ) % toInt( param ) !== 0 ) {
3320					valid = false;
3321				}
3322
3323				return this.optional( element ) || valid;
3324			},
3325
3326			// https://jqueryvalidation.org/equalTo-method/
3327			equalTo: function( value, element, param ) {
3328				// Bind to the blur event of the target in order to revalidate whenever the target field is updated
3329				var target = $( param );
3330				if ( this.settings.onfocusout && target.not( '.tc_validate-equalTo-blur' ).length ) {
3331					target.addClass( 'validate-equalTo-blur' ).on( 'blur.tc_validate-equalTo', function() {
3332						$( element ).tc_valid();
3333					} );
3334				}
3335				return value === target.val();
3336			},
3337
3338			// https://jqueryvalidation.org/remote-method/
3339			remote: function( value, element, param, method ) {
3340				var previous;
3341				var tc_validator;
3342				var data;
3343				var optionDataString;
3344
3345				if ( this.optional( element ) ) {
3346					return 'dependency-mismatch';
3347				}
3348
3349				method = ( typeof method === 'string' && method ) || 'remote';
3350
3351				previous = this.previousValue( element, method );
3352
3353				if ( ! this.settings.messages[ element.name ] ) {
3354					this.settings.messages[ element.name ] = {};
3355				}
3356				previous.originalMessage = previous.originalMessage || this.settings.messages[ element.name ][ method ];
3357				this.settings.messages[ element.name ][ method ] = previous.message;
3358
3359				param = ( typeof param === 'string' && { url: param } ) || param;
3360				optionDataString = $.param( $.extend( { data: value }, param.data ) );
3361				if ( previous.old === optionDataString ) {
3362					return previous.valid;
3363				}
3364
3365				previous.old = optionDataString;
3366				tc_validator = this;
3367				this.startRequest( element );
3368				data = {};
3369				data[ element.name ] = value;
3370				$.ajax(
3371					$.extend(
3372						true,
3373						{
3374							mode: 'abort',
3375							port: 'validate' + element.name,
3376							dataType: 'json',
3377							data: data,
3378							context: tc_validator.currentForm,
3379							success: function( response ) {
3380								var valid = response === true || response === 'true',
3381									errors,
3382									message,
3383									submitted;
3384
3385								tc_validator.settings.messages[ element.name ][ method ] = previous.originalMessage;
3386								if ( valid ) {
3387									submitted = tc_validator.formSubmitted;
3388									tc_validator.resetInternals();
3389									tc_validator.toHide = tc_validator.errorsFor( element );
3390									tc_validator.formSubmitted = submitted;
3391									tc_validator.successList.push( element );
3392									tc_validator.invalid[ element.name ] = false;
3393									tc_validator.showErrors();
3394								} else {
3395									errors = {};
3396									message = response || tc_validator.defaultMessage( element, { method: method, parameters: value } );
3397									previous.message = message;
3398									errors[ element.name ] = message;
3399									tc_validator.invalid[ element.name ] = true;
3400									tc_validator.showErrors( errors );
3401								}
3402								previous.valid = valid;
3403								tc_validator.stopRequest( element, valid );
3404							}
3405						},
3406						param
3407					)
3408				);
3409				return 'pending';
3410			}
3411		}
3412	} );
3413
3414	// Ajax mode: abort
3415	// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
3416	// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()
3417
3418	// Use a prefilter if available (1.5+)
3419	if ( $.ajaxPrefilter ) {
3420		$.ajaxPrefilter( function( settings, _, xhr ) {
3421			var port = settings.port;
3422			if ( settings.mode === 'abort' ) {
3423				if ( pendingRequests[ port ] ) {
3424					pendingRequests[ port ].abort();
3425				}
3426				pendingRequests[ port ] = xhr;
3427			}
3428		} );
3429	} else {
3430		// Proxy ajax
3431		ajax = $.ajax;
3432		$.ajax = function( settings ) {
3433			var mode = ( 'mode' in settings ? settings : $.ajaxSettings ).mode,
3434				port = ( 'port' in settings ? settings : $.ajaxSettings ).port;
3435			if ( mode === 'abort' ) {
3436				if ( pendingRequests[ port ] ) {
3437					pendingRequests[ port ].abort();
3438				}
3439				pendingRequests[ port ] = ajax.apply( this, arguments );
3440				return pendingRequests[ port ];
3441			}
3442			return ajax.apply( this, arguments );
3443		};
3444	}
3445	return $;
3446} ) );
3447;
3448/**
3449 * Lazy Load XT v1.1.0 2016-01-12
3450 * http://ressio.github.io/lazy-load-xt
3451 * (C) 2016 RESS.io
3452 * Licensed under MIT
3453 * reformatted code by ThemeComplete
3454 */
3455
3456( function( $, window, document ) {
3457	'use strict';
3458
3459	// options
3460	var lazyLoadXT = 'lazyLoadXT',
3461		dataLazied = 'lazied',
3462		loadError = 'load error',
3463		classLazyHidden = 'lazy-hidden',
3464		docElement = document.documentElement || document.body,
3465		//  force load all images in Opera Mini and some mobile browsers without scroll event or getBoundingClientRect()
3466		forceLoad =
3467			window.onscroll === undefined ||
3468			!! window.operamini ||
3469			! docElement.getBoundingClientRect,
3470		options = {
3471			autoInit: true, // auto initialize in document ready
3472			selector: 'img[data-src]', // selector for lazyloading elements
3473			blankImage: 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',
3474			throttle: 99, // interval (ms) for changes check
3475			forceLoad: forceLoad, // force auto load all images
3476
3477			loadEvent: 'pageshow', // check AJAX-loaded content in jQueryMobile
3478			updateEvent: 'load orientationchange resize scroll touchmove focus', // page-modified events
3479			forceEvent: 'lazyloadall', // force loading of all elements
3480
3481			//onstart: null,
3482			oninit: { removeClass: 'lazy' }, // init handler
3483			onshow: { addClass: classLazyHidden }, // start loading handler
3484			onload: { removeClass: classLazyHidden, addClass: 'lazy-loaded' }, // load success handler
3485			onerror: { removeClass: classLazyHidden }, // error handler
3486			//oncomplete: null, // complete handler
3487
3488			//scrollContainer: undefined,
3489			checkDuplicates: true
3490		},
3491		elementOptions = {
3492			srcAttr: 'data-src',
3493			edgeX: 0,
3494			edgeY: 0,
3495			visibleOnly: true
3496		},
3497		$window = $( window ),
3498		$isFunction = function( f ) {
3499			return typeof f === 'function';
3500		},
3501		$extend = $.extend,
3502		$data =
3503			$.data ||
3504			function( el, name ) {
3505				return $( el ).data( name );
3506			},
3507		elements = [],
3508		topLazy = 0,
3509		/*
3510         waitingMode=0 : no setTimeout
3511         waitingMode=1 : setTimeout, no deferred events
3512         waitingMode=2 : setTimeout, deferred events
3513         */
3514		waitingMode = 0;
3515
3516	$[ lazyLoadXT ] = $extend( options, elementOptions, $[ lazyLoadXT ] );
3517
3518	/**
3519	 * Return options.prop if obj.prop is undefined, otherwise return obj.prop
3520	 *
3521	 * @param {*} obj
3522	 * @param {*} prop
3523	 */
3524	function getOrDef( obj, prop ) {
3525		return obj[ prop ] === undefined ? options[ prop ] : obj[ prop ];
3526	}
3527
3528	function scrollTop() {
3529		var scroll = window.pageYOffset;
3530		return scroll === undefined ? docElement.scrollTop : scroll;
3531	}
3532
3533	/**
3534	 * Run check of lazy elements after timeout
3535	 */
3536	function timeoutLazyElements() {
3537		if ( waitingMode > 1 ) {
3538			waitingMode = 1;
3539			checkLazyElements();
3540			setTimeout( timeoutLazyElements, options.throttle );
3541		} else {
3542			waitingMode = 0;
3543		}
3544	}
3545
3546	/**
3547	 * Queue check of lazy elements because of event e
3548	 *
3549	 * @param {Event} [e]
3550	 */
3551	function queueCheckLazyElements( e ) {
3552		if ( ! elements.length ) {
3553			return;
3554		}
3555
3556		// fast check for scroll event without new visible elements
3557		if ( e && e.type === 'scroll' && e.currentTarget === window ) {
3558			if ( topLazy >= scrollTop() ) {
3559				return;
3560			}
3561		}
3562
3563		if ( ! waitingMode ) {
3564			setTimeout( timeoutLazyElements, 0 );
3565		}
3566		waitingMode = 2;
3567	}
3568
3569	/**
3570	 * Process function/object event handler
3571	 *
3572	 * @param {string} event suffix
3573	 * @param {jQuery} $el
3574	 */
3575	function triggerEvent( event, $el ) {
3576		var handler = options[ 'on' + event ];
3577		if ( handler ) {
3578			if ( $isFunction( handler ) ) {
3579				handler.call( $el[ 0 ] );
3580			} else {
3581				if ( handler.addClass ) {
3582					$el.addClass( handler.addClass );
3583				}
3584				if ( handler.removeClass ) {
3585					$el.removeClass( handler.removeClass );
3586				}
3587			}
3588		}
3589
3590		$el.trigger( 'lazy' + event, [ $el ] );
3591
3592		// queue next check as images may be resized after loading of actual file
3593		queueCheckLazyElements();
3594	}
3595
3596	/**
3597	 * Trigger onload/onerror handler
3598	 *
3599	 * @param {Event} e
3600	 */
3601	function triggerLoadOrError( e ) {
3602		triggerEvent( e.type, $( e.currentTarget ).off( loadError, triggerLoadOrError ) );
3603	}
3604
3605	/**
3606	 * Load visible elements
3607	 *
3608	 * @param {bool} [force] loading of all elements
3609	 */
3610	function checkLazyElements( force ) {
3611		var viewportTop, viewportHeight, viewportWidth, i, length;
3612
3613		var $el, el, objData, removeNode, visible, topEdge;
3614
3615		var elPos, edgeX, edgeY;
3616
3617		var srcAttr, src;
3618
3619		if ( ! elements.length ) {
3620			return;
3621		}
3622
3623		force = force || options.forceLoad;
3624
3625		topLazy = Infinity;
3626
3627		viewportTop = scrollTop();
3628		viewportHeight = window.innerHeight || docElement.clientHeight;
3629		viewportWidth = window.innerWidth || docElement.clientWidth;
3630
3631		for ( i = 0, length = elements.length; i < length; i += 1 ) {
3632			$el = elements[ i ];
3633			el = $el[ 0 ];
3634			objData = $el[ lazyLoadXT ];
3635			removeNode = false;
3636			visible = force || $data( el, dataLazied ) < 0;
3637
3638			// remove items that are not in DOM
3639			if ( ! $.contains( docElement, el ) ) {
3640				removeNode = true;
3641			} else if ( force || ! objData.visibleOnly || el.offsetWidth || el.offsetHeight ) {
3642				if ( ! visible ) {
3643					elPos = el.getBoundingClientRect();
3644					edgeX = objData.edgeX;
3645					edgeY = objData.edgeY;
3646
3647					topEdge = elPos.top + viewportTop - edgeY - viewportHeight;
3648
3649					visible = topEdge <= viewportTop && elPos.bottom > -edgeY && elPos.left <= viewportWidth + edgeX && elPos.right > -edgeX;
3650				}
3651
3652				if ( visible ) {
3653					$el.on( loadError, triggerLoadOrError );
3654
3655					triggerEvent( 'show', $el );
3656
3657					srcAttr = objData.srcAttr;
3658					src = $isFunction( srcAttr ) ? srcAttr( $el ) : el.getAttribute( srcAttr );
3659
3660					if ( src ) {
3661						el.src = src;
3662					}
3663
3664					removeNode = true;
3665				} else if ( topEdge < topLazy ) {
3666					topLazy = topEdge;
3667				}
3668			}
3669
3670			if ( removeNode ) {
3671				$data( el, dataLazied, 0 );
3672				elements.splice( i, 1 );
3673				i = i - 1;
3674				length = length - 1;
3675			}
3676		}
3677
3678		if ( ! length ) {
3679			triggerEvent( 'complete', $( docElement ) );
3680		}
3681	}
3682
3683	/**
3684	 * Add new elements to lazy-load list:
3685	 * $(elements).lazyLoadXT() or $(window).lazyLoadXT()
3686	 *
3687	 * @param {Object} [overrides] override global options
3688	 */
3689	$.fn[ lazyLoadXT ] = function( overrides ) {
3690		var blankImage;
3691		var checkDuplicates;
3692		var scrollContainer;
3693		var forceShow;
3694		var elementOptionsOverrides = {};
3695
3696		overrides = overrides || {};
3697
3698		blankImage = getOrDef( overrides, 'blankImage' );
3699		checkDuplicates = getOrDef( overrides, 'checkDuplicates' );
3700		scrollContainer = getOrDef( overrides, 'scrollContainer' );
3701		forceShow = getOrDef( overrides, 'show' );
3702
3703		// empty overrides.scrollContainer is supported by both jQuery and Zepto
3704		$( scrollContainer ).on( 'scroll', queueCheckLazyElements );
3705
3706		Object.keys( elementOptions ).forEach( function( prop ) {
3707			elementOptionsOverrides[ prop ] = getOrDef( overrides, prop );
3708		} );
3709
3710		return this.each( function( index, el ) {
3711			var duplicate;
3712			var $el;
3713			if ( el === window ) {
3714				$( options.selector ).lazyLoadXT( overrides );
3715			} else {
3716				duplicate = checkDuplicates && $data( el, dataLazied );
3717				$el = $( el ).data( dataLazied, forceShow ? -1 : 1 );
3718
3719				// prevent duplicates
3720				if ( duplicate ) {
3721					queueCheckLazyElements();
3722					return;
3723				}
3724
3725				if ( blankImage && el.tagName === 'IMG' && ! el.src ) {
3726					el.src = blankImage;
3727				}
3728
3729				// clone elementOptionsOverrides object
3730				$el[ lazyLoadXT ] = $extend( {}, elementOptionsOverrides );
3731
3732				triggerEvent( 'init', $el );
3733
3734				elements.push( $el );
3735				queueCheckLazyElements();
3736			}
3737		} );
3738	};
3739
3740	/**
3741	 * Initialize list of hidden elements
3742	 */
3743	function initLazyElements() {
3744		$window.lazyLoadXT();
3745	}
3746
3747	/**
3748	 * Loading of all elements
3749	 */
3750	function forceLoadAll() {
3751		checkLazyElements( true );
3752	}
3753
3754	/**
3755	 * Initialization
3756	 */
3757	// document ready
3758	$( function() {
3759		triggerEvent( 'start', $window );
3760
3761		$window.on( options.updateEvent, queueCheckLazyElements ).on( options.forceEvent, forceLoadAll );
3762
3763		$( document ).on( options.updateEvent, queueCheckLazyElements );
3764
3765		if ( options.autoInit ) {
3766			$window.on( options.loadEvent, initLazyElements );
3767			initLazyElements(); // standard initialization
3768		}
3769	} );
3770}( window.jQuery || window.Zepto || window.$, window, document ) );
3771
3772( function( $ ) {
3773	'use strict';
3774
3775	var options = $.lazyLoadXT;
3776
3777	options.selector += ',video,iframe[data-src]';
3778	options.videoPoster = 'data-poster';
3779
3780	$( document ).on( 'lazyshow', 'video', function( e, $el ) {
3781		var srcAttr = $el.lazyLoadXT.srcAttr,
3782			isFuncSrcAttr = typeof srcAttr === 'function',
3783			changed = false;
3784
3785		$el.attr( 'poster', $el.attr( options.videoPoster ) );
3786		$el.children( 'source,track' ).each( function( index, el ) {
3787			var $child = $( el ),
3788				src = isFuncSrcAttr ? srcAttr( $child ) : $child.attr( srcAttr );
3789			if ( src ) {
3790				$child.attr( 'src', src );
3791				changed = true;
3792			}
3793		} );
3794
3795		// reload video
3796		if ( changed ) {
3797			this.load();
3798		}
3799	} );
3800}( window.jQuery || window.Zepto || window.$ ) );
3801;
3802/*! nouislider - 15.7.1 - 6/14/2023 */
3803// reformatted code by ThemeComplete
3804( function( global, factory ) {
3805	'use strict';
3806
3807	factory( global.noUiSlider = {} );
3808}( this, ( function( exports ) {
3809	'use strict';
3810
3811	var nouislider;
3812	//region Defaults
3813	var defaultFormatter = {
3814		to: function( value ) {
3815			return value === undefined ? '' : value.toFixed( 2 );
3816		},
3817		from: Number
3818	};
3819	var cssClasses = {
3820		target: 'target',
3821		base: 'base',
3822		origin: 'origin',
3823		handle: 'handle',
3824		handleLower: 'handle-lower',
3825		handleUpper: 'handle-upper',
3826		touchArea: 'touch-area',
3827		horizontal: 'horizontal',
3828		vertical: 'vertical',
3829		background: 'background',
3830		connect: 'connect',
3831		connects: 'connects',
3832		ltr: 'ltr',
3833		rtl: 'rtl',
3834		textDirectionLtr: 'txt-dir-ltr',
3835		textDirectionRtl: 'txt-dir-rtl',
3836		draggable: 'draggable',
3837		drag: 'state-drag',
3838		tap: 'state-tap',
3839		active: 'active',
3840		tooltip: 'tooltip',
3841		pips: 'pips',
3842		pipsHorizontal: 'pips-horizontal',
3843		pipsVertical: 'pips-vertical',
3844		marker: 'marker',
3845		markerHorizontal: 'marker-horizontal',
3846		markerVertical: 'marker-vertical',
3847		markerNormal: 'marker-normal',
3848		markerLarge: 'marker-large',
3849		markerSub: 'marker-sub',
3850		value: 'value',
3851		valueHorizontal: 'value-horizontal',
3852		valueVertical: 'value-vertical',
3853		valueNormal: 'value-normal',
3854		valueLarge: 'value-large',
3855		valueSub: 'value-sub'
3856	};
3857	// Namespaces of internal event listeners
3858	var INTERNAL_EVENT_NS = {
3859		tooltips: '.__tooltips',
3860		aria: '.__aria'
3861	};
3862	//endregion
3863	//region Spectrum
3864	var Spectrum = /** @class */ ( function() {
3865		function Spectrumc( entry, snap, singleStep ) {
3866			var index;
3867			var ordered = [];
3868			this.xPct = [];
3869			this.xVal = [];
3870			this.xSteps = [];
3871			this.xNumSteps = [];
3872			this.xHighestCompleteStep = [];
3873			this.xSteps = [ singleStep || false ];
3874			this.xNumSteps = [ false ];
3875			this.snap = snap;
3876			// Map the object keys to an array.
3877			Object.keys( entry ).forEach( function( iindex ) {
3878				ordered.push( [ asArray( entry[ iindex ] ), iindex ] );
3879			} );
3880			// Sort all entries by value (numeric sort).
3881			ordered.sort( function( a, b ) {
3882				return a[ 0 ][ 0 ] - b[ 0 ][ 0 ];
3883			} );
3884			// Convert all entries to subranges.
3885			for ( index = 0; index < ordered.length; index++ ) {
3886				this.handleEntryPoint( ordered[ index ][ 1 ], ordered[ index ][ 0 ] );
3887			}
3888			// Store the actual step values.
3889			// xSteps is sorted in the same order as xPct and xVal.
3890			this.xNumSteps = this.xSteps.slice( 0 );
3891			// Convert all numeric steps to the percentage of the subrange they represent.
3892			for ( index = 0; index < this.xNumSteps.length; index++ ) {
3893				this.handleStepPoint( index, this.xNumSteps[ index ] );
3894			}
3895		}
3896		Spectrumc.prototype.getDistance = function( value ) {
3897			var index;
3898			var distances = [];
3899			for ( index = 0; index < this.xNumSteps.length - 1; index++ ) {
3900				distances[ index ] = fromPercentage( this.xVal, value, index );
3901			}
3902			return distances;
3903		};
3904		// Calculate the percentual distance over the whole scale of ranges.
3905		// direction: 0 = backwards / 1 = forwards
3906		Spectrumc.prototype.getAbsoluteDistance = function( value, distances, direction ) {
3907			var xPct_index = 0;
3908			var start_factor;
3909			var rest_factor = 1;
3910			var rest_rel_distance;
3911			var range_pct = 0;
3912			var rel_range_distance = 0;
3913			var abs_distance_counter = 0;
3914			var range_counter = 0;
3915
3916			// Calculate range where to start calculation
3917			if ( value < this.xPct[ this.xPct.length - 1 ] ) {
3918				while ( value > this.xPct[ xPct_index + 1 ] ) {
3919					xPct_index++;
3920				}
3921			} else if ( value === this.xPct[ this.xPct.length - 1 ] ) {
3922				xPct_index = this.xPct.length - 2;
3923			}
3924			// If looking backwards and the value is exactly at a range separator then look one range further
3925			if ( ! direction && value === this.xPct[ xPct_index + 1 ] ) {
3926				xPct_index++;
3927			}
3928			if ( distances === null ) {
3929				distances = [];
3930			}
3931			rest_rel_distance = distances[ xPct_index ];
3932
3933			// Calculate what part of the start range the value is
3934			if ( direction ) {
3935				start_factor = ( value - this.xPct[ xPct_index ] ) / ( this.xPct[ xPct_index + 1 ] - this.xPct[ xPct_index ] );
3936			} else {
3937				start_factor = ( this.xPct[ xPct_index + 1 ] - value ) / ( this.xPct[ xPct_index + 1 ] - this.xPct[ xPct_index ] );
3938			}
3939			// Do until the complete distance across ranges is calculated
3940			while ( rest_rel_distance > 0 ) {
3941				// Calculate the percentage of total range
3942				range_pct = this.xPct[ xPct_index + 1 + range_counter ] - this.xPct[ xPct_index + range_counter ];
3943				// Detect if the margin, padding or limit is larger then the current range and calculate
3944				if ( ( distances[ xPct_index + range_counter ] * rest_factor ) + 100 - ( start_factor * 100 ) > 100 ) {
3945					// If larger then take the percentual distance of the whole range
3946					rel_range_distance = range_pct * start_factor;
3947					// Rest factor of relative percentual distance still to be calculated
3948					rest_factor = ( rest_rel_distance - ( 100 * start_factor ) ) / distances[ xPct_index + range_counter ];
3949					// Set start factor to 1 as for next range it does not apply.
3950					start_factor = 1;
3951				} else {
3952					// If smaller or equal then take the percentual distance of the calculate percentual part of that range
3953					rel_range_distance = ( ( distances[ xPct_index + range_counter ] * range_pct ) / 100 ) * rest_factor;
3954					// No rest left as the rest fits in current range
3955					rest_factor = 0;
3956				}
3957				if ( direction ) {
3958					abs_distance_counter = abs_distance_counter - rel_range_distance;
3959					// Limit range to first range when distance becomes outside of minimum range
3960					if ( this.xPct.length + range_counter >= 1 ) {
3961						range_counter--;
3962					}
3963				} else {
3964					abs_distance_counter = abs_distance_counter + rel_range_distance;
3965					// Limit range to last range when distance becomes outside of maximum range
3966					if ( this.xPct.length - range_counter >= 1 ) {
3967						range_counter++;
3968					}
3969				}
3970				// Rest of relative percentual distance still to be calculated
3971				rest_rel_distance = distances[ xPct_index + range_counter ] * rest_factor;
3972			}
3973			return value + abs_distance_counter;
3974		};
3975		Spectrumc.prototype.toStepping = function( value ) {
3976			value = toStepping( this.xVal, this.xPct, value );
3977			return value;
3978		};
3979		Spectrumc.prototype.fromStepping = function( value ) {
3980			return fromStepping( this.xVal, this.xPct, value );
3981		};
3982		Spectrumc.prototype.getStep = function( value ) {
3983			value = getStep( this.xPct, this.xSteps, this.snap, value );
3984			return value;
3985		};
3986		Spectrumc.prototype.getDefaultStep = function( value, isDown, size ) {
3987			var j = getJ( value, this.xPct );
3988			// When at the top or stepping down, look at the previous sub-range
3989			if ( value === 100 || ( isDown && value === this.xPct[ j - 1 ] ) ) {
3990				j = Math.max( j - 1, 1 );
3991			}
3992			return ( this.xVal[ j ] - this.xVal[ j - 1 ] ) / size;
3993		};
3994		Spectrumc.prototype.getNearbySteps = function( value ) {
3995			var j = getJ( value, this.xPct );
3996			return {
3997				stepBefore: {
3998					startValue: this.xVal[ j - 2 ],
3999					step: this.xNumSteps[ j - 2 ],
4000					highestStep: this.xHighestCompleteStep[ j - 2 ]
4001				},
4002				thisStep: {
4003					startValue: this.xVal[ j - 1 ],
4004					step: this.xNumSteps[ j - 1 ],
4005					highestStep: this.xHighestCompleteStep[ j - 1 ]
4006				},
4007				stepAfter: {
4008					startValue: this.xVal[ j ],
4009					step: this.xNumSteps[ j ],
4010					highestStep: this.xHighestCompleteStep[ j ]
4011				}
4012			};
4013		};
4014		Spectrumc.prototype.countStepDecimals = function() {
4015			var stepDecimals = this.xNumSteps.map( countDecimals );
4016			return Math.max.apply( null, stepDecimals );
4017		};
4018		Spectrumc.prototype.hasNoSize = function() {
4019			return this.xVal[ 0 ] === this.xVal[ this.xVal.length - 1 ];
4020		};
4021		// Outside testing
4022		Spectrumc.prototype.convert = function( value ) {
4023			return this.getStep( this.toStepping( value ) );
4024		};
4025		Spectrumc.prototype.handleEntryPoint = function( index, value ) {
4026			var percentage;
4027			var value1;
4028			// Covert min/max syntax to 0 and 100.
4029			if ( index === 'min' ) {
4030				percentage = 0;
4031			} else if ( index === 'max' ) {
4032				percentage = 100;
4033			} else {
4034				percentage = parseFloat( index );
4035			}
4036			// Check for correct input.
4037			if ( ! isNumeric( percentage ) || ! isNumeric( value[ 0 ] ) ) {
4038				throw new Error( "noUiSlider: 'range' value isn't numeric." );
4039			}
4040			// Store values.
4041			this.xPct.push( percentage );
4042			this.xVal.push( value[ 0 ] );
4043			value1 = Number( value[ 1 ] );
4044			// NaN will evaluate to false too, but to keep
4045			// logging clear, set step explicitly. Make sure
4046			// not to override the 'step' setting with false.
4047			if ( ! percentage ) {
4048				if ( ! isNaN( value1 ) ) {
4049					this.xSteps[ 0 ] = value1;
4050				}
4051			} else {
4052				this.xSteps.push( isNaN( value1 ) ? false : value1 );
4053			}
4054			this.xHighestCompleteStep.push( 0 );
4055		};
4056		Spectrumc.prototype.handleStepPoint = function( i, n ) {
4057			var totalSteps;
4058			var highestStep;
4059			var step;
4060			// Ignore 'false' stepping.
4061			if ( ! n ) {
4062				return;
4063			}
4064			// Step over zero-length ranges (#948);
4065			if ( this.xVal[ i ] === this.xVal[ i + 1 ] ) {
4066				this.xSteps[ i ] = this.xHighestCompleteStep[ i ] = this.xVal[ i ];
4067				return;
4068			}
4069			// Factor to range ratio
4070			this.xSteps[ i ] =
4071                fromPercentage( [ this.xVal[ i ], this.xVal[ i + 1 ] ], n, 0 ) / subRangeRatio( this.xPct[ i ], this.xPct[ i + 1 ] );
4072			totalSteps = ( this.xVal[ i + 1 ] - this.xVal[ i ] ) / this.xNumSteps[ i ];
4073			highestStep = Math.ceil( Number( totalSteps.toFixed( 3 ) ) - 1 );
4074			step = this.xVal[ i ] + ( this.xNumSteps[ i ] * highestStep );
4075			this.xHighestCompleteStep[ i ] = step;
4076		};
4077		return Spectrumc;
4078	}() );
4079	//endregion
4080	exports.PipsMode = void 0;
4081	( function( PipsMode ) {
4082		PipsMode.Range = 'range';
4083		PipsMode.Steps = 'steps';
4084		PipsMode.Positions = 'positions';
4085		PipsMode.Count = 'count';
4086		PipsMode.Values = 'values';
4087	}( exports.PipsMode || ( exports.PipsMode = {} ) ) );
4088	exports.PipsType = void 0;
4089	( function( PipsType ) {
4090		PipsType[ PipsType.None = -1 ] = 'None';
4091		PipsType[ PipsType.NoValue = 0 ] = 'NoValue';
4092		PipsType[ PipsType.LargeValue = 1 ] = 'LargeValue';
4093		PipsType[ PipsType.SmallValue = 2 ] = 'SmallValue';
4094	}( exports.PipsType || ( exports.PipsType = {} ) ) );
4095	//region Helper Methods
4096	function isValidFormatter( entry ) {
4097		return isValidPartialFormatter( entry ) && typeof entry.from === 'function';
4098	}
4099	function isValidPartialFormatter( entry ) {
4100		// partial formatters only need a to function and not a from function
4101		return typeof entry === 'object' && typeof entry.to === 'function';
4102	}
4103	function removeElement( el ) {
4104		el.parentElement.removeChild( el );
4105	}
4106	function isSet( value ) {
4107		return value !== null && value !== undefined;
4108	}
4109	// Bindable version
4110	function preventDefault( e ) {
4111		e.preventDefault();
4112	}
4113	// Removes duplicates from an array.
4114	function unique( array ) {
4115		return array.filter( function( a ) {
4116			return ! this[ a ] ? ( this[ a ] = true ) : false;
4117		}, {} );
4118	}
4119	// Round a value to the closest 'to'.
4120	function closest( value, to ) {
4121		return Math.round( value / to ) * to;
4122	}
4123	// Current position of an element relative to the document.
4124	function offset( elem, orientation ) {
4125		var rect = elem.getBoundingClientRect();
4126		var doc = elem.ownerDocument;
4127		var docElem = doc.documentElement;
4128		var pageOffset = getPageOffset( doc );
4129		// getBoundingClientRect contains left scroll in Chrome on Android.
4130		// I haven't found a feature detection that proves this. Worst case
4131		// scenario on mis-match: the 'tap' feature on horizontal sliders breaks.
4132		if ( /webkit.*Chrome.*Mobile/i.test( navigator.userAgent ) ) {
4133			pageOffset.x = 0;
4134		}
4135		return orientation ? rect.top + pageOffset.y - docElem.clientTop : rect.left + pageOffset.x - docElem.clientLeft;
4136	}
4137	// Checks whether a value is numerical.
4138	function isNumeric( a ) {
4139		return typeof a === 'number' && ! isNaN( a ) && isFinite( a );
4140	}
4141	// Sets a class and removes it after [duration] ms.
4142	function addClassFor( element, className, duration ) {
4143		if ( duration > 0 ) {
4144			addClass( element, className );
4145			setTimeout( function() {
4146				removeClass( element, className );
4147			}, duration );
4148		}
4149	}
4150	// Limits a value to 0 - 100
4151	function limit( a ) {
4152		return Math.max( Math.min( a, 100 ), 0 );
4153	}
4154	// Wraps a variable as an array, if it isn't one yet.
4155	// Note that an input array is returned by reference!
4156	function asArray( a ) {
4157		return Array.isArray( a ) ? a : [ a ];
4158	}
4159	// Counts decimals
4160	function countDecimals( numStr ) {
4161		var pieces;
4162		numStr = String( numStr );
4163		pieces = numStr.split( '.' );
4164		return pieces.length > 1 ? pieces[ 1 ].length : 0;
4165	}
4166	// http://youmightnotneedjquery.com/#add_class
4167	function addClass( el, className ) {
4168		if ( el.classList && ! /\s/.test( className ) ) {
4169			el.classList.add( className );
4170		} else {
4171			el.className += ' ' + className;
4172		}
4173	}
4174	// http://youmightnotneedjquery.com/#remove_class
4175	function removeClass( el, className ) {
4176		if ( el.classList && ! /\s/.test( className ) ) {
4177			el.classList.remove( className );
4178		} else {
4179			el.className = el.className.replace( new RegExp( '(^|\\b)' + className.split( ' ' ).join( '|' ) + '(\\b|$)', 'gi' ), ' ' );
4180		}
4181	}
4182	// https://plainjs.com/javascript/attributes/adding-removing-and-testing-for-classes-9/
4183	function hasClass( el, className ) {
4184		return el.classList ? el.classList.contains( className ) : new RegExp( '\\b' + className + '\\b' ).test( el.className );
4185	}
4186	// https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollY#Notes
4187	function getPageOffset( doc ) {
4188		var supportPageOffset = window.pageXOffset !== undefined;
4189		var isCSS1Compat = ( doc.compatMode || '' ) === 'CSS1Compat';
4190		var x = supportPageOffset ? window.pageXOffset : isCSS1Compat ? doc.documentElement.scrollLeft : doc.body.scrollLeft;
4191		var y = supportPageOffset ? window.pageYOffset : isCSS1Compat ? doc.documentElement.scrollTop : doc.body.scrollTop;
4192		return {
4193			x: x,
4194			y: y
4195		};
4196	}
4197	// we provide a function to compute constants instead
4198	// of accessing window.* as soon as the module needs it
4199	// so that we do not compute anything if not needed
4200	function getActions() {
4201		// Determine the events to bind. IE11 implements pointerEvents without
4202		// a prefix, which breaks compatibility with the IE10 implementation.
4203		return window.navigator.pointerEnabled ? {
4204			start: 'pointerdown',
4205			move: 'pointermove',
4206			end: 'pointerup'
4207		} : window.navigator.msPointerEnabled ? {
4208			start: 'MSPointerDown',
4209			move: 'MSPointerMove',
4210			end: 'MSPointerUp'
4211		} : {
4212			start: 'mousedown touchstart',
4213			move: 'mousemove touchmove',
4214			end: 'mouseup touchend'
4215		};
4216	}
4217	// https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
4218	// Issue #785
4219	function getSupportsPassive() {
4220		var supportsPassive = false;
4221		/* eslint-disable */
4222        try {
4223            var opts = Object.defineProperty({}, "passive", {
4224                get: function () {
4225                    supportsPassive = true;
4226                },
4227            });
4228            // @ts-ignore
4229            window.addEventListener("test", null, opts);
4230        }
4231        catch (e) { }
4232        /* eslint-enable */
4233		return supportsPassive;
4234	}
4235	function getSupportsTouchActionNone() {
4236		return window.CSS && CSS.supports && CSS.supports( 'touch-action', 'none' );
4237	}
4238	//endregion
4239	//region Range Calculation
4240	// Determine the size of a sub-range in relation to a full range.
4241	function subRangeRatio( pa, pb ) {
4242		return 100 / ( pb - pa );
4243	}
4244	// (percentage) How many percent is this value of this range?
4245	function fromPercentage( range, value, startRange ) {
4246		return ( value * 100 ) / ( range[ startRange + 1 ] - range[ startRange ] );
4247	}
4248	// (percentage) Where is this value on this range?
4249	function toPercentage( range, value ) {
4250		return fromPercentage( range, range[ 0 ] < 0 ? value + Math.abs( range[ 0 ] ) : value - range[ 0 ], 0 );
4251	}
4252	// (value) How much is this percentage on this range?
4253	function isPercentage( range, value ) {
4254		return ( ( value * ( range[ 1 ] - range[ 0 ] ) ) / 100 ) + range[ 0 ];
4255	}
4256	function getJ( value, arr ) {
4257		var j = 1;
4258		while ( value >= arr[ j ] ) {
4259			j += 1;
4260		}
4261		return j;
4262	}
4263	// (percentage) Input a value, find where, on a scale of 0-100, it applies.
4264	function toStepping( xVal, xPct, value ) {
4265		var j;
4266		var va;
4267		var vb;
4268		var pa;
4269		var pb;
4270		if ( value >= xVal.slice( -1 )[ 0 ] ) {
4271			return 100;
4272		}
4273		j = getJ( value, xVal );
4274		va = xVal[ j - 1 ];
4275		vb = xVal[ j ];
4276		pa = xPct[ j - 1 ];
4277		pb = xPct[ j ];
4278		return pa + ( toPercentage( [ va, vb ], value ) / subRangeRatio( pa, pb ) );
4279	}
4280	// (value) Input a percentage, find where it is on the specified range.
4281	function fromStepping( xVal, xPct, value ) {
4282		var j;
4283		var va;
4284		var vb;
4285		var pa;
4286		var pb;
4287		// There is no range group that fits 100
4288		if ( value >= 100 ) {
4289			return xVal.slice( -1 )[ 0 ];
4290		}
4291		j = getJ( value, xPct );
4292		va = xVal[ j - 1 ];
4293		vb = xVal[ j ];
4294		pa = xPct[ j - 1 ];
4295		pb = xPct[ j ];
4296		return isPercentage( [ va, vb ], ( value - pa ) * subRangeRatio( pa, pb ) );
4297	}
4298	// (percentage) Get the step that applies at a certain value.
4299	function getStep( xPct, xSteps, snap, value ) {
4300		var j;
4301		var a;
4302		var b;
4303		if ( value === 100 ) {
4304			return value;
4305		}
4306		j = getJ( value, xPct );
4307		a = xPct[ j - 1 ];
4308		b = xPct[ j ];
4309		// If 'snap' is set, steps are used as fixed points on the slider.
4310		if ( snap ) {
4311			// Find the closest position, a or b.
4312			if ( value - a > ( b - a ) / 2 ) {
4313				return b;
4314			}
4315			return a;
4316		}
4317		if ( ! xSteps[ j - 1 ] ) {
4318			return value;
4319		}
4320		return xPct[ j - 1 ] + closest( value - xPct[ j - 1 ], xSteps[ j - 1 ] );
4321	}
4322	//endregion
4323	//region Options
4324	/*	Every input option is tested and parsed. This will prevent
4325        endless validation in internal methods. These tests are
4326        structured with an item for every option available. An
4327        option can be marked as required by setting the 'r' flag.
4328        The testing function is provided with three arguments:
4329            - The provided value for the option;
4330            - A reference to the options object;
4331            - The name for the option;
4332
4333        The testing function returns false when an error is detected,
4334        or true when everything is OK. It can also modify the option
4335        object, to make sure all values can be correctly looped elsewhere. */
4336
4337	function testStep( parsed, entry ) {
4338		if ( ! isNumeric( entry ) ) {
4339			throw new Error( "noUiSlider: 'step' is not numeric." );
4340		}
4341		// The step option can still be used to set stepping
4342		// for linear sliders. Overwritten if set in 'range'.
4343		parsed.singleStep = entry;
4344	}
4345	function testKeyboardPageMultiplier( parsed, entry ) {
4346		if ( ! isNumeric( entry ) ) {
4347			throw new Error( "noUiSlider: 'keyboardPageMultiplier' is not numeric." );
4348		}
4349		parsed.keyboardPageMultiplier = entry;
4350	}
4351	function testKeyboardMultiplier( parsed, entry ) {
4352		if ( ! isNumeric( entry ) ) {
4353			throw new Error( "noUiSlider: 'keyboardMultiplier' is not numeric." );
4354		}
4355		parsed.keyboardMultiplier = entry;
4356	}
4357	function testKeyboardDefaultStep( parsed, entry ) {
4358		if ( ! isNumeric( entry ) ) {
4359			throw new Error( "noUiSlider: 'keyboardDefaultStep' is not numeric." );
4360		}
4361		parsed.keyboardDefaultStep = entry;
4362	}
4363	function testRange( parsed, entry ) {
4364		// Filter incorrect input.
4365		if ( typeof entry !== 'object' || Array.isArray( entry ) ) {
4366			throw new Error( "noUiSlider: 'range' is not an object." );
4367		}
4368		// Catch missing start or end.
4369		if ( entry.min === undefined || entry.max === undefined ) {
4370			throw new Error( "noUiSlider: Missing 'min' or 'max' in 'range'." );
4371		}
4372		parsed.spectrum = new Spectrum( entry, parsed.snap || false, parsed.singleStep );
4373	}
4374	function testStart( parsed, entry ) {
4375		entry = asArray( entry );
4376		// Validate input. Values aren't tested, as the public .val method
4377		// will always provide a valid location.
4378		if ( ! Array.isArray( entry ) || ! entry.length ) {
4379			throw new Error( "noUiSlider: 'start' option is incorrect." );
4380		}
4381		// Store the number of handles.
4382		parsed.handles = entry.length;
vendor: 3,263 bytes, lines 4383-4481
4383		// When the slider is initialized, the .val method will
4384		// be called with the start options.
4385		parsed.start = entry;
4386	}
4387	function testSnap( parsed, entry ) {
4388		if ( typeof entry !== 'boolean' ) {
4389			throw new Error( "noUiSlider: 'snap' option must be a boolean." );
4390		}
4391		// Enforce 100% stepping within subranges.
4392		parsed.snap = entry;
4393	}
4394	function testAnimate( parsed, entry ) {
4395		if ( typeof entry !== 'boolean' ) {
4396			throw new Error( "noUiSlider: 'animate' option must be a boolean." );
4397		}
4398		// Enforce 100% stepping within subranges.
4399		parsed.animate = entry;
4400	}
4401	function testAnimationDuration( parsed, entry ) {
4402		if ( typeof entry !== 'number' ) {
4403			throw new Error( "noUiSlider: 'animationDuration' option must be a number." );
4404		}
4405		parsed.animationDuration = entry;
4406	}
4407	function testConnect( parsed, entry ) {
4408		var connect = [ false ];
4409		var i;
4410		// Map legacy options
4411		if ( entry === 'lower' ) {
4412			entry = [ true, false ];
4413		} else if ( entry === 'upper' ) {
4414			entry = [ false, true ];
4415		}
4416		// Handle boolean options
4417		if ( entry === true || entry === false ) {
4418			for ( i = 1; i < parsed.handles; i++ ) {
4419				connect.push( entry );
4420			}
4421			connect.push( false );
4422		} else if ( ! Array.isArray( entry ) || ! entry.length || entry.length !== parsed.handles + 1 ) { // Reject invalid input
4423			throw new Error( "noUiSlider: 'connect' option doesn't match handle count." );
4424		} else {
4425			connect = entry;
4426		}
4427		parsed.connect = connect;
4428	}
4429	function testOrientation( parsed, entry ) {
4430		// Set orientation to an a numerical value for easy
4431		// array selection.
4432		switch ( entry ) {
4433			case 'horizontal':
4434				parsed.ort = 0;
4435				break;
4436			case 'vertical':
4437				parsed.ort = 1;
4438				break;
4439			default:
4440				throw new Error( "noUiSlider: 'orientation' option is invalid." );
4441		}
4442	}
4443	function testMargin( parsed, entry ) {
4444		if ( ! isNumeric( entry ) ) {
4445			throw new Error( "noUiSlider: 'margin' option must be numeric." );
4446		}
4447		// Issue #582
4448		if ( entry === 0 ) {
4449			return;
4450		}
4451		parsed.margin = parsed.spectrum.getDistance( entry );
4452	}
4453	function testLimit( parsed, entry ) {
4454		if ( ! isNumeric( entry ) ) {
4455			throw new Error( "noUiSlider: 'limit' option must be numeric." );
4456		}
4457		parsed.limit = parsed.spectrum.getDistance( entry );
4458		if ( ! parsed.limit || parsed.handles < 2 ) {
4459			throw new Error( "noUiSlider: 'limit' option is only supported on linear sliders with 2 or more handles." );
4460		}
4461	}
4462	function testPadding( parsed, entry ) {
4463		var index;
4464		var totalPadding;
4465		var firstValue;
4466		var lastValue;
4467		if ( ! isNumeric( entry ) && ! Array.isArray( entry ) ) {
4468			throw new Error( "noUiSlider: 'padding' option must be numeric or array of exactly 2 numbers." );
4469		}
4470		if ( Array.isArray( entry ) && ! ( entry.length === 2 || isNumeric( entry[ 0 ] ) || isNumeric( entry[ 1 ] ) ) ) {
4471			throw new Error( "noUiSlider: 'padding' option must be numeric or array of exactly 2 numbers." );
4472		}
4473		if ( entry === 0 ) {
4474			return;
4475		}
4476		if ( ! Array.isArray( entry ) ) {
4477			entry = [ entry, entry ];
4478		}
4479		// 'getDistance' returns false for invalid values.
4480		parsed.padding = [ parsed.spectrum.getDistance( entry[ 0 ] ), parsed.spectrum.getDistance( entry[ 1 ] ) ];
4481		for ( index = 0; index < parsed.spectrum.xNumSteps.length - 1; index++ ) {
4482			// last "range" can't contain step size as it is purely an endpoint.
4483			if ( parsed.padding[ 0 ][ index ] < 0 || parsed.padding[ 1 ][ index ] < 0 ) {
4484				throw new Error( "noUiSlider: 'padding' option must be a positive number(s)." );
4485			}
4486		}
4487		totalPadding = entry[ 0 ] + entry[ 1 ];
4488		firstValue = parsed.spectrum.xVal[ 0 ];
4489		lastValue = parsed.spectrum.xVal[ parsed.spectrum.xVal.length - 1 ];
4490		if ( totalPadding / ( lastValue - firstValue ) > 1 ) {
4491			throw new Error( "noUiSlider: 'padding' option must not exceed 100% of the range." );
4492		}
4493	}
4494	function testDirection( parsed, entry ) {
4495		// Set direction as a numerical value for easy parsing.
4496		// Invert connection for RTL sliders, so that the proper
4497		// handles get the connect/background classes.
4498		switch ( entry ) {
4499			case 'ltr':
4500				parsed.dir = 0;
4501				break;
4502			case 'rtl':
4503				parsed.dir = 1;
4504				break;
4505			default:
4506				throw new Error( "noUiSlider: 'direction' option was not recognized." );
4507		}
4508	}
4509	function testBehaviour( parsed, entry ) {
4510		var tap;
4511		var drag;
4512		var fixed;
4513		var snap;
4514		var hover;
4515		var unconstrained;
4516		var dragAll;
4517		var smoothSteps;
4518		// Make sure the input is a string.
4519		if ( typeof entry !== 'string' ) {
4520			throw new Error( "noUiSlider: 'behaviour' must be a string containing options." );
4521		}
4522		// Check if the string contains any keywords.
4523		// None are required.
4524		tap = entry.indexOf( 'tap' ) >= 0;
4525		drag = entry.indexOf( 'drag' ) >= 0;
4526		fixed = entry.indexOf( 'fixed' ) >= 0;
4527		snap = entry.indexOf( 'snap' ) >= 0;
4528		hover = entry.indexOf( 'hover' ) >= 0;
4529		unconstrained = entry.indexOf( 'unconstrained' ) >= 0;
4530		dragAll = entry.indexOf( 'drag-all' ) >= 0;
4531		smoothSteps = entry.indexOf( 'smooth-steps' ) >= 0;
4532		if ( fixed ) {
4533			if ( parsed.handles !== 2 ) {
4534				throw new Error( "noUiSlider: 'fixed' behaviour must be used with 2 handles" );
4535			}
4536			// Use margin to enforce fixed state
4537			testMargin( parsed, parsed.start[ 1 ] - parsed.start[ 0 ] );
4538		}
4539		if ( unconstrained && ( parsed.margin || parsed.limit ) ) {
4540			throw new Error( "noUiSlider: 'unconstrained' behaviour cannot be used with margin or limit" );
4541		}
4542		parsed.events = {
4543			tap: tap || snap,
4544			drag: drag,
4545			dragAll: dragAll,
4546			smoothSteps: smoothSteps,
4547			fixed: fixed,
4548			snap: snap,
4549			hover: hover,
4550			unconstrained: unconstrained
4551		};
4552	}
4553	function testTooltips( parsed, entry ) {
4554		var i;
4555		if ( entry === false ) {
4556			return;
4557		}
4558		if ( entry === true || isValidPartialFormatter( entry ) ) {
4559			parsed.tooltips = [];
4560			for ( i = 0; i < parsed.handles; i++ ) {
4561				parsed.tooltips.push( entry );
4562			}
4563		} else {
4564			entry = asArray( entry );
4565			if ( entry.length !== parsed.handles ) {
4566				throw new Error( 'noUiSlider: must pass a formatter for all handles.' );
4567			}
4568			entry.forEach( function( formatter ) {
4569				if ( typeof formatter !== 'boolean' && ! isValidPartialFormatter( formatter ) ) {
4570					throw new Error( "noUiSlider: 'tooltips' must be passed a formatter or 'false'." );
4571				}
4572			} );
4573			parsed.tooltips = entry;
4574		}
4575	}
4576	function testHandleAttributes( parsed, entry ) {
4577		if ( entry.length !== parsed.handles ) {
4578			throw new Error( 'noUiSlider: must pass a attributes for all handles.' );
4579		}
4580		parsed.handleAttributes = entry;
4581	}
4582	function testAriaFormat( parsed, entry ) {
4583		if ( ! isValidPartialFormatter( entry ) ) {
4584			throw new Error( "noUiSlider: 'ariaFormat' requires 'to' method." );
4585		}
4586		parsed.ariaFormat = entry;
4587	}
4588	function testFormat( parsed, entry ) {
4589		if ( ! isValidFormatter( entry ) ) {
4590			throw new Error( "noUiSlider: 'format' requires 'to' and 'from' methods." );
4591		}
4592		parsed.format = entry;
4593	}
4594	function testKeyboardSupport( parsed, entry ) {
4595		if ( typeof entry !== 'boolean' ) {
4596			throw new Error( "noUiSlider: 'keyboardSupport' option must be a boolean." );
4597		}
4598		parsed.keyboardSupport = entry;
4599	}
4600	function testDocumentElement( parsed, entry ) {
4601		// This is an advanced option. Passed values are used without validation.
4602		parsed.documentElement = entry;
4603	}
4604	function testCssPrefix( parsed, entry ) {
4605		if ( typeof entry !== 'string' && entry !== false ) {
4606			throw new Error( "noUiSlider: 'cssPrefix' must be a string or `false`." );
4607		}
4608		parsed.cssPrefix = entry;
4609	}
4610	function testCssClasses( parsed, entry ) {
4611		if ( typeof entry !== 'object' ) {
4612			throw new Error( "noUiSlider: 'cssClasses' must be an object." );
4613		}
4614		if ( typeof parsed.cssPrefix === 'string' ) {
4615			parsed.cssClasses = {};
4616			Object.keys( entry ).forEach( function( key ) {
4617				parsed.cssClasses[ key ] = parsed.cssPrefix + entry[ key ];
4618			} );
4619		} else {
4620			parsed.cssClasses = entry;
4621		}
4622	}
4623	// Test all developer settings and parse to assumption-safe values.
4624	function testOptions( options ) {
4625		// To prove a fix for #537, freeze options here.
4626		// If the object is modified, an error will be thrown.
4627		// Object.freeze(options);
4628		var parsed = {
4629			margin: null,
4630			limit: null,
4631			padding: null,
4632			animate: true,
4633			animationDuration: 300,
4634			ariaFormat: defaultFormatter,
4635			format: defaultFormatter
4636		};
4637		// Tests are executed in the order they are presented here.
4638		var tests = {
4639			step: { r: false, t: testStep },
4640			keyboardPageMultiplier: { r: false, t: testKeyboardPageMultiplier },
4641			keyboardMultiplier: { r: false, t: testKeyboardMultiplier },
4642			keyboardDefaultStep: { r: false, t: testKeyboardDefaultStep },
4643			start: { r: true, t: testStart },
4644			connect: { r: true, t: testConnect },
4645			direction: { r: true, t: testDirection },
4646			snap: { r: false, t: testSnap },
4647			animate: { r: false, t: testAnimate },
4648			animationDuration: { r: false, t: testAnimationDuration },
4649			range: { r: true, t: testRange },
4650			orientation: { r: false, t: testOrientation },
4651			margin: { r: false, t: testMargin },
4652			limit: { r: false, t: testLimit },
4653			padding: { r: false, t: testPadding },
4654			behaviour: { r: true, t: testBehaviour },
4655			ariaFormat: { r: false, t: testAriaFormat },
4656			format: { r: false, t: testFormat },
4657			tooltips: { r: false, t: testTooltips },
4658			keyboardSupport: { r: true, t: testKeyboardSupport },
4659			documentElement: { r: false, t: testDocumentElement },
4660			cssPrefix: { r: true, t: testCssPrefix },
4661			cssClasses: { r: true, t: testCssClasses },
4662			handleAttributes: { r: false, t: testHandleAttributes }
4663		};
4664		var defaults = {
4665			connect: false,
4666			direction: 'ltr',
4667			behaviour: 'tap',
4668			orientation: 'horizontal',
4669			keyboardSupport: true,
4670			cssPrefix: 'noui-',
4671			cssClasses: cssClasses,
4672			keyboardPageMultiplier: 5,
4673			keyboardMultiplier: 1,
4674			keyboardDefaultStep: 10
4675		};
4676		var d;
4677		var msPrefix;
4678		var noPrefix;
4679		var styles;
4680		// AriaFormat defaults to regular format, if any.
4681		if ( options.format && ! options.ariaFormat ) {
4682			options.ariaFormat = options.format;
4683		}
4684		// Run all options through a testing mechanism to ensure correct
4685		// input. It should be noted that options might get modified to
4686		// be handled properly. E.g. wrapping integers in arrays.
4687		Object.keys( tests ).forEach( function( name ) {
4688			// If the option isn't set, but it is required, throw an error.
4689			if ( ! isSet( options[ name ] ) && defaults[ name ] === undefined ) {
4690				if ( tests[ name ].r ) {
4691					throw new Error( "noUiSlider: '" + name + "' is required." );
4692				}
4693				return;
4694			}
4695			tests[ name ].t( parsed, ! isSet( options[ name ] ) ? defaults[ name ] : options[ name ] );
4696		} );
4697		// Forward pips options
4698		parsed.pips = options.pips;
4699		// All recent browsers accept unprefixed transform.
4700		// We need -ms- for IE9 and -webkit- for older Android;
4701		// Assume use of -webkit- if unprefixed and -ms- are not supported.
4702		// https://caniuse.com/#feat=transforms2d
4703		d = document.createElement( 'div' );
4704		msPrefix = d.style.msTransform !== undefined;
4705		noPrefix = d.style.transform !== undefined;
4706		parsed.transformRule = noPrefix ? 'transform' : msPrefix ? 'msTransform' : 'webkitTransform';
4707		// Pips don't move, so we can place them using left/top.
4708		styles = [
4709			[ 'left', 'top' ],
4710			[ 'right', 'bottom' ]
4711		];
4712		parsed.style = styles[ parsed.dir ][ parsed.ort ];
4713		return parsed;
4714	}
4715	//endregion
4716	function scope( target, options, originalOptions ) {
4717		var actions = getActions();
4718		var supportsTouchActionNone = getSupportsTouchActionNone();
4719		var supportsPassive = supportsTouchActionNone && getSupportsPassive();
4720		// All variables local to 'scope' are prefixed with 'scope_'
4721		// Slider DOM Nodes
4722		var scope_Target = target;
4723		var scope_Base;
4724		var scope_Handles;
4725		var scope_Connects;
4726		var scope_Pips;
4727		var scope_Tooltips;
4728		// Slider state values
4729		var scope_Spectrum = options.spectrum;
4730		var scope_Values = [];
4731		var scope_Locations = [];
4732		var scope_HandleNumbers = [];
4733		var scope_ActiveHandlesCount = 0;
4734		var scope_Events = {};
4735		// Document Nodes
4736		var scope_Document = target.ownerDocument;
4737		var scope_DocumentElement = options.documentElement || scope_Document.documentElement;
4738		var scope_Body = scope_Document.body;
4739		// For horizontal sliders in standard ltr documents,
4740		// make .noUi-origin overflow to the left so the document doesn't scroll.
4741		var scope_DirOffset = scope_Document.dir === 'rtl' || options.ort === 1 ? 0 : 100;
4742		var scope_Self;
4743		// Creates a node, adds it to target, returns the new node.
4744		function addNodeTo( addTarget, className ) {
4745			var div = scope_Document.createElement( 'div' );
4746			if ( className ) {
4747				addClass( div, className );
4748			}
4749			addTarget.appendChild( div );
4750			return div;
4751		}
4752		// Append a origin to the base
4753		function addOrigin( base, handleNumber ) {
4754			var origin = addNodeTo( base, options.cssClasses.origin );
4755			var handle = addNodeTo( origin, options.cssClasses.handle );
4756			var attributes_1;
4757			addNodeTo( handle, options.cssClasses.touchArea );
4758			handle.setAttribute( 'data-handle', String( handleNumber ) );
4759			if ( options.keyboardSupport ) {
4760				// https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex
4761				// 0 = focusable and reachable
4762				handle.setAttribute( 'tabindex', '0' );
4763				handle.addEventListener( 'keydown', function( event ) {
vendor: 19,677 bytes, lines 4764-5261
4764					return eventKeydown( event, handleNumber );
4765				} );
4766			}
4767			if ( options.handleAttributes !== undefined ) {
4768				attributes_1 = options.handleAttributes[ handleNumber ];
4769				Object.keys( attributes_1 ).forEach( function( attribute ) {
4770					handle.setAttribute( attribute, attributes_1[ attribute ] );
4771				} );
4772			}
4773			handle.setAttribute( 'role', 'slider' );
4774			handle.setAttribute( 'aria-orientation', options.ort ? 'vertical' : 'horizontal' );
4775			if ( handleNumber === 0 ) {
4776				addClass( handle, options.cssClasses.handleLower );
4777			} else if ( handleNumber === options.handles - 1 ) {
4778				addClass( handle, options.cssClasses.handleUpper );
4779			}
4780			origin.handle = handle;
4781			return origin;
4782		}
4783		// Insert nodes for connect elements
4784		function addConnect( base, add ) {
4785			if ( ! add ) {
4786				return false;
4787			}
4788			return addNodeTo( base, options.cssClasses.connect );
4789		}
4790		// Add handles to the slider base.
4791		function addElements( connectOptions, base ) {
4792			var connectBase = addNodeTo( base, options.cssClasses.connects );
4793			var i;
4794			scope_Handles = [];
4795			scope_Connects = [];
4796			scope_Connects.push( addConnect( connectBase, connectOptions[ 0 ] ) );
4797			// [::::O====O====O====]
4798			// connectOptions = [0, 1, 1, 1]
4799			for ( i = 0; i < options.handles; i++ ) {
4800				// Keep a list of all added handles.
4801				scope_Handles.push( addOrigin( base, i ) );
4802				scope_HandleNumbers[ i ] = i;
4803				scope_Connects.push( addConnect( connectBase, connectOptions[ i + 1 ] ) );
4804			}
4805		}
4806		// Initialize a single slider.
4807		function addSlider( addTarget ) {
4808			var textDirection;
4809			// Apply classes and data to the target.
4810			addClass( addTarget, options.cssClasses.target );
4811			if ( options.dir === 0 ) {
4812				addClass( addTarget, options.cssClasses.ltr );
4813			} else {
4814				addClass( addTarget, options.cssClasses.rtl );
4815			}
4816			if ( options.ort === 0 ) {
4817				addClass( addTarget, options.cssClasses.horizontal );
4818			} else {
4819				addClass( addTarget, options.cssClasses.vertical );
4820			}
4821			textDirection = getComputedStyle( addTarget ).direction;
4822			if ( textDirection === 'rtl' ) {
4823				addClass( addTarget, options.cssClasses.textDirectionRtl );
4824			} else {
4825				addClass( addTarget, options.cssClasses.textDirectionLtr );
4826			}
4827			return addNodeTo( addTarget, options.cssClasses.base );
4828		}
4829		function addTooltip( handle, handleNumber ) {
4830			if ( ! options.tooltips || ! options.tooltips[ handleNumber ] ) {
4831				return false;
4832			}
4833			return addNodeTo( handle.firstChild, options.cssClasses.tooltip );
4834		}
4835		function isSliderDisabled() {
4836			return scope_Target.hasAttribute( 'disabled' );
4837		}
4838		// Disable the slider dragging if any handle is disabled
4839		function isHandleDisabled( handleNumber ) {
4840			var handleOrigin = scope_Handles[ handleNumber ];
4841			return handleOrigin.hasAttribute( 'disabled' );
4842		}
4843		function disable( handleNumber ) {
4844			if ( handleNumber !== null && handleNumber !== undefined ) {
4845				scope_Handles[ handleNumber ].setAttribute( 'disabled', '' );
4846				scope_Handles[ handleNumber ].handle.removeAttribute( 'tabindex' );
4847			} else {
4848				scope_Target.setAttribute( 'disabled', '' );
4849				scope_Handles.forEach( function( handle ) {
4850					handle.handle.removeAttribute( 'tabindex' );
4851				} );
4852			}
4853		}
4854		function enable( handleNumber ) {
4855			if ( handleNumber !== null && handleNumber !== undefined ) {
4856				scope_Handles[ handleNumber ].removeAttribute( 'disabled' );
4857				scope_Handles[ handleNumber ].handle.setAttribute( 'tabindex', '0' );
4858			} else {
4859				scope_Target.removeAttribute( 'disabled' );
4860				scope_Handles.forEach( function( handle ) {
4861					handle.removeAttribute( 'disabled' );
4862					handle.handle.setAttribute( 'tabindex', '0' );
4863				} );
4864			}
4865		}
4866		function removeTooltips() {
4867			if ( scope_Tooltips ) {
4868				removeEvent( 'update' + INTERNAL_EVENT_NS.tooltips );
4869				scope_Tooltips.forEach( function( tooltip ) {
4870					if ( tooltip ) {
4871						removeElement( tooltip );
4872					}
4873				} );
4874				scope_Tooltips = null;
4875			}
4876		}
4877		// The tooltips option is a shorthand for using the 'update' event.
4878		function tooltips() {
4879			var formattedValue;
4880			removeTooltips();
4881			// Tooltips are added with options.tooltips in original order.
4882			scope_Tooltips = scope_Handles.map( addTooltip );
4883			bindEvent( 'update' + INTERNAL_EVENT_NS.tooltips, function( values, handleNumber, unencoded ) {
4884				if ( ! scope_Tooltips || ! options.tooltips ) {
4885					return;
4886				}
4887				if ( scope_Tooltips[ handleNumber ] === false ) {
4888					return;
4889				}
4890				formattedValue = values[ handleNumber ];
4891				if ( options.tooltips[ handleNumber ] !== true ) {
4892					formattedValue = options.tooltips[ handleNumber ].to( unencoded[ handleNumber ] );
4893				}
4894				scope_Tooltips[ handleNumber ].innerHTML = formattedValue;
4895			} );
4896		}
4897		function aria() {
4898			removeEvent( 'update' + INTERNAL_EVENT_NS.aria );
4899			bindEvent( 'update' + INTERNAL_EVENT_NS.aria, function( values, handleNumber, unencoded, tap, positions ) {
4900				// Update Aria Values for all handles, as a change in one changes min and max values for the next.
4901				scope_HandleNumbers.forEach( function( index ) {
4902					var handle = scope_Handles[ index ];
4903					var min = checkHandlePosition( scope_Locations, index, 0, true, true, true );
4904					var max = checkHandlePosition( scope_Locations, index, 100, true, true, true );
4905					var now = positions[ index ];
4906					// Formatted value for display
4907					var text = String( options.ariaFormat.to( unencoded[ index ] ) );
4908					// Map to slider range values
4909					min = scope_Spectrum.fromStepping( min ).toFixed( 1 );
4910					max = scope_Spectrum.fromStepping( max ).toFixed( 1 );
4911					now = scope_Spectrum.fromStepping( now ).toFixed( 1 );
4912					handle.children[ 0 ].setAttribute( 'aria-valuemin', min );
4913					handle.children[ 0 ].setAttribute( 'aria-valuemax', max );
4914					handle.children[ 0 ].setAttribute( 'aria-valuenow', now );
4915					handle.children[ 0 ].setAttribute( 'aria-valuetext', text );
4916				} );
4917			} );
4918		}
4919		function getGroup( ipips ) {
4920			var interval;
4921			var spread;
4922			var values;
4923			// Use the range.
4924			if ( ipips.mode === exports.PipsMode.Range || ipips.mode === exports.PipsMode.Steps ) {
4925				return scope_Spectrum.xVal;
4926			}
4927			if ( ipips.mode === exports.PipsMode.Count ) {
4928				if ( ipips.values < 2 ) {
4929					throw new Error( "noUiSlider: 'values' (>= 2) required for mode 'count'." );
4930				}
4931				// Divide 0 - 100 in 'count' parts.
4932				interval = ipips.values - 1;
4933				spread = 100 / interval;
4934				values = [];
4935				// List these parts and have them handled as 'positions'.
4936				while ( interval-- ) {
4937					values[ interval ] = interval * spread;
4938				}
4939				values.push( 100 );
4940				return mapToRange( values, ipips.stepped );
4941			}
4942			if ( ipips.mode === exports.PipsMode.Positions ) {
4943				// Map all percentages to on-range values.
4944				return mapToRange( ipips.values, ipips.stepped );
4945			}
4946			if ( ipips.mode === exports.PipsMode.Values ) {
4947				// If the value must be stepped, it needs to be converted to a percentage first.
4948				if ( ipips.stepped ) {
4949					return ipips.values.map( function( value ) {
4950						// Convert to percentage, apply step, return to value.
4951						return scope_Spectrum.fromStepping( scope_Spectrum.getStep( scope_Spectrum.toStepping( value ) ) );
4952					} );
4953				}
4954				// Otherwise, we can simply use the values.
4955				return ipips.values;
4956			}
4957			return []; // ipips.mode = never
4958		}
4959		function mapToRange( values, stepped ) {
4960			return values.map( function( value ) {
4961				return scope_Spectrum.fromStepping( stepped ? scope_Spectrum.getStep( value ) : value );
4962			} );
4963		}
4964		function generateSpread( ipips ) {
4965			var group = getGroup( ipips );
4966			var indexes = {};
4967			var firstInRange = scope_Spectrum.xVal[ 0 ];
4968			var lastInRange = scope_Spectrum.xVal[ scope_Spectrum.xVal.length - 1 ];
4969			var ignoreFirst = false;
4970			var ignoreLast = false;
4971			var prevPct = 0;
4972			function safeIncrement( value, increment ) {
4973				// Avoid floating point variance by dropping the smallest decimal places.
4974				return Number( ( value + increment ).toFixed( 7 ) );
4975			}
4976			// Create a copy of the group, sort it and filter away all duplicates.
4977			group = unique( group.slice().sort( function( a, b ) {
4978				return a - b;
4979			} ) );
4980			// Make sure the range starts with the first element.
4981			if ( group[ 0 ] !== firstInRange ) {
4982				group.unshift( firstInRange );
4983				ignoreFirst = true;
4984			}
4985			// Likewise for the last one.
4986			if ( group[ group.length - 1 ] !== lastInRange ) {
4987				group.push( lastInRange );
4988				ignoreLast = true;
4989			}
4990			group.forEach( function( current, index ) {
4991				// Get the current step and the lower + upper positions.
4992				var step;
4993				var i;
4994				var q;
4995				var low = current;
4996				var high = group[ index + 1 ];
4997				var newPct;
4998				var pctDifference;
4999				var pctPos;
5000				var type;
5001				var steps;
5002				var realSteps;
5003				var stepSize;
5004				var isSteps = ipips.mode === exports.PipsMode.Steps;
5005				// When using 'steps' mode, use the provided steps.
5006				// Otherwise, we'll step on to the next subrange.
5007				if ( isSteps ) {
5008					step = scope_Spectrum.xNumSteps[ index ];
5009				}
5010				// Default to a 'full' step.
5011				if ( ! step ) {
5012					step = high - low;
5013				}
5014				// If high is undefined we are at the last subrange. Make sure it iterates once (#1088)
5015				if ( high === undefined ) {
5016					high = low;
5017				}
5018				// Make sure step isn't 0, which would cause an infinite loop (#654)
5019				step = Math.max( step, 0.0000001 );
5020				// Find all steps in the subrange.
5021				for ( i = low; i <= high; i = safeIncrement( i, step ) ) {
5022					// Get the percentage value for the current step,
5023					// calculate the size for the subrange.
5024					newPct = scope_Spectrum.toStepping( i );
5025					pctDifference = newPct - prevPct;
5026					steps = pctDifference / ( ipips.density || 1 );
5027					realSteps = Math.round( steps );
5028					// This ratio represents the amount of percentage-space a point indicates.
5029					// For a density 1 the points/percentage = 1. For density 2, that percentage needs to be re-divided.
5030					// Round the percentage offset to an even number, then divide by two
5031					// to spread the offset on both sides of the range.
5032					stepSize = pctDifference / realSteps;
5033					// Divide all points evenly, adding the correct number to this subrange.
5034					// Run up to <= so that 100% gets a point, event if ignoreLast is set.
5035					for ( q = 1; q <= realSteps; q += 1 ) {
5036						// The ratio between the rounded value and the actual size might be ~1% off.
5037						// Correct the percentage offset by the number of points
5038						// per subrange. density = 1 will result in 100 points on the
5039						// full range, 2 for 50, 4 for 25, etc.
5040						pctPos = prevPct + ( q * stepSize );
5041						indexes[ pctPos.toFixed( 5 ) ] = [ scope_Spectrum.fromStepping( pctPos ), 0 ];
5042					}
5043					// Determine the point type.
5044					type = group.indexOf( i ) > -1 ? exports.PipsType.LargeValue : isSteps ? exports.PipsType.SmallValue : exports.PipsType.NoValue;
5045					// Enforce the 'ignoreFirst' option by overwriting the type for 0.
5046					if ( ! index && ignoreFirst && i !== high ) {
5047						type = 0;
5048					}
5049					if ( ! ( i === high && ignoreLast ) ) {
5050						// Mark the 'type' of this point. 0 = plain, 1 = real value, 2 = step value.
5051						indexes[ newPct.toFixed( 5 ) ] = [ i, type ];
5052					}
5053					// Update the percentage count.
5054					prevPct = newPct;
5055				}
5056			} );
5057			return indexes;
5058		}
5059		function addMarking( spread, filterFunc, formatter ) {
5060			var _a, _b;
5061			var element = scope_Document.createElement( 'div' );
5062			var valueSizeClasses = ( _a = {},
5063			_a[ exports.PipsType.None ] = '',
5064			_a[ exports.PipsType.NoValue ] = options.cssClasses.valueNormal,
5065			_a[ exports.PipsType.LargeValue ] = options.cssClasses.valueLarge,
5066			_a[ exports.PipsType.SmallValue ] = options.cssClasses.valueSub,
5067			_a );
5068			var markerSizeClasses = ( _b = {},
5069			_b[ exports.PipsType.None ] = '',
5070			_b[ exports.PipsType.NoValue ] = options.cssClasses.markerNormal,
5071			_b[ exports.PipsType.LargeValue ] = options.cssClasses.markerLarge,
5072			_b[ exports.PipsType.SmallValue ] = options.cssClasses.markerSub,
5073			_b );
5074			var valueOrientationClasses = [ options.cssClasses.valueHorizontal, options.cssClasses.valueVertical ];
5075			var markerOrientationClasses = [ options.cssClasses.markerHorizontal, options.cssClasses.markerVertical ];
5076			addClass( element, options.cssClasses.pips );
5077			addClass( element, options.ort === 0 ? options.cssClasses.pipsHorizontal : options.cssClasses.pipsVertical );
5078			function getClasses( type, source ) {
5079				var a = source === options.cssClasses.value;
5080				var orientationClasses = a ? valueOrientationClasses : markerOrientationClasses;
5081				var sizeClasses = a ? valueSizeClasses : markerSizeClasses;
5082				return source + ' ' + orientationClasses[ options.ort ] + ' ' + sizeClasses[ type ];
5083			}
5084			function addSpread( key, value, type ) {
5085				var node;
5086				// Apply the filter function, if it is set.
5087				type = filterFunc ? filterFunc( value, type ) : type;
5088				if ( type === exports.PipsType.None ) {
5089					return;
5090				}
5091				// Add a marker for every point
5092				node = addNodeTo( element, false );
5093				node.className = getClasses( type, options.cssClasses.marker );
5094				node.style[ options.style ] = key + '%';
5095				// Values are only appended for points marked '1' or '2'.
5096				if ( type > exports.PipsType.NoValue ) {
5097					node = addNodeTo( element, false );
5098					node.className = getClasses( type, options.cssClasses.value );
5099					node.setAttribute( 'data-value', String( value ) );
5100					node.style[ options.style ] = key + '%';
5101					node.innerHTML = String( formatter.to( value ) );
5102				}
5103			}
5104			// Append all points.
5105			Object.keys( spread ).forEach( function( key ) {
5106				addSpread( key, spread[ key ][ 0 ], spread[ key ][ 1 ] );
5107			} );
5108			return element;
5109		}
5110		function removePips() {
5111			if ( scope_Pips ) {
5112				removeElement( scope_Pips );
5113				scope_Pips = null;
5114			}
5115		}
5116		function pips( ipips ) {
5117			var filter;
5118			var spread;
5119			var format;
5120			// Fix #669
5121			removePips();
5122			spread = generateSpread( ipips );
5123			filter = ipips.filter;
5124			format = ipips.format || {
5125				to: function( value ) {
5126					return String( Math.round( value ) );
5127				}
5128			};
5129			scope_Pips = scope_Target.appendChild( addMarking( spread, filter, format ) );
5130			return scope_Pips;
5131		}
5132		// Shorthand for base dimensions.
5133		function baseSize() {
5134			var rect = scope_Base.getBoundingClientRect();
5135			var alt = ( 'offset' + [ 'Width', 'Height' ][ options.ort ] );
5136			return options.ort === 0 ? rect.width || scope_Base[ alt ] : rect.height || scope_Base[ alt ];
5137		}
5138		// Handler for attaching events trough a proxy.
5139		function attachEvent( events, element, callback, data ) {
5140			// This function can be used to 'filter' events to the slider.
5141			// element is a node, not a nodeList
5142			var method = function( event ) {
5143				var e = fixEvent( event, data.pageOffset, data.target || element );
5144				// fixEvent returns false if this event has a different target
5145				// when handling (multi-) touch events;
5146				if ( ! e ) {
5147					return false;
5148				}
5149				// doNotReject is passed by all end events to make sure released touches
5150				// are not rejected, leaving the slider "stuck" to the cursor;
5151				if ( isSliderDisabled() && ! data.doNotReject ) {
5152					return false;
5153				}
5154				// Stop if an active 'tap' transition is taking place.
5155				if ( hasClass( scope_Target, options.cssClasses.tap ) && ! data.doNotReject ) {
5156					return false;
5157				}
5158				// Ignore right or middle clicks on start #454
5159				if ( events === actions.start && e.buttons !== undefined && e.buttons > 1 ) {
5160					return false;
5161				}
5162				// Ignore right or middle clicks on start #454
5163				if ( data.hover && e.buttons ) {
5164					return false;
5165				}
5166				// 'supportsPassive' is only true if a browser also supports touch-action: none in CSS.
5167				// iOS safari does not, so it doesn't get to benefit from passive scrolling. iOS does support
5168				// touch-action: manipulation, but that allows panning, which breaks
5169				// sliders after zooming/on non-responsive pages.
5170				// See: https://bugs.webkit.org/show_bug.cgi?id=133112
5171				if ( ! supportsPassive ) {
5172					e.preventDefault();
5173				}
5174				e.calcPoint = e.points[ options.ort ];
5175				// Call the event handler with the event [ and additional data ].
5176				callback( e, data );
5177			};
5178			var methods = [];
5179			// Bind a closure on the target for every event type.
5180			events.split( ' ' ).forEach( function( eventName ) {
5181				element.addEventListener( eventName, method, supportsPassive ? { passive: true } : false );
5182				methods.push( [ eventName, method ] );
5183			} );
5184			return methods;
5185		}
5186		// Provide a clean event with standardized offset values.
5187		function fixEvent( e, pageOffset, eventTarget ) {
5188			// Filter the event to register the type, which can be
5189			// touch, mouse or pointer. Offset changes need to be
5190			// made on an event specific basis.
5191			var touch = e.type.indexOf( 'touch' ) === 0;
5192			var mouse = e.type.indexOf( 'mouse' ) === 0;
5193			var pointer = e.type.indexOf( 'pointer' ) === 0;
5194			var x = 0;
5195			var y = 0;
5196			var isTouchOnTarget;
5197			var targetTouches;
5198			var targetTouch;
5199			// IE10 implemented pointer events with a prefix;
5200			if ( e.type.indexOf( 'MSPointer' ) === 0 ) {
5201				pointer = true;
5202			}
5203			// Erroneous events seem to be passed in occasionally on iOS/iPadOS after user finishes interacting with
5204			// the slider. They appear to be of type MouseEvent, yet they don't have usual properties set. Ignore
5205			// events that have no touches or buttons associated with them. (#1057, #1079, #1095)
5206			if ( e.type === 'mousedown' && ! e.buttons && ! e.touches ) {
5207				return false;
5208			}
5209			// The only thing one handle should be concerned about is the touches that originated on top of it.
5210			if ( touch ) {
5211				// Returns true if a touch originated on the target.
5212				isTouchOnTarget = function( checkTouch ) {
5213					return ( checkTouch.target === eventTarget ||
5214                        eventTarget.contains( checkTouch.target ) ||
5215                        ( e.composed && e.composedPath().shift() === eventTarget ) );
5216				};
5217				// In the case of touchstart events, we need to make sure there is still no more than one
5218				// touch on the target so we look amongst all touches.
5219				if ( e.type === 'touchstart' ) {
5220					targetTouches = Array.prototype.filter.call( e.touches, isTouchOnTarget );
5221					// Do not support more than one touch per handle.
5222					if ( targetTouches.length > 1 ) {
5223						return false;
5224					}
5225					x = targetTouches[ 0 ].pageX;
5226					y = targetTouches[ 0 ].pageY;
5227				} else {
5228					// In the other cases, find on changedTouches is enough.
5229					targetTouch = Array.prototype.find.call( e.changedTouches, isTouchOnTarget );
5230					// Cancel if the target touch has not moved.
5231					if ( ! targetTouch ) {
5232						return false;
5233					}
5234					x = targetTouch.pageX;
5235					y = targetTouch.pageY;
5236				}
5237			}
5238			pageOffset = pageOffset || getPageOffset( scope_Document );
5239			if ( mouse || pointer ) {
5240				x = e.clientX + pageOffset.x;
5241				y = e.clientY + pageOffset.y;
5242			}
5243			e.pageOffset = pageOffset;
5244			e.points = [ x, y ];
5245			e.cursor = mouse || pointer; // Fix #435
5246			return e;
5247		}
5248		// Translate a coordinate in the document to a percentage on the slider
5249		function calcPointToPercentage( calcPoint ) {
5250			var location = calcPoint - offset( scope_Base, options.ort );
5251			var proposal = ( location * 100 ) / baseSize();
5252			// Clamp proposal between 0% and 100%
5253			// Out-of-bound coordinates may occur when .noUi-base pseudo-elements
5254			// are used (e.g. contained handles feature)
5255			proposal = limit( proposal );
5256			return options.dir ? 100 - proposal : proposal;
5257		}
5258		// Find handle closest to a certain percentage on the slider
5259		function getClosestHandle( clickedPosition ) {
5260			var smallestDifference = 100;
5261			var handleNumber = false;
5262			scope_Handles.forEach( function( handle, index ) {
5263				var handlePosition;
5264				var differenceWithThisHandle;
5265				var clickAtEdge;
5266				var isCloser;
5267				var isCloserAfter;
5268				// Disabled handles are ignored
5269				if ( isHandleDisabled( index ) ) {
5270					return;
5271				}
5272				handlePosition = scope_Locations[ index ];
5273				differenceWithThisHandle = Math.abs( handlePosition - clickedPosition );
5274				// Initial state
5275				clickAtEdge = differenceWithThisHandle === 100 && smallestDifference === 100;
5276				// Difference with this handle is smaller than the previously checked handle
5277				isCloser = differenceWithThisHandle < smallestDifference;
5278				isCloserAfter = differenceWithThisHandle <= smallestDifference && clickedPosition > handlePosition;
5279				if ( isCloser || isCloserAfter || clickAtEdge ) {
5280					handleNumber = index;
5281					smallestDifference = differenceWithThisHandle;
5282				}
5283			} );
5284			return handleNumber;
5285		}
5286		// Fire 'end' when a mouse or pen leaves the document.
5287		function documentLeave( event, data ) {
5288			if ( event.type === 'mouseout' &&
5289                event.target.nodeName === 'HTML' &&
5290                event.relatedTarget === null ) {
5291				eventEnd( event, data );
5292			}
5293		}
5294		// Handle movement on document for handle and range drag.
5295		function eventMove( event, data ) {
5296			var movement;
5297			var proposal;
5298			// Fix #498
5299			// Check value of .buttons in 'start' to work around a bug in IE10 mobile (data.buttonsProperty).
5300			// https://connect.microsoft.com/IE/feedback/details/927005/mobile-ie10-windows-phone-buttons-property-of-pointermove-event-always-zero
5301			// IE9 has .buttons and .which zero on mousemove.
5302			// Firefox breaks the spec MDN defines.
5303			if ( navigator.appVersion.indexOf( 'MSIE 9' ) === -1 && event.buttons === 0 && data.buttonsProperty !== 0 ) {
5304				return eventEnd( event, data );
5305			}
5306			// Check if we are moving up or down
5307			movement = ( options.dir ? -1 : 1 ) * ( event.calcPoint - data.startCalcPoint );
5308			// Convert the movement into a percentage of the slider width/height
5309			proposal = ( movement * 100 ) / data.baseSize;
5310			moveHandles( movement > 0, proposal, data.locations, data.handleNumbers, data.connect );
5311		}
5312		// Unbind move events on document, call callbacks.
5313		function eventEnd( event, data ) {
5314			// The handle is no longer active, so remove the class.
5315			if ( data.handle ) {
5316				removeClass( data.handle, options.cssClasses.active );
5317				scope_ActiveHandlesCount -= 1;
5318			}
5319			// Unbind the move and end events, which are added on 'start'.
5320			data.listeners.forEach( function( c ) {
5321				scope_DocumentElement.removeEventListener( c[ 0 ], c[ 1 ] );
5322			} );
5323			if ( scope_ActiveHandlesCount === 0 ) {
5324				// Remove dragging class.
5325				removeClass( scope_Target, options.cssClasses.drag );
5326				setZindex();
5327				// Remove cursor styles and text-selection events bound to the body.
5328				if ( event.cursor ) {
5329					scope_Body.style.cursor = '';
5330					scope_Body.removeEventListener( 'selectstart', preventDefault );
5331				}
5332			}
5333			if ( options.events.smoothSteps ) {
5334				data.handleNumbers.forEach( function( handleNumber ) {
5335					setHandle( handleNumber, scope_Locations[ handleNumber ], true, true, false, false );
5336				} );
5337				data.handleNumbers.forEach( function( handleNumber ) {
5338					fireEvent( 'update', handleNumber );
5339				} );
5340			}
5341			data.handleNumbers.forEach( function( handleNumber ) {
5342				fireEvent( 'change', handleNumber );
5343				fireEvent( 'set', handleNumber );
5344				fireEvent( 'end', handleNumber );
5345			} );
5346		}
5347		// Bind move events on document.
5348		function eventStart( event, data ) {
5349			var handle;
5350			var handleOrigin;
5351			var listeners;
5352			var moveEvent;
5353			var endEvent;
5354			var outEvent;
5355			// Ignore event if any handle is disabled
5356			if ( data.handleNumbers.some( isHandleDisabled ) ) {
5357				return;
5358			}
5359			if ( data.handleNumbers.length === 1 ) {
5360				handleOrigin = scope_Handles[ data.handleNumbers[ 0 ] ];
5361				handle = handleOrigin.children[ 0 ];
5362				scope_ActiveHandlesCount += 1;
5363				// Mark the handle as 'active' so it can be styled.
5364				addClass( handle, options.cssClasses.active );
5365			}
5366			// A drag should never propagate up to the 'tap' event.
5367			event.stopPropagation();
5368			// Record the event listeners.
5369			listeners = [];
5370			// Attach the move and end events.
5371			moveEvent = attachEvent( actions.move, scope_DocumentElement, eventMove, {
5372				// The event target has changed so we need to propagate the original one so that we keep
5373				// relying on it to extract target touches.
5374				target: event.target,
5375				handle: handle,
5376				connect: data.connect,
5377				listeners: listeners,
5378				startCalcPoint: event.calcPoint,
5379				baseSize: baseSize(),
5380				pageOffset: event.pageOffset,
5381				handleNumbers: data.handleNumbers,
5382				buttonsProperty: event.buttons,
5383				locations: scope_Locations.slice()
5384			} );
5385			endEvent = attachEvent( actions.end, scope_DocumentElement, eventEnd, {
5386				target: event.target,
5387				handle: handle,
5388				listeners: listeners,
5389				doNotReject: true,
5390				handleNumbers: data.handleNumbers
5391			} );
5392			outEvent = attachEvent( 'mouseout', scope_DocumentElement, documentLeave, {
5393				target: event.target,
5394				handle: handle,
5395				listeners: listeners,
5396				doNotReject: true,
5397				handleNumbers: data.handleNumbers
5398			} );
5399			// We want to make sure we pushed the listeners in the listener list rather than creating
5400			// a new one as it has already been passed to the event handlers.
5401			listeners.push.apply( listeners, moveEvent.concat( endEvent, outEvent ) );
5402			// Text selection isn't an issue on touch devices,
5403			// so adding cursor styles can be skipped.
5404			if ( event.cursor ) {
5405				// Prevent the 'I' cursor and extend the range-drag cursor.
5406				scope_Body.style.cursor = getComputedStyle( event.target ).cursor;
5407				// Mark the target with a dragging state.
5408				if ( scope_Handles.length > 1 ) {
5409					addClass( scope_Target, options.cssClasses.drag );
5410				}
5411				// Prevent text selection when dragging the handles.
5412				// In noUiSlider <= 9.2.0, this was handled by calling preventDefault on mouse/touch start/move,
5413				// which is scroll blocking. The selectstart event is supported by FireFox starting from version 52,
5414				// meaning the only holdout is iOS Safari. This doesn't matter: text selection isn't triggered there.
5415				// The 'cursor' flag is false.
5416				// See: http://caniuse.com/#search=selectstart
5417				scope_Body.addEventListener( 'selectstart', preventDefault, false );
5418			}
5419			data.handleNumbers.forEach( function( handleNumber ) {
5420				fireEvent( 'start', handleNumber );
5421			} );
5422		}
5423		// Move closest handle to tapped location.
5424		function eventTap( event ) {
5425			var proposal;
5426			var handleNumber;
5427			// The tap event shouldn't propagate up
5428			event.stopPropagation();
5429			proposal = calcPointToPercentage( event.calcPoint );
5430			handleNumber = getClosestHandle( proposal );
5431			// Tackle the case that all handles are 'disabled'.
5432			if ( handleNumber === false ) {
5433				return;
5434			}
5435			// Flag the slider as it is now in a transitional state.
5436			// Transition takes a configurable amount of ms (default 300). Re-enable the slider after that.
5437			if ( ! options.events.snap ) {
5438				addClassFor( scope_Target, options.cssClasses.tap, options.animationDuration );
5439			}
5440			setHandle( handleNumber, proposal, true, true );
5441			setZindex();
5442			fireEvent( 'slide', handleNumber, true );
5443			fireEvent( 'update', handleNumber, true );
5444			if ( ! options.events.snap ) {
5445				fireEvent( 'change', handleNumber, true );
5446				fireEvent( 'set', handleNumber, true );
5447			} else {
5448				eventStart( event, { handleNumbers: [ handleNumber ] } );
5449			}
5450		}
5451		// Fires a 'hover' event for a hovered mouse/pen position.
5452		function eventHover( event ) {
5453			var proposal = calcPointToPercentage( event.calcPoint );
5454			var to = scope_Spectrum.getStep( proposal );
5455			var value = scope_Spectrum.fromStepping( to );
5456			Object.keys( scope_Events ).forEach( function( targetEvent ) {
5457				if ( 'hover' === targetEvent.split( '.' )[ 0 ] ) {
5458					scope_Events[ targetEvent ].forEach( function( callback ) {
5459						callback.call( scope_Self, value );
5460					} );
5461				}
5462			} );
5463		}
5464		// Handles keydown on focused handles
5465		// Don't move the document when pressing arrow keys on focused handles
5466		function eventKeydown( event, handleNumber ) {
5467			var horizontalKeys;
5468			var verticalKeys;
5469			var largeStepKeys;
5470			var edgeKeys;
5471			var key;
5472			var isLargeDown;
5473			var isLargeUp;
5474			var isDown;
5475			var isUp;
5476			var isMin;
5477			var isMax;
5478			var to;
5479			var direction;
5480			var steps;
5481			var step;
5482			if ( isSliderDisabled() || isHandleDisabled( handleNumber ) ) {
5483				return false;
5484			}
5485			horizontalKeys = [ 'Left', 'Right' ];
5486			verticalKeys = [ 'Down', 'Up' ];
5487			largeStepKeys = [ 'PageDown', 'PageUp' ];
5488			edgeKeys = [ 'Home', 'End' ];
5489			if ( options.dir && ! options.ort ) {
5490				// On an right-to-left slider, the left and right keys act inverted
5491				horizontalKeys.reverse();
5492			} else if ( options.ort && ! options.dir ) {
5493				// On a top-to-bottom slider, the up and down keys act inverted
5494				verticalKeys.reverse();
5495				largeStepKeys.reverse();
5496			}
5497			// Strip "Arrow" for IE compatibility. https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
5498			key = event.key.replace( 'Arrow', '' );
5499			isLargeDown = key === largeStepKeys[ 0 ];
5500			isLargeUp = key === largeStepKeys[ 1 ];
5501			isDown = key === verticalKeys[ 0 ] || key === horizontalKeys[ 0 ] || isLargeDown;
5502			isUp = key === verticalKeys[ 1 ] || key === horizontalKeys[ 1 ] || isLargeUp;
5503			isMin = key === edgeKeys[ 0 ];
5504			isMax = key === edgeKeys[ 1 ];
5505			if ( ! isDown && ! isUp && ! isMin && ! isMax ) {
5506				return true;
5507			}
5508			event.preventDefault();
5509			if ( isUp || isDown ) {
5510				direction = isDown ? 0 : 1;
5511				steps = getNextStepsForHandle( handleNumber );
5512				step = steps[ direction ];
5513				// At the edge of a slider, do nothing
5514				if ( step === null ) {
5515					return false;
5516				}
5517				// No step set, use the default of 10% of the sub-range
5518				if ( step === false ) {
5519					step = scope_Spectrum.getDefaultStep( scope_Locations[ handleNumber ], isDown, options.keyboardDefaultStep );
5520				}
5521				if ( isLargeUp || isLargeDown ) {
5522					step *= options.keyboardPageMultiplier;
5523				} else {
5524					step *= options.keyboardMultiplier;
5525				}
5526				// Step over zero-length ranges (#948);
5527				step = Math.max( step, 0.0000001 );
5528				// Decrement for down steps
5529				step = ( isDown ? -1 : 1 ) * step;
5530				to = scope_Values[ handleNumber ] + step;
5531			} else if ( isMax ) {
5532				// End key
5533				to = options.spectrum.xVal[ options.spectrum.xVal.length - 1 ];
5534			} else {
5535				// Home key
5536				to = options.spectrum.xVal[ 0 ];
5537			}
5538			setHandle( handleNumber, scope_Spectrum.toStepping( to ), true, true );
5539			fireEvent( 'slide', handleNumber );
5540			fireEvent( 'update', handleNumber );
5541			fireEvent( 'change', handleNumber );
5542			fireEvent( 'set', handleNumber );
5543			return false;
vendor: 6,566 bytes, lines 5544-5707
5544		}
5545		// Attach events to several slider parts.
5546		function bindSliderEvents( behaviour ) {
5547			var handleBefore;
5548			var handleAfter;
5549			var eventHolders;
5550			var handlesToDrag;
5551			var handleNumbersToDrag;
5552			// Attach the standard drag event to the handles.
5553			if ( ! behaviour.fixed ) {
5554				scope_Handles.forEach( function( handle, index ) {
5555					// These events are only bound to the visual handle
5556					// element, not the 'real' origin element.
5557					attachEvent( actions.start, handle.children[ 0 ], eventStart, {
5558						handleNumbers: [ index ]
5559					} );
5560				} );
5561			}
5562			// Attach the tap event to the slider base.
5563			if ( behaviour.tap ) {
5564				attachEvent( actions.start, scope_Base, eventTap, {} );
5565			}
5566			// Fire hover events
5567			if ( behaviour.hover ) {
5568				attachEvent( actions.move, scope_Base, eventHover, {
5569					hover: true
5570				} );
5571			}
5572			// Make the range draggable.
5573			if ( behaviour.drag ) {
5574				scope_Connects.forEach( function( connect, index ) {
5575					if ( connect === false || index === 0 || index === scope_Connects.length - 1 ) {
5576						return;
5577					}
5578					handleBefore = scope_Handles[ index - 1 ];
5579					handleAfter = scope_Handles[ index ];
5580					eventHolders = [ connect ];
5581					handlesToDrag = [ handleBefore, handleAfter ];
5582					handleNumbersToDrag = [ index - 1, index ];
5583					addClass( connect, options.cssClasses.draggable );
5584					// When the range is fixed, the entire range can
5585					// be dragged by the handles. The handle in the first
5586					// origin will propagate the start event upward,
5587					// but it needs to be bound manually on the other.
5588					if ( behaviour.fixed ) {
5589						eventHolders.push( handleBefore.children[ 0 ] );
5590						eventHolders.push( handleAfter.children[ 0 ] );
5591					}
5592					if ( behaviour.dragAll ) {
5593						handlesToDrag = scope_Handles;
5594						handleNumbersToDrag = scope_HandleNumbers;
5595					}
5596					eventHolders.forEach( function( eventHolder ) {
5597						attachEvent( actions.start, eventHolder, eventStart, {
5598							handles: handlesToDrag,
5599							handleNumbers: handleNumbersToDrag,
5600							connect: connect
5601						} );
5602					} );
5603				} );
5604			}
5605		}
5606		// Attach an event to this slider, possibly including a namespace
5607		function bindEvent( namespacedEvent, callback ) {
5608			scope_Events[ namespacedEvent ] = scope_Events[ namespacedEvent ] || [];
5609			scope_Events[ namespacedEvent ].push( callback );
5610			// If the event bound is 'update,' fire it immediately for all handles.
5611			if ( namespacedEvent.split( '.' )[ 0 ] === 'update' ) {
5612				scope_Handles.forEach( function( a, index ) {
5613					fireEvent( 'update', index );
5614				} );
5615			}
5616		}
5617		function isInternalNamespace( namespace ) {
5618			return namespace === INTERNAL_EVENT_NS.aria || namespace === INTERNAL_EVENT_NS.tooltips;
5619		}
5620		// Undo attachment of event
5621		function removeEvent( namespacedEvent ) {
5622			var event = namespacedEvent && namespacedEvent.split( '.' )[ 0 ];
5623			var namespace = event ? namespacedEvent.substring( event.length ) : namespacedEvent;
5624			Object.keys( scope_Events ).forEach( function( bind ) {
5625				var tEvent = bind.split( '.' )[ 0 ];
5626				var tNamespace = bind.substring( tEvent.length );
5627				if ( ( ! event || event === tEvent ) && ( ! namespace || namespace === tNamespace ) ) {
5628					// only delete protected internal event if intentional
5629					if ( ! isInternalNamespace( tNamespace ) || namespace === tNamespace ) {
5630						delete scope_Events[ bind ];
5631					}
5632				}
5633			} );
5634		}
5635		// External event handling
5636		function fireEvent( eventName, handleNumber, tap ) {
5637			Object.keys( scope_Events ).forEach( function( targetEvent ) {
5638				var eventType = targetEvent.split( '.' )[ 0 ];
5639				if ( eventName === eventType ) {
5640					scope_Events[ targetEvent ].forEach( function( callback ) {
5641						callback.call(
5642							// Use the slider public API as the scope ('this')
5643							scope_Self,
5644							// Return values as array, so arg_1[arg_2] is always valid.
5645							scope_Values.map( options.format.to ),
5646							// Handle index, 0 or 1
5647							handleNumber,
5648							// Un-formatted slider values
5649							scope_Values.slice(),
5650							// Event is fired by tap, true or false
5651							tap || false,
5652							// Left offset of the handle, in relation to the slider
5653							scope_Locations.slice(),
5654							// add the slider public API to an accessible parameter when this is unavailable
5655							scope_Self );
5656					} );
5657				}
5658			} );
5659		}
5660		// Split out the handle positioning logic so the Move event can use it, too
5661		function checkHandlePosition( reference, handleNumber, to, lookBackward, lookForward, getValue, smoothSteps ) {
5662			var distance;
5663			// For sliders with multiple handles, limit movement to the other handle.
5664			// Apply the margin option by adding it to the handle positions.
5665			if ( scope_Handles.length > 1 && ! options.events.unconstrained ) {
5666				if ( lookBackward && handleNumber > 0 ) {
5667					distance = scope_Spectrum.getAbsoluteDistance( reference[ handleNumber - 1 ], options.margin, false );
5668					to = Math.max( to, distance );
5669				}
5670				if ( lookForward && handleNumber < scope_Handles.length - 1 ) {
5671					distance = scope_Spectrum.getAbsoluteDistance( reference[ handleNumber + 1 ], options.margin, true );
5672					to = Math.min( to, distance );
5673				}
5674			}
5675			// The limit option has the opposite effect, limiting handles to a
5676			// maximum distance from another. Limit must be > 0, as otherwise
5677			// handles would be unmovable.
5678			if ( scope_Handles.length > 1 && options.limit ) {
5679				if ( lookBackward && handleNumber > 0 ) {
5680					distance = scope_Spectrum.getAbsoluteDistance( reference[ handleNumber - 1 ], options.limit, false );
5681					to = Math.min( to, distance );
5682				}
5683				if ( lookForward && handleNumber < scope_Handles.length - 1 ) {
5684					distance = scope_Spectrum.getAbsoluteDistance( reference[ handleNumber + 1 ], options.limit, true );
5685					to = Math.max( to, distance );
5686				}
5687			}
5688			// The padding option keeps the handles a certain distance from the
5689			// edges of the slider. Padding must be > 0.
5690			if ( options.padding ) {
5691				if ( handleNumber === 0 ) {
5692					distance = scope_Spectrum.getAbsoluteDistance( 0, options.padding[ 0 ], false );
5693					to = Math.max( to, distance );
5694				}
5695				if ( handleNumber === scope_Handles.length - 1 ) {
5696					distance = scope_Spectrum.getAbsoluteDistance( 100, options.padding[ 1 ], true );
5697					to = Math.min( to, distance );
5698				}
5699			}
5700			if ( ! smoothSteps ) {
5701				to = scope_Spectrum.getStep( to );
5702			}
5703			// Limit percentage to the 0 - 100 range
5704			to = limit( to );
5705			// Return false if handle can't move
5706			if ( to === reference[ handleNumber ] && ! getValue ) {
5707				return false;
5708			}
5709			return to;
5710		}
5711		// Uses slider orientation to create CSS rules. a = base value;
5712		function inRuleOrder( v, a ) {
5713			var o = options.ort;
5714			return ( o ? a : v ) + ', ' + ( o ? v : a );
5715		}
5716		// Moves handle(s) by a percentage
5717		// (bool, % to move, [% where handle started, ...], [index in scope_Handles, ...])
5718		function moveHandles( upward, proposal, locations, handleNumbers, connect ) {
5719			var proposals = locations.slice();
5720			// Store first handle now, so we still have it in case handleNumbers is reversed
5721			var firstHandle = handleNumbers[ 0 ];
5722			var smoothSteps = options.events.smoothSteps;
5723			var b = [ ! upward, upward ];
5724			var f = [ upward, ! upward ];
5725			var state = false;
5726			// Copy handleNumbers so we don't change the dataset
5727			handleNumbers = handleNumbers.slice();
5728			// Check to see which handle is 'leading'.
5729			// If that one can't move the second can't either.
5730			if ( upward ) {
5731				handleNumbers.reverse();
5732			}
5733			// Step 1: get the maximum percentage that any of the handles can move
5734			if ( handleNumbers.length > 1 ) {
5735				handleNumbers.forEach( function( handleNumber, o ) {
5736					var to = checkHandlePosition( proposals, handleNumber, proposals[ handleNumber ] + proposal, b[ o ], f[ o ], false, smoothSteps );
5737					// Stop if one of the handles can't move.
5738					if ( to === false ) {
5739						proposal = 0;
5740					} else {
5741						proposal = to - proposals[ handleNumber ];
5742						proposals[ handleNumber ] = to;
5743					}
5744				} );
5745			} else { // If using one handle, check backward AND forward
5746				b = f = [ true ];
5747			}
5748			// Step 2: Try to set the handles with the found percentage
5749			handleNumbers.forEach( function( handleNumber, o ) {
5750				state =
5751                    setHandle( handleNumber, locations[ handleNumber ] + proposal, b[ o ], f[ o ], false, smoothSteps ) || state;
5752			} );
5753			// Step 3: If a handle moved, fire events
5754			if ( state ) {
5755				handleNumbers.forEach( function( handleNumber ) {
5756					fireEvent( 'update', handleNumber );
5757					fireEvent( 'slide', handleNumber );
5758				} );
5759				// If target is a connect, then fire drag event
5760				if ( connect !== undefined ) {
5761					fireEvent( 'drag', firstHandle );
5762				}
5763			}
5764		}
5765		// Takes a base value and an offset. This offset is used for the connect bar size.
5766		// In the initial design for this feature, the origin element was 1% wide.
5767		// Unfortunately, a rounding bug in Chrome makes it impossible to implement this feature
5768		// in this manner: https://bugs.chromium.org/p/chromium/issues/detail?id=798223
5769		function transformDirection( a, b ) {
5770			return options.dir ? 100 - a - b : a;
5771		}
5772		// Updates scope_Locations and scope_Values, updates visual state
5773		function updateHandlePosition( handleNumber, to ) {
5774			var translation;
5775			var translateRule;
5776			// Update locations.
5777			scope_Locations[ handleNumber ] = to;
5778			// Convert the value to the slider stepping/range.
5779			scope_Values[ handleNumber ] = scope_Spectrum.fromStepping( to );
5780			translation = transformDirection( to, 0 ) - scope_DirOffset;
5781			translateRule = 'translate(' + inRuleOrder( translation + '%', '0' ) + ')';
5782			scope_Handles[ handleNumber ].style[ options.transformRule ] = translateRule;
5783			updateConnect( handleNumber );
5784			updateConnect( handleNumber + 1 );
5785		}
5786		// Handles before the slider middle are stacked later = higher,
5787		// Handles after the middle later is lower
5788		// [[7] [8] .......... | .......... [5] [4]
5789		function setZindex() {
5790			scope_HandleNumbers.forEach( function( handleNumber ) {
5791				var dir = scope_Locations[ handleNumber ] > 50 ? -1 : 1;
5792				var zIndex = 3 + ( scope_Handles.length + ( dir * handleNumber ) );
5793				scope_Handles[ handleNumber ].style.zIndex = String( zIndex );
5794			} );
5795		}
5796		// Test suggested values and apply margin, step.
5797		// if exactInput is true, don't run checkHandlePosition, then the handle can be placed in between steps (#436)
5798		function setHandle( handleNumber, to, lookBackward, lookForward, exactInput, smoothSteps ) {
5799			if ( ! exactInput ) {
5800				to = checkHandlePosition( scope_Locations, handleNumber, to, lookBackward, lookForward, false, smoothSteps );
5801			}
5802			if ( to === false ) {
5803				return false;
5804			}
5805			updateHandlePosition( handleNumber, to );
5806			return true;
5807		}
5808		// Updates style attribute for connect nodes
5809		function updateConnect( index ) {
5810			var l;
5811			var h;
5812			var connectWidth;
5813			var translateRule;
5814			var scaleRule;
5815			// Skip connects set to false
5816			if ( ! scope_Connects[ index ] ) {
5817				return;
5818			}
5819			l = 0;
5820			h = 100;
5821			if ( index !== 0 ) {
5822				l = scope_Locations[ index - 1 ];
5823			}
5824			if ( index !== scope_Connects.length - 1 ) {
5825				h = scope_Locations[ index ];
5826			}
5827			// We use two rules:
5828			// 'translate' to change the left/top offset;
5829			// 'scale' to change the width of the element;
5830			// As the element has a width of 100%, a translation of 100% is equal to 100% of the parent (.noUi-base)
5831			connectWidth = h - l;
5832			translateRule = 'translate(' + inRuleOrder( transformDirection( l, connectWidth ) + '%', '0' ) + ')';
5833			scaleRule = 'scale(' + inRuleOrder( connectWidth / 100, '1' ) + ')';
5834			scope_Connects[ index ].style[ options.transformRule ] =
5835                translateRule + ' ' + scaleRule;
5836		}
5837		// Parses value passed to .set method. Returns current value if not parse-able.
5838		function resolveToValue( to, handleNumber ) {
5839			// Setting with null indicates an 'ignore'.
5840			// Inputting 'false' is invalid.
5841			if ( to === null || to === false || to === undefined ) {
5842				return scope_Locations[ handleNumber ];
5843			}
5844			// If a formatted number was passed, attempt to decode it.
5845			if ( typeof to === 'number' ) {
5846				to = String( to );
5847			}
5848			to = options.format.from( to );
5849			if ( to !== false ) {
5850				to = scope_Spectrum.toStepping( to );
5851			}
5852			// If parsing the number failed, use the current value.
5853			if ( to === false || isNaN( to ) ) {
5854				return scope_Locations[ handleNumber ];
5855			}
5856			return to;
5857		}
5858		function scope_HandleNumbersCallback( exactInput ) {
5859			return function( handleNumber ) {
5860				setHandle( handleNumber, scope_Locations[ handleNumber ], true, true, exactInput );
5861			};
5862		}
5863		// Set the slider value.
5864		function valueSet( input, fireSetEvent, exactInput ) {
5865			var values = asArray( input );
5866			var isInit = scope_Locations[ 0 ] === undefined;
5867			var i;
5868			var space_1;
5869			// Event fires by default
5870			fireSetEvent = fireSetEvent === undefined ? true : fireSetEvent;
5871			// Animation is optional.
5872			// Make sure the initial values were set before using animated placement.
5873			if ( options.animate && ! isInit ) {
5874				addClassFor( scope_Target, options.cssClasses.tap, options.animationDuration );
5875			}
5876			// First pass, without lookAhead but with lookBackward. Values are set from left to right.
5877			scope_HandleNumbers.forEach( function( handleNumber ) {
5878				setHandle( handleNumber, resolveToValue( values[ handleNumber ], handleNumber ), true, false, exactInput );
5879			} );
5880			i = scope_HandleNumbers.length === 1 ? 0 : 1;
5881			// Spread handles evenly across the slider if the range has no size (min=max)
5882			if ( isInit && scope_Spectrum.hasNoSize() ) {
5883				exactInput = true;
5884				scope_Locations[ 0 ] = 0;
5885				if ( scope_HandleNumbers.length > 1 ) {
5886					space_1 = 100 / ( scope_HandleNumbers.length - 1 );
5887					scope_HandleNumbers.forEach( function( handleNumber ) {
5888						scope_Locations[ handleNumber ] = handleNumber * space_1;
5889					} );
5890				}
5891			}
5892			// Secondary passes. Now that all base values are set, apply constraints.
5893			// Iterate all handles to ensure constraints are applied for the entire slider (Issue #1009)
5894			for ( ; i < scope_HandleNumbers.length; ++i ) {
5895				scope_HandleNumbers.forEach( scope_HandleNumbersCallback( exactInput ) );
5896			}
5897			setZindex();
5898			scope_HandleNumbers.forEach( function( handleNumber ) {
5899				fireEvent( 'update', handleNumber );
5900				// Fire the event only for handles that received a new value, as per #579
5901				if ( values[ handleNumber ] !== null && fireSetEvent ) {
5902					fireEvent( 'set', handleNumber );
5903				}
5904			} );
5905		}
5906		// Reset slider to initial values
5907		function valueReset( fireSetEvent ) {
5908			valueSet( options.start, fireSetEvent );
5909		}
5910		// Set value for a single handle
5911		function valueSetHandle( handleNumber, value, fireSetEvent, exactInput ) {
5912			// Ensure numeric input
5913			handleNumber = Number( handleNumber );
5914			if ( ! ( handleNumber >= 0 && handleNumber < scope_HandleNumbers.length ) ) {
5915				throw new Error( 'noUiSlider: invalid handle number, got: ' + handleNumber );
5916			}
5917			// Look both backward and forward, since we don't want this handle to "push" other handles (#960);
5918			// The exactInput argument can be used to ignore slider stepping (#436)
5919			setHandle( handleNumber, resolveToValue( value, handleNumber ), true, true, exactInput );
5920			fireEvent( 'update', handleNumber );
5921			if ( fireSetEvent ) {
5922				fireEvent( 'set', handleNumber );
5923			}
5924		}
5925		// Get the slider value.
5926		function valueGet( unencoded ) {
5927			var values;
5928			if ( unencoded === void 0 ) {
5929				unencoded = false;
vendor: 6,568 bytes, lines 5930-6129
5930			}
5931			if ( unencoded ) {
5932				// return a copy of the raw values
5933				return scope_Values.length === 1 ? scope_Values[ 0 ] : scope_Values.slice( 0 );
5934			}
5935			values = scope_Values.map( options.format.to );
5936			// If only one handle is used, return a single value.
5937			if ( values.length === 1 ) {
5938				return values[ 0 ];
5939			}
5940			return values;
5941		}
5942		// Removes classes from the root and empties it.
5943		function destroy() {
5944			// remove protected internal listeners
5945			removeEvent( INTERNAL_EVENT_NS.aria );
5946			removeEvent( INTERNAL_EVENT_NS.tooltips );
5947			Object.keys( options.cssClasses ).forEach( function( key ) {
5948				removeClass( scope_Target, options.cssClasses[ key ] );
5949			} );
5950			while ( scope_Target.firstChild ) {
5951				scope_Target.removeChild( scope_Target.firstChild );
5952			}
5953			delete scope_Target.noUiSlider;
5954		}
5955		function getNextStepsForHandle( handleNumber ) {
5956			var location = scope_Locations[ handleNumber ];
5957			var nearbySteps = scope_Spectrum.getNearbySteps( location );
5958			var value = scope_Values[ handleNumber ];
5959			var increment = nearbySteps.thisStep.step;
5960			var decrement = null;
5961			var stepDecimals;
5962			// If snapped, directly use defined step value
5963			if ( options.snap ) {
5964				return [
5965					value - nearbySteps.stepBefore.startValue || null,
5966					nearbySteps.stepAfter.startValue - value || null
5967				];
5968			}
5969			// If the next value in this step moves into the next step,
5970			// the increment is the start of the next step - the current value
5971			if ( increment !== false ) {
5972				if ( value + increment > nearbySteps.stepAfter.startValue ) {
5973					increment = nearbySteps.stepAfter.startValue - value;
5974				}
5975			}
5976			// If the value is beyond the starting point
5977			if ( value > nearbySteps.thisStep.startValue ) {
5978				decrement = nearbySteps.thisStep.step;
5979			} else if ( nearbySteps.stepBefore.step === false ) {
5980				decrement = false;
5981			} else { // If a handle is at the start of a step, it always steps back into the previous step first
5982				decrement = value - nearbySteps.stepBefore.highestStep;
5983			}
5984			// Now, if at the slider edges, there is no in/decrement
5985			if ( location === 100 ) {
5986				increment = null;
5987			} else if ( location === 0 ) {
5988				decrement = null;
5989			}
5990			// As per #391, the comparison for the decrement step can have some rounding issues.
5991			stepDecimals = scope_Spectrum.countStepDecimals();
5992			// Round per #391
5993			if ( increment !== null && increment !== false ) {
5994				increment = Number( increment.toFixed( stepDecimals ) );
5995			}
5996			if ( decrement !== null && decrement !== false ) {
5997				decrement = Number( decrement.toFixed( stepDecimals ) );
5998			}
5999			return [ decrement, increment ];
6000		}
6001		// Get the current step size for the slider.
6002		function getNextSteps() {
6003			return scope_HandleNumbers.map( getNextStepsForHandle );
6004		}
6005		// Updatable: margin, limit, padding, step, range, animate, snap
6006		function updateOptions( optionsToUpdate, fireSetEvent ) {
6007			// Spectrum is created using the range, snap, direction and step options.
6008			// 'snap' and 'step' can be updated.
6009			// If 'snap' and 'step' are not passed, they should remain unchanged.
6010			var v = valueGet();
6011			var updateAble = [
6012				'margin',
6013				'limit',
6014				'padding',
6015				'range',
6016				'animate',
6017				'snap',
6018				'step',
6019				'format',
6020				'pips',
6021				'tooltips'
6022			];
6023			var newOptions;
6024			// Only change options that we're actually passed to update.
6025			updateAble.forEach( function( name ) {
6026				// Check for undefined. null removes the value.
6027				if ( optionsToUpdate[ name ] !== undefined ) {
6028					originalOptions[ name ] = optionsToUpdate[ name ];
6029				}
6030			} );
6031			newOptions = testOptions( originalOptions );
6032			// Load new options into the slider state
6033			updateAble.forEach( function( name ) {
6034				if ( optionsToUpdate[ name ] !== undefined ) {
6035					options[ name ] = newOptions[ name ];
6036				}
6037			} );
6038			scope_Spectrum = newOptions.spectrum;
6039			// Limit, margin and padding depend on the spectrum but are stored outside of it. (#677)
6040			options.margin = newOptions.margin;
6041			options.limit = newOptions.limit;
6042			options.padding = newOptions.padding;
6043			// Update pips, removes existing.
6044			if ( options.pips ) {
6045				pips( options.pips );
6046			} else {
6047				removePips();
6048			}
6049			// Update tooltips, removes existing.
6050			if ( options.tooltips ) {
6051				tooltips();
6052			} else {
6053				removeTooltips();
6054			}
6055			// Invalidate the current positioning so valueSet forces an update.
6056			scope_Locations = [];
6057			valueSet( isSet( optionsToUpdate.start ) ? optionsToUpdate.start : v, fireSetEvent );
6058		}
6059		// Initialization steps
6060		function setupSlider() {
6061			// Create the base element, initialize HTML and set classes.
6062			// Add handles and connect elements.
6063			scope_Base = addSlider( scope_Target );
6064			addElements( options.connect, scope_Base );
6065			// Attach user events.
6066			bindSliderEvents( options.events );
6067			// Use the public value method to set the start values.
6068			valueSet( options.start );
6069			if ( options.pips ) {
6070				pips( options.pips );
6071			}
6072			if ( options.tooltips ) {
6073				tooltips();
6074			}
6075			aria();
6076		}
6077		setupSlider();
6078		scope_Self = {
6079			destroy: destroy,
6080			steps: getNextSteps,
6081			on: bindEvent,
6082			off: removeEvent,
6083			get: valueGet,
6084			set: valueSet,
6085			setHandle: valueSetHandle,
6086			reset: valueReset,
6087			disable: disable,
6088			enable: enable,
6089			// Exposed for unit testing, don't use this in your application.
6090			__moveHandles: function( upward, proposal, handleNumbers ) {
6091				moveHandles( upward, proposal, scope_Locations, handleNumbers );
6092			},
6093			options: originalOptions,
6094			updateOptions: updateOptions,
6095			target: scope_Target,
6096			removePips: removePips,
6097			removeTooltips: removeTooltips,
6098			getPositions: function() {
6099				return scope_Locations.slice();
6100			},
6101			getTooltips: function() {
6102				return scope_Tooltips;
6103			},
6104			getOrigins: function() {
6105				return scope_Handles;
6106			},
6107			pips: pips // Issue #594
6108		};
6109		return scope_Self;
6110	}
6111	// Run the standard initializer
6112	function initialize( target, originalOptions ) {
6113		var options;
6114		var api;
6115		if ( ! target || ! target.nodeName ) {
6116			throw new Error( 'noUiSlider: create requires a single element, got: ' + target );
6117		}
6118		// Throw an error if the slider was already initialized.
6119		if ( target.noUiSlider ) {
6120			throw new Error( 'noUiSlider: Slider was already initialized.' );
6121		}
6122		// Test the options and create the slider environment;
6123		options = testOptions( originalOptions );
6124		api = scope( target, options, originalOptions );
6125		target.noUiSlider = api;
6126		return api;
6127	}
6128	nouislider = {
6129		// Exposed for unit testing, don't use this in your application.
6130		__spectrum: Spectrum,
6131		// A reference to the default classes, allows global changes.
6132		// Use the cssClasses option for changes to one slider.
6133		cssClasses: cssClasses,
6134		create: initialize
6135	};
6136
6137	exports.create = initialize;
6138	exports.cssClasses = cssClasses;
6139	exports.default = nouislider;
6140
6141	Object.defineProperty( exports, '__esModule', { value: true } );
6142} ) ) );
6143;
6144/**
6145 * jquery.resizestop (and resizestart)
6146 * by: Fatih Kadir Akın
6147 * https://github.com/f/jquery.resizestop
6148 * License is CC0, published to the public domain.
6149 * @param {jQuery} $
6150 */
6151// Used by datepicker when position is top or bottom of the screen
6152( function( $ ) {
6153	'use strict';
6154	// Slice shortcut
6155	var slice = Array.prototype.slice;
6156
6157	// Special event definition
6158	$.extend( $.event.special, {
6159		// resize stop special event.
6160		resizestop: {
6161			add: function( handle ) {
6162				// shortcut to the event handler.
6163				var handler = handle.handler;
6164
6165				// event modifying
6166				$( this ).on( 'resize', function( e ) {
6167					var _proxy;
6168					// every resize event clears handler's timer.
6169					// and every handler must have a timer.
6170					clearTimeout( handler._timer );
6171					// change event type text.
6172					e.type = 'resizestop';
6173					// push the "resize" event to the handler.
6174					_proxy = $.proxy( handler, this, e );
6175					// if no resize event fired for a time that we decide,
6176					// then it means its stopped.
6177					handler._timer = setTimeout( _proxy, handle.data || 200 );
6178				} );
6179			}
6180		},
6181		// resize start special event
6182		resizestart: {
6183			add: function( handle ) {
6184				// shortcut to the event handler.
6185				var handler = handle.handler;
6186
6187				// event modifying
6188				$( this ).on( 'resize', function( e ) {
6189					// every resize event clears handler's timer.
6190					// and every handler must have a timer.
6191					clearTimeout( handler._timer );
6192					// we suddenly fire the event, then we can put
6193					// a flag with name _started knows if it's already fired.
6194					if ( ! handler._started ) {
6195						e.type = 'resizestart';
6196						handler.apply( this, arguments );
6197						// after firing the handler, put the flag with the value "true"
6198						handler._started = true;
6199					}
6200					handler._timer = setTimeout(
6201						$.proxy( function() {
6202							// after a while, it will make the flag false.
6203							handler._started = false;
6204						}, this ),
6205						handle.data || 300
6206					);
6207				} );
6208			}
6209		}
6210	} );
6211	// binding and currying the shortcuts.
6212	$.extend( $.fn, {
6213		// $(window).resizestop instead of $(window).on('resizestop')
6214		resizestop: function() {
6215			// will push the "resizestop" argument at the beginning of arguments
6216			$( this ).on.apply( this, [ 'resizestop' ].concat( slice.call( arguments ) ) );
6217		},
6218		// $(window).resizestart instead of $(window).on('resizestart')
6219		resizestart: function() {
6220			// will push the "resizestart" argument at the beginning of arguments
6221			$( this ).on.apply( this, [ 'resizestart' ].concat( slice.call( arguments ) ) );
6222		}
6223	} );
6224}( window.jQuery ) );
6225;
6226/*! jQuery UI - v1.12.1 - 2019-02-17
6227 * http://jqueryui.com
6228 * Modified version of jQuery Datepicker
6229 * Copyright jQuery Foundation and other contributors; Licensed MIT */
6230
6231( function( factory ) {
6232	'use strict';
6233	if ( typeof window.define === 'function' && window.define.amd ) {
6234		// AMD. Register as an anonymous module.
6235		window.define( [ 'jquery' ], factory );
6236	} else {
6237		// Browser globals
6238		factory( window.jQuery );
6239	}
6240}( function( $ ) {
6241	'use strict';
6242	/*!
6243	 * jQuery UI Datepicker 1.12.1 (Modified version of jQuery Datepicker)
6244	 * http://jqueryui.com
6245	 *
6246	 * Copyright jQuery Foundation and other contributors
6247	 * Released under the MIT license.
6248	 * http://jquery.org/license
6249	 *
6250	 * http://api.jqueryui.com/datepicker/
6251	 */
6252
6253	var datepicker_instActive;
6254
6255	$.ui.tm_datepicker = $.ui.tm_datepicker || {};
6256	if ( $.ui.tm_datepicker.version ) {
6257		return;
6258	}
6259
6260	$.extend( $.ui, { tm_datepicker: { version: '1.11.4' } } );
6261
6262	function datepicker_getZindex( elem ) {
6263		var position, value;
6264		while ( elem.length && elem[ 0 ] !== document ) {
6265			// Ignore z-index if position is set to a value where z-index is ignored by the browser
6266			// This makes behavior of this function consistent across browsers
6267			// WebKit always returns auto if the element is positioned
6268			position = elem.css( 'position' );
6269			if ( position === 'absolute' || position === 'relative' || position === 'fixed' ) {
6270				// IE returns 0 when zIndex is not specified
6271				// other browsers return a string
6272				// we ignore the case of nested elements with an explicit value of 0
6273				value = parseInt( elem.css( 'zIndex' ), 10 );
6274				if ( ! isNaN( value ) && value !== 0 ) {
6275					return value;
6276				}
6277			}
6278			elem = elem.parent();
6279		}
6280
6281		return 0;
6282	}
6283
6284	/*
6285	 * Bind hover events for datepicker elements.
6286	 * Done via delegate so the binding only occurs once in the lifetime of the parent div.
6287	 * Global datepicker_instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
6288	 */
6289	function datepicker_handleMouseover( that ) {
6290		if ( ! $.tm_datepicker._isDisabledDatepicker( datepicker_instActive.inline ? datepicker_instActive.dpDiv.parent()[ 0 ] : datepicker_instActive.input[ 0 ] ) ) {
6291			$( that ).parents( '.ui-tm-datepicker-calendar' ).find( 'a' ).removeClass( 'ui-state-hover' );
6292			$( that ).addClass( 'ui-state-hover' );
6293			if ( that.className.indexOf( 'ui-tm-datepicker-prev' ) !== -1 ) {
6294				$( that ).addClass( 'ui-tm-datepicker-prev-hover' );
6295			}
6296			if ( that.className.indexOf( 'ui-tm-datepicker-next' ) !== -1 ) {
6297				$( that ).addClass( 'ui-tm-datepicker-next-hover' );
6298			}
6299		}
6300	}
6301
6302	function datepicker_bindHover( dpDiv ) {
6303		var selector = 'button, .ui-tm-datepicker-prev, .ui-tm-datepicker-next, .ui-tm-datepicker-calendar td a';
6304		return dpDiv
6305			.on( 'mouseout', selector, function() {
6306				$( this ).removeClass( 'ui-state-hover' );
6307				if ( this.className.indexOf( 'ui-tm-datepicker-prev' ) !== -1 ) {
6308					$( this ).removeClass( 'ui-tm-datepicker-prev-hover' );
6309				}
6310				if ( this.className.indexOf( 'ui-tm-datepicker-next' ) !== -1 ) {
6311					$( this ).removeClass( 'ui-tm-datepicker-next-hover' );
6312				}
6313			} )
6314			.on( 'mouseover', selector, function() {
6315				datepicker_handleMouseover( this );
6316			} );
6317	}
6318
6319	/* jQuery extend now ignores nulls! */
6320	function datepicker_extendRemove( target, props ) {
6321		var name;
6322
6323		$.extend( target, props );
6324		for ( name in props ) {
6325			if ( props[ name ] === null ) {
6326				target[ name ] = props[ name ];
6327			}
6328		}
6329		return target;
6330	}
6331
6332	/* Date picker manager.
6333     Use the singleton instance of this class, $.tm_datepicker, to interact with the date picker.
6334     Settings for (groups of) date pickers are maintained in an instance object,
6335     allowing multiple different settings on the same page. */
6336
6337	function Datepicker() {
6338		this._curInst = null; // The current instance in use
6339		this._keyEvent = false; // If the last event was a key event
6340		this._disabledInputs = []; // List of date picker inputs that have been disabled
6341		this._datepickerShowing = false; // True if the popup picker is showing , false if not
6342		this._inDialog = false; // True if showing within a "dialog", false if not
6343		this._mainDivId = 'ui-tm-datepicker-div'; // The ID of the main datepicker division
6344		this._inlineClass = 'ui-tm-datepicker-inline'; // The name of the inline marker class
6345		this._appendClass = 'ui-tm-datepicker-append'; // The name of the append marker class
6346		this._triggerClass = 'ui-tm-datepicker-trigger'; // The name of the trigger marker class
6347		this._dialogClass = 'ui-tm-datepicker-dialog'; // The name of the dialog marker class
6348		this._disableClass = 'ui-tm-datepicker-disabled'; // The name of the disabled covering marker class
6349		this._unselectableClass = 'ui-tm-datepicker-unselectable'; // The name of the unselectable cell marker class
6350		this._currentClass = 'ui-tm-datepicker-current-day'; // The name of the current day marker class
6351		this._dayOverClass = 'ui-tm-datepicker-days-cell-over'; // The name of the day hover marker class
6352		this.regional = []; // Available regional settings, indexed by language code
6353		this.regional[ '' ] = {
6354			// Default regional settings
6355			closeText: 'Done', // Display text for close link
6356			prevText: 'Prev', // Display text for previous month link
6357			nextText: 'Next', // Display text for next month link
6358			currentText: 'Today', // Display text for current month link
6359			monthNames: [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ], // Names of months for drop-down and formatting
6360			monthNamesShort: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ], // For formatting
6361			dayNames: [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ], // For formatting
6362			dayNamesShort: [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ], // For formatting
6363			dayNamesMin: [ 'Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa' ], // Column headings for days starting at Sunday
6364			weekHeader: 'Wk', // Column header for week of the year
6365			dateFormat: 'mm/dd/yy', // See format options on parseDate
6366			firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
6367			isRTL: false, // True if right-to-left language, false if left-to-right
6368			showMonthAfterYear: false, // True if the year select precedes month, false for month then year
6369			yearSuffix: '' // Additional text to append to the year in the month headers
6370		};
6371		this._defaults = {
6372			// Global defaults for all the date picker instances
6373			showOn: 'focus', // "focus" for popup on focus,
6374			// "button" for trigger button, or "both" for either
vendor: 10,028 bytes, lines 6375-6607
6375			showAnim: 'fadeIn', // Name of jQuery animation for popup
6376			showOptions: {}, // Options for enhanced animations
6377			defaultDate: null, // Used when field is blank: actual date,
6378			// +/-number for offset from today, null for today
6379			appendText: '', // Display text following the input box, e.g. showing the format
6380			buttonText: '...', // Text for trigger button
6381			buttonImage: '', // URL for trigger button image
6382			buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
6383			hideIfNoPrevNext: false, // True to hide next/previous month links
6384			// if not applicable, false to just disable them
6385			navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
6386			gotoCurrent: false, // True if today link goes back to current selection instead
6387			changeMonth: false, // True if month can be selected directly, false if only prev/next
6388			changeYear: false, // True if year can be selected directly, false if only prev/next
6389			yearRange: 'c-10:c+10', // Range of years to display in drop-down,
6390			// either relative to today's year (-nn:+nn), relative to currently displayed year
6391			// (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
6392			showOtherMonths: false, // True to show dates in other months, false to leave blank
6393			selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
6394			showWeek: false, // True to show week of the year, false to not show it
6395			calculateWeek: this.iso8601Week, // How to calculate the week of the year,
6396			// takes a Date and returns the number of the week for it
6397			shortYearCutoff: '+10', // Short year values < this are in the current century,
6398			// > this are in the previous century,
6399			// string value starting with "+" for current year + value
6400			minDate: null, // The earliest selectable date, or null for no limit
6401			maxDate: null, // The latest selectable date, or null for no limit
6402			duration: 'fast', // Duration of display/closure
6403			beforeShowDay: null, // Function that takes a date and returns an array with
6404			// [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "",
6405			// [2] = cell title (optional), e.g. $.tm_datepicker.noWeekends
6406			beforeShow: null, // Function that takes an input field and
6407			// returns a set of custom settings for the date picker
6408			onSelect: null, // Define a callback function when a date is selected
6409			onChangeMonthYear: null, // Define a callback function when the month or year is changed
6410			onClose: null, // Define a callback function when the datepicker is closed
6411			numberOfMonths: 1, // Number of months to show at a time
6412			showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
6413			stepMonths: 1, // Number of months to step back/forward
6414			stepBigMonths: 12, // Number of months to step back/forward for the big links
6415			altField: '', // Selector for an alternate field to store selected dates into
6416			altFormat: '', // The date format to use for the alternate field
6417			constrainInput: true, // The input is constrained by the current date format
6418			showButtonPanel: false, // True to show button panel, false to not show it
6419			autoSize: false, // True to size the input for the date format, false to leave as is
6420			disabled: false // The initial disabled state
6421		};
6422		$.extend( this._defaults, this.regional[ '' ] );
6423		this.regional.en = $.extend( true, {}, this.regional[ '' ] );
6424		this.regional[ 'en-US' ] = $.extend( true, {}, this.regional.en );
6425		this.dpDiv = datepicker_bindHover( $( "<div id='" + this._mainDivId + "' class='ui-tm-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>" ) );
6426	}
6427
6428	$.extend( Datepicker.prototype, {
6429		/* Class name added to elements to indicate already configured with a date picker. */
6430		markerClassName: 'hasDatepicker',
6431
6432		//Keep track of the maximum number of rows displayed (see #7043)
6433		maxRows: 4,
6434
6435		// TODO rename to "widget" when switching to widget factory
6436		_widgetDatepicker: function() {
6437			return this.dpDiv;
6438		},
6439
6440		/* Override the default settings for all instances of the date picker.
6441		 * @param  settings  object - the new settings to use as defaults (anonymous object)
6442		 * @return the manager object
6443		 */
6444		setDefaults: function( settings ) {
6445			datepicker_extendRemove( this._defaults, settings || {} );
6446			return this;
6447		},
6448
6449		/* Attach the date picker to a jQuery selection.
6450		 * @param  target   element - the target input field or division or span
6451		 * @param  settings  object - the new settings to use for this date picker instance (anonymous)
6452		 */
6453		_attachDatepicker: function( target, settings ) {
6454			var nodeName, inline, inst;
6455			nodeName = target.nodeName.toLowerCase();
6456			inline = nodeName === 'div' || nodeName === 'span';
6457			if ( ! target.id ) {
6458				this.uuid += 1;
6459				target.id = 'dp' + this.uuid;
6460			}
6461			inst = this._newInst( $( target ), inline );
6462			inst.settings = $.extend( {}, settings || {} );
6463			if ( nodeName === 'input' ) {
6464				this._connectDatepicker( target, inst );
6465			} else if ( inline ) {
6466				this._inlineDatepicker( target, inst );
6467			}
6468		},
6469
6470		/* Create a new instance object. */
6471		_newInst: function( target, inline ) {
6472			var id = target[ 0 ].id.replace( /([^A-Za-z0-9_-])/g, '\\\\$1' ); // escape jQuery meta chars
6473			return {
6474				id: id,
6475				input: target, // associated target
6476				selectedDay: 0,
6477				selectedMonth: 0,
6478				selectedYear: 0, // current selection
6479				drawMonth: 0,
6480				drawYear: 0, // month being drawn
6481				inline: inline, // is datepicker inline or not
6482				// presentation div
6483				dpDiv: ! inline ? this.dpDiv : datepicker_bindHover( $( "<div class='" + this._inlineClass + " ui-tm-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>" ) )
6484			};
6485		},
6486
6487		/* Attach the date picker to an input field. */
6488		_connectDatepicker: function( target, inst ) {
6489			var input = $( target );
6490			inst.append = $( [] );
6491			inst.trigger = $( [] );
6492			if ( input.hasClass( this.markerClassName ) ) {
6493				return;
6494			}
6495			this._attachments( input, inst );
6496			input.addClass( this.markerClassName ).on( 'keydown', this._doKeyDown ).on( 'keypress', this._doKeyPress ).on( 'keyup', this._doKeyUp );
6497			this._autoSize( inst );
6498			$.data( target, 'datepicker', inst );
6499
6500			//If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
6501			if ( inst.settings.disabled ) {
6502				this._disableDatepicker( target );
6503			}
6504		},
6505
6506		/* Make attachments based on settings. */
6507		_attachments: function( input, inst ) {
6508			var showOn,
6509				buttonText,
6510				buttonImage,
6511				appendText = this._get( inst, 'appendText' ),
6512				isRTL = this._get( inst, 'isRTL' );
6513
6514			if ( inst.append ) {
6515				inst.append.remove();
6516			}
6517			if ( appendText ) {
6518				inst.append = $( "<span class='" + this._appendClass + "'>" + appendText + '</span>' );
6519				input[ isRTL ? 'before' : 'after' ]( inst.append );
6520			}
6521
6522			input.off( 'focus', this._showDatepicker );
6523
6524			if ( inst.trigger ) {
6525				inst.trigger.remove();
6526			}
6527
6528			showOn = this._get( inst, 'showOn' );
6529			if ( showOn === 'focus' || showOn === 'both' ) {
6530				// pop-up date picker when in the marked field
6531				input.on( 'focus', this._showDatepicker );
6532			}
6533			if ( showOn === 'button' || showOn === 'both' ) {
6534				// pop-up date picker when button clicked
6535				buttonText = this._get( inst, 'buttonText' );
6536				buttonImage = this._get( inst, 'buttonImage' );
6537				inst.trigger = $(
6538					this._get( inst, 'buttonImageOnly' )
6539						? $( '<img/>' ).addClass( this._triggerClass ).attr( { src: buttonImage, alt: buttonText, title: buttonText } )
6540						: $( "<button type='button'></button>" )
6541							.addClass( this._triggerClass )
6542							.html( ! buttonImage ? buttonText : $( '<img/>' ).attr( { src: buttonImage, alt: buttonText, title: buttonText } ) )
6543				);
6544				input[ isRTL ? 'before' : 'after' ]( inst.trigger );
6545				inst.trigger.on( 'click', function() {
6546					if ( $.tm_datepicker._datepickerShowing && $.tm_datepicker._lastInput === input[ 0 ] ) {
6547						$.tm_datepicker._hideDatepicker();
6548					} else if ( $.tm_datepicker._datepickerShowing && $.tm_datepicker._lastInput !== input[ 0 ] ) {
6549						$.tm_datepicker._hideDatepicker();
6550						$.tm_datepicker._showDatepicker( input[ 0 ] );
6551					} else {
6552						$.tm_datepicker._showDatepicker( input[ 0 ] );
6553					}
6554					return false;
6555				} );
6556			}
6557		},
6558
6559		/* Apply the maximum length for the date format. */
6560		_autoSize: function( inst ) {
6561			var findMax;
6562			var max;
6563			var maxI;
6564			var i;
6565			var date;
6566			var dateFormat = this._get( inst, 'dateFormat' );
6567
6568			if ( this._get( inst, 'autoSize' ) && ! inst.inline ) {
6569				date = new Date( 2009, 12 - 1, 20 ); // Ensure double digits
6570				dateFormat = this._get( inst, 'dateFormat' );
6571
6572				if ( dateFormat.match( /[DM]/ ) ) {
6573					findMax = function( names ) {
6574						max = 0;
6575						maxI = 0;
6576						for ( i = 0; i < names.length; i += 1 ) {
6577							if ( names[ i ].length > max ) {
6578								max = names[ i ].length;
6579								maxI = i;
6580							}
6581						}
6582						return maxI;
6583					};
6584					date.setMonth( findMax( this._get( inst, dateFormat.match( /MM/ ) ? 'monthNames' : 'monthNamesShort' ) ) );
6585					date.setDate( findMax( this._get( inst, dateFormat.match( /DD/ ) ? 'dayNames' : 'dayNamesShort' ) ) + 20 - date.getDay() );
6586				}
6587				inst.input.attr( 'size', this._formatDate( inst, date ).length );
6588			}
6589		},
6590
6591		/* Attach an inline date picker to a div. */
6592		_inlineDatepicker: function( target, inst ) {
6593			var divSpan = $( target );
6594			if ( divSpan.hasClass( this.markerClassName ) ) {
6595				return;
6596			}
6597			divSpan.addClass( this.markerClassName ).append( inst.dpDiv );
6598			$.data( target, 'datepicker', inst );
6599			this._setDate( inst, this._getDefaultDate( inst ), true );
6600			this._updateDatepicker( inst );
6601			this._updateAlternate( inst );
6602
6603			//If disabled option is true, disable the datepicker before showing it (see ticket #5665)
6604			if ( inst.settings.disabled ) {
6605				this._disableDatepicker( target );
6606			}
6607
6608			// Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
6609			// http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
6610			inst.dpDiv.css( 'display', 'block' );
6611		},
6612
6613		/* Pop-up the date picker in a "dialog" box.
6614		 * @param  input element - ignored
6615		 * @param  date string or Date - the initial date to display
6616		 * @param  onSelect  function - the function to call when a date is selected
6617		 * @param  settings  object - update the dialog date picker instance's settings (anonymous object)
6618		 * @param  pos int[2] - coordinates for the dialog's position within the screen or
6619		 *                  event - with x/y coordinates or
6620		 *                  leave empty for default (screen centre)
6621		 * @return the manager object
6622		 */
6623		_dialogDatepicker: function( input, date, onSelect, settings, pos ) {
6624			var id,
6625				browserWidth,
6626				browserHeight,
6627				scrollX,
6628				scrollY,
6629				inst = this._dialogInst; // internal instance
6630
6631			if ( ! inst ) {
6632				this.uuid += 1;
6633				id = 'dp' + this.uuid;
6634				this._dialogInput = $( "<input type='text' id='" + id + "'>" );
6635				this._dialogInput.on( 'keydown', this._doKeyDown );
6636				$( 'body' ).append( this._dialogInput );
6637				inst = this._newInst( this._dialogInput, false );
6638				this._dialogInst = inst;
6639				inst.settings = {};
6640				$.data( this._dialogInput[ 0 ], 'datepicker', inst );
6641			}
6642			datepicker_extendRemove( inst.settings, settings || {} );
6643			date = date && date.constructor === Date ? this._formatDate( inst, date ) : date;
6644			this._dialogInput.val( date );
6645
6646			this._pos = pos ? ( pos.length ? pos : [ pos.pageX, pos.pageY ] ) : null;
6647			if ( ! this._pos ) {
6648				browserWidth = document.documentElement.clientWidth;
6649				browserHeight = document.documentElement.clientHeight;
6650				scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
6651				scrollY = document.documentElement.scrollTop || document.body.scrollTop;
6652				this._pos =
6653					// should use actual width/height below
6654					[ ( browserWidth / 2 ) - 100 + scrollX, ( browserHeight / 2 ) - 150 + scrollY ];
6655			}
6656
6657			// Move input on screen for focus, but hidden behind dialog
6658			this._dialogInput.css( 'left', this._pos[ 0 ] + 20 + 'px' ).css( 'top', this._pos[ 1 ] + 'px' );
6659			inst.settings.onSelect = onSelect;
6660			this._inDialog = true;
6661			this.dpDiv.addClass( this._dialogClass );
6662			this._showDatepicker( this._dialogInput[ 0 ] );
6663			if ( $.blockUI ) {
6664				$.blockUI( this.dpDiv );
6665			}
6666			$.data( this._dialogInput[ 0 ], 'datepicker', inst );
6667			return this;
6668		},
6669
6670		/* Detach a datepicker from its control.
6671		 * @param  target   element - the target input field or division or span
6672		 */
6673		_destroyDatepicker: function( target ) {
6674			var nodeName,
6675				$target = $( target ),
6676				inst = $.data( target, 'datepicker' );
6677
6678			if ( ! $target.hasClass( this.markerClassName ) ) {
6679				return;
6680			}
6681
6682			nodeName = target.nodeName.toLowerCase();
6683			$.removeData( target, 'datepicker' );
6684			if ( nodeName === 'input' ) {
vendor: 11,989 bytes, lines 6685-7028
6685				inst.append.remove();
6686				inst.trigger.remove();
6687				$target.removeClass( this.markerClassName ).off( 'focus', this._showDatepicker ).off( 'keydown', this._doKeyDown ).off( 'keypress', this._doKeyPress ).off( 'keyup', this._doKeyUp );
6688			} else if ( nodeName === 'div' || nodeName === 'span' ) {
6689				$target.removeClass( this.markerClassName ).empty();
6690			}
6691
6692			if ( datepicker_instActive === inst ) {
6693				datepicker_instActive = null;
6694			}
6695		},
6696
6697		/* Enable the date picker to a jQuery selection.
6698		 * @param  target   element - the target input field or division or span
6699		 */
6700		_enableDatepicker: function( target ) {
6701			var nodeName,
6702				inline,
6703				$target = $( target ),
6704				inst = $.data( target, 'datepicker' );
6705
6706			if ( ! $target.hasClass( this.markerClassName ) ) {
6707				return;
6708			}
6709
6710			nodeName = target.nodeName.toLowerCase();
6711			if ( nodeName === 'input' ) {
6712				target.disabled = false;
6713				inst.trigger
6714					.filter( 'button' )
6715					.each( function() {
6716						this.disabled = false;
6717					} )
6718					.end()
6719					.filter( 'img' )
6720					.css( { opacity: '1.0', cursor: '' } );
6721			} else if ( nodeName === 'div' || nodeName === 'span' ) {
6722				inline = $target.children( '.' + this._inlineClass );
6723				inline.children().removeClass( 'ui-state-disabled' );
6724				inline.find( 'select.ui-tm-datepicker-month, select.ui-tm-datepicker-year' ).prop( 'disabled', false );
6725			}
6726			this._disabledInputs = $.map( this._disabledInputs, function( value ) {
6727				return value === target ? null : value;
6728			} ); // delete entry
6729		},
6730
6731		/* Disable the date picker to a jQuery selection.
6732		 * @param  target   element - the target input field or division or span
6733		 */
6734		_disableDatepicker: function( target ) {
6735			var nodeName,
6736				inline,
6737				$target = $( target ),
6738				inst = $.data( target, 'datepicker' );
6739
6740			if ( ! $target.hasClass( this.markerClassName ) ) {
6741				return;
6742			}
6743
6744			nodeName = target.nodeName.toLowerCase();
6745			if ( nodeName === 'input' ) {
6746				target.disabled = true;
6747				inst.trigger
6748					.filter( 'button' )
6749					.each( function() {
6750						this.disabled = true;
6751					} )
6752					.end()
6753					.filter( 'img' )
6754					.css( { opacity: '0.5', cursor: 'default' } );
6755			} else if ( nodeName === 'div' || nodeName === 'span' ) {
6756				inline = $target.children( '.' + this._inlineClass );
6757				inline.children().addClass( 'ui-state-disabled' );
6758				inline.find( 'select.ui-tm-datepicker-month, select.ui-tm-datepicker-year' ).prop( 'disabled', true );
6759			}
6760			this._disabledInputs = $.map( this._disabledInputs, function( value ) {
6761				return value === target ? null : value;
6762			} ); // delete entry
6763			this._disabledInputs[ this._disabledInputs.length ] = target;
6764		},
6765
6766		/* Is the first field in a jQuery collection disabled as a datepicker?
6767		 * @param  target   element - the target input field or division or span
6768		 * @return boolean - true if disabled, false if enabled
6769		 */
6770		_isDisabledDatepicker: function( target ) {
6771			var i;
6772			if ( ! target ) {
6773				return false;
6774			}
6775			for ( i = 0; i < this._disabledInputs.length; i += 1 ) {
6776				if ( this._disabledInputs[ i ] === target ) {
6777					return true;
6778				}
6779			}
6780			return false;
6781		},
6782
6783		/* Retrieve the instance data for the target control.
6784		 * @param  target  element - the target input field or division or span
6785		 * @return  object - the associated instance data
6786		 * @throws  error if a jQuery problem getting data
6787		 */
6788		_getInst: function( target ) {
6789			try {
6790				return $.data( target, 'datepicker' );
6791			} catch ( err ) {
6792				throw 'Missing instance data for this datepicker';
6793			}
6794		},
6795
6796		/* Update or retrieve the settings for a date picker attached to an input field or division.
6797		 * @param  target  element - the target input field or division or span
6798		 * @param  name object - the new settings to update or
6799		 *              string - the name of the setting to change or retrieve,
6800		 *              when retrieving also "all" for all instance settings or
6801		 *              "defaults" for all global defaults
6802		 * @param  value   any - the new value for the setting
6803		 *              (omit if above is an object or to retrieve a value)
6804		 */
6805		_optionDatepicker: function( target, name, value ) {
6806			var settings,
6807				date,
6808				minDate,
6809				maxDate,
6810				inst = this._getInst( target );
6811
6812			if ( arguments.length === 2 && typeof name === 'string' ) {
6813				return name === 'defaults' ? $.extend( {}, $.tm_datepicker._defaults ) : inst ? ( name === 'all' ? $.extend( {}, inst.settings ) : this._get( inst, name ) ) : null;
6814			}
6815
6816			settings = name || {};
6817			if ( typeof name === 'string' ) {
6818				settings = {};
6819				settings[ name ] = value;
6820			}
6821
6822			if ( inst ) {
6823				if ( this._curInst === inst ) {
6824					this._hideDatepicker();
6825				}
6826
6827				date = this._getDateDatepicker( target, true );
6828				minDate = this._getMinMaxDate( inst, 'min' );
6829				maxDate = this._getMinMaxDate( inst, 'max' );
6830				datepicker_extendRemove( inst.settings, settings );
6831
6832				// reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
6833				if ( minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined ) {
6834					inst.settings.minDate = this._formatDate( inst, minDate );
6835				}
6836				if ( maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined ) {
6837					inst.settings.maxDate = this._formatDate( inst, maxDate );
6838				}
6839				if ( 'disabled' in settings ) {
6840					if ( settings.disabled ) {
6841						this._disableDatepicker( target );
6842					} else {
6843						this._enableDatepicker( target );
6844					}
6845				}
6846				this._attachments( $( target ), inst );
6847				this._autoSize( inst );
6848				this._setDate( inst, date );
6849				this._updateAlternate( inst );
6850				this._updateDatepicker( inst );
6851			}
6852		},
6853
6854		// Change method deprecated
6855		_changeDatepicker: function( target, name, value ) {
6856			this._optionDatepicker( target, name, value );
6857		},
6858
6859		/* Redraw the date picker attached to an input field or division.
6860		 * @param  target  element - the target input field or division or span
6861		 */
6862		_refreshDatepicker: function( target ) {
6863			var inst = this._getInst( target );
6864			if ( inst ) {
6865				this._updateDatepicker( inst );
6866			}
6867		},
6868
6869		/* Set the dates for a jQuery selection.
6870		 * @param  target element - the target input field or division or span
6871		 * @param  date Date - the new date
6872		 */
6873		_setDateDatepicker: function( target, date ) {
6874			var inst = this._getInst( target );
6875			if ( inst ) {
6876				this._setDate( inst, date );
6877				this._updateDatepicker( inst );
6878				this._updateAlternate( inst );
6879			}
6880		},
6881
6882		/* Get the date(s) for the first entry in a jQuery selection.
6883		 * @param  target element - the target input field or division or span
6884		 * @param  noDefault boolean - true if no default date is to be used
6885		 * @return Date - the current date
6886		 */
6887		_getDateDatepicker: function( target, noDefault ) {
6888			var inst = this._getInst( target );
6889			if ( inst && ! inst.inline ) {
6890				this._setDateFromField( inst, noDefault );
6891			}
6892			return inst ? this._getDate( inst ) : null;
6893		},
6894
6895		/* Handle keystrokes. */
6896		_doKeyDown: function( event ) {
6897			var onSelect,
6898				dateStr,
6899				sel,
6900				inst = $.tm_datepicker._getInst( event.target ),
6901				handled = true,
6902				isRTL = inst.dpDiv.is( '.ui-tm-datepicker-rtl' );
6903
6904			inst._keyEvent = true;
6905			if ( $.tm_datepicker._datepickerShowing ) {
6906				switch ( event.keyCode ) {
6907					case 9:
6908						$.tm_datepicker._hideDatepicker();
6909						handled = false;
6910						break; // hide on tab out
6911					case 13:
6912						sel = $( 'td.' + $.tm_datepicker._dayOverClass + ':not(.' + $.tm_datepicker._currentClass + ')', inst.dpDiv );
6913						if ( sel[ 0 ] ) {
6914							$.tm_datepicker._selectDay( event.target, inst.selectedMonth, inst.selectedYear, sel[ 0 ] );
6915						}
6916
6917						onSelect = $.tm_datepicker._get( inst, 'onSelect' );
6918						if ( onSelect ) {
6919							dateStr = $.tm_datepicker._formatDate( inst );
6920
6921							// Trigger custom callback
6922							onSelect.apply( inst.input ? inst.input[ 0 ] : null, [ dateStr, inst ] );
6923						} else {
6924							$.tm_datepicker._hideDatepicker();
6925						}
6926
6927						return false; // don't submit the form
6928					case 27:
6929						$.tm_datepicker._hideDatepicker();
6930						break; // hide on escape
6931					case 33:
6932						$.tm_datepicker._adjustDate( event.target, event.ctrlKey ? -$.tm_datepicker._get( inst, 'stepBigMonths' ) : -$.tm_datepicker._get( inst, 'stepMonths' ), 'M' );
6933						break; // previous month/year on page up/+ ctrl
6934					case 34:
6935						$.tm_datepicker._adjustDate( event.target, event.ctrlKey ? +$.tm_datepicker._get( inst, 'stepBigMonths' ) : +$.tm_datepicker._get( inst, 'stepMonths' ), 'M' );
6936						break; // next month/year on page down/+ ctrl
6937					case 35:
6938						if ( event.ctrlKey || event.metaKey ) {
6939							$.tm_datepicker._clearDate( event.target );
6940						}
6941						handled = event.ctrlKey || event.metaKey;
6942						break; // clear on ctrl or command +end
6943					case 36:
6944						if ( event.ctrlKey || event.metaKey ) {
6945							$.tm_datepicker._gotoToday( event.target );
6946						}
6947						handled = event.ctrlKey || event.metaKey;
6948						break; // current on ctrl or command +home
6949					case 37:
6950						if ( event.ctrlKey || event.metaKey ) {
6951							$.tm_datepicker._adjustDate( event.target, isRTL ? +1 : -1, 'D' );
6952						}
6953						handled = event.ctrlKey || event.metaKey;
6954						// -1 day on ctrl or command +left
6955						if ( event.originalEvent.altKey ) {
6956							$.tm_datepicker._adjustDate( event.target, event.ctrlKey ? -$.tm_datepicker._get( inst, 'stepBigMonths' ) : -$.tm_datepicker._get( inst, 'stepMonths' ), 'M' );
6957						}
6958						// next month/year on alt +left on Mac
6959						break;
6960					case 38:
6961						if ( event.ctrlKey || event.metaKey ) {
6962							$.tm_datepicker._adjustDate( event.target, -7, 'D' );
6963						}
6964						handled = event.ctrlKey || event.metaKey;
6965						break; // -1 week on ctrl or command +up
6966					case 39:
6967						if ( event.ctrlKey || event.metaKey ) {
6968							$.tm_datepicker._adjustDate( event.target, isRTL ? -1 : +1, 'D' );
6969						}
6970						handled = event.ctrlKey || event.metaKey;
6971						// +1 day on ctrl or command +right
6972						if ( event.originalEvent.altKey ) {
6973							$.tm_datepicker._adjustDate( event.target, event.ctrlKey ? +$.tm_datepicker._get( inst, 'stepBigMonths' ) : +$.tm_datepicker._get( inst, 'stepMonths' ), 'M' );
6974						}
6975						// next month/year on alt +right
6976						break;
6977					case 40:
6978						if ( event.ctrlKey || event.metaKey ) {
6979							$.tm_datepicker._adjustDate( event.target, +7, 'D' );
6980						}
6981						handled = event.ctrlKey || event.metaKey;
6982						break; // +1 week on ctrl or command +down
6983					default:
6984						handled = false;
6985				}
6986			} else if ( event.keyCode === 36 && event.ctrlKey ) {
6987				// display the date picker on ctrl+home
6988				$.tm_datepicker._showDatepicker( this );
6989			} else {
6990				handled = false;
6991			}
6992
6993			if ( handled ) {
6994				event.preventDefault();
6995				event.stopPropagation();
6996			}
6997		},
6998
6999		/* Filter entered characters - based on date format. */
7000		_doKeyPress: function( event ) {
7001			var chars,
7002				chr,
7003				inst = $.tm_datepicker._getInst( event.target );
7004
7005			if ( $.tm_datepicker._get( inst, 'constrainInput' ) ) {
7006				chars = $.tm_datepicker._possibleChars( $.tm_datepicker._get( inst, 'dateFormat' ) );
7007				chr = String.fromCharCode( event.charCode === null ? event.keyCode : event.charCode );
7008				return event.ctrlKey || event.metaKey || chr < ' ' || ! chars || chars.indexOf( chr ) > -1;
7009			}
7010		},
7011
7012		/* Synchronise manual entry and field/alternate field. */
7013		_doKeyUp: function( event ) {
7014			var date,
7015				inst = $.tm_datepicker._getInst( event.target );
7016
7017			if ( inst.input.val() !== inst.lastVal ) {
7018				try {
7019					date = $.tm_datepicker.parseDate( $.tm_datepicker._get( inst, 'dateFormat' ), inst.input ? inst.input.val() : null, $.tm_datepicker._getFormatConfig( inst ) );
7020
7021					if ( date ) {
7022						// only if valid
7023						$.tm_datepicker._setDateFromField( inst );
7024						$.tm_datepicker._updateAlternate( inst );
7025						$.tm_datepicker._updateDatepicker( inst );
7026					}
7027				} catch ( err ) {
7028					return false;
vendor: 4,862 bytes, lines 7029-7170
7029				}
7030			}
7031			return true;
7032		},
7033
7034		/* Pop-up the date picker for a given input field.
7035		 * If false returned from beforeShow event handler do not show.
7036		 * @param  input  element - the input field attached to the date picker or
7037		 *                  event - if triggered by focus
7038		 */
7039		_showDatepicker: function( input ) {
7040			var inst;
7041			var beforeShow;
7042			var beforeShowSettings;
7043			var isFixed;
7044			var offset;
7045			var showAnim;
7046			var duration;
7047
7048			input = input.target || input;
7049			if ( input.nodeName.toLowerCase() !== 'input' ) {
7050				// find from button/image trigger
7051				input = $( 'input', input.parentNode )[ 0 ];
7052			}
7053
7054			if ( $.tm_datepicker._isDisabledDatepicker( input ) || $.tm_datepicker._lastInput === input ) {
7055				// already here
7056				return;
7057			}
7058
7059			inst = $.tm_datepicker._getInst( input );
7060			if ( $.tm_datepicker._curInst && $.tm_datepicker._curInst !== inst ) {
7061				$.tm_datepicker._curInst.dpDiv.stop( true, true );
7062				if ( inst && $.tm_datepicker._datepickerShowing ) {
7063					$.tm_datepicker._hideDatepicker( $.tm_datepicker._curInst.input[ 0 ] );
7064				}
7065			}
7066
7067			beforeShow = $.tm_datepicker._get( inst, 'beforeShow' );
7068			beforeShowSettings = beforeShow ? beforeShow.apply( input, [ input, inst ] ) : {};
7069			if ( beforeShowSettings === false ) {
7070				return;
7071			}
7072			datepicker_extendRemove( inst.settings, beforeShowSettings );
7073
7074			inst.lastVal = null;
7075			$.tm_datepicker._lastInput = input;
7076			$.tm_datepicker._setDateFromField( inst );
7077
7078			if ( $.tm_datepicker._inDialog ) {
7079				// hide cursor
7080				input.value = '';
7081			}
7082			if ( ! $.tm_datepicker._pos ) {
7083				// position below input
7084				$.tm_datepicker._pos = $.tm_datepicker._findPos( input );
7085				$.tm_datepicker._pos[ 1 ] += input.offsetHeight; // add the height
7086			}
7087
7088			isFixed = false;
7089			$( input )
7090				.parents()
7091				.each( function() {
7092					isFixed = isFixed || $( this ).css( 'position' ) === 'fixed';
7093					return ! isFixed;
7094				} );
7095
7096			offset = { left: $.tm_datepicker._pos[ 0 ], top: $.tm_datepicker._pos[ 1 ] };
7097			$.tm_datepicker._pos = null;
7098			//to avoid flashes on Firefox
7099			inst.dpDiv.empty();
7100			// determine sizing offscreen
7101			inst.dpDiv.css( { position: 'absolute', display: 'block', top: '-1000px' } );
7102			$.tm_datepicker._updateDatepicker( inst );
7103			// fix width for dynamic number of date pickers
7104			// and adjust position before showing
7105			offset = $.tm_datepicker._checkOffset( inst, offset, isFixed );
7106			inst.dpDiv.css( {
7107				position: $.tm_datepicker._inDialog && $.blockUI ? 'static' : isFixed ? 'fixed' : 'absolute',
7108				display: 'none',
7109				left: offset.left + 'px',
7110				top: offset.top + 'px'
7111			} );
7112
7113			if ( ! inst.inline ) {
7114				showAnim = $.tm_datepicker._get( inst, 'showAnim' );
7115				duration = $.tm_datepicker._get( inst, 'duration' );
7116				inst.dpDiv.css( 'z-index', datepicker_getZindex( $( input ) ) + 1 );
7117				$.tm_datepicker._datepickerShowing = true;
7118
7119				if ( $.effects && $.effects.effect[ showAnim ] ) {
7120					inst.dpDiv.show( showAnim, $.tm_datepicker._get( inst, 'showOptions' ), duration );
7121				} else {
7122					inst.dpDiv[ showAnim || 'show' ]( showAnim ? duration : null );
7123				}
7124
7125				if ( $.tm_datepicker._shouldFocusInput( inst ) ) {
7126					inst.input.trigger( 'focus' );
7127				}
7128
7129				$.tm_datepicker._curInst = inst;
7130			}
7131		},
7132
7133		/* Generate the date picker content. */
7134		_updateDatepicker: function( inst ) {
7135			var origyearshtml;
7136			var numMonths;
7137			var cols;
7138			var width = 17;
7139			var activeCell;
7140
7141			this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
7142			datepicker_instActive = inst; // for delegate hover events
7143			inst.dpDiv.empty().append( this._generateHTML( inst ) );
7144			this._attachHandlers( inst );
7145
7146			numMonths = this._getNumberOfMonths( inst );
7147			cols = numMonths[ 1 ];
7148			activeCell = inst.dpDiv.find( '.' + this._dayOverClass + ' a' );
7149
7150			if ( activeCell.length > 0 ) {
7151				datepicker_handleMouseover.apply( activeCell.get( 0 ), [ activeCell.get( 0 ) ] );
7152			}
7153
7154			inst.dpDiv.removeClass( 'ui-tm-datepicker-multi-2 ui-tm-datepicker-multi-3 ui-tm-datepicker-multi-4' ).width( '' );
7155			if ( cols > 1 ) {
7156				inst.dpDiv.addClass( 'ui-tm-datepicker-multi-' + cols ).css( 'width', ( width * cols ) + 'em' );
7157			}
7158			inst.dpDiv[ ( numMonths[ 0 ] !== 1 || numMonths[ 1 ] !== 1 ? 'add' : 'remove' ) + 'Class' ]( 'ui-tm-datepicker-multi' );
7159			inst.dpDiv[ ( this._get( inst, 'isRTL' ) ? 'add' : 'remove' ) + 'Class' ]( 'ui-tm-datepicker-rtl' );
7160
7161			if ( inst === $.tm_datepicker._curInst && $.tm_datepicker._datepickerShowing && $.tm_datepicker._shouldFocusInput( inst ) ) {
7162				inst.input.trigger( 'focus' );
7163			}
7164
7165			// deffered render of the years select (to avoid flashes on Firefox)
7166			if ( inst.yearshtml ) {
7167				origyearshtml = inst.yearshtml;
7168				setTimeout( function() {
7169					//assure that inst.yearshtml didn't change.
7170					if ( origyearshtml === inst.yearshtml && inst.yearshtml ) {
vendor: 9,356 bytes, lines 7171-7444
7171						inst.dpDiv.find( 'select.ui-tm-datepicker-year:first' ).replaceWith( inst.yearshtml );
7172					}
7173					origyearshtml = null;
7174					inst.yearshtml = null;
7175				}, 0 );
7176			}
7177		},
7178
7179		// #6694 - don't focus the input if it's already focused
7180		// this breaks the change event in IE
7181		// Support: IE and jQuery <1.9
7182		_shouldFocusInput: function( inst ) {
7183			return inst.input && inst.input.is( ':visible' ) && ! inst.input.is( ':disabled' ) && ! inst.input.is( ':focus' );
7184		},
7185
7186		/* Check positioning to remain on screen. */
7187		_checkOffset: function( inst, offset, isFixed ) {
7188			var dpWidth = inst.dpDiv.outerWidth(),
7189				dpHeight = inst.dpDiv.outerHeight(),
7190				inputWidth = inst.input ? inst.input.outerWidth() : 0,
7191				inputHeight = inst.input ? inst.input.outerHeight() : 0,
7192				viewWidth = document.documentElement.clientWidth + ( isFixed ? 0 : $( document ).scrollLeft() ),
7193				viewHeight = document.documentElement.clientHeight + ( isFixed ? 0 : $( document ).scrollTop() );
7194
7195			offset.left -= this._get( inst, 'isRTL' ) ? dpWidth - inputWidth : 0;
7196			offset.left -= isFixed && offset.left === inst.input.offset().left ? $( document ).scrollLeft() : 0;
7197			offset.top -= isFixed && offset.top === inst.input.offset().top + inputHeight ? $( document ).scrollTop() : 0;
7198
7199			// Now check if datepicker is showing outside window viewport - move to a better place if so.
7200			offset.left -= Math.min( offset.left, offset.left + dpWidth > viewWidth && viewWidth > dpWidth ? Math.abs( offset.left + dpWidth - viewWidth ) : 0 );
7201			offset.top -= Math.min( offset.top, offset.top + dpHeight > viewHeight && viewHeight > dpHeight ? Math.abs( dpHeight + inputHeight ) : 0 );
7202
7203			return offset;
7204		},
7205
7206		/* Find an object's position on the screen. */
7207		_findPos: function( obj ) {
7208			var position,
7209				inst = this._getInst( obj ),
7210				isRTL = this._get( inst, 'isRTL' );
7211
7212			while ( obj && ( obj.type === 'hidden' || obj.nodeType !== 1 || $.expr.pseudos.hidden( obj ) ) ) {
7213				obj = obj[ isRTL ? 'previousSibling' : 'nextSibling' ];
7214			}
7215
7216			position = $( obj ).offset();
7217			return [ position.left, position.top ];
7218		},
7219
7220		/* Hide the date picker from view.
7221		 * @param  input  element - the input field attached to the date picker
7222		 */
7223		_hideDatepicker: function( input ) {
7224			var showAnim,
7225				duration,
7226				postProcess,
7227				onClose,
7228				inst = this._curInst;
7229
7230			if ( ! inst || ( input && inst !== $.data( input, 'datepicker' ) ) ) {
7231				return;
7232			}
7233
7234			if ( this._datepickerShowing ) {
7235				showAnim = this._get( inst, 'showAnim' );
7236				duration = this._get( inst, 'duration' );
7237				postProcess = function() {
7238					$.tm_datepicker._tidyDialog( inst );
7239				};
7240
7241				// DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
7242				if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) {
7243					inst.dpDiv.hide( showAnim, $.tm_datepicker._get( inst, 'showOptions' ), duration, postProcess );
7244				} else {
7245					inst.dpDiv[ showAnim === 'slideDown' ? 'slideUp' : showAnim === 'fadeIn' ? 'fadeOut' : 'hide' ]( showAnim ? duration : null, postProcess );
7246				}
7247
7248				if ( ! showAnim ) {
7249					postProcess();
7250				}
7251				this._datepickerShowing = false;
7252
7253				onClose = this._get( inst, 'onClose' );
7254				if ( onClose ) {
7255					onClose.apply( inst.input ? inst.input[ 0 ] : null, [ inst.input ? inst.input.val() : '', inst ] );
7256				}
7257
7258				this._lastInput = null;
7259				if ( this._inDialog ) {
7260					this._dialogInput.css( { position: 'absolute', left: '0', top: '-100px' } );
7261					if ( $.blockUI ) {
7262						$.unblockUI();
7263						$( 'body' ).append( this.dpDiv );
7264					}
7265				}
7266				this._inDialog = false;
7267			}
7268		},
7269
7270		/* Tidy up after a dialog display. */
7271		_tidyDialog: function( inst ) {
7272			inst.dpDiv.removeClass( this._dialogClass ).off( '.ui-tm-datepicker-calendar' );
7273		},
7274
7275		/* Close date picker if clicked elsewhere. */
7276		_checkExternalClick: function( event ) {
7277			var $target;
7278			var inst;
7279
7280			if ( ! $.tm_datepicker._curInst ) {
7281				return;
7282			}
7283
7284			$target = $( event.target );
7285			inst = $.tm_datepicker._getInst( $target[ 0 ] );
7286
7287			if ( ( $target[ 0 ].id !== $.tm_datepicker._mainDivId && $target.parents( '#' + $.tm_datepicker._mainDivId ).length === 0 && ! $target.hasClass( $.tm_datepicker.markerClassName ) && ! $target.closest( '.' + $.tm_datepicker._triggerClass ).length && $.tm_datepicker._datepickerShowing && ! ( $.tm_datepicker._inDialog && $.blockUI ) ) || ( $target.hasClass( $.tm_datepicker.markerClassName ) && $.tm_datepicker._curInst !== inst ) ) {
7288				$.tm_datepicker._hideDatepicker();
7289			}
7290		},
7291
7292		/* Adjust one of the date sub-fields. */
7293		_adjustDate: function( id, offset, period ) {
7294			var target = $( id ),
7295				inst = this._getInst( target[ 0 ] );
7296
7297			if ( this._isDisabledDatepicker( target[ 0 ] ) ) {
7298				return;
7299			}
7300			this._adjustInstDate(
7301				inst,
7302				offset + ( period === 'M' ? this._get( inst, 'showCurrentAtPos' ) : 0 ), // undo positioning
7303				period
7304			);
7305			this._updateDatepicker( inst );
7306		},
7307
7308		/* Action for current link. */
7309		_gotoToday: function( id ) {
7310			var date,
7311				target = $( id ),
7312				inst = this._getInst( target[ 0 ] );
7313
7314			if ( this._get( inst, 'gotoCurrent' ) && inst.currentDay ) {
7315				inst.selectedDay = inst.currentDay;
7316				inst.drawMonth = inst.currentMonth;
7317				inst.selectedMonth = inst.currentMonth;
7318				inst.drawYear = inst.currentYear;
7319				inst.selectedYear = inst.currentYear;
7320			} else {
7321				date = new Date();
7322				inst.selectedDay = date.getDate();
7323				inst.drawMonth = date.getMonth();
7324				inst.selectedMonth = inst.drawMonth;
7325				inst.drawYear = date.getFullYear();
7326				inst.selectedYear = inst.drawYear;
7327			}
7328			this._notifyChange( inst );
7329			this._adjustDate( target );
7330		},
7331
7332		/* Action for selecting a new month/year. */
7333		_selectMonthYear: function( id, select, period ) {
7334			var target = $( id ),
7335				inst = this._getInst( target[ 0 ] );
7336
7337			inst[ 'selected' + ( period === 'M' ? 'Month' : 'Year' ) ] = parseInt( select.options[ select.selectedIndex ].value, 10 );
7338			inst[ 'draw' + ( period === 'M' ? 'Month' : 'Year' ) ] = parseInt( select.options[ select.selectedIndex ].value, 10 );
7339
7340			this._notifyChange( inst );
7341			this._adjustDate( target );
7342		},
7343
7344		/* Action for selecting a day. */
7345		_selectDay: function( id, month, year, td ) {
7346			var inst,
7347				target = $( id );
7348
7349			if ( $( td ).hasClass( this._unselectableClass ) || this._isDisabledDatepicker( target[ 0 ] ) ) {
7350				return;
7351			}
7352
7353			inst = this._getInst( target[ 0 ] );
7354			inst.selectedDay = $( 'a', td ).html();
7355			inst.currentDay = inst.selectedDay;
7356			inst.selectedMonth = month;
7357			inst.currentMonth = month;
7358			inst.selectedYear = year;
7359			inst.currentYear = year;
7360			this._selectDate( id, this._formatDate( inst, inst.currentDay, inst.currentMonth, inst.currentYear ) );
7361		},
7362
7363		/* Erase the input field and hide the date picker. */
7364		_clearDate: function( id ) {
7365			var target = $( id );
7366			this._selectDate( target, '' );
7367		},
7368
7369		/* Update the input field with the selected date. */
7370		_selectDate: function( id, dateStr ) {
7371			var onSelect,
7372				target = $( id ),
7373				inst = this._getInst( target[ 0 ] );
7374
7375			dateStr = dateStr !== null ? dateStr : this._formatDate( inst );
7376			if ( inst.input ) {
7377				inst.input.val( dateStr );
7378			}
7379			this._updateAlternate( inst );
7380
7381			onSelect = this._get( inst, 'onSelect' );
7382			if ( onSelect ) {
7383				onSelect.apply( inst.input ? inst.input[ 0 ] : null, [ dateStr, inst ] ); // trigger custom callback
7384			} else if ( inst.input ) {
7385				inst.input.trigger( 'change' ); // fire the change event
7386			}
7387
7388			if ( inst.inline ) {
7389				this._updateDatepicker( inst );
7390			} else {
7391				this._hideDatepicker();
7392				this._lastInput = inst.input[ 0 ];
7393				if ( typeof inst.input[ 0 ] !== 'object' ) {
7394					inst.input.trigger( 'focus' ); // restore focus
7395				}
7396				this._lastInput = null;
7397			}
7398		},
7399
7400		/* Update any alternate field to synchronise with the main field. */
7401		_updateAlternate: function( inst ) {
7402			var altFormat,
7403				date,
7404				dateStr,
7405				altField = this._get( inst, 'altField' );
7406
7407			if ( altField ) {
7408				// update alternate field too
7409				altFormat = this._get( inst, 'altFormat' ) || this._get( inst, 'dateFormat' );
7410				date = this._getDate( inst );
7411				dateStr = this.formatDate( altFormat, date, this._getFormatConfig( inst ) );
7412				$( altField ).val( dateStr );
7413			}
7414		},
7415
7416		/* Set as beforeShowDay function to prevent selection of weekends.
7417		 * @param  date  Date - the date to customise
7418		 * @return [boolean, string] - is this date selectable?, what is its CSS class?
7419		 */
7420		noWeekends: function( date ) {
7421			var day = date.getDay();
7422			return [ day > 0 && day < 6, '' ];
7423		},
7424
7425		/* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
7426		 * @param  date  Date - the date to get the week for
7427		 * @return  number - the number of the week within the year that contains this date
7428		 */
7429		iso8601Week: function( date ) {
7430			var time,
7431				checkDate = new Date( date.getTime() );
7432
7433			// Find Thursday of this week starting on Monday
7434			checkDate.setDate( checkDate.getDate() + 4 - ( checkDate.getDay() || 7 ) );
7435
7436			time = checkDate.getTime();
7437			checkDate.setMonth( 0 ); // Compare with Jan 1
7438			checkDate.setDate( 1 );
7439			return Math.floor( Math.round( ( time - checkDate ) / 86400000 ) / 7 ) + 1;
7440		},
7441
7442		/* Parse a string value into a date object.
7443		 * See formatDate below for the possible formats.
7444		 *
7445		 * @param  format string - the expected format of the date
7446		 * @param  value string - the date in the above format
7447		 * @param  settings Object - attributes include:
7448		 *                  shortYearCutoff  number - the cutoff year for determining the century (optional)
7449		 *                  dayNamesShort   string[7] - abbreviated names of the days from Sunday (optional)
7450		 *                  dayNames        string[7] - names of the days from Sunday (optional)
7451		 *                  monthNamesShort string[12] - abbreviated names of the months (optional)
7452		 *                  monthNames      string[12] - names of the months (optional)
7453		 * @return  Date - the extracted date value or null if value is blank
7454		 */
7455		parseDate: function( format, value, settings ) {
7456			var iFormat;
7457			var dim;
7458			var extra;
7459			var iValue = 0;
7460			var shortYearCutoffTemp;
7461			var shortYearCutoff;
7462			var dayNamesShort;
7463			var dayNames;
7464			var monthNamesShort;
7465			var monthNames;
7466			var year = -1;
7467			var month = -1;
7468			var day = -1;
7469			var doy = -1;
7470			var literal = false;
7471			var date;
7472			var lookAhead;
7473			var getNumber;
7474			var getName;
7475			var checkLiteral;
7476			var condition;
7477
7478			if ( format === null || value === null ) {
7479				throw 'Invalid arguments';
7480			}
7481
7482			value = typeof value === 'object' ? value.toString() : value + '';
7483			if ( value === '' ) {
7484				return null;
7485			}
7486
7487			shortYearCutoffTemp = ( settings ? settings.shortYearCutoff : null ) || this._defaults.shortYearCutoff;
7488			shortYearCutoff = typeof shortYearCutoffTemp !== 'string' ? shortYearCutoffTemp : ( new Date().getFullYear() % 100 ) + parseInt( shortYearCutoffTemp, 10 );
7489			dayNamesShort = ( settings ? settings.dayNamesShort : null ) || this._defaults.dayNamesShort;
7490			dayNames = ( settings ? settings.dayNames : null ) || this._defaults.dayNames;
7491			monthNamesShort = ( settings ? settings.monthNamesShort : null ) || this._defaults.monthNamesShort;
7492			monthNames = ( settings ? settings.monthNames : null ) || this._defaults.monthNames;
7493
7494			// Check whether a format character is doubled
7495			lookAhead = function( match ) {
7496				var matches = iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match;
7497				if ( matches ) {
7498					iFormat = iFormat + 1;
7499				}
7500				return matches;
7501			};
7502
7503			// Extract a number from the string value
7504			getNumber = function( match ) {
7505				var isDoubled = lookAhead( match ),
7506					size = match === '@' ? 14 : match === '!' ? 20 : match === 'y' && isDoubled ? 4 : match === 'o' ? 3 : 2,
7507					minSize = match === 'y' ? size : 1,
7508					digits = new RegExp( '^\\d{' + minSize + ',' + size + '}' ),
7509					num = value.substring( iValue ).match( digits );
7510				if ( ! num ) {
7511					throw 'Missing number at position ' + iValue;
7512				}
7513				iValue += num[ 0 ].length;
7514				return parseInt( num[ 0 ], 10 );
7515			};
7516
7517			// Extract a name from the string value and convert to an index
7518			getName = function( match, shortNames, longNames ) {
7519				var index = -1,
7520					names = $.map( lookAhead( match ) ? longNames : shortNames, function( v, k ) {
7521						return [ [ k, v ] ];
7522					} ).sort( function( a, b ) {
7523						return -( a[ 1 ].length - b[ 1 ].length );
7524					} );
7525
7526				$.each( names, function( i, pair ) {
7527					var name = pair[ 1 ];
7528					if ( value.substring( iValue, parseInt( iValue, 10 ) + name.length ).toLowerCase() === name.toLowerCase() ) {
7529						index = pair[ 0 ];
7530						iValue += name.length;
7531						return false;
7532					}
7533				} );
7534				if ( index !== -1 ) {
7535					return index + 1;
7536				}
7537				throw 'Unknown name at position ' + iValue;
7538			};
7539
7540			// Confirm that a literal character matches the string value
7541			checkLiteral = function() {
7542				if ( value.charAt( iValue ) !== format.charAt( iFormat ) ) {
7543					throw 'Unexpected literal at position ' + iValue;
7544				}
7545				iValue = iValue + 1;
7546			};
7547
7548			for ( iFormat = 0; iFormat < format.length; iFormat += 1 ) {
7549				if ( literal ) {
7550					if ( format.charAt( iFormat ) === "'" && ! lookAhead( "'" ) ) {
7551						literal = false;
7552					} else {
7553						checkLiteral();
7554					}
7555				} else {
7556					switch ( format.charAt( iFormat ) ) {
7557						case 'd':
7558							day = getNumber( 'd' );
7559							break;
7560						case 'D':
7561							getName( 'D', dayNamesShort, dayNames );
7562							break;
7563						case 'o':
7564							doy = getNumber( 'o' );
7565							break;
7566						case 'm':
7567							month = getNumber( 'm' );
7568							break;
7569						case 'M':
7570							month = getName( 'M', monthNamesShort, monthNames );
7571							break;
7572						case 'y':
7573							year = getNumber( 'y' );
7574							break;
7575						case '@':
7576							date = new Date( getNumber( '@' ) );
7577							year = date.getFullYear();
7578							month = date.getMonth() + 1;
7579							day = date.getDate();
7580							break;
7581						case '!':
7582							date = new Date( ( getNumber( '!' ) - this._ticksTo1970 ) / 10000 );
7583							year = date.getFullYear();
7584							month = date.getMonth() + 1;
7585							day = date.getDate();
7586							break;
7587						case "'":
7588							if ( lookAhead( "'" ) ) {
7589								checkLiteral();
7590							} else {
7591								literal = true;
7592							}
7593							break;
7594						default:
7595							checkLiteral();
7596					}
7597				}
7598			}
7599
7600			if ( iValue < value.length ) {
7601				extra = value.substring( iValue );
7602				if ( ! /^\s+/.test( extra ) ) {
7603					throw 'Extra/unparsed characters found in date: ' + extra;
7604				}
7605			}
7606
7607			if ( year === -1 ) {
7608				year = new Date().getFullYear();
7609			} else if ( year < 100 ) {
7610				year += new Date().getFullYear() - ( new Date().getFullYear() % 100 ) + ( year <= shortYearCutoff ? 0 : -100 );
7611			}
7612
7613			if ( doy > -1 ) {
7614				month = 1;
7615				day = doy;
7616				condition = true;
7617				do {
7618					dim = this._getDaysInMonth( year, month - 1 );
7619					if ( day <= dim ) {
7620						condition = false;
7621					} else {
7622						month = month + 1;
7623						day -= dim;
7624					}
7625				} while ( condition );
7626			}
7627
7628			date = this._daylightSavingAdjust( new Date( year, month - 1, day ) );
7629			if ( date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day ) {
7630				throw 'Invalid date'; // E.g. 31/02/00
7631			}
7632			return date;
7633		},
7634
7635		/* Standard date formats. */
7636		ATOM: 'yy-mm-dd', // RFC 3339 (ISO 8601)
7637		COOKIE: 'D, dd M yy',
7638		ISO_8601: 'yy-mm-dd',
7639		RFC_822: 'D, d M y',
7640		RFC_850: 'DD, dd-M-y',
7641		RFC_1036: 'D, d M y',
7642		RFC_1123: 'D, d M yy',
7643		RFC_2822: 'D, d M yy',
7644		RSS: 'D, d M y', // RFC 822
7645		TICKS: '!',
7646		TIMESTAMP: '@',
7647		W3C: 'yy-mm-dd', // ISO 8601
7648
7649		_ticksTo1970: ( ( ( 1970 - 1 ) * 365 ) + Math.floor( 1970 / 4 ) - Math.floor( 1970 / 100 ) + Math.floor( 1970 / 400 ) ) * 24 * 60 * 60 * 10000000,
7650
7651		/* Format a date object into a string value.
7652		 * The format can be combinations of the following:
7653		 * d  - day of month (no leading zero)
7654		 * dd - day of month (two digit)
7655		 * o  - day of year (no leading zeros)
7656		 * oo - day of year (three digit)
7657		 * D  - day name short
7658		 * DD - day name long
7659		 * m  - month of year (no leading zero)
7660		 * mm - month of year (two digit)
7661		 * M  - month name short
7662		 * MM - month name long
7663		 * y  - year (two digit)
7664		 * yy - year (four digit)
7665		 * @ - Unix timestamp (ms since 01/01/1970)
7666		 * ! - Windows ticks (100ns since 01/01/0001)
7667		 * "..." - literal text
7668		 * '' - single quote
7669		 *
7670		 * @param  format string - the desired format of the date
7671		 * @param  date Date - the date value to format
7672		 * @param  settings Object - attributes include:
7673		 *                  dayNamesShort   string[7] - abbreviated names of the days from Sunday (optional)
7674		 *                  dayNames        string[7] - names of the days from Sunday (optional)
7675		 *                  monthNamesShort string[12] - abbreviated names of the months (optional)
7676		 *                  monthNames      string[12] - names of the months (optional)
7677		 * @return  string - the date in the above format
7678		 */
7679		formatDate: function( format, date, settings ) {
7680			var iFormat,
7681				dayNamesShort = ( settings ? settings.dayNamesShort : null ) || this._defaults.dayNamesShort,
7682				dayNames = ( settings ? settings.dayNames : null ) || this._defaults.dayNames,
7683				monthNamesShort = ( settings ? settings.monthNamesShort : null ) || this._defaults.monthNamesShort,
7684				monthNames = ( settings ? settings.monthNames : null ) || this._defaults.monthNames,
7685				// Check whether a format character is doubled
7686				lookAhead = function( match ) {
7687					var matches = iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match;
7688					if ( matches ) {
7689						iFormat = iFormat + 1;
7690					}
7691					return matches;
7692				},
7693				// Format a number, with leading zero if necessary
7694				formatNumber = function( match, value, len ) {
7695					var num = '' + value;
7696					if ( lookAhead( match ) ) {
7697						while ( num.length < len ) {
7698							num = '0' + num;
7699						}
7700					}
7701					return num;
7702				},
7703				// Format a name, short or long as requested
7704				formatName = function( match, value, shortNames, longNames ) {
7705					return lookAhead( match ) ? longNames[ value ] : shortNames[ value ];
7706				},
7707				output = '',
7708				literal = false;
7709
7710			if ( ! date ) {
7711				return '';
7712			}
7713
7714			if ( date ) {
7715				for ( iFormat = 0; iFormat < format.length; iFormat += 1 ) {
7716					if ( literal ) {
7717						if ( format.charAt( iFormat ) === "'" && ! lookAhead( "'" ) ) {
7718							literal = false;
7719						} else {
7720							output += format.charAt( iFormat );
7721						}
7722					} else {
7723						switch ( format.charAt( iFormat ) ) {
7724							case 'd':
7725								output += formatNumber( 'd', date.getDate(), 2 );
7726								break;
7727							case 'D':
7728								output += formatName( 'D', date.getDay(), dayNamesShort, dayNames );
7729								break;
7730							case 'o':
7731								output += formatNumber( 'o', Math.round( ( new Date( date.getFullYear(), date.getMonth(), date.getDate() ).getTime() - new Date( date.getFullYear(), 0, 0 ).getTime() ) / 86400000 ), 3 );
7732								break;
7733							case 'm':
7734								output += formatNumber( 'm', date.getMonth() + 1, 2 );
7735								break;
7736							case 'M':
7737								output += formatName( 'M', date.getMonth(), monthNamesShort, monthNames );
7738								break;
7739							case 'y':
7740								output += lookAhead( 'y' ) ? date.getFullYear() : ( date.getFullYear() % 100 < 10 ? '0' : '' ) + ( date.getFullYear() % 100 );
7741								break;
7742							case '@':
7743								output += date.getTime();
7744								break;
7745							case '!':
7746								output += ( date.getTime() * 10000 ) + this._ticksTo1970;
7747								break;
7748							case "'":
7749								if ( lookAhead( "'" ) ) {
7750									output += "'";
7751								} else {
7752									literal = true;
7753								}
7754								break;
7755							default:
7756								output += format.charAt( iFormat );
7757						}
7758					}
7759				}
7760			}
7761			return output;
7762		},
7763
7764		/* Extract all possible characters from the date format. */
7765		_possibleChars: function( format ) {
7766			var iFormat,
7767				chars = '',
7768				literal = false,
7769				// Check whether a format character is doubled
7770				lookAhead = function( match ) {
7771					var matches = iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match;
7772					if ( matches ) {
7773						iFormat = iFormat + 1;
7774					}
7775					return matches;
7776				};
7777
7778			for ( iFormat = 0; iFormat < format.length; iFormat += 1 ) {
7779				if ( literal ) {
7780					if ( format.charAt( iFormat ) === "'" && ! lookAhead( "'" ) ) {
7781						literal = false;
7782					} else {
7783						chars += format.charAt( iFormat );
7784					}
7785				} else {
7786					switch ( format.charAt( iFormat ) ) {
7787						case 'd':
7788						case 'm':
7789						case 'y':
7790						case '@':
7791							chars += '0123456789';
7792							break;
7793						case 'D':
7794						case 'M':
7795							return null; // Accept anything
7796						case "'":
7797							if ( lookAhead( "'" ) ) {
7798								chars += "'";
7799							} else {
7800								literal = true;
7801							}
7802							break;
7803						default:
7804							chars += format.charAt( iFormat );
7805					}
7806				}
7807			}
7808			return chars;
7809		},
7810
7811		/* Get a setting value, defaulting if necessary. */
7812		_get: function( inst, name ) {
7813			return inst.settings[ name ] !== undefined ? inst.settings[ name ] : this._defaults[ name ];
7814		},
7815
7816		/* Parse existing date and initialise date picker. */
7817		_setDateFromField: function( inst, noDefault ) {
7818			var dateFormat;
7819			var dates;
7820			var defaultDate;
7821			var date;
7822			var settings;
7823
7824			if ( inst.input.val() === inst.lastVal ) {
7825				return;
7826			}
7827
7828			dateFormat = this._get( inst, 'dateFormat' );
7829			dates = inst.input ? inst.input.val() : null;
7830			defaultDate = this._getDefaultDate( inst );
7831			date = defaultDate;
7832			settings = this._getFormatConfig( inst );
7833
7834			inst.lastVal = dates;
7835
7836			try {
7837				date = this.parseDate( dateFormat, dates, settings ) || defaultDate;
7838			} catch ( event ) {
7839				dates = noDefault ? '' : dates;
7840			}
7841			inst.selectedDay = date.getDate();
7842			inst.selectedMonth = date.getMonth();
7843			inst.drawMonth = inst.selectedMonth;
7844			inst.selectedYear = date.getFullYear();
7845			inst.drawYear = inst.selectedYear;
7846			inst.currentDay = dates ? date.getDate() : 0;
7847			inst.currentMonth = dates ? date.getMonth() : 0;
7848			inst.currentYear = dates ? date.getFullYear() : 0;
7849			this._adjustInstDate( inst );
7850		},
7851
7852		/* Retrieve the default date shown on opening. */
7853		_getDefaultDate: function( inst ) {
7854			return this._restrictMinMax( inst, this._determineDate( inst, this._get( inst, 'defaultDate' ), new Date() ) );
7855		},
7856
7857		/* A date may be specified as an exact value or a relative one. */
7858		_determineDate: function( inst, date, defaultDate ) {
7859			var offsetNumeric = function( offset ) {
7860					var sdate = new Date();
7861					sdate.setDate( sdate.getDate() + offset );
7862					return sdate;
7863				},
7864				offsetString = function( offset ) {
7865					var sdate;
7866					var year;
7867					var month;
7868					var day;
7869					var pattern;
7870					var matches;
7871
7872					try {
7873						return $.tm_datepicker.parseDate( $.tm_datepicker._get( inst, 'dateFormat' ), offset, $.tm_datepicker._getFormatConfig( inst ) );
7874					} catch ( e ) {
7875						// Ignore
7876					}
7877
7878					sdate = ( offset.toLowerCase().match( /^c/ ) ? $.tm_datepicker._getDate( inst ) : null ) || new Date();
7879					year = sdate.getFullYear();
7880					month = sdate.getMonth();
7881					day = sdate.getDate();
7882					pattern = /([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g;
7883					matches = pattern.exec( offset );
7884
7885					while ( matches ) {
7886						switch ( matches[ 2 ] || 'd' ) {
7887							case 'd':
7888							case 'D':
7889								day += parseInt( matches[ 1 ], 10 );
7890								break;
7891							case 'w':
7892							case 'W':
7893								day += parseInt( matches[ 1 ], 10 ) * 7;
7894								break;
7895							case 'm':
7896							case 'M':
7897								month += parseInt( matches[ 1 ], 10 );
7898								day = Math.min( day, $.tm_datepicker._getDaysInMonth( year, month ) );
7899								break;
7900							case 'y':
7901							case 'Y':
7902								year += parseInt( matches[ 1 ], 10 );
7903								day = Math.min( day, $.tm_datepicker._getDaysInMonth( year, month ) );
7904								break;
7905						}
7906						matches = pattern.exec( offset );
7907					}
7908					return new Date( year, month, day );
7909				},
7910				newDate = date === null || date === '' ? defaultDate : typeof date === 'string' ? offsetString( date ) : typeof date === 'number' ? ( ! Number.isFinite( date ) ? defaultDate : offsetNumeric( date ) ) : new Date( date.getTime() );
7911
7912			newDate = newDate && newDate.toString() === 'Invalid Date' ? defaultDate : newDate;
7913			if ( newDate ) {
7914				newDate.setHours( 0 );
7915				newDate.setMinutes( 0 );
7916				newDate.setSeconds( 0 );
7917				newDate.setMilliseconds( 0 );
7918			}
7919			return this._daylightSavingAdjust( newDate );
7920		},
7921
7922		/* Handle switch to/from daylight saving.
7923		 * Hours may be non-zero on daylight saving cut-over:
7924		 * > 12 when midnight changeover, but then cannot generate
7925		 * midnight datetime, so jump to 1AM, otherwise reset.
7926		 * @param  date  (Date) the date to check
7927		 * @return  (Date) the corrected date
7928		 */
7929		_daylightSavingAdjust: function( date ) {
7930			if ( ! date ) {
7931				return null;
7932			}
7933			date.setHours( date.getHours() > 12 ? date.getHours() + 2 : 0 );
7934			return date;
7935		},
7936
7937		/* Set the date(s) directly. */
7938		_setDate: function( inst, date, noChange ) {
7939			var clear = ! date,
7940				origMonth = inst.selectedMonth,
7941				origYear = inst.selectedYear,
7942				newDate = this._restrictMinMax( inst, this._determineDate( inst, date, new Date() ) );
7943
7944			inst.currentDay = newDate.getDate();
7945			inst.selectedDay = inst.currentDay;
7946			inst.currentMonth = newDate.getMonth();
7947			inst.drawMonth = inst.currentMonth;
7948			inst.selectedMonth = inst.currentMonth;
7949			inst.currentYear = newDate.getFullYear();
7950			inst.drawYear = inst.currentYear;
7951			inst.selectedYear = inst.currentYear;
7952			if ( ( origMonth !== inst.selectedMonth || origYear !== inst.selectedYear ) && ! noChange ) {
7953				this._notifyChange( inst );
7954			}
7955			this._adjustInstDate( inst );
7956			if ( inst.input ) {
7957				inst.input.val( clear ? '' : this._formatDate( inst ) );
7958			}
7959		},
7960
7961		/* Retrieve the date(s) directly. */
7962		_getDate: function( inst ) {
7963			var startDate = ! inst.currentYear || ( inst.input && inst.input.val() === '' ) ? null : this._daylightSavingAdjust( new Date( inst.currentYear, inst.currentMonth, inst.currentDay ) );
7964			return startDate;
7965		},
7966
7967		/* Attach the onxxx handlers.  These are declared statically so
7968		 * they work with static code transformers like Caja.
7969		 */
7970		_attachHandlers: function( inst ) {
7971			var stepMonths = this._get( inst, 'stepMonths' ),
7972				id = '#' + inst.id.replace( /\\\\/g, '\\' );
7973			inst.dpDiv.find( '[data-handler]' ).map( function() {
7974				var handler = {
7975					prevM: function() {
7976						$.tm_datepicker._adjustDate( id, -1, 'M' );
7977					},
7978					nextM: function() {
7979						$.tm_datepicker._adjustDate( id, +1, 'M' );
7980					},
7981					prevY: function() {
7982						$.tm_datepicker._adjustDate( id, -1, 'Y' );
7983					},
7984					nextY: function() {
7985						$.tm_datepicker._adjustDate( id, +1, 'Y' );
7986					},
7987					prev: function() {
7988						$.tm_datepicker._adjustDate( id, -stepMonths, 'M' );
7989					},
7990					next: function() {
7991						$.tm_datepicker._adjustDate( id, +stepMonths, 'M' );
7992					},
7993					hide: function() {
7994						$.tm_datepicker._hideDatepicker();
7995					},
7996					today: function() {
7997						/*var tempDate = new Date(),
7998                         today = $.tm_datepicker._daylightSavingAdjust(
7999                         new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate()));
8000                         $.tm_datepicker._setDate(inst, today);
8001                         $.tm_datepicker._gotoToday(id);*/
8002					},
8003					selectDay: function() {
8004						$.tm_datepicker._selectDay( id, +this.getAttribute( 'data-month' ), +this.getAttribute( 'data-year' ), this );
8005						return false;
8006					},
8007					selectMonth: function() {
8008						$.tm_datepicker._selectMonthYear( id, this, 'M' );
8009						return false;
8010					},
8011					selectYear: function() {
8012						$.tm_datepicker._selectMonthYear( id, this, 'Y' );
8013						return false;
8014					}
8015				};
8016				$( this ).on( this.getAttribute( 'data-event' ), handler[ this.getAttribute( 'data-handler' ) ] );
8017				return null;
8018			} );
8019		},
8020		/* Determines if we should allow a "next/prev" month display change. */
8021		_tm_canAdjustMonth: function( inst, offset, curYear, curMonth ) {
8022			var date = this._daylightSavingAdjust( new Date( curYear, curMonth + offset, 1 ) );
8023
8024			if ( offset < 0 ) {
8025				date.setDate( this._getDaysInMonth( date.getFullYear(), date.getMonth() ) );
8026			}
8027			return this._isInRange( inst, date );
8028		},
8029
8030		/* Generate the HTML for the current state of the date picker. */
8031		_generateHTML: function( inst ) {
8032			var maxDraw,
8033				prevText,
8034				prev,
8035				nextText,
8036				next,
8037				currentText,
8038				gotoDate,
8039				firstDay,
8040				showWeek,
8041				dayNames,
8042				dayNamesMin,
8043				monthNames,
8044				beforeShowDay,
8045				showOtherMonths,
8046				selectOtherMonths,
8047				defaultDate,
8048				html,
8049				dow,
8050				row,
8051				group,
8052				col,
8053				selectedDate,
8054				cornerClass,
8055				calender,
8056				thead,
8057				day,
8058				daysInMonth,
8059				leadDays,
8060				curRows,
8061				numRows,
8062				printDate,
8063				dRow,
8064				tbody,
8065				daySettings,
8066				otherMonth,
8067				unselectable,
8068				tempDate = new Date(),
8069				today = this._daylightSavingAdjust( new Date( tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate() ) ), // clear time
8070				isRTL = this._get( inst, 'isRTL' ),
8071				showButtonPanel = this._get( inst, 'showButtonPanel' ),
8072				hideIfNoPrevNext = this._get( inst, 'hideIfNoPrevNext' ),
8073				navigationAsDateFormat = this._get( inst, 'navigationAsDateFormat' ),
8074				numMonths = this._getNumberOfMonths( inst ),
8075				showCurrentAtPos = this._get( inst, 'showCurrentAtPos' ),
8076				stepMonths = this._get( inst, 'stepMonths' ),
8077				isMultiMonth = numMonths[ 0 ] !== 1 || numMonths[ 1 ] !== 1,
8078				currentDate = this._daylightSavingAdjust( ! inst.currentDay ? new Date( 9999, 9, 9 ) : new Date( inst.currentYear, inst.currentMonth, inst.currentDay ) ),
8079				minDate = this._getMinMaxDate( inst, 'min' ),
8080				maxDate = this._getMinMaxDate( inst, 'max' ),
8081				drawMonth = inst.drawMonth - showCurrentAtPos,
8082				drawYear = inst.drawYear,
8083				tm_month,
8084				tm_year,
8085				tm_prev_arrow_m,
8086				tm_next_arrow_m,
8087				tm_prev_arrow_y,
8088				tm_next_arrow_y,
8089				tm_controls,
8090				tm_buttonPanel;
8091			var can_adjust_1;
8092			var can_adjust_2;
8093			var can_adjust_3;
8094			var can_adjust_4;
8095
8096			if ( drawMonth < 0 ) {
8097				drawMonth += 12;
8098				drawYear -= 1;
8099			}
8100			if ( maxDate ) {
8101				maxDraw = this._daylightSavingAdjust( new Date( maxDate.getFullYear(), maxDate.getMonth() - ( numMonths[ 0 ] * numMonths[ 1 ] ) + 1, maxDate.getDate() ) );
8102				maxDraw = minDate && maxDraw < minDate ? minDate : maxDraw;
8103				while ( this._daylightSavingAdjust( new Date( drawYear, drawMonth, 1 ) ) > maxDraw ) {
8104					drawMonth -= 1;
8105					if ( drawMonth < 0 ) {
8106						drawMonth = 11;
8107						drawYear -= 1;
8108					}
8109				}
8110			}
8111			inst.drawMonth = drawMonth;
8112			inst.drawYear = drawYear;
8113
8114			prevText = this._get( inst, 'prevText' );
8115			prevText = ! navigationAsDateFormat ? prevText : this.formatDate( prevText, this._daylightSavingAdjust( new Date( drawYear, drawMonth - stepMonths, 1 ) ), this._getFormatConfig( inst ) );
8116
8117			prev = this._canAdjustMonth( inst, -1, drawYear, drawMonth ) ? "<a class='ui-tm-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" + " title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? 'e' : 'w' ) + "'>" + prevText + '</span></a>' : hideIfNoPrevNext ? '' : "<a class='ui-tm-datepicker-prev ui-corner-all ui-state-disabled' title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? 'e' : 'w' ) + "'>" + prevText + '</span></a>';
8118
8119			nextText = this._get( inst, 'nextText' );
8120			nextText = ! navigationAsDateFormat ? nextText : this.formatDate( nextText, this._daylightSavingAdjust( new Date( drawYear, drawMonth + stepMonths, 1 ) ), this._getFormatConfig( inst ) );
8121
8122			next = this._canAdjustMonth( inst, +1, drawYear, drawMonth ) ? "<a class='ui-tm-datepicker-next ui-corner-all' data-handler='next' data-event='click'" + " title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? 'w' : 'e' ) + "'>" + nextText + '</span></a>' : hideIfNoPrevNext ? '' : "<a class='ui-tm-datepicker-next ui-corner-all ui-state-disabled' title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? 'w' : 'e' ) + "'>" + nextText + '</span></a>';
8123
8124			currentText = this._get( inst, 'currentText' );
8125			gotoDate = this._get( inst, 'gotoCurrent' ) && inst.currentDay ? currentDate : today;
8126			currentText = ! navigationAsDateFormat ? currentText : this.formatDate( currentText, gotoDate, this._getFormatConfig( inst ) );
8127
8128			tm_controls = ! inst.inline ? "<div class='tm-ui-dp-btn-wrap tm-ui-dp-close'><div role='button' class='tm-ui-dp-btn ui-tm-datepicker-close' data-handler='hide' data-event='click'>" + this._get( inst, 'closeText' ) + '</div></div>' : '';
8129
8130			tm_buttonPanel = showButtonPanel ? "<div class='tm-ui-dp-buttonpane'>" + ( isRTL ? tm_controls : '' ) + ( this._isInRange( inst, gotoDate ) ? "<div class='tm-ui-dp-btn-wrap tm-ui-dp-current'><div role='button' class='tm-ui-dp-btn ui-tm-datepicker-current' data-handler='today' data-event='click'" + '>' + currentText + '</div></div>' : '' ) + ( isRTL ? '' : tm_controls ) + '</div>' : '';
8131
8132			firstDay = parseInt( this._get( inst, 'firstDay' ), 10 );
8133			firstDay = isNaN( firstDay ) ? 0 : firstDay;
8134
8135			showWeek = this._get( inst, 'showWeek' );
8136			dayNames = this._get( inst, 'dayNames' );
8137			dayNamesMin = this._get( inst, 'dayNamesMin' );
8138			monthNames = this._get( inst, 'monthNames' );
8139			//monthNamesShort = this._get(inst, "monthNamesShort");
8140			beforeShowDay = this._get( inst, 'beforeShowDay' );
8141			showOtherMonths = this._get( inst, 'showOtherMonths' );
8142			selectOtherMonths = this._get( inst, 'selectOtherMonths' );
8143			defaultDate = this._getDefaultDate( inst );
8144			html = '';
8145
8146			can_adjust_1 = this._tm_canAdjustMonth( inst, -1, drawYear, drawMonth );
8147			can_adjust_2 = this._tm_canAdjustMonth( inst, +1, drawYear, drawMonth );
8148			can_adjust_3 = this._tm_canAdjustMonth( inst, -12, drawYear, drawMonth );
8149			can_adjust_4 = this._tm_canAdjustMonth( inst, +12, drawYear, drawMonth );
8150
8151			tm_prev_arrow_m = '<div class="tm-ui-dp-button-prev tm-ui-dp-button">' + '<div ' + ( can_adjust_1 ? 'data-handler="prevM" data-event="click" ' : '' ) + 'aria-label="' + prevText + '" class="' + ( can_adjust_1 ? '' : 'tm-ui-dp-button-disabled ' ) + 'tm-ui-dp-button-arrow tcfa tcfa-angle-left" tabindex="0" role="button"></div>' + '</div>';
8152			tm_next_arrow_m = '<div class="tm-ui-dp-button-next tm-ui-dp-button">' + '<div ' + ( can_adjust_2 ? 'data-handler="nextM" data-event="click" ' : '' ) + 'aria-label="' + nextText + '" class="' + ( can_adjust_2 ? '' : 'tm-ui-dp-button-disabled ' ) + 'tm-ui-dp-button-arrow tcfa tcfa-angle-right" tabindex="0" role="button"></div>' + '</div>';
8153			tm_prev_arrow_y = '<div class="tm-ui-dp-button-prev tm-ui-dp-button">' + '<div ' + ( can_adjust_3 ? 'data-handler="prevY" data-event="click" ' : '' ) + 'aria-label="' + prevText + '" class="' + ( can_adjust_3 ? '' : 'tm-ui-dp-button-disabled ' ) + 'tm-ui-dp-button-arrow tcfa tcfa-angle-left" tabindex="0" role="button"></div>' + '</div>';
8154			tm_next_arrow_y = '<div class="tm-ui-dp-button-next tm-ui-dp-button">' + '<div ' + ( can_adjust_4 ? 'data-handler="nextY" data-event="click" ' : '' ) + 'aria-label="' + nextText + '" class="' + ( can_adjust_4 ? '' : 'tm-ui-dp-button-disabled ' ) + 'tm-ui-dp-button-arrow tcfa tcfa-angle-right" tabindex="0" role="button"></div>' + '</div>';
8155
8156			tm_month = hideIfNoPrevNext ? '' : '<div class="tm-ui-dp-title-button">' + ( isRTL ? tm_next_arrow_m : tm_prev_arrow_m ) + '<span aria-label="' + monthNames[ drawMonth ] + '" role="button" class="tm-ui-dp-month">' + monthNames[ drawMonth ] + '</span>' + ( isRTL ? tm_prev_arrow_m : tm_next_arrow_m ) + '</div>';
8157
8158			tm_year = hideIfNoPrevNext ? '' : '<div class="tm-ui-dp-title-button">' + ( isRTL ? tm_next_arrow_y : tm_prev_arrow_y ) + '<span class="tm-ui-dp-year" role="button" aria-label="' + drawYear + '">' + drawYear + '</span>' + ( isRTL ? tm_prev_arrow_y : tm_next_arrow_y ) + '</div>';
8159
8160			html += "<div class='tm-ui-dp-wrap'>" + "<div class='tm-ui-dp-overlay'></div>" + "<div class='tm-ui-dp'>" + "<div class='tm-ui-dp-container'>" + "<div class='tm-ui-dp-inner-container'>" + "<div class='tm-ui-dp-ui'>" + "<div class='tm-ui-dp-ui-inner'>" + "<div class='tm-ui-dp-main-wrap'>" + "<div class='tm-ui-dp-main'>";
8161
8162			for ( row = 0; row < numMonths[ 0 ]; row += 1 ) {
8163				group = '';
8164				this.maxRows = 4;
8165				for ( col = 0; col < numMonths[ 1 ]; col += 1 ) {
8166					selectedDate = this._daylightSavingAdjust( new Date( drawYear, drawMonth, inst.selectedDay ) );
8167					cornerClass = ' ui-corner-all';
8168					calender = '';
8169					if ( isMultiMonth ) {
8170						calender += "<div class='ui-tm-datepicker-group";
8171						if ( numMonths[ 1 ] > 1 ) {
8172							switch ( col ) {
8173								case 0:
8174									calender += ' ui-tm-datepicker-group-first';
8175									cornerClass = ' ui-corner-' + ( isRTL ? 'right' : 'left' );
8176									break;
8177								case numMonths[ 1 ] - 1:
8178									calender += ' ui-tm-datepicker-group-last';
8179									cornerClass = ' ui-corner-' + ( isRTL ? 'left' : 'right' );
8180									break;
8181								default:
8182									calender += ' ui-tm-datepicker-group-middle';
8183									cornerClass = '';
8184									break;
8185							}
8186						}
8187						calender += "'>";
8188					}
8189					calender +=
8190						"<div class='tm-ui-dp-header ui-tm-datepicker-header ui-widget-header ui-helper-clearfix" +
8191						cornerClass +
8192						"'>" +
8193						( /all|left/.test( cornerClass ) && row === 0 ? ( isRTL ? next : prev ) : '' ) +
8194						( /all|right/.test( cornerClass ) && row === 0 ? ( isRTL ? prev : next ) : '' ) +
8195						// month headers
8196
8197						'<div class="ui-tm-datepicker-title tm-ui-dp-title">' +
8198						( /all|left/.test( cornerClass ) && row === 0 ? ( isRTL ? tm_year + tm_month : tm_month + tm_year ) : '' ) +
8199						'</div>' +
8200						'</div>';
8201
8202					calender += "<div class='tm-ui-dp-main-content'>";
8203
8204					/* Days start */
8205					calender += "<div class='tm-ui-dp-main-header-wrap'><div class='tm-ui-dp-main-header'>";
8206					calender += "<table class='ui-tm-datepicker-calendar'><tbody>" + '<tr>';
8207					thead = showWeek ? "<th class='ui-tm-datepicker-week-col'>" + this._get( inst, 'weekHeader' ) + '</th>' : '';
8208					for ( dow = 0; dow < 7; dow += 1 ) {
8209						// days of the week
8210						day = ( dow + firstDay ) % 7;
8211						thead += "<th scope='col'" + ( ( dow + firstDay + 6 ) % 7 >= 5 ? " class='ui-tm-datepicker-week-end'" : '' ) + '>
8211' + "<span title='" + dayNames[ day ] + "'>" + dayNamesMin[ day ] + '</span></th>';
8212					}
8213					calender += thead + '</tr></tbody></table>';
8214					calender += '</div></div>';
8215					/* Days end */
8216
8217					calender += "<div class='tm-ui-dp-main-table'>";
8218
8219					daysInMonth = this._getDaysInMonth( drawYear, drawMonth );
8220					if ( drawYear === inst.selectedYear && drawMonth === inst.selectedMonth ) {
8221						inst.selectedDay = Math.min( inst.selectedDay, daysInMonth );
8222					}
8223					leadDays = ( this._getFirstDayOfMonth( drawYear, drawMonth ) - firstDay + 7 ) % 7;
8224					curRows = 6;
8225					numRows = isMultiMonth ? ( this.maxRows > curRows ? this.maxRows : curRows ) : curRows; //If multiple months, use the higher number of rows
8226					// (see #7043)
8227					this.maxRows = numRows;
8228					printDate = this._daylightSavingAdjust( new Date( drawYear, drawMonth, 1 - leadDays ) );
8229					for ( dRow = 0; dRow < numRows; dRow += 1 ) {
8230						// create date picker rows
8231						calender += "<div class='tm-ui-dp-main-row'>";
8232						tbody = ! showWeek ? '' : "<div class='ui-tm-datepicker-week-col'>" + this._get( inst, 'calculateWeek' )( printDate ) + '</div>';
8233						for ( dow = 0; dow < 7; dow += 1 ) {
8234							// create date picker days
8235							daySettings = beforeShowDay ? beforeShowDay.apply( inst.input ? inst.input[ 0 ] : null, [ printDate ] ) : [ true, '' ];
8236							otherMonth = printDate.getMonth() !== drawMonth;
8237							unselectable = ( otherMonth && ! selectOtherMonths ) || ! daySettings[ 0 ] || ( minDate && printDate < minDate ) || ( maxDate && printDate > maxDate );
8238
8239							tbody +=
8240								"<div class='tm-ui-dp-main-cell " +
8241								( ( dow + firstDay + 6 ) % 7 >= 5 ? ' ui-tm-datepicker-week-end' : '' ) + // highlight weekends
8242								( otherMonth ? ' ui-tm-datepicker-other-month' : '' ) + // highlight days from other months
8243								( ( printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent ) || ( defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime() ) ? ' ' + this._dayOverClass : '' ) +
8244								( unselectable ? ' ' + this._unselectableClass + ' ui-state-disabled' : '' ) + // highlight unselectable days
8245								( otherMonth && ! showOtherMonths ? '' : ' ' + daySettings[ 1 ] + ( printDate.getTime() === currentDate.getTime() ? ' ' + this._currentClass : '' ) + ( printDate.getTime() === today.getTime() ? ' ui-tm-datepicker-today' : '' ) ) +
8246								"'" + // highlight today (if different)
8247								( ( ! otherMonth || showOtherMonths ) && daySettings[ 2 ] ? " title='" + daySettings[ 2 ].replace( /'/g, '&#39;' ) + "'" : '' ) + // cell
8248								// title
8249								( unselectable ? '' : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'" ) +
8250								'>' + // actions
8251								"<div class='tm-ui-dp-main-cell-inner'>" +
8252								"<div class='tm-ui-dp-main-cell-content'>" +
8253								( otherMonth && ! showOtherMonths ? '&#xa0;' : unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + '</span>' : "<a class='ui-state-default" + ( printDate.getTime() === today.getTime() ? ' ui-state-highlight' : '' ) + ( printDate.getTime() === currentDate.getTime() ? ' ui-state-active' : '' ) + ( otherMonth ? ' ui-priority-secondary' : '' ) + "' href='#'>" + printDate.getDate() + '</a>' ) +
8254								'</div></div>' +
8255								'</div>'; // display selectable date
8256							printDate.setDate( printDate.getDate() + 1 );
8257							printDate = this._daylightSavingAdjust( printDate );
8258						}
8259						calender += tbody + '</div>';
8260					}
8261					drawMonth += 1;
8262					if ( drawMonth > 11 ) {
8263						drawMonth = 0;
8264						drawYear += 1;
8265					}
8266
8267					calender += '</div>';
8268
8269					calender += '</div>' + ( isMultiMonth ? '</div>' + ( numMonths[ 0 ] > 0 && col === numMonths[ 1 ] - 1 ? "<div class='ui-tm-datepicker-row-break'></div>" : '' ) : '' );
8270					group += calender;
8271				}
8272				html += group;
8273			}
8274			html += '</div></div></div></div></div>';
8275			html += tm_buttonPanel;
8276			html += '</div></div></div>';
8277			inst._keyEvent = false;
8278			return html;
8279		},
8280
8281		/* Generate the month and year header. */
8282		_generateMonthYearHeader: function( inst, drawMonth, drawYear, minDate, maxDate, secondary, monthNames, monthNamesShort ) {
8283			var inMinYear,
8284				inMaxYear,
8285				month,
8286				years,
8287				thisYear,
8288				determineYear,
8289				year,
8290				endYear,
8291				changeMonth = this._get( inst, 'changeMonth' ),
8292				changeYear = this._get( inst, 'changeYear' ),
8293				showMonthAfterYear = this._get( inst, 'showMonthAfterYear' ),
8294				html = "<div class='ui-tm-datepicker-title'>",
8295				monthHtml = '';
8296
8297			// month selection
8298			if ( secondary || ! changeMonth ) {
8299				monthHtml += "<span class='ui-tm-datepicker-month'>" + monthNames[ drawMonth ] + '</span>';
8300			} else {
8301				inMinYear = minDate && minDate.getFullYear() === drawYear;
8302				inMaxYear = maxDate && maxDate.getFullYear() === drawYear;
8303				monthHtml += "<select class='ui-tm-datepicker-month' data-handler='selectMonth' data-event='change'>";
8304				for ( month = 0; month < 12; month += 1 ) {
8305					if ( ( ! inMinYear || month >= minDate.getMonth() ) && ( ! inMaxYear || month <= maxDate.getMonth() ) ) {
8306						monthHtml += "<option value='" + month + "'" + ( month === drawMonth ? " selected='selected'" : '' ) + '>
vendor: 8,079 bytes, lines 8306-8509
8306' + monthNamesShort[ month ] + '</option>';
8307					}
8308				}
8309				monthHtml += '</select>';
8310			}
8311
8312			if ( ! showMonthAfterYear ) {
8313				html += monthHtml + ( secondary || ! ( changeMonth && changeYear ) ? '&#xa0;' : '' );
8314			}
8315
8316			// year selection
8317			if ( ! inst.yearshtml ) {
8318				inst.yearshtml = '';
8319				if ( secondary || ! changeYear ) {
8320					html += "<span class='ui-tm-datepicker-year'>" + drawYear + '</span>';
8321				} else {
8322					// determine range of years to display
8323					years = this._get( inst, 'yearRange' ).split( ':' );
8324					thisYear = new Date().getFullYear();
8325					determineYear = function( value ) {
8326						var syear = value.match( /c[+-].*/ ) ? drawYear + parseInt( value.substring( 1 ), 10 ) : value.match( /[+-].*/ ) ? thisYear + parseInt( value, 10 ) : parseInt( value, 10 );
8327						return isNaN( syear ) ? thisYear : syear;
8328					};
8329					year = determineYear( years[ 0 ] );
8330					endYear = Math.max( year, determineYear( years[ 1 ] || '' ) );
8331					year = minDate ? Math.max( year, minDate.getFullYear() ) : year;
8332					endYear = maxDate ? Math.min( endYear, maxDate.getFullYear() ) : endYear;
8333					inst.yearshtml += "<select class='ui-tm-datepicker-year' data-handler='selectYear' data-event='change'>";
8334					for ( ; year <= endYear; year += 1 ) {
8335						inst.yearshtml += "<option value='" + year + "'" + ( year === drawYear ? " selected='selected'" : '' ) + '>' + year + '</option>';
8336					}
8337					inst.yearshtml += '</select>';
8338
8339					html += inst.yearshtml;
8340					inst.yearshtml = null;
8341				}
8342			}
8343
8344			html += this._get( inst, 'yearSuffix' );
8345			if ( showMonthAfterYear ) {
8346				html += ( secondary || ! ( changeMonth && changeYear ) ? '&#xa0;' : '' ) + monthHtml;
8347			}
8348			html += '</div>'; // Close datepicker_header
8349			return html;
8350		},
8351
8352		/* Adjust one of the date sub-fields. */
8353		_adjustInstDate: function( inst, offset, period ) {
8354			var year = inst.selectedYear + ( period === 'Y' ? offset : 0 ),
8355				month = inst.selectedMonth + ( period === 'M' ? offset : 0 ),
8356				day = Math.min( inst.selectedDay, this._getDaysInMonth( year, month ) ) + ( period === 'D' ? offset : 0 ),
8357				date = this._restrictMinMax( inst, this._daylightSavingAdjust( new Date( year, month, day ) ) );
8358
8359			inst.selectedDay = date.getDate();
8360			inst.selectedMonth = date.getMonth();
8361			inst.drawMonth = inst.selectedMonth;
8362			inst.selectedYear = date.getFullYear();
8363			inst.drawYear = inst.selectedYear;
8364			if ( period === 'M' || period === 'Y' ) {
8365				this._notifyChange( inst );
8366			}
8367		},
8368
8369		/* Ensure a date is within any min/max bounds. */
8370		_restrictMinMax: function( inst, date ) {
8371			var minDate = this._getMinMaxDate( inst, 'min' ),
8372				maxDate = this._getMinMaxDate( inst, 'max' ),
8373				newDate = minDate && date < minDate ? minDate : date;
8374			return maxDate && newDate > maxDate ? maxDate : newDate;
8375		},
8376
8377		/* Notify change of month/year. */
8378		_notifyChange: function( inst ) {
8379			var onChange = this._get( inst, 'onChangeMonthYear' );
8380			if ( onChange ) {
8381				onChange.apply( inst.input ? inst.input[ 0 ] : null, [ inst.selectedYear, inst.selectedMonth + 1, inst ] );
8382			}
8383		},
8384
8385		/* Determine the number of months to show. */
8386		_getNumberOfMonths: function( inst ) {
8387			var numMonths = this._get( inst, 'numberOfMonths' );
8388			return numMonths === null ? [ 1, 1 ] : typeof numMonths === 'number' ? [ 1, numMonths ] : numMonths;
8389		},
8390
8391		/* Determine the current maximum date - ensure no time components are set. */
8392		_getMinMaxDate: function( inst, minMax ) {
8393			return this._determineDate( inst, this._get( inst, minMax + 'Date' ), null );
8394		},
8395
8396		/* Find the number of days in a given month. */
8397		_getDaysInMonth: function( year, month ) {
8398			return 32 - this._daylightSavingAdjust( new Date( year, month, 32 ) ).getDate();
8399		},
8400
8401		/* Find the day of the week of the first of a month. */
8402		_getFirstDayOfMonth: function( year, month ) {
8403			return new Date( year, month, 1 ).getDay();
8404		},
8405
8406		/* Determines if we should allow a "next/prev" month display change. */
8407		_canAdjustMonth: function( inst, offset, curYear, curMonth ) {
8408			var numMonths = this._getNumberOfMonths( inst ),
8409				date = this._daylightSavingAdjust( new Date( curYear, curMonth + ( offset < 0 ? offset : numMonths[ 0 ] * numMonths[ 1 ] ), 1 ) );
8410
8411			if ( offset < 0 ) {
8412				date.setDate( this._getDaysInMonth( date.getFullYear(), date.getMonth() ) );
8413			}
8414			return this._isInRange( inst, date );
8415		},
8416
8417		/* Is the given date in the accepted range? */
8418		_isInRange: function( inst, date ) {
8419			var yearSplit,
8420				currentYear,
8421				minDate = this._getMinMaxDate( inst, 'min' ),
8422				maxDate = this._getMinMaxDate( inst, 'max' ),
8423				minYear = null,
8424				maxYear = null,
8425				years = this._get( inst, 'yearRange' );
8426			if ( years ) {
8427				yearSplit = years.split( ':' );
8428				currentYear = new Date().getFullYear();
8429				minYear = parseInt( yearSplit[ 0 ], 10 );
8430				maxYear = parseInt( yearSplit[ 1 ], 10 );
8431				if ( yearSplit[ 0 ].match( /[+-].*/ ) ) {
8432					minYear += currentYear;
8433				}
8434				if ( yearSplit[ 1 ].match( /[+-].*/ ) ) {
8435					maxYear += currentYear;
8436				}
8437			}
8438
8439			return ( ! minDate || date.getTime() >= minDate.getTime() ) && ( ! maxDate || date.getTime() <= maxDate.getTime() ) && ( ! minYear || date.getFullYear() >= minYear ) && ( ! maxYear || date.getFullYear() <= maxYear );
8440		},
8441
8442		/* Provide the configuration settings for formatting/parsing. */
8443		_getFormatConfig: function( inst ) {
8444			var shortYearCutoff = this._get( inst, 'shortYearCutoff' );
8445			shortYearCutoff = typeof shortYearCutoff !== 'string' ? shortYearCutoff : ( new Date().getFullYear() % 100 ) + parseInt( shortYearCutoff, 10 );
8446			return {
8447				shortYearCutoff: shortYearCutoff,
8448				dayNamesShort: this._get( inst, 'dayNamesShort' ),
8449				dayNames: this._get( inst, 'dayNames' ),
8450				monthNamesShort: this._get( inst, 'monthNamesShort' ),
8451				monthNames: this._get( inst, 'monthNames' )
8452			};
8453		},
8454
8455		/* Format the given date for display. */
8456		_formatDate: function( inst, day, month, year ) {
8457			var date;
8458
8459			if ( ! day ) {
8460				inst.currentDay = inst.selectedDay;
8461				inst.currentMonth = inst.selectedMonth;
8462				inst.currentYear = inst.selectedYear;
8463			}
8464			date = day ? ( typeof day === 'object' ? day : this._daylightSavingAdjust( new Date( year, month, day ) ) ) : this._daylightSavingAdjust( new Date( inst.currentYear, inst.currentMonth, inst.currentDay ) );
8465			return this.formatDate( this._get( inst, 'dateFormat' ), date, this._getFormatConfig( inst ) );
8466		}
8467	} );
8468
8469	/* Invoke the datepicker functionality.
8470     @param  options  string - a command, optionally followed by additional parameters or
8471     Object - settings for attaching new datepicker functionality
8472     @return  jQuery object */
8473	$.fn.tm_datepicker = function( options ) {
8474		var otherArgs;
8475
8476		/* Verify an empty collection wasn't passed - Fixes #6976 */
8477		if ( ! this.length ) {
8478			return this;
8479		}
8480
8481		/* Initialise the date picker. */
8482		if ( ! $.tm_datepicker.initialized ) {
8483			$( document ).on( 'mousedown', $.tm_datepicker._checkExternalClick );
8484			$.tm_datepicker.initialized = true;
8485		}
8486
8487		/* Append datepicker main container to body if not exist. */
8488		if ( $( '#' + $.tm_datepicker._mainDivId ).length === 0 ) {
8489			$( 'body' ).append( $.tm_datepicker.dpDiv );
8490		}
8491
8492		otherArgs = Array.prototype.slice.call( arguments, 1 );
8493		if ( typeof options === 'string' && ( options === 'isDisabled' || options === 'getDate' || options === 'widget' ) ) {
8494			return $.tm_datepicker[ '_' + options + 'Datepicker' ].apply( $.tm_datepicker, [ this[ 0 ] ].concat( otherArgs ) );
8495		}
8496		if ( options === 'option' && arguments.length === 2 && typeof arguments[ 1 ] === 'string' ) {
8497			return $.tm_datepicker[ '_' + options + 'Datepicker' ].apply( $.tm_datepicker, [ this[ 0 ] ].concat( otherArgs ) );
8498		}
8499		return this.each( function() {
8500			if ( typeof options === 'string' ) {
8501				$.tm_datepicker[ '_' + options + 'Datepicker' ].apply( $.tm_datepicker, [ this ].concat( otherArgs ) );
8502			} else {
8503				$.tm_datepicker._attachDatepicker( this, options );
8504			}
8505		} );
8506	};
8507
8508	$.tm_datepicker = new Datepicker(); // singleton instance
8509	$.tm_datepicker.initialized = false;
8510	$.tm_datepicker.uuid = new Date().getTime();
8511	$.tm_datepicker.version = '1.11.4';
8512} ) );
8513;
8514/*! jQuery Timepicker Addon - v1.6.3 - 2016-04-20
8515 * http://trentrichardson.com/examples/timepicker
8516 * Modified version of jQuery Timepicker
8517 * Copyright (c) 2016 Trent Richardson; Licensed MIT */
8518( function( factory ) {
8519	'use strict';
8520	if ( typeof window.define === 'function' && window.define.amd ) {
8521		window.define( [ 'jquery', 'jquery-ui' ], factory );
8522	} else {
8523		factory( window.jQuery );
8524	}
8525}( function( $ ) {
8526	'use strict';
8527
8528	/*
8529	 * jQuery extend now ignores nulls!
8530	 */
8531	var extendRemove = function( target, props ) {
8532		var name;
8533		$.extend( target, props );
8534		for ( name in props ) {
8535			if ( props[ name ] === null || props[ name ] === undefined ) {
8536				target[ name ] = props[ name ];
8537			}
8538		}
8539		return target;
8540	};
8541
8542	/*
8543	 * jQuery isEmptyObject does not check hasOwnProperty - if someone has added to the object prototype,
8544	 * it will return false for all objects
8545	 */
8546	var isEmptyObject = function( obj ) {
8547		var prop;
8548		for ( prop in obj ) {
8549			if ( Object.prototype.hasOwnProperty.call( obj, prop ) ) {
8550				return false;
8551			}
8552		}
8553		return true;
8554	};
8555
8556	/*
8557	 * Determine by the time format which units are supported
8558	 * Returns an object of booleans for each unit
8559	 */
8560	var detectSupport = function( timeFormat ) {
8561		var tf = timeFormat.replace( /'.*?'/g, '' ).toLowerCase(), // removes literals
8562			isIn = function( f, t ) {
8563				// does the format contain the token?
8564				return f.indexOf( t ) !== -1 ? true : false;
8565			};
8566		return {
8567			hour: isIn( tf, 'h' ),
8568			minute: isIn( tf, 'm' ),
8569			second: isIn( tf, 's' ),
8570			millisec: isIn( tf, 'l' ),
8571			microsec: isIn( tf, 'c' ),
8572			timezone: isIn( tf, 'z' ),
8573			ampm: isIn( tf, 't' ) && isIn( timeFormat, 'h' ),
8574			iso8601: isIn( timeFormat, 'Z' )
8575		};
8576	};
8577
8578	/*
8579	 * Converts 24 hour format into 12 hour
8580	 * Returns 12 hour without leading 0
8581	 */
8582	var convert24to12 = function( hour ) {
8583		hour %= 12;
8584
8585		if ( hour === 0 ) {
8586			hour = 12;
8587		}
8588
8589		return String( hour );
8590	};
8591
8592	var computeEffectiveSetting = function( settings, property ) {
8593		return settings && settings[ property ] ? settings[ property ] : $.tm_timepicker._defaults[ property ];
8594	};
8595
8596	/*
8597	 * Splits datetime string into date and time substrings.
8598	 * Throws exception when date can't be parsed
8599	 * Returns {dateString: dateString, timeString: timeString}
8600	 */
8601	var splitDateTime = function( dateTimeString, timeSettings ) {
8602		// The idea is to get the number separator occurrences in datetime and the time format requested (since time has
8603		// fewer unknowns, mostly numbers and am/pm). We will use the time pattern to split.
8604		var separator = computeEffectiveSetting( timeSettings, 'separator' ),
8605			format = computeEffectiveSetting( timeSettings, 'timeFormat' ),
8606			timeParts = format.split( separator ), // how many occurrences of separator may be in our format?
8607			timePartsLen = timeParts.length,
8608			allParts = dateTimeString.split( separator ),
8609			allPartsLen = allParts.length;
8610
8611		if ( allPartsLen > 1 ) {
8612			return {
8613				dateString: allParts.splice( 0, allPartsLen - timePartsLen ).join( separator ),
8614				timeString: allParts.splice( 0, timePartsLen ).join( separator )
8615			};
8616		}
8617
8618		return {
8619			dateString: dateTimeString,
8620			timeString: ''
8621		};
8622	};
8623
8624	/*
8625	 * Internal function to parse datetime interval
8626	 * Returns: {date: Date, timeObj: Object}, where
8627	 *   date - parsed date without time (type Date)
8628	 *   timeObj = {hour: , minute: , second: , millisec: , microsec: } - parsed time. Optional
8629	 */
8630	var parseDateTimeInternal = function( dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings ) {
8631		var date, parts, parsedTime;
8632
8633		parts = splitDateTime( dateTimeString, timeSettings );
8634		date = $.tm_datepicker._base_parseDate( dateFormat, parts.dateString, dateSettings );
8635
8636		if ( parts.timeString === '' ) {
8637			return {
8638				date: date
8639			};
8640		}
8641
8642		parsedTime = $.tm_datepicker.parseTime( timeFormat, parts.timeString, timeSettings );
8643
8644		if ( ! parsedTime ) {
8645			throw 'Wrong time format';
8646		}
8647
8648		return {
8649			date: date,
8650			timeObj: parsedTime
8651		};
8652	};
8653
8654	/*
8655	 * Internal function to set timezone_select to the local timezone
8656	 */
8657	var selectLocalTimezone = function( tp_inst, date ) {
8658		var now;
8659		if ( tp_inst && tp_inst.timezone_select ) {
8660			now = date || new Date();
8661			tp_inst.timezone_select.val( -now.getTimezoneOffset() );
8662		}
8663	};
8664
8665	/*
8666	 * Timepicker manager.
8667	 * Use the singleton instance of this class, $.tm_timepicker, to interact with the time picker.
8668	 * Settings for (groups of) time pickers are maintained in an instance object,
8669	 * allowing multiple different settings on the same page.
8670	 */
8671	var Timepicker = function() {
8672		this.regional = []; // Available regional settings, indexed by language code
8673		this.regional[ '' ] = {
8674			// Default regional settings
8675			currentText: 'Now',
8676			closeText: 'Done',
8677			amNames: [ 'AM', 'A' ],
8678			pmNames: [ 'PM', 'P' ],
8679			timeFormat: 'HH:mm',
8680			timeSuffix: '',
8681			timeOnlyTitle: 'Choose Time',
8682			timeText: 'Time',
8683			hourText: 'Hour',
8684			minuteText: 'Minute',
8685			secondText: 'Second',
8686			millisecText: 'Millisecond',
8687			microsecText: 'Microsecond',
8688			timezoneText: 'Time Zone',
8689			isRTL: false
8690		};
8691		this._defaults = {
8692			// Global defaults for all the datetime picker instances
8693			showButtonPanel: true,
8694			timeOnly: false,
8695			timeOnlyShowDate: false,
8696			showHour: null,
8697			showMinute: null,
8698			showSecond: null,
8699			showMillisec: null,
8700			showMicrosec: null,
8701			showTimezone: null,
8702			showTime: true,
8703			stepHour: 1,
8704			stepMinute: 1,
8705			stepSecond: 1,
8706			stepMillisec: 1,
8707			stepMicrosec: 1,
8708			hour: 0,
8709			minute: 0,
8710			second: 0,
8711			millisec: 0,
8712			microsec: 0,
8713			timezone: null,
8714			hourMin: 0,
8715			minuteMin: 0,
8716			secondMin: 0,
8717			millisecMin: 0,
8718			microsecMin: 0,
8719			hourMax: 23,
8720			minuteMax: 59,
8721			secondMax: 59,
8722			millisecMax: 999,
8723			microsecMax: 999,
8724			minDateTime: null,
8725			maxDateTime: null,
8726			maxTime: null,
8727			minTime: null,
8728			onSelect: null,
8729			hourGrid: 0,
8730			minuteGrid: 0,
8731			secondGrid: 0,
8732			millisecGrid: 0,
8733			microsecGrid: 0,
8734			alwaysSetTime: true,
8735			separator: ' ',
8736			altFieldTimeOnly: true,
8737			altTimeFormat: null,
8738			altSeparator: null,
8739			altTimeSuffix: null,
8740			altRedirectFocus: true,
8741			pickerTimeFormat: null,
8742			pickerTimeSuffix: null,
8743			showTimepicker: true,
8744			timezoneList: null,
8745			addSliderAccess: false,
8746			sliderAccessArgs: null,
8747			controlType: 'slider',
8748			oneLine: false,
8749			defaultValue: null,
8750			parse: 'strict',
8751			afterInject: null
8752		};
8753		$.extend( this._defaults, this.regional[ '' ] );
8754	};
8755
8756	/*
8757	 * Lets not redefine tm_timepicker, Prevent "Uncaught RangeError: Maximum call stack size exceeded"
8758	 */
8759	$.ui.tm_timepicker = $.ui.tm_timepicker || {};
8760	if ( $.ui.tm_timepicker.version ) {
8761		return;
8762	}
8763
8764	/*
8765	 * Extend jQueryUI, get it started with our version number
8766	 */
8767	$.extend( $.ui, {
8768		tm_timepicker: {
8769			version: '1.6.3'
8770		}
8771	} );
8772
8773	$.extend( Timepicker.prototype, {
8774		$input: null,
8775		$altInput: null,
8776		$timeObj: null,
8777		inst: null,
8778		hour_slider: null,
8779		minute_slider: null,
8780		second_slider: null,
8781		millisec_slider: null,
8782		microsec_slider: null,
8783		timezone_select: null,
8784		maxTime: null,
8785		minTime: null,
8786		hour: 0,
8787		minute: 0,
8788		second: 0,
8789		millisec: 0,
8790		microsec: 0,
8791		timezone: null,
8792		hourMinOriginal: null,
8793		minuteMinOriginal: null,
8794		secondMinOriginal: null,
8795		millisecMinOriginal: null,
8796		microsecMinOriginal: null,
8797		hourMaxOriginal: null,
8798		minuteMaxOriginal: null,
8799		secondMaxOriginal: null,
8800		millisecMaxOriginal: null,
8801		microsecMaxOriginal: null,
8802		ampm: '',
8803		formattedDate: '',
8804		formattedTime: '',
8805		formattedDateTime: '',
8806		timezoneList: null,
8807		units: [ 'hour', 'minute', 'second', 'millisec', 'microsec' ],
8808		support: {},
8809		control: null,
8810
8811		/*
8812		 * Override the default settings for all instances of the time picker.
8813		 * @param  {Object} settings  object - the new settings to use as defaults (anonymous object)
8814		 * @return {Object} the manager object
8815		 */
8816		setDefaults: function( settings ) {
8817			extendRemove( this._defaults, settings || {} );
8818			return this;
8819		},
8820
8821		/*
8822		 * Create a new Timepicker instance
8823		 */
8824		_newInst: function( $input, opts ) {
8825			var tp_inst = new Timepicker(),
8826				inlineSettings = {},
8827				fns = {},
8828				overrides,
8829				i;
8830			var attrName;
8831			var attrValue;
8832			var timezoneList;
8833			var tzl;
8834			var tzi = 0;
8835			var tzv = null;
8836
8837			for ( attrName in this._defaults ) {
8838				if ( Object.prototype.hasOwnProperty.call( this._defaults, attrName ) ) {
8839					attrValue = $input.attr( 'data-' + attrName );
8840					if ( attrValue ) {
8841						inlineSettings[ attrName ] = attrValue;
8842					}
8843				}
8844			}
8845
8846			overrides = {
8847				beforeShow: function( input, dp_inst ) {
8848					if ( typeof tp_inst._defaults.evnts.beforeShow === 'function' ) {
8849						return tp_inst._defaults.evnts.beforeShow.call( $input[ 0 ], input, dp_inst, tp_inst );
8850					}
8851				},
8852				onChangeMonthYear: function( year, month, dp_inst ) {
8853					// Update the time as well : this prevents the time from disappearing from the $input field.
8854					// tp_inst._updateDateTime(dp_inst);
8855					if ( typeof tp_inst._defaults.evnts.onChangeMonthYear === 'function' ) {
8856						tp_inst._defaults.evnts.onChangeMonthYear.call( $input[ 0 ], year, month, dp_inst, tp_inst );
8857					}
8858				},
8859				onClose: function( dateText, dp_inst ) {
8860					if ( tp_inst.timeDefined === true && $input.val() !== '' ) {
8861						tp_inst._updateDateTime( dp_inst );
8862					}
8863					if ( typeof tp_inst._defaults.evnts.onClose === 'function' ) {
8864						tp_inst._defaults.evnts.onClose.call( $input[ 0 ], dateText, dp_inst, tp_inst );
8865					}
8866				}
8867			};
8868			for ( i in overrides ) {
8869				if ( Object.prototype.hasOwnProperty.call( overrides, i ) ) {
8870					fns[ i ] = opts[ i ] || this._defaults[ i ] || null;
8871				}
8872			}
8873
8874			tp_inst._defaults = $.extend( {}, this._defaults, inlineSettings, opts, overrides, {
8875				evnts: fns,
8876				tm_timepicker: tp_inst // add tm_timepicker as a property of tm_datepicker: $.tm_datepicker._get(dp_inst, 'tm_timepicker');
8877			} );
8878			tp_inst.amNames = $.map( tp_inst._defaults.amNames, function( val ) {
8879				return val.toUpperCase();
8880			} );
8881			tp_inst.pmNames = $.map( tp_inst._defaults.pmNames, function( val ) {
8882				return val.toUpperCase();
8883			} );
8884
8885			// detect which units are supported
8886			tp_inst.support = detectSupport( tp_inst._defaults.timeFormat + ( tp_inst._defaults.pickerTimeFormat ? tp_inst._defaults.pickerTimeFormat : '' ) + ( tp_inst._defaults.altTimeFormat ? tp_inst._defaults.altTimeFormat : '' ) );
8887
8888			// controlType is string - key to our this._controls
8889			if ( typeof tp_inst._defaults.controlType === 'string' ) {
8890				if ( tp_inst._defaults.controlType === 'slider' && typeof $.ui.slider === 'undefined' ) {
8891					tp_inst._defaults.controlType = 'select';
8892				}
8893				tp_inst.control = tp_inst._controls[ tp_inst._defaults.controlType ];
8894			} else {
8895				// controlType is an object and must implement create, options, value methods
8896				tp_inst.control = tp_inst._defaults.controlType;
8897			}
8898
8899			// prep the timezone options
8900			timezoneList = [ -720, -660, -600, -570, -540, -480, -420, -360, -300, -270, -240, -210, -180, -120, -60, 0, 60, 120, 180, 210, 240, 270, 300, 330, 345, 360, 390, 420, 480, 525, 540, 570, 600, 630, 660, 690, 720, 765, 780, 840 ];
8901			if ( tp_inst._defaults.timezoneList !== null ) {
8902				timezoneList = tp_inst._defaults.timezoneList;
8903			}
8904			tzl = timezoneList.length;
8905
8906			if ( tzl > 0 && typeof timezoneList[ 0 ] !== 'object' ) {
8907				for ( ; tzi < tzl; tzi += 1 ) {
8908					tzv = timezoneList[ tzi ];
8909					timezoneList[ tzi ] = {
8910						value: tzv,
8911						label: $.tm_timepicker.timezoneOffsetString( tzv, tp_inst.support.iso8601 )
8912					};
8913				}
8914			}
8915			tp_inst._defaults.timezoneList = timezoneList;
8916
8917			// set the default units
8918			tp_inst.timezone = tp_inst._defaults.timezone !== null ? $.tm_timepicker.timezoneOffsetNumber( tp_inst._defaults.timezone ) : new Date().getTimezoneOffset() * -1;
8919			tp_inst.hour = tp_inst._defaults.hour < tp_inst._defaults.hourMin ? tp_inst._defaults.hourMin : tp_inst._defaults.hour > tp_inst._defaults.hourMax ? tp_inst._defaults.hourMax : tp_inst._defaults.hour;
8920			tp_inst.minute = tp_inst._defaults.minute < tp_inst._defaults.minuteMin ? tp_inst._defaults.minuteMin : tp_inst._defaults.minute > tp_inst._defaults.minuteMax ? tp_inst._defaults.minuteMax : tp_inst._defaults.minute;
8921			tp_inst.second = tp_inst._defaults.second < tp_inst._defaults.secondMin ? tp_inst._defaults.secondMin : tp_inst._defaults.second > tp_inst._defaults.secondMax ? tp_inst._defaults.secondMax : tp_inst._defaults.second;
8922			tp_inst.millisec =
8923				tp_inst._defaults.millisec < tp_inst._defaults.millisecMin ? tp_inst._defaults.millisecMin : tp_inst._defaults.millisec > tp_inst._defaults.millisecMax ? tp_inst._defaults.millisecMax : tp_inst._defaults.millisec;
8924			tp_inst.microsec =
8925				tp_inst._defaults.microsec < tp_inst._defaults.microsecMin ? tp_inst._defaults.microsecMin : tp_inst._defaults.microsec > tp_inst._defaults.microsecMax ? tp_inst._defaults.microsecMax : tp_inst._defaults.microsec;
8926			tp_inst.ampm = '';
8927			tp_inst.$input = $input;
8928
8929			if ( tp_inst._defaults.altField ) {
8930				tp_inst.$altInput = $( tp_inst._defaults.altField );
8931				if ( tp_inst._defaults.altRedirectFocus === true ) {
8932					tp_inst.$altInput
8933						.css( {
8934							cursor: 'pointer'
8935						} )
8936						.on( 'focus', function() {
8937							$input.trigger( 'focus' );
8938						} );
8939				}
8940			}
8941
8942			if ( tp_inst._defaults.minDate === 0 || tp_inst._defaults.minDateTime === 0 ) {
8943				tp_inst._defaults.minDate = new Date();
8944			}
8945			if ( tp_inst._defaults.maxDate === 0 || tp_inst._defaults.maxDateTime === 0 ) {
8946				tp_inst._defaults.maxDate = new Date();
8947			}
8948
8949			// tm_datepicker needs minDate/maxDate, tm_timepicker needs minDateTime/maxDateTime..
8950			if ( tp_inst._defaults.minDate !== undefined && tp_inst._defaults.minDate instanceof Date ) {
8951				tp_inst._defaults.minDateTime = new Date( tp_inst._defaults.minDate.getTime() );
8952			}
8953			if ( tp_inst._defaults.minDateTime !== undefined && tp_inst._defaults.minDateTime instanceof Date ) {
8954				tp_inst._defaults.minDate = new Date( tp_inst._defaults.minDateTime.getTime() );
8955			}
8956			if ( tp_inst._defaults.maxDate !== undefined && tp_inst._defaults.maxDate instanceof Date ) {
8957				tp_inst._defaults.maxDateTime = new Date( tp_inst._defaults.maxDate.getTime() );
8958			}
8959			if ( tp_inst._defaults.maxDateTime !== undefined && tp_inst._defaults.maxDateTime instanceof Date ) {
8960				tp_inst._defaults.maxDate = new Date( tp_inst._defaults.maxDateTime.getTime() );
8961			}
8962			tp_inst.$input.on( 'focus', function() {
8963				tp_inst._onFocus();
8964			} );
8965
8966			tp_inst.$input.next( '.ui-tm-timepicker-trigger' ).on( 'click', function() {
8967				tp_inst._onFocus();
8968				/*if ( $.tm_datepicker._datepickerShowing && $.tm_datepicker._lastInput === input[ 0 ] ) {
8969                 $.tm_datepicker._hideDatepicker();
8970                 } else if ( $.tm_datepicker._datepickerShowing && $.tm_datepicker._lastInput !== input[ 0 ] ) {
8971                 $.tm_datepicker._hideDatepicker();
8972                 $.tm_datepicker._showDatepicker( input[ 0 ] );
8973                 } else {
8974                 $.tm_datepicker._showDatepicker( input[ 0 ] );
8975                 }*/
8976				return false;
8977			} );
8978
8979			return tp_inst;
8980		},
8981
8982		/*
8983		 * add our sliders to the calendar
8984		 */
8985		_addTimePicker: function( dp_inst ) {
8986			var currDT = $.epoAPI.util.trim( this.$altInput && this._defaults.altFieldTimeOnly ? this.$input.val() + ' ' + this.$altInput.val() : this.$input.val() );
8987
8988			this.timeDefined = this._parseTime( currDT );
8989			this._limitMinMaxDateTime( dp_inst, false );
8990			this._injectTimePicker();
8991			this._afterInject();
8992		},
8993
8994		/*
8995		 * parse the time string from input value or _setTime
8996		 */
8997		_parseTime: function( timeString, withDate ) {
8998			var dp_dateFormat;
8999			var parseRes;
9000			var timeObj;
9001
9002			if ( ! this.inst ) {
9003				this.inst = $.tm_datepicker._getInst( this.$input[ 0 ] );
9004			}
9005
9006			if ( withDate || ! this._defaults.timeOnly ) {
9007				dp_dateFormat = $.tm_datepicker._get( this.inst, 'dateFormat' );
9008				try {
9009					parseRes = parseDateTimeInternal( dp_dateFormat, this._defaults.timeFormat, timeString, $.tm_datepicker._getFormatConfig( this.inst ), this._defaults );
9010					if ( ! parseRes.timeObj ) {
9011						return false;
9012					}
9013					$.extend( this, parseRes.timeObj );
9014				} catch ( err ) {
9015					$.tm_timepicker.log( 'Error parsing the date/time string: ' + err + '\ndate/time string = ' + timeString + '\ntimeFormat = ' + this._defaults.timeFormat + '\ndateFormat = ' + dp_dateFormat );
9016					return false;
9017				}
9018				return true;
9019			}
9020			timeObj = $.tm_datepicker.parseTime( this._defaults.timeFormat, timeString, this._defaults );
9021			if ( ! timeObj ) {
9022				return false;
9023			}
9024			$.extend( this, timeObj );
9025			return true;
9026		},
9027
9028		/*
9029		 * Handle callback option after injecting tm_timepicker
9030		 */
9031		_afterInject: function() {
9032			var o = this.inst.settings;
9033			if ( typeof o.afterInject === 'function' ) {
9034				o.afterInject.call( this );
9035			}
9036		},
9037
9038		_ui_tpicker_onclick: function( event ) {
9039			var $t = $( event.target ),
9040				h = $t.html(),
9041				n = parseInt( h.replace( /[^0-9]/g ), 10 ),
9042				ap = h.replace( /[^apm]/gi ),
9043				f = $t.data( 'for' ); // loses scope, so we use data-for
9044
9045			if ( f === 'hour' ) {
9046				if ( ap.indexOf( 'p' ) !== -1 && n < 12 ) {
9047					n += 12;
9048				} else if ( ap.indexOf( 'a' ) !== -1 && n === 12 ) {
9049					n = 0;
9050				}
9051			}
9052
9053			event.data.tp_inst.control.value( event.data.tp_inst, event.data.tp_inst[ f + '_slider' ], event.data.litem, n );
9054
9055			event.data.tp_inst._onTimeChange();
9056			event.data.tp_inst._onSelectHandler();
9057		},
9058		/*
9059		 * generate and inject html for tm_timepicker into ui tm_datepicker
9060		 */
9061		_injectTimePicker: function() {
9062			var $dp = this.inst.dpDiv,
9063				o = this.inst.settings,
9064				tp_inst = this,
9065				litem = '',
9066				uitem = '',
9067				show = null,
9068				max = {},
9069				gridSize = {},
9070				size = null,
9071				i = 0,
9072				l = 0;
9073			var noDisplay;
9074			var html;
9075			var h;
9076			var tmph;
9077			var m;
9078			var showTz;
9079			var $tp;
9080			var local_timezone;
9081			var $buttonPanel;
9082			var timeDefined;
9083			var sliderAccessArgs;
9084			var rtl;
9085
9086			// Prevent displaying twice
9087			if ( $dp.find( 'div.ui-tm-timepicker-div' ).length === 0 && o.showTimepicker ) {
9088				noDisplay = ' ui_tpicker_unit_hide';
9089				html =
9090					'<div class="ui-tm-timepicker-div' +
9091					( o.isRTL ? ' ui-tm-timepicker-rtl' : '' ) +
9092					( o.oneLine && o.controlType === 'select' ? ' ui-tm-timepicker-oneLine' : '' ) +
9093					'"><dl>' +
9094					'<dt class="ui_tpicker_time_label' +
9095					( o.showTime ? '' : noDisplay ) +
9096					'">' +
9097					o.timeText +
9098					'</dt>' +
9099					'<dd class="ui_tpicker_time ' +
9100					( o.showTime ? '' : noDisplay ) +
9101					'"><input class="ui_tpicker_time_input" ' +
9102					( o.timeInput ? '' : 'disabled' ) +
9103					'></dd>';
9104
9105				// Create the markup
9106				for ( i = 0, l = this.units.length; i < l; i += 1 ) {
9107					litem = this.units[ i ];
9108					uitem = litem.substring( 0, 1 ).toUpperCase() + litem.substring( 1 );
9109					show = o[ 'show' + uitem ] !== null ? o[ 'show' + uitem ] : this.support[ litem ];
9110
9111					// Added by Peter Medeiros:
9112					// - Figure out what the hour/minute/second max should be based on the step values.
9113					// - Example: if stepMinute is 15, then minMax is 45.
9114					max[ litem ] = parseInt( o[ litem + 'Max' ] - ( ( o[ litem + 'Max' ] - o[ litem + 'Min' ] ) % o[ 'step' + uitem ] ), 10 );
9115					gridSize[ litem ] = 0;
9116
9117					html +=
9118						'<dt class="ui_tpicker_' +
9119						litem +
9120						'_label' +
9121						( show ? '' : noDisplay ) +
9122						'">' +
9123						o[ litem + 'Text' ] +
9124						'</dt>' +
9125						'<dd class="ui_tpicker_' +
9126						litem +
9127						( show ? '' : noDisplay ) +
9128						'"><div class="ui_tpicker_' +
9129						litem +
9130						'_slider' +
9131						( show ? '' : noDisplay ) +
9132						'"></div>';
9133
9134					if ( show && o[ litem + 'Grid' ] > 0 ) {
9135						html += '<div><table class="ui-tpicker-grid-label"><tr>';
9136
9137						if ( litem === 'hour' ) {
9138							for ( h = o[ litem + 'Min' ]; h <= max[ litem ]; h += parseInt( o[ litem + 'Grid' ], 10 ) ) {
9139								gridSize[ litem ] += 1;
9140								tmph = $.tm_datepicker.formatTime( this.support.ampm ? 'hht' : 'HH', { hour: h }, o );
9141								html += '<td data-for="' + litem + '">' + tmph + '</td>';
9142							}
9143						} else {
9144							for ( m = o[ litem + 'Min' ]; m <= max[ litem ]; m += parseInt( o[ litem + 'Grid' ], 10 ) ) {
9145								gridSize[ litem ] += 1;
9146								html += '<td data-for="' + litem + '">' + ( m < 10 ? '0' : '' ) + m + '</td>';
9147							}
9148						}
9149
9150						html += '</tr></table></div>';
9151					}
9152					html += '</dd>';
9153				}
9154
9155				// Timezone
9156				showTz = o.showTimezone !== null ? o.showTimezone : this.support.timezone;
9157				html += '<dt class="ui_tpicker_timezone_label' + ( showTz ? '' : noDisplay ) + '">' + o.timezoneText + '</dt>';
9158				html += '<dd class="ui_tpicker_timezone' + ( showTz ? '' : noDisplay ) + '"></dd>';
9159
9160				// Create the elements from string
9161				html += '</dl></div>';
9162				$tp = $( html );
9163
9164				// if we only want time picker...
9165				if ( o.timeOnly === true ) {
9166					$tp.prepend( '<div class="tm-ui-widget-header">' + '<div class="tm-ui-tp-title">' + o.timeOnlyTitle + '</div>' + '</div>' );
9167					$dp.find( '.tm-ui-dp-inner-container' ).hide();
9168				}
9169
9170				// add sliders, adjust grids, add events
9171				for ( i = 0, l = tp_inst.units.length; i < l; i += 1 ) {
9172					litem = tp_inst.units[ i ];
9173					uitem = litem.substring( 0, 1 ).toUpperCase() + litem.substring( 1 );
9174					show = o[ 'show' + uitem ] !== null ? o[ 'show' + uitem ] : this.support[ litem ];
9175
9176					// add the slider
9177					tp_inst[ litem + '_slider' ] = tp_inst.control.create( tp_inst, $tp.find( '.ui_tpicker_' + litem + '_slider' ), litem, tp_inst[ litem ], o[ litem + 'Min' ], max[ litem ], o[ 'step' + uitem ] );
9178
9179					// adjust the grid and add click event
9180					if ( show && o[ litem + 'Grid' ] > 0 ) {
9181						size = ( 100 * gridSize[ litem ] * o[ litem + 'Grid' ] ) / ( max[ litem ] - o[ litem + 'Min' ] );
9182						$tp.find( '.ui_tpicker_' + litem + ' table' )
9183							.css( {
9184								width: size + '%',
9185								marginLeft: o.isRTL ? '0' : ( size / ( -2 * gridSize[ litem ] ) ) + '%',
9186								marginRight: o.isRTL ? ( size / ( -2 * gridSize[ litem ] ) ) + '%' : '0',
9187								borderCollapse: 'collapse'
9188							} )
9189							.find( 'td' )
9190							.on( 'click', { that: this, tp_inst: tp_inst, litem: litem }, this._ui_tpicker_onclick )
9191							.css( {
9192								cursor: 'pointer',
9193								width: ( 100 / gridSize[ litem ] ) + '%',
9194								textAlign: 'center',
9195								overflow: 'hidden'
9196							} );
9197					} // end if grid > 0
9198				} // end for loop
9199
9200				// Add timezone options
9201				this.timezone_select = $tp.find( '.ui_tpicker_timezone' ).append( '<select></select>' ).find( 'select' );
9202				$.fn.append.apply(
9203					this.timezone_select,
9204					$.map( o.timezoneList, function( val ) {
9205						return $( '<option />' )
9206							.val( typeof val === 'object' ? val.value : val )
9207							.text( typeof val === 'object' ? val.label : val );
9208					} )
9209				);
9210				if ( typeof this.timezone !== 'undefined' && this.timezone !== null && this.timezone !== '' ) {
9211					local_timezone = new Date( this.inst.selectedYear, this.inst.selectedMonth, this.inst.selectedDay, 12 ).getTimezoneOffset() * -1;
9212					if ( local_timezone === this.timezone ) {
9213						selectLocalTimezone( tp_inst );
9214					} else {
9215						this.timezone_select.val( this.timezone );
9216					}
9217				} else if ( typeof this.hour !== 'undefined' && this.hour !== null && this.hour !== '' ) {
9218					this.timezone_select.val( o.timezone );
9219				} else {
9220					selectLocalTimezone( tp_inst );
9221				}
9222				this.timezone_select.on( 'change', function() {
9223					tp_inst._onTimeChange();
9224					tp_inst._onSelectHandler();
9225					tp_inst._afterInject();
9226				} );
9227				// End timezone options
9228
9229				// inject tm_timepicker into tm_datepicker
9230				$buttonPanel = $dp.find( '.tm-ui-dp-buttonpane' );
9231				if ( $buttonPanel.length ) {
9232					$buttonPanel.before( $tp );
9233				} else {
9234					$dp.append( $tp );
9235				}
9236
9237				this.$timeObj = $tp.find( '.ui_tpicker_time_input' );
9238				this.$timeObj.on( 'change', function() {
9239					var timeFormat = tp_inst.inst.settings.timeFormat;
9240					var parsedTime = $.tm_datepicker.parseTime( timeFormat, this.value );
9241					var update = new Date();
9242					if ( parsedTime ) {
9243						update.setHours( parsedTime.hour );
9244						update.setMinutes( parsedTime.minute );
9245						update.setSeconds( parsedTime.second );
9246						$.datepicker._setTime( tp_inst.inst, update );
9247					} else {
9248						this.value = tp_inst.formattedTime;
9249						this.trigger( 'blur' );
9250					}
9251				} );
9252
9253				if ( this.inst !== null ) {
9254					timeDefined = this.timeDefined;
9255					this._onTimeChange();
9256					this.timeDefined = timeDefined;
9257				}
9258
9259				// slideAccess integration: http://trentrichardson.com/2011/11/11/jquery-ui-sliders-and-touch-accessibility/
9260				if ( this._defaults.addSliderAccess ) {
9261					sliderAccessArgs = this._defaults.sliderAccessArgs;
9262					rtl = this._defaults.isRTL;
9263					sliderAccessArgs.isRTL = rtl;
9264
9265					setTimeout( function() {
9266						// fix for inline mode
9267						var sliderAccessWidth;
9268						if ( $tp.find( '.ui-slider-access' ).length === 0 ) {
9269							$tp.find( '.ui-slider:visible' ).sliderAccess( sliderAccessArgs );
9270
9271							// fix any grids since sliders are shorter
9272							sliderAccessWidth = $tp.find( '.ui-slider-access:eq(0)' ).outerWidth( true );
9273							if ( sliderAccessWidth ) {
9274								$tp.find( 'table:visible' ).each( function() {
9275									var $g = $( this ),
9276										oldWidth = $g.outerWidth(),
9277										oldMarginLeft = $g
9278											.css( rtl ? 'marginRight' : 'marginLeft' )
9279											.toString()
9280											.replace( '%', '' ),
9281										newWidth = oldWidth - sliderAccessWidth,
9282										newMarginLeft = ( ( oldMarginLeft * newWidth ) / oldWidth ) + '%',
9283										css = { width: newWidth, marginRight: 0, marginLeft: 0 };
9284									css[ rtl ? 'marginRight' : 'marginLeft' ] = newMarginLeft;
9285									$g.css( css );
9286								} );
9287							}
9288						}
9289					}, 10 );
9290				}
9291				// end slideAccess integration
9292
9293				tp_inst._limitMinMaxDateTime( this.inst, true );
9294			}
9295		},
9296
9297		/*
9298		 * This function tries to limit the ability to go outside the
9299		 * min/max date range
9300		 */
9301		_limitMinMaxDateTime: function( dp_inst, adjustSliders ) {
9302			var o = this._defaults,
9303				dp_date = new Date( dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay );
9304			var minDateTime;
9305			var minDateTimeDate;
9306			var maxDateTime;
9307			var maxDateTimeDate;
9308			var tempMinTime;
9309			var tempMaxTime;
9310			var hourMax;
9311			var minMax;
9312			var secMax;
9313			var millisecMax;
vendor: 4,262 bytes, lines 9314-9400
9314			var microsecMax;
9315
9316			if ( ! this._defaults.showTimepicker ) {
9317				return;
9318			} // No time so nothing to check here
9319
9320			if ( $.tm_datepicker._get( dp_inst, 'minDateTime' ) !== null && $.tm_datepicker._get( dp_inst, 'minDateTime' ) !== undefined && dp_date ) {
9321				minDateTime = $.tm_datepicker._get( dp_inst, 'minDateTime' );
9322				minDateTimeDate = new Date( minDateTime.getFullYear(), minDateTime.getMonth(), minDateTime.getDate(), 0, 0, 0, 0 );
9323
9324				if ( this.hourMinOriginal === null || this.minuteMinOriginal === null || this.secondMinOriginal === null || this.millisecMinOriginal === null || this.microsecMinOriginal === null ) {
9325					this.hourMinOriginal = o.hourMin;
9326					this.minuteMinOriginal = o.minuteMin;
9327					this.secondMinOriginal = o.secondMin;
9328					this.millisecMinOriginal = o.millisecMin;
9329					this.microsecMinOriginal = o.microsecMin;
9330				}
9331
9332				if ( dp_inst.settings.timeOnly || minDateTimeDate.getTime() === dp_date.getTime() ) {
9333					this._defaults.hourMin = minDateTime.getHours();
9334					if ( this.hour <= this._defaults.hourMin ) {
9335						this.hour = this._defaults.hourMin;
9336						this._defaults.minuteMin = minDateTime.getMinutes();
9337						if ( this.minute <= this._defaults.minuteMin ) {
9338							this.minute = this._defaults.minuteMin;
9339							this._defaults.secondMin = minDateTime.getSeconds();
9340							if ( this.second <= this._defaults.secondMin ) {
9341								this.second = this._defaults.secondMin;
9342								this._defaults.millisecMin = minDateTime.getMilliseconds();
9343								if ( this.millisec <= this._defaults.millisecMin ) {
9344									this.millisec = this._defaults.millisecMin;
9345									this._defaults.microsecMin = minDateTime.getMicroseconds();
9346								} else {
9347									if ( this.microsec < this._defaults.microsecMin ) {
9348										this.microsec = this._defaults.microsecMin;
9349									}
9350									this._defaults.microsecMin = this.microsecMinOriginal;
9351								}
9352							} else {
9353								this._defaults.millisecMin = this.millisecMinOriginal;
9354								this._defaults.microsecMin = this.microsecMinOriginal;
9355							}
9356						} else {
9357							this._defaults.secondMin = this.secondMinOriginal;
9358							this._defaults.millisecMin = this.millisecMinOriginal;
9359							this._defaults.microsecMin = this.microsecMinOriginal;
9360						}
9361					} else {
9362						this._defaults.minuteMin = this.minuteMinOriginal;
9363						this._defaults.secondMin = this.secondMinOriginal;
9364						this._defaults.millisecMin = this.millisecMinOriginal;
9365						this._defaults.microsecMin = this.microsecMinOriginal;
9366					}
9367				} else {
9368					this._defaults.hourMin = this.hourMinOriginal;
9369					this._defaults.minuteMin = this.minuteMinOriginal;
9370					this._defaults.secondMin = this.secondMinOriginal;
9371					this._defaults.millisecMin = this.millisecMinOriginal;
9372					this._defaults.microsecMin = this.microsecMinOriginal;
9373				}
9374			}
9375
9376			if ( $.tm_datepicker._get( dp_inst, 'maxDateTime' ) !== null && $.tm_datepicker._get( dp_inst, 'maxDateTime' ) !== undefined && dp_date ) {
9377				maxDateTime = $.tm_datepicker._get( dp_inst, 'maxDateTime' );
9378				maxDateTimeDate = new Date( maxDateTime.getFullYear(), maxDateTime.getMonth(), maxDateTime.getDate(), 0, 0, 0, 0 );
9379
9380				if ( this.hourMaxOriginal === null || this.minuteMaxOriginal === null || this.secondMaxOriginal === null || this.millisecMaxOriginal === null ) {
9381					this.hourMaxOriginal = o.hourMax;
9382					this.minuteMaxOriginal = o.minuteMax;
9383					this.secondMaxOriginal = o.secondMax;
9384					this.millisecMaxOriginal = o.millisecMax;
9385					this.microsecMaxOriginal = o.microsecMax;
9386				}
9387
9388				if ( dp_inst.settings.timeOnly || maxDateTimeDate.getTime() === dp_date.getTime() ) {
9389					this._defaults.hourMax = maxDateTime.getHours();
9390					if ( this.hour >= this._defaults.hourMax ) {
9391						this.hour = this._defaults.hourMax;
9392						this._defaults.minuteMax = maxDateTime.getMinutes();
9393						if ( this.minute >= this._defaults.minuteMax ) {
9394							this.minute = this._defaults.minuteMax;
9395							this._defaults.secondMax = maxDateTime.getSeconds();
9396							if ( this.second >= this._defaults.secondMax ) {
9397								this.second = this._defaults.secondMax;
9398								this._defaults.millisecMax = maxDateTime.getMilliseconds();
9399								if ( this.millisec >= this._defaults.millisecMax ) {
9400									this.millisec = this._defaults.millisecMax;
9401									this._defaults.microsecMax = maxDateTime.getMicroseconds();
9402								} else {
9403									if ( this.microsec > this._defaults.microsecMax ) {
9404										this.microsec = this._defaults.microsecMax;
9405									}
9406									this._defaults.microsecMax = this.microsecMaxOriginal;
9407								}
9408							} else {
9409								this._defaults.millisecMax = this.millisecMaxOriginal;
9410								this._defaults.microsecMax = this.microsecMaxOriginal;
9411							}
9412						} else {
9413							this._defaults.secondMax = this.secondMaxOriginal;
9414							this._defaults.millisecMax = this.millisecMaxOriginal;
9415							this._defaults.microsecMax = this.microsecMaxOriginal;
9416						}
9417					} else {
9418						this._defaults.minuteMax = this.minuteMaxOriginal;
9419						this._defaults.secondMax = this.secondMaxOriginal;
9420						this._defaults.millisecMax = this.millisecMaxOriginal;
9421						this._defaults.microsecMax = this.microsecMaxOriginal;
9422					}
9423				} else {
9424					this._defaults.hourMax = this.hourMaxOriginal;
9425					this._defaults.minuteMax = this.minuteMaxOriginal;
9426					this._defaults.secondMax = this.secondMaxOriginal;
9427					this._defaults.millisecMax = this.millisecMaxOriginal;
9428					this._defaults.microsecMax = this.microsecMaxOriginal;
9429				}
9430			}
9431
9432			if ( dp_inst.settings.minTime !== null ) {
9433				tempMinTime = new Date( '01/01/1970 ' + dp_inst.settings.minTime );
9434				if ( parseFloat( dp_inst.settings.minTime ).toString() === dp_inst.settings.minTime ) {
9435					tempMinTime = new Date( Date.now() + parseFloat( dp_inst.settings.minTime ) );
9436				}
9437				if ( this.hour < tempMinTime.getHours() ) {
9438					this._defaults.hourMin = tempMinTime.getHours();
9439					this.hour = this._defaults.hourMin;
9440					this._defaults.minuteMin = tempMinTime.getMinutes();
9441					this.minute = this._defaults.minuteMin;
9442				} else if ( this.hour === tempMinTime.getHours() && this.minute < tempMinTime.getMinutes() ) {
9443					this._defaults.minuteMin = tempMinTime.getMinutes();
9444					this.minute = this._defaults.minuteMin;
9445				} else if ( this._defaults.hourMin < tempMinTime.getHours() ) {
9446					this._defaults.hourMin = tempMinTime.getHours();
9447					this._defaults.minuteMin = tempMinTime.getMinutes();
9448				} else if ( ( this._defaults.hourMin === tempMinTime.getHours() ) === this.hour && this._defaults.minuteMin < tempMinTime.getMinutes() ) {
9449					this._defaults.minuteMin = tempMinTime.getMinutes();
9450				} else {
9451					this._defaults.minuteMin = 0;
9452				}
9453			}
9454
9455			if ( dp_inst.settings.maxTime !== null ) {
9456				tempMaxTime = new Date( '01/01/1970 ' + dp_inst.settings.maxTime );
9457				if ( this.hour > tempMaxTime.getHours() ) {
9458					this._defaults.hourMax = tempMaxTime.getHours();
9459					this.hour = this._defaults.hourMax;
9460					this._defaults.minuteMax = tempMaxTime.getMinutes();
9461					this.minute = this._defaults.minuteMax;
9462				} else if ( this.hour === tempMaxTime.getHours() && this.minute > tempMaxTime.getMinutes() ) {
9463					this._defaults.minuteMax = tempMaxTime.getMinutes();
9464					this.minute = this._defaults.minuteMax;
9465				} else if ( this._defaults.hourMax > tempMaxTime.getHours() ) {
9466					this._defaults.hourMax = tempMaxTime.getHours();
9467					this._defaults.minuteMax = tempMaxTime.getMinutes();
9468				} else if ( ( this._defaults.hourMax === tempMaxTime.getHours() ) === this.hour && this._defaults.minuteMax > tempMaxTime.getMinutes() ) {
9469					this._defaults.minuteMax = tempMaxTime.getMinutes();
9470				} else {
9471					this._defaults.minuteMax = 59;
9472				}
9473			}
9474
9475			if ( adjustSliders !== undefined && adjustSliders === true ) {
9476				hourMax = parseInt( this._defaults.hourMax - ( ( this._defaults.hourMax - this._defaults.hourMin ) % this._defaults.stepHour ), 10 );
9477				minMax = parseInt( this._defaults.minuteMax - ( ( this._defaults.minuteMax - this._defaults.minuteMin ) % this._defaults.stepMinute ), 10 );
9478				secMax = parseInt( this._defaults.secondMax - ( ( this._defaults.secondMax - this._defaults.secondMin ) % this._defaults.stepSecond ), 10 );
9479				millisecMax = parseInt( this._defaults.millisecMax - ( ( this._defaults.millisecMax - this._defaults.millisecMin ) % this._defaults.stepMillisec ), 10 );
9480				microsecMax = parseInt( this._defaults.microsecMax - ( ( this._defaults.microsecMax - this._defaults.microsecMin ) % this._defaults.stepMicrosec ), 10 );
9481
9482				if ( this.hour_slider ) {
9483					this.control.options( this, this.hour_slider, 'hour', {
9484						min: this._defaults.hourMin,
9485						max: hourMax,
9486						step: this._defaults.stepHour
9487					} );
9488					this.control.value( this, this.hour_slider, 'hour', this.hour - ( this.hour % this._defaults.stepHour ) );
9489				}
9490				if ( this.minute_slider ) {
9491					this.control.options( this, this.minute_slider, 'minute', {
9492						min: this._defaults.minuteMin,
9493						max: minMax,
9494						step: this._defaults.stepMinute
9495					} );
9496					this.control.value( this, this.minute_slider, 'minute', this.minute - ( this.minute % this._defaults.stepMinute ) );
9497				}
9498				if ( this.second_slider ) {
9499					this.control.options( this, this.second_slider, 'second', {
9500						min: this._defaults.secondMin,
9501						max: secMax,
9502						step: this._defaults.stepSecond
9503					} );
9504					this.control.value( this, this.second_slider, 'second', this.second - ( this.second % this._defaults.stepSecond ) );
9505				}
9506				if ( this.millisec_slider ) {
9507					this.control.options( this, this.millisec_slider, 'millisec', {
9508						min: this._defaults.millisecMin,
9509						max: millisecMax,
9510						step: this._defaults.stepMillisec
9511					} );
9512					this.control.value( this, this.millisec_slider, 'millisec', this.millisec - ( this.millisec % this._defaults.stepMillisec ) );
9513				}
9514				if ( this.microsec_slider ) {
9515					this.control.options( this, this.microsec_slider, 'microsec', {
9516						min: this._defaults.microsecMin,
9517						max: microsecMax,
9518						step: this._defaults.stepMicrosec
9519					} );
9520					this.control.value( this, this.microsec_slider, 'microsec', this.microsec - ( this.microsec % this._defaults.stepMicrosec ) );
9521				}
9522			}
9523		},
9524
9525		/*
9526		 * when a slider moves, set the internal time...
9527		 * on time change is also called when the time is updated in the text field
9528		 */
9529		_onTimeChange: function() {
9530			var hour = this.hour_slider ? this.control.value( this, this.hour_slider, 'hour' ) : false,
9531				minute = this.minute_slider ? this.control.value( this, this.minute_slider, 'minute' ) : false,
9532				second = this.second_slider ? this.control.value( this, this.second_slider, 'second' ) : false,
9533				millisec = this.millisec_slider ? this.control.value( this, this.millisec_slider, 'millisec' ) : false,
9534				microsec = this.microsec_slider ? this.control.value( this, this.microsec_slider, 'microsec' ) : false,
9535				timezone = this.timezone_select ? this.timezone_select.val() : false,
9536				o = this._defaults,
9537				pickerTimeFormat = o.pickerTimeFormat || o.timeFormat,
9538				pickerTimeSuffix = o.pickerTimeSuffix || o.timeSuffix;
9539			var ampm;
9540			var hasChanged;
9541			var sPos;
9542			var ePos;
9543
9544			if ( ! this._defaults.showTimepicker ) {
9545				return;
9546			}
9547
9548			if ( typeof hour === 'object' ) {
9549				hour = false;
vendor: 20,658 bytes, lines 9550-10227
9550			}
9551			if ( typeof minute === 'object' ) {
9552				minute = false;
9553			}
9554			if ( typeof second === 'object' ) {
9555				second = false;
9556			}
9557			if ( typeof millisec === 'object' ) {
9558				millisec = false;
9559			}
9560			if ( typeof microsec === 'object' ) {
9561				microsec = false;
9562			}
9563			if ( typeof timezone === 'object' ) {
9564				timezone = false;
9565			}
9566
9567			if ( hour !== false ) {
9568				hour = parseInt( hour, 10 );
9569			}
9570			if ( minute !== false ) {
9571				minute = parseInt( minute, 10 );
9572			}
9573			if ( second !== false ) {
9574				second = parseInt( second, 10 );
9575			}
9576			if ( millisec !== false ) {
9577				millisec = parseInt( millisec, 10 );
9578			}
9579			if ( microsec !== false ) {
9580				microsec = parseInt( microsec, 10 );
9581			}
9582			if ( timezone !== false ) {
9583				timezone = timezone.toString();
9584			}
9585
9586			ampm = o[ hour < 12 ? 'amNames' : 'pmNames' ][ 0 ];
9587
9588			// If the update was done in the input field, the input field should not be updated.
9589			// If the update was done using the sliders, update the input field.
9590			hasChanged =
9591				hour !== parseInt( this.hour, 10 ) || // sliders should all be numeric
9592				minute !== parseInt( this.minute, 10 ) ||
9593				second !== parseInt( this.second, 10 ) ||
9594				millisec !== parseInt( this.millisec, 10 ) ||
9595				microsec !== parseInt( this.microsec, 10 ) ||
9596				( this.ampm.length > 0 && ( hour < 12 ) !== ( $.inArray( this.ampm.toUpperCase(), this.amNames ) !== -1 ) ) ||
9597				( this.timezone !== null && timezone !== this.timezone.toString() ); // could be numeric or "EST" format, so use toString()
9598
9599			if ( hasChanged ) {
9600				if ( hour !== false ) {
9601					this.hour = hour;
9602				}
9603				if ( minute !== false ) {
9604					this.minute = minute;
9605				}
9606				if ( second !== false ) {
9607					this.second = second;
9608				}
9609				if ( millisec !== false ) {
9610					this.millisec = millisec;
9611				}
9612				if ( microsec !== false ) {
9613					this.microsec = microsec;
9614				}
9615				if ( timezone !== false ) {
9616					this.timezone = timezone;
9617				}
9618
9619				if ( ! this.inst ) {
9620					this.inst = $.tm_datepicker._getInst( this.$input[ 0 ] );
9621				}
9622
9623				this._limitMinMaxDateTime( this.inst, true );
9624			}
9625			if ( this.support.ampm ) {
9626				this.ampm = ampm;
9627			}
9628
9629			// Updates the time within the tm_timepicker
9630			this.formattedTime = $.tm_datepicker.formatTime( o.timeFormat, this, o );
9631			if ( this.$timeObj ) {
9632				if ( pickerTimeFormat === o.timeFormat ) {
9633					this.$timeObj.val( this.formattedTime + pickerTimeSuffix );
9634				} else {
9635					this.$timeObj.val( $.tm_datepicker.formatTime( pickerTimeFormat, this, o ) + pickerTimeSuffix );
9636				}
9637				if ( this.$timeObj[ 0 ].setSelectionRange ) {
9638					sPos = this.$timeObj[ 0 ].selectionStart;
9639					ePos = this.$timeObj[ 0 ].selectionEnd;
9640					this.$timeObj[ 0 ].setSelectionRange( sPos, ePos );
9641				}
9642			}
9643
9644			this.timeDefined = true;
9645			if ( hasChanged ) {
9646				this._updateDateTime();
9647				//this.$input.trigger(' focus' ); // may automatically open the picker on setDate
9648			}
9649		},
9650
9651		/*
9652		 * call custom onSelect.
9653		 * bind to sliders slidestop, and grid click.
9654		 */
9655		_onSelectHandler: function() {
9656			var onSelect = this._defaults.onSelect || this.inst.settings.onSelect;
9657			var inputEl = this.$input ? this.$input[ 0 ] : null;
9658			if ( onSelect && inputEl ) {
9659				onSelect.apply( inputEl, [ this.formattedDateTime, this ] );
9660			}
9661		},
9662
9663		/*
9664		 * update our input with the new date time..
9665		 */
9666		_updateDateTime: function( dp_inst ) {
9667			var dtTmp;
9668			var dt;
9669			var dateFmt;
9670			var formatCfg;
9671			var timeAvailable;
9672			var formattedDateTime;
9673			var altFormattedDateTime = '';
9674			var altSeparator;
9675			var altTimeSuffix;
9676
9677			dp_inst = this.inst || dp_inst;
9678			dtTmp = dp_inst.currentYear > 0 ? new Date( dp_inst.currentYear, dp_inst.currentMonth, dp_inst.currentDay ) : new Date( dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay );
9679			dt = $.tm_datepicker._daylightSavingAdjust( dtTmp );
9680			//dt = $.tm_datepicker._daylightSavingAdjust(new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay)),
9681			//dt = $.tm_datepicker._daylightSavingAdjust(new Date(dp_inst.currentYear, dp_inst.currentMonth, dp_inst.currentDay)),
9682			dateFmt = $.tm_datepicker._get( dp_inst, 'dateFormat' );
9683			formatCfg = $.tm_datepicker._getFormatConfig( dp_inst );
9684			timeAvailable = dt !== null && this.timeDefined;
9685			this.formattedDate = $.tm_datepicker.formatDate( dateFmt, dt === null ? new Date() : dt, formatCfg );
9686			formattedDateTime = this.formattedDate;
9687
9688			// if a slider was changed but tm_datepicker doesn't have a value yet, set it
9689			if ( dp_inst.lastVal === '' ) {
9690				dp_inst.currentYear = dp_inst.selectedYear;
9691				dp_inst.currentMonth = dp_inst.selectedMonth;
9692				dp_inst.currentDay = dp_inst.selectedDay;
9693			}
9694
9695			/*
9696			 * remove following lines to force every changes in date picker to change the input value
9697			 * Bug descriptions: when an input field has a default value, and click on the field to pop up the date picker.
9698			 * If the user manually empty the value in the input field, the date picker will never change selected value.
9699			 */
9700			//if (dp_inst.lastVal !== undefined && (dp_inst.lastVal.length > 0 && this.$input.val().length === 0)) {
9701			//return;
9702			//}
9703
9704			if ( this._defaults.timeOnly === true && this._defaults.timeOnlyShowDate === false ) {
9705				formattedDateTime = this.formattedTime;
9706			} else if ( ( this._defaults.timeOnly !== true && ( this._defaults.alwaysSetTime || timeAvailable ) ) || ( this._defaults.timeOnly === true && this._defaults.timeOnlyShowDate === true ) ) {
9707				formattedDateTime += this._defaults.separator + this.formattedTime + this._defaults.timeSuffix;
9708			}
9709
9710			this.formattedDateTime = formattedDateTime;
9711
9712			if ( ! this._defaults.showTimepicker ) {
9713				this.$input.val( this.formattedDate );
9714			} else if ( this.$altInput && this._defaults.timeOnly === false && this._defaults.altFieldTimeOnly === true ) {
9715				this.$altInput.val( this.formattedTime );
9716				this.$input.val( this.formattedDate );
9717			} else if ( this.$altInput ) {
9718				this.$input.val( formattedDateTime );
9719				altFormattedDateTime = '';
9720				altSeparator = this._defaults.altSeparator !== null ? this._defaults.altSeparator : this._defaults.separator;
9721				altTimeSuffix = this._defaults.altTimeSuffix !== null ? this._defaults.altTimeSuffix : this._defaults.timeSuffix;
9722
9723				if ( ! this._defaults.timeOnly ) {
9724					if ( this._defaults.altFormat ) {
9725						altFormattedDateTime = $.tm_datepicker.formatDate( this._defaults.altFormat, dt === null ? new Date() : dt, formatCfg );
9726					} else {
9727						altFormattedDateTime = this.formattedDate;
9728					}
9729
9730					if ( altFormattedDateTime ) {
9731						altFormattedDateTime += altSeparator;
9732					}
9733				}
9734
9735				if ( this._defaults.altTimeFormat !== null ) {
9736					altFormattedDateTime += $.tm_datepicker.formatTime( this._defaults.altTimeFormat, this, this._defaults ) + altTimeSuffix;
9737				} else {
9738					altFormattedDateTime += this.formattedTime + altTimeSuffix;
9739				}
9740				this.$altInput.val( altFormattedDateTime );
9741			} else {
9742				this.$input.val( formattedDateTime );
9743			}
9744
9745			this.$input.trigger( 'change' );
9746		},
9747
9748		_onFocus: function() {
9749			var inst;
9750			var tp_inst;
9751			if ( ! this.$input.val() && this._defaults.defaultValue ) {
9752				this.$input.val( this._defaults.defaultValue );
9753				inst = $.tm_datepicker._getInst( this.$input.get( 0 ) );
9754				tp_inst = $.tm_datepicker._get( inst, 'tm_timepicker' );
9755				if ( tp_inst ) {
9756					if ( tp_inst._defaults.timeOnly && inst.input.val() !== inst.lastVal ) {
9757						try {
9758							$.tm_datepicker._updateDatepicker( inst );
9759						} catch ( err ) {
9760							$.tm_timepicker.log( err );
9761						}
9762					}
9763				}
9764			}
9765		},
9766
9767		/*
9768		 * Small abstraction to control types
9769		 * We can add more, just be sure to follow the pattern: create, options, value
9770		 */
9771		_controls: {
9772			// slider methods
9773			slider: {
9774				create: function( tp_inst, obj, unit, val, min, max, step ) {
9775					var rtl = tp_inst._defaults.isRTL; // if rtl go -60->0 instead of 0->60
9776					return obj.prop( 'slide', null ).slider( {
9777						orientation: 'horizontal',
9778						value: rtl ? val * -1 : val,
9779						min: rtl ? max * -1 : min,
9780						max: rtl ? min * -1 : max,
9781						step: step,
9782						slide: function( event, ui ) {
9783							tp_inst.control.value( tp_inst, $( this ), unit, rtl ? ui.value * -1 : ui.value );
9784							tp_inst._onTimeChange();
9785						},
9786						stop: function() {
9787							tp_inst._onSelectHandler();
9788						}
9789					} );
9790				},
9791				options: function( tp_inst, obj, unit, opts, val ) {
9792					var min;
9793					var max;
9794					if ( tp_inst._defaults.isRTL ) {
9795						if ( typeof opts === 'string' ) {
9796							if ( opts === 'min' || opts === 'max' ) {
9797								if ( val !== undefined ) {
9798									return obj.slider( opts, val * -1 );
9799								}
9800								return Math.abs( obj.slider( opts ) );
9801							}
9802							return obj.slider( opts );
9803						}
9804						min = opts.min;
9805						max = opts.max;
9806						opts.max = null;
9807						opts.min = null;
9808						if ( min !== undefined ) {
9809							opts.max = min * -1;
9810						}
9811						if ( max !== undefined ) {
9812							opts.min = max * -1;
9813						}
9814						return obj.slider( opts );
9815					}
9816					if ( typeof opts === 'string' && val !== undefined ) {
9817						return obj.slider( opts, val );
9818					}
9819					return obj.slider( opts );
9820				},
9821				value: function( tp_inst, obj, unit, val ) {
9822					if ( tp_inst._defaults.isRTL ) {
9823						if ( val !== undefined ) {
9824							return obj.slider( 'value', val * -1 );
9825						}
9826						return Math.abs( obj.slider( 'value' ) );
9827					}
9828					if ( val !== undefined ) {
9829						return obj.slider( 'value', val );
9830					}
9831					return obj.slider( 'value' );
9832				}
9833			},
9834			// select methods
9835			select: {
9836				create: function( tp_inst, obj, unit, val, min, max, step ) {
9837					var sel = '<select class="ui-tm-timepicker-select ui-state-default ui-corner-all" data-unit="' + unit + '" data-min="' + min + '" data-max="' + max + '" data-step="' + step + '">',
9838						format = tp_inst._defaults.pickerTimeFormat || tp_inst._defaults.timeFormat;
9839					var i;
9840
9841					for ( i = min; i <= max; i += step ) {
9842						sel += '<option value="' + i + '"' + ( i === val ? ' selected' : '' ) + '>';
9843						if ( unit === 'hour' ) {
9844							sel += $.tm_datepicker.formatTime( $.epoAPI.util.trim( format.replace( /[^ht ]/gi, '' ) ), { hour: i }, tp_inst._defaults );
9845						} else if ( unit === 'millisec' || unit === 'microsec' || i >= 10 ) {
9846							sel += i;
9847						} else {
9848							sel += '0' + i.toString();
9849						}
9850						sel += '</option>';
9851					}
9852					sel += '</select>';
9853
9854					obj.children( 'select' ).remove();
9855
9856					$( sel )
9857						.appendTo( obj )
9858						.on( 'change', function() {
9859							tp_inst._onTimeChange();
9860							tp_inst._onSelectHandler();
9861							tp_inst._afterInject();
9862						} );
9863
9864					return obj;
9865				},
9866				options: function( tp_inst, obj, unit, opts, val ) {
9867					var o = {},
9868						$t = obj.children( 'select' );
9869					if ( typeof opts === 'string' ) {
9870						if ( val === undefined ) {
9871							return $t.data( opts );
9872						}
9873						o[ opts ] = val;
9874					} else {
9875						o = opts;
9876					}
9877					return tp_inst.control.create( tp_inst, obj, $t.data( 'unit' ), $t.val(), o.min >= 0 ? o.min : $t.data( 'min' ), o.max || $t.data( 'max' ), o.step || $t.data( 'step' ) );
9878				},
9879				value: function( tp_inst, obj, unit, val ) {
9880					var $t = obj.children( 'select' );
9881					if ( val !== undefined ) {
9882						return $t.val( val );
9883					}
9884					return $t.val();
9885				}
9886			}
9887		} // end _controls
9888	} );
9889
9890	$.fn.extend( {
9891		/*
9892		 * shorthand just to use tm_timepicker.
9893		 */
9894		tm_timepicker: function( o ) {
9895			var tmp_args = Array.prototype.slice.call( arguments );
9896			o = o || {};
9897
9898			if ( typeof o === 'object' ) {
9899				tmp_args[ 0 ] = $.extend( o, {
9900					timeOnly: true
9901				} );
9902			}
9903
9904			return $( this ).each( function() {
9905				$.fn.tm_datetimepicker.apply( $( this ), tmp_args );
9906			} );
9907		},
9908
9909		/*
9910		 * extend tm_timepicker to tm_datepicker
9911		 */
9912		tm_datetimepicker: function( o ) {
9913			var tmp_args = arguments;
9914			o = o || {};
9915
9916			if ( typeof o === 'string' ) {
9917				if ( o === 'getDate' || ( o === 'option' && tmp_args.length === 2 && typeof tmp_args[ 1 ] === 'string' ) ) {
9918					return $.fn.tm_datepicker.apply( $( this[ 0 ] ), tmp_args );
9919				}
9920				return this.each( function() {
9921					var $t = $( this );
9922					$t.tm_datepicker.apply( $t, tmp_args );
9923				} );
9924			}
9925			return this.each( function() {
9926				var $t = $( this );
9927				$t.tm_datepicker( $.tm_timepicker._newInst( $t, o )._defaults );
9928			} );
9929		}
9930	} );
9931
9932	/*
9933	 * Public Utility to parse date and time
9934	 */
9935	$.tm_datepicker.parseDateTime = function( dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings ) {
9936		var parseRes = parseDateTimeInternal( dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings );
9937		var t;
9938		if ( parseRes.timeObj ) {
9939			t = parseRes.timeObj;
9940			parseRes.date.setHours( t.hour, t.minute, t.second, t.millisec );
9941			parseRes.date.setMicroseconds( t.microsec );
9942		}
9943
9944		return parseRes.date;
9945	};
9946
9947	/*
9948	 * Public utility to parse time
9949	 */
9950	$.tm_datepicker.parseTime = function( timeFormat, timeString, options ) {
9951		var o = extendRemove( extendRemove( {}, $.tm_timepicker._defaults ), options || {} );
9952
9953		// Strict parse requires the timeString to match the timeFormat exactly
9954		var strictParse = function( f, s, oo ) {
9955			// pattern for standard and localized AM/PM markers
9956			var getPatternAmpm = function( amNames, pmNames ) {
9957				var markers = [];
9958				if ( amNames ) {
9959					$.merge( markers, amNames );
9960				}
9961				if ( pmNames ) {
9962					$.merge( markers, pmNames );
9963				}
9964				markers = $.map( markers, function( val ) {
9965					return val.replace( /[.*+?|()[\]{}\\]/g, '\\$&' );
9966				} );
9967				return '(' + markers.join( '|' ) + ')?';
9968			};
9969
9970			// figure out position of time elements.. cause js cant do named captures
9971			var getFormatPositions = function( formatoftime ) {
9972				var finds = formatoftime.toLowerCase().match( /(h{1,2}|m{1,2}|s{1,2}|l{1}|c{1}|t{1,2}|z|'.*?')/g ),
9973					orders = {
9974						h: -1,
9975						m: -1,
9976						s: -1,
9977						l: -1,
9978						c: -1,
9979						t: -1,
9980						z: -1
9981					};
9982				var i;
9983
9984				if ( finds ) {
9985					for ( i = 0; i < finds.length; i += 1 ) {
9986						if ( orders[ finds[ i ].toString().charAt( 0 ) ] === -1 ) {
9987							orders[ finds[ i ].toString().charAt( 0 ) ] = i + 1;
9988						}
9989					}
9990				}
9991				return orders;
9992			};
9993
9994			var regstr =
9995					'^' +
9996					f
9997						.toString()
9998						.replace( /([hH]{1,2}|mm?|ss?|[tT]{1,2}|[zZ]|[lc]|'.*?')/g, function( match ) {
9999							var ml = match.length;
10000							switch ( match.charAt( 0 ).toLowerCase() ) {
10001								case 'h':
10002									return ml === 1 ? '(\\d?\\d)' : '(\\d{' + ml + '})';
10003								case 'm':
10004									return ml === 1 ? '(\\d?\\d)' : '(\\d{' + ml + '})';
10005								case 's':
10006									return ml === 1 ? '(\\d?\\d)' : '(\\d{' + ml + '})';
10007								case 'l':
10008									return '(\\d?\\d?\\d)';
10009								case 'c':
10010									return '(\\d?\\d?\\d)';
10011								case 'z':
10012									return '(z|[-+]\\d\\d:?\\d\\d|\\S+)?';
10013								case 't':
10014									return getPatternAmpm( oo.amNames, oo.pmNames );
10015								default:
10016									// literal escaped in quotes
10017									return (
10018										'(' +
10019										match.replace( /'/g, '' ).replace( /(\.|\$|\^|\\|\/|\(|\)|\[|\]|\?|\+|\*)/g, function( m ) {
10020											return '\\' + m;
10021										} ) +
10022										')?'
10023									);
10024							}
10025						} )
10026						.replace( /\s/g, '\\s?' ) +
10027					oo.timeSuffix +
10028					'$',
10029				order = getFormatPositions( f ),
10030				ampm = '',
10031				treg;
10032
10033			var resTime = {
10034				hour: 0,
10035				minute: 0,
10036				second: 0,
10037				millisec: 0,
10038				microsec: 0
10039			};
10040
10041			treg = s.match( new RegExp( regstr, 'i' ) );
10042
10043			if ( treg ) {
10044				if ( order.t !== -1 ) {
10045					if ( treg[ order.t ] === undefined || treg[ order.t ].length === 0 ) {
10046						ampm = '';
10047						resTime.ampm = '';
10048					} else {
10049						ampm =
10050							$.inArray(
10051								treg[ order.t ].toUpperCase(),
10052								$.map( oo.amNames, function( x ) {
10053									return x.toUpperCase();
10054								} )
10055							) !== -1
10056								? 'AM'
10057								: 'PM';
10058						resTime.ampm = oo[ ampm === 'AM' ? 'amNames' : 'pmNames' ][ 0 ];
10059					}
10060				}
10061
10062				if ( order.h !== -1 ) {
10063					if ( ampm === 'AM' && treg[ order.h ] === '12' ) {
10064						resTime.hour = 0; // 12am = 0 hour
10065					} else if ( ampm === 'PM' && treg[ order.h ] !== '12' ) {
10066						resTime.hour = parseInt( treg[ order.h ], 10 ) + 12; // 12pm = 12 hour, any other pm = hour + 12
10067					} else {
10068						resTime.hour = Number( treg[ order.h ] );
10069					}
10070				}
10071
10072				if ( order.m !== -1 ) {
10073					resTime.minute = Number( treg[ order.m ] );
10074				}
10075				if ( order.s !== -1 ) {
10076					resTime.second = Number( treg[ order.s ] );
10077				}
10078				if ( order.l !== -1 ) {
10079					resTime.millisec = Number( treg[ order.l ] );
10080				}
10081				if ( order.c !== -1 ) {
10082					resTime.microsec = Number( treg[ order.c ] );
10083				}
10084				if ( order.z !== -1 && treg[ order.z ] !== undefined ) {
10085					resTime.timezone = $.tm_timepicker.timezoneOffsetNumber( treg[ order.z ] );
10086				}
10087
10088				return resTime;
10089			}
10090			return false;
10091		}; // end strictParse
10092
10093		// First try JS Date, if that fails, use strictParse
10094		var looseParse = function( f, s, oo ) {
10095			var d;
10096			try {
10097				d = new Date( '2012-01-01 ' + s );
10098				if ( isNaN( d.getTime() ) ) {
10099					d = new Date( '2012-01-01T' + s );
10100					if ( isNaN( d.getTime() ) ) {
10101						d = new Date( '01/01/2012 ' + s );
10102						if ( isNaN( d.getTime() ) ) {
10103							throw 'Unable to parse time with native Date: ' + s;
10104						}
10105					}
10106				}
10107
10108				return {
10109					hour: d.getHours(),
10110					minute: d.getMinutes(),
10111					second: d.getSeconds(),
10112					millisec: d.getMilliseconds(),
10113					microsec: d.getMicroseconds(),
10114					timezone: d.getTimezoneOffset() * -1
10115				};
10116			} catch ( err ) {
10117				try {
10118					return strictParse( f, s, oo );
10119				} catch ( err2 ) {
10120					$.tm_timepicker.log( 'Unable to parse \ntimeString: ' + s + '\ntimeFormat: ' + f );
10121				}
10122			}
10123			return false;
10124		}; // end looseParse
10125
10126		if ( typeof o.parse === 'function' ) {
10127			return o.parse( timeFormat, timeString, o );
10128		}
10129		if ( o.parse === 'loose' ) {
10130			return looseParse( timeFormat, timeString, o );
10131		}
10132		return strictParse( timeFormat, timeString, o );
10133	};
10134
10135	/**
10136	 * Public utility to format the time.
10137	 *
10138	 * @param {string} format    format of the time
10139	 * @param {Object} time      Object not a Date for timezones
10140	 * @param {Object} [options] essentially the regional[].. amNames, pmNames, ampm
10141	 * @returns {string} the formatted time
10142	 */
10143	$.tm_datepicker.formatTime = function( format, time, options ) {
10144		var tmptime;
10145		var ampmName;
10146		var hour;
10147
10148		options = options || {};
10149		options = $.extend( {}, $.tm_timepicker._defaults, options );
10150		time = $.extend(
10151			{
10152				hour: 0,
10153				minute: 0,
10154				second: 0,
10155				millisec: 0,
10156				microsec: 0,
10157				timezone: null
10158			},
10159			time
10160		);
10161
10162		tmptime = format;
10163		ampmName = options.amNames[ 0 ];
10164		hour = parseInt( time.hour, 10 );
10165
10166		if ( hour > 11 ) {
10167			ampmName = options.pmNames[ 0 ];
10168		}
10169
10170		tmptime = tmptime.replace( /(?:HH?|hh?|mm?|ss?|[tT]{1,2}|[zZ]|[lc]|'.*?')/g, function( match ) {
10171			switch ( match ) {
10172				case 'HH':
10173					return ( '0' + hour ).slice( -2 );
10174				case 'H':
10175					return hour;
10176				case 'hh':
10177					return ( '0' + convert24to12( hour ) ).slice( -2 );
10178				case 'h':
10179					return convert24to12( hour );
10180				case 'mm':
10181					return ( '0' + time.minute ).slice( -2 );
10182				case 'm':
10183					return time.minute;
10184				case 'ss':
10185					return ( '0' + time.second ).slice( -2 );
10186				case 's':
10187					return time.second;
10188				case 'l':
10189					return ( '00' + time.millisec ).slice( -3 );
10190				case 'c':
10191					return ( '00' + time.microsec ).slice( -3 );
10192				case 'z':
10193					return $.tm_timepicker.timezoneOffsetString( time.timezone === null ? options.timezone : time.timezone, false );
10194				case 'Z':
10195					return $.tm_timepicker.timezoneOffsetString( time.timezone === null ? options.timezone : time.timezone, true );
10196				case 'T':
10197					return ampmName.charAt( 0 ).toUpperCase();
10198				case 'TT':
10199					return ampmName.toUpperCase();
10200				case 't':
10201					return ampmName.charAt( 0 ).toLowerCase();
10202				case 'tt':
10203					return ampmName.toLowerCase();
10204				default:
10205					return match.replace( /'/g, '' );
10206			}
10207		} );
10208
10209		return tmptime;
10210	};
10211
10212	/*
10213     * the bad hack :/ override tm_datepicker so it doesn't close on select
10214     // inspired: http://stackoverflow.com/questions/1252512/jquery-datepicker-prevent-closing-picker-when-clicking-a-date/1762378#1762378
10215     */
10216	$.tm_datepicker._base_selectDate = $.tm_datepicker._selectDate;
10217	$.tm_datepicker._selectDate = function( id, dateStr ) {
10218		var inst = this._getInst( $( id )[ 0 ] ),
10219			tp_inst = this._get( inst, 'tm_timepicker' ),
10220			was_inline;
10221
10222		if ( tp_inst && inst.settings.showTimepicker ) {
10223			tp_inst._limitMinMaxDateTime( inst, true );
10224			was_inline = inst.inline;
10225			inst.stay_open = true;
10226			inst.inline = true;
10227			//This way the onSelect handler called from calendarpicker get the full dateTime
10228			this._base_selectDate( id, dateStr );
10229			inst.inline = was_inline;
10230			inst.stay_open = false;
10231			this._notifyChange( inst );
10232			this._updateDatepicker( inst );
10233		} else {
10234			this._base_selectDate( id, dateStr );
10235		}
10236	};
10237
10238	/*
10239	 * second bad hack :/ override tm_datepicker so it triggers an event when changing the input field
10240	 * and does not redraw the tm_datepicker on every selectDate event
10241	 */
10242	$.tm_datepicker._base_updateDatepicker = $.tm_datepicker._updateDatepicker;
10243	$.tm_datepicker._updateDatepicker = function( inst ) {
10244		// don't popup the tm_datepicker if there is another instance already opened
10245		var input = inst.input[ 0 ];
10246		var tp_inst;
10247		if ( $.tm_datepicker._curInst && $.tm_datepicker._curInst !== inst && $.tm_datepicker._datepickerShowing && $.tm_datepicker._lastInput !== input ) {
10248			return;
10249		}
10250
10251		if ( typeof inst.stay_open !== 'boolean' || inst.stay_open === false ) {
10252			this._base_updateDatepicker( inst );
10253
10254			// Reload the time control when changing something in the input text field.
10255			tp_inst = this._get( inst, 'tm_timepicker' );
10256			if ( tp_inst ) {
10257				tp_inst._addTimePicker( inst );
10258			}
10259		}
10260	};
10261
10262	/*
10263	 * third bad hack :/ override tm_datepicker so it allows spaces and colon in the input field
10264	 */
10265	$.tm_datepicker._base_doKeyPress = $.tm_datepicker._doKeyPress;
10266	$.tm_datepicker._doKeyPress = function( event ) {
10267		var inst = $.tm_datepicker._getInst( event.target ),
10268			tp_inst = $.tm_datepicker._get( inst, 'tm_timepicker' );
10269		var ampm;
10270		var tz;
10271		var dateChars;
10272		var datetimeChars;
10273		var chr;
10274
10275		if ( tp_inst ) {
10276			if ( $.tm_datepicker._get( inst, 'constrainInput' ) ) {
10277				ampm = tp_inst.support.ampm;
10278				tz = tp_inst._defaults.showTimezone !== null ? tp_inst._defaults.showTimezone : tp_inst.support.timezone;
10279				dateChars = $.tm_datepicker._possibleChars( $.tm_datepicker._get( inst, 'dateFormat' ) );
10280				datetimeChars =
10281					tp_inst._defaults.timeFormat
10282						.toString()
10283						.replace( /[hms]/g, '' )
10284						.replace( /TT/g, ampm ? 'APM' : '' )
10285						.replace( /Tt/g, ampm ? 'AaPpMm' : '' )
10286						.replace( /tT/g, ampm ? 'AaPpMm' : '' )
10287						.replace( /T/g, ampm ? 'AP' : '' )
10288						.replace( /tt/g, ampm ? 'apm' : '' )
10289						.replace( /t/g, ampm ? 'ap' : '' ) +
10290					' ' +
10291					tp_inst._defaults.separator +
10292					tp_inst._defaults.timeSuffix +
10293					( tz ? tp_inst._defaults.timezoneList.join( '' ) : '' ) +
10294					tp_inst._defaults.amNames.join( '' ) +
10295					tp_inst._defaults.pmNames.join( '' ) +
10296					dateChars;
10297				chr = String.fromCharCode( event.charCode === undefined ? event.keyCode : event.charCode );
10298				return event.ctrlKey || chr < ' ' || ! dateChars || datetimeChars.indexOf( chr ) > -1;
10299			}
10300		}
10301
10302		return $.tm_datepicker._base_doKeyPress( event );
10303	};
10304
10305	/*
10306	 * Fourth bad hack :/ override _updateAlternate function used in inline mode to init altField
10307	 * Update any alternate field to synchronise with the main field.
10308	 */
10309	$.tm_datepicker._base_updateAlternate = $.tm_datepicker._updateAlternate;
10310	$.tm_datepicker._updateAlternate = function( inst ) {
10311		var tp_inst = this._get( inst, 'tm_timepicker' );
10312		var altField;
10313		var date;
10314		var formatCfg;
10315		var altFormattedDateTime;
10316		var altSeparator;
10317		var altTimeSuffix;
10318		var altTimeFormat;
10319		if ( tp_inst ) {
10320			altField = tp_inst._defaults.altField;
10321			if ( altField ) {
10322				// update alternate field too
10323				date = this._getDate( inst );
10324				formatCfg = $.tm_datepicker._getFormatConfig( inst );
10325				altFormattedDateTime = '';
10326				altSeparator = tp_inst._defaults.altSeparator ? tp_inst._defaults.altSeparator : tp_inst._defaults.separator;
10327				altTimeSuffix = tp_inst._defaults.altTimeSuffix ? tp_inst._defaults.altTimeSuffix : tp_inst._defaults.timeSuffix;
10328				altTimeFormat = tp_inst._defaults.altTimeFormat !== null ? tp_inst._defaults.altTimeFormat : tp_inst._defaults.timeFormat;
10329
10330				altFormattedDateTime += $.tm_datepicker.formatTime( altTimeFormat, tp_inst, tp_inst._defaults ) + altTimeSuffix;
10331				if ( ! tp_inst._defaults.timeOnly && ! tp_inst._defaults.altFieldTimeOnly && date !== null ) {
10332					if ( tp_inst._defaults.altFormat ) {
10333						altFormattedDateTime = $.tm_datepicker.formatDate( tp_inst._defaults.altFormat, date, formatCfg ) + altSeparator + altFormattedDateTime;
10334					} else {
10335						altFormattedDateTime = tp_inst.formattedDate + altSeparator + altFormattedDateTime;
10336					}
10337				}
10338				$( altField ).val( inst.input.val() ? altFormattedDateTime : '' );
10339			}
10340		} else {
10341			$.tm_datepicker._base_updateAlternate( inst );
10342		}
10343	};
10344
10345	/*
10346	 * Override key up event to sync manual input changes.
10347	 */
10348	$.tm_datepicker._base_doKeyUp = $.tm_datepicker._doKeyUp;
10349	$.tm_datepicker._doKeyUp = function( event ) {
10350		var inst = $.tm_datepicker._getInst( event.target ),
10351			tp_inst = $.tm_datepicker._get( inst, 'tm_timepicker' );
10352
10353		if ( tp_inst ) {
10354			if ( tp_inst._defaults.timeOnly && inst.input.val() !== inst.lastVal ) {
10355				try {
10356					$.tm_datepicker._updateDatepicker( inst );
10357				} catch ( err ) {
10358					$.tm_timepicker.log( err );
10359				}
10360			}
10361		}
10362
10363		return $.tm_datepicker._base_doKeyUp( event );
10364	};
10365
10366	/*
10367	 * override "Today" button to also grab the time and set it to input field.
10368	 */
10369	$.tm_datepicker._base_gotoToday = $.tm_datepicker._gotoToday;
10370	$.tm_datepicker._gotoToday = function( id ) {
10371		var inst = this._getInst( $( id )[ 0 ] );
10372		var tp_inst;
10373		var tzoffset;
10374		var now;
10375		this._base_gotoToday( id );
10376		tp_inst = this._get( inst, 'timepicker' );
10377		if ( ! tp_inst ) {
10378			return;
10379		}
10380
10381		tzoffset = $.tm_timepicker.timezoneOffsetNumber( tp_inst.timezone );
10382		now = new Date();
10383		now.setMinutes( now.getMinutes() + now.getTimezoneOffset() + parseInt( tzoffset, 10 ) );
10384		this._setTime( inst, now );
10385		this._setDate( inst, now );
10386		tp_inst._onSelectHandler();
10387	};
10388
10389	/*
10390	 * Disable & enable the Time in the datetimepicker
10391	 */
10392	$.tm_datepicker._disableTimepickerDatepicker = function( target ) {
10393		var inst = this._getInst( target );
10394		var tp_inst;
10395		if ( ! inst ) {
10396			return;
10397		}
10398
10399		tp_inst = this._get( inst, 'tm_timepicker' );
10400		$( target ).tm_datepicker( 'getDate' ); // Init selected[Year|Month|Day]
10401		if ( tp_inst ) {
10402			inst.settings.showTimepicker = false;
10403			tp_inst._defaults.showTimepicker = false;
10404			tp_inst._updateDateTime( inst );
10405		}
10406	};
10407
10408	$.tm_datepicker._enableTimepickerDatepicker = function( target ) {
10409		var inst = this._getInst( target );
10410		var tp_inst;
10411		if ( ! inst ) {
10412			return;
10413		}
10414
10415		tp_inst = this._get( inst, 'tm_timepicker' );
10416		$( target ).tm_datepicker( 'getDate' ); // Init selected[Year|Month|Day]
10417		if ( tp_inst ) {
10418			inst.settings.showTimepicker = true;
10419			tp_inst._defaults.showTimepicker = true;
10420			tp_inst._addTimePicker( inst ); // Could be disabled on page load
10421			tp_inst._updateDateTime( inst );
10422		}
10423	};
10424
10425	/*
10426	 * Create our own set time function
10427	 */
10428	$.tm_datepicker._setTime = function( inst, date ) {
10429		var tp_inst = this._get( inst, 'tm_timepicker' );
10430		var defaults;
10431		if ( tp_inst ) {
10432			defaults = tp_inst._defaults;
10433
10434			// calling _setTime with no date sets time to defaults
10435			tp_inst.hour = date ? date.getHours() : defaults.hour;
10436			tp_inst.minute = date ? date.getMinutes() : defaults.minute;
10437			tp_inst.second = date ? date.getSeconds() : defaults.second;
10438			tp_inst.millisec = date ? date.getMilliseconds() : defaults.millisec;
10439			tp_inst.microsec = date ? date.getMicroseconds() : defaults.microsec;
10440
10441			//check if within min/max times..
10442			tp_inst._limitMinMaxDateTime( inst, true );
10443
10444			tp_inst._onTimeChange();
10445			tp_inst._updateDateTime( inst );
10446		}
10447	};
10448
10449	/*
10450	 * Create new public method to set only time, callable as $().tm_datepicker('setTime', date)
10451	 */
10452	$.tm_datepicker._setTimeDatepicker = function( target, date, withDate ) {
10453		var inst = this._getInst( target );
10454		var tp_inst;
10455		var tp_date;
10456
10457		if ( ! inst ) {
10458			return;
10459		}
10460
10461		tp_inst = this._get( inst, 'tm_timepicker' );
10462
10463		if ( tp_inst ) {
10464			this._setDateFromField( inst );
10465
10466			if ( date ) {
10467				if ( typeof date === 'string' ) {
10468					tp_inst._parseTime( date, withDate );
10469					tp_date = new Date();
10470					tp_date.setHours( tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec );
10471					tp_date.setMicroseconds( tp_inst.microsec );
10472				} else {
10473					tp_date = new Date( date.getTime() );
10474					tp_date.setMicroseconds( date.getMicroseconds() );
10475				}
10476				if ( tp_date.toString() === 'Invalid Date' ) {
10477					tp_date = undefined;
10478				}
10479				this._setTime( inst, tp_date );
10480			}
10481		}
10482	};
10483
10484	/*
10485	 * override setDate() to allow setting time too within Date object
10486	 */
10487	$.tm_datepicker._base_setDateDatepicker = $.tm_datepicker._setDateDatepicker;
10488	$.tm_datepicker._setDateDatepicker = function( target, _date ) {
10489		var inst = this._getInst( target );
10490		var date = _date;
10491		var tp_inst;
10492		var tp_date;
10493		if ( ! inst ) {
10494			return;
10495		}
10496
10497		if ( typeof _date === 'string' ) {
10498			date = new Date( _date );
10499			if ( ! date.getTime() ) {
10500				this._base_setDateDatepicker.apply( this, arguments );
10501				date = $( target ).tm_datepicker( 'getDate' );
10502			}
10503		}
10504
10505		tp_inst = this._get( inst, 'tm_timepicker' );
10506		if ( date instanceof Date ) {
10507			tp_date = new Date( date.getTime() );
10508			tp_date.setMicroseconds( date.getMicroseconds() );
10509		} else {
10510			tp_date = date;
10511		}
10512
10513		// This is important if you are using the timezone option, javascript's Date
10514		// object will only return the timezone offset for the current locale, so we
10515		// adjust it accordingly.  If not using timezone option this won't matter..
10516		// If a timezone is different in tp, keep the timezone as is
10517		if ( tp_inst && tp_date ) {
10518			// look out for DST if tz wasn't specified
10519			if ( ! tp_inst.support.timezone && tp_inst._defaults.timezone === null ) {
10520				tp_inst.timezone = tp_date.getTimezoneOffset() * -1;
10521			}
10522			date = $.tm_timepicker.timezoneAdjust( date, $.tm_timepicker.timezoneOffsetString( -date.getTimezoneOffset() ), tp_inst.timezone );
10523			tp_date = $.tm_timepicker.timezoneAdjust( tp_date, $.tm_timepicker.timezoneOffsetString( -tp_date.getTimezoneOffset() ), tp_inst.timezone );
10524		}
10525
10526		this._updateDatepicker( inst );
10527		this._base_setDateDatepicker.apply( this, arguments );
10528		this._setTimeDatepicker( target, tp_date, true );
10529	};
10530
10531	/*
10532	 * override getDate() to allow getting time too within Date object
10533	 */
10534	$.tm_datepicker._base_getDateDatepicker = $.tm_datepicker._getDateDatepicker;
10535	$.tm_datepicker._getDateDatepicker = function( target, noDefault ) {
10536		var inst = this._getInst( target );
10537		var tp_inst;
10538		var date;
10539		var currDT;
10540
10541		if ( ! inst ) {
10542			return;
10543		}
10544
10545		tp_inst = this._get( inst, 'tm_timepicker' );
10546
10547		if ( tp_inst ) {
10548			// if it hasn't yet been defined, grab from field
10549			if ( inst.lastVal === undefined ) {
10550				this._setDateFromField( inst, noDefault );
10551			}
10552
10553			date = this._getDate( inst );
10554
10555			currDT = null;
10556
10557			if ( tp_inst.$altInput && tp_inst._defaults.altFieldTimeOnly ) {
10558				currDT = tp_inst.$input.val() + ' ' + tp_inst.$altInput.val();
10559			} else if ( tp_inst.$input.get( 0 ).tagName !== 'INPUT' && tp_inst.$altInput ) {
10560				/**
10561				 * in case the datetimepicker has been applied to a non-input tag for inline UI,
10562				 * and the user has not configured the plugin to display only time in altInput,
10563				 * pick current date time from the altInput (and hope for the best, for now, until "ER1" is applied)
10564				 *
10565				 * @todo ER1. Since altInput can have a totally difference format, convert it to standard format by reading input format from "altFormat" and "altTimeFormat" option values
10566				 */
10567				currDT = tp_inst.$altInput.val();
10568			} else {
10569				currDT = tp_inst.$input.val();
10570			}
10571
10572			if ( date && tp_inst._parseTime( currDT, ! inst.settings.timeOnly ) ) {
vendor: 7,140 bytes, lines 10573-10790
10573				date.setHours( tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec );
10574				date.setMicroseconds( tp_inst.microsec );
10575
10576				// This is important if you are using the timezone option, javascript's Date
10577				// object will only return the timezone offset for the current locale, so we
10578				// adjust it accordingly.  If not using timezone option this won't matter..
10579				if ( tp_inst.timezone !== null ) {
10580					// look out for DST if tz wasn't specified
10581					if ( ! tp_inst.support.timezone && tp_inst._defaults.timezone === null ) {
10582						tp_inst.timezone = date.getTimezoneOffset() * -1;
10583					}
10584					date = $.tm_timepicker.timezoneAdjust( date, tp_inst.timezone, $.tm_timepicker.timezoneOffsetString( -date.getTimezoneOffset() ) );
10585				}
10586			}
10587			return date;
10588		}
10589		return this._base_getDateDatepicker( target, noDefault );
10590	};
10591
10592	/*
10593	 * override parseDate() because UI 1.8.14 throws an error about "Extra characters"
10594	 * An option in datapicker to ignore extra format characters would be nicer.
10595	 */
10596	$.tm_datepicker._base_parseDate = $.tm_datepicker.parseDate;
10597	$.tm_datepicker.parseDate = function( format, value, settings ) {
10598		var date;
10599		try {
10600			date = this._base_parseDate( format, value, settings );
10601		} catch ( err ) {
10602			// Hack!  The error message ends with a colon, a space, and
10603			// the "extra" characters.  We rely on that instead of
10604			// attempting to perfectly reproduce the parsing algorithm.
10605			if ( err.indexOf( ':' ) >= 0 ) {
10606				date = this._base_parseDate( format, value.substring( 0, value.length - ( err.length - err.indexOf( ':' ) - 2 ) ), settings );
10607				$.tm_timepicker.log( 'Error parsing the date string: ' + err + '\ndate string = ' + value + '\ndate format = ' + format );
10608			} else {
10609				throw err;
10610			}
10611		}
10612		return date;
10613	};
10614
10615	/*
10616	 * override formatDate to set date with time to the input
10617	 */
10618	$.tm_datepicker._base_formatDate = $.tm_datepicker._formatDate;
10619	$.tm_datepicker._formatDate = function( inst ) {
10620		var tp_inst = this._get( inst, 'tm_timepicker' );
10621		if ( tp_inst ) {
10622			tp_inst._updateDateTime( inst );
10623			return tp_inst.$input.val();
10624		}
10625		return this._base_formatDate( inst );
10626	};
10627
10628	/*
10629	 * override options setter to add time to maxDate(Time) and minDate(Time). MaxDate
10630	 */
10631	$.tm_datepicker._base_optionDatepicker = $.tm_datepicker._optionDatepicker;
10632	$.tm_datepicker._optionDatepicker = function( target, name, value ) {
10633		var inst = this._getInst( target ),
10634			name_clone;
10635		var tp_inst;
10636		var min, max, onselect, overrides, fns, prop, ret, oldVal, $target;
10637
10638		if ( ! inst ) {
10639			return null;
10640		}
10641
10642		tp_inst = this._get( inst, 'tm_timepicker' );
10643		if ( tp_inst ) {
10644			min = null;
10645			max = null;
10646			onselect = null;
10647			overrides = tp_inst._defaults.evnts;
10648			fns = {};
10649
10650			if ( typeof name === 'string' ) {
10651				// if min/max was set with the string
10652				if ( name === 'minDate' || name === 'minDateTime' ) {
10653					min = value;
10654				} else if ( name === 'maxDate' || name === 'maxDateTime' ) {
10655					max = value;
10656				} else if ( name === 'onSelect' ) {
10657					onselect = value;
10658				} else if ( Object.prototype.hasOwnProperty.call( overrides, name ) ) {
10659					if ( typeof value === 'undefined' ) {
10660						return overrides[ name ];
10661					}
10662					fns[ name ] = value;
10663					name_clone = {}; //empty results in exiting function after overrides updated
10664				}
10665			} else if ( typeof name === 'object' ) {
10666				//if min/max was set with the JSON
10667				if ( name.minDate ) {
10668					min = name.minDate;
10669				} else if ( name.minDateTime ) {
10670					min = name.minDateTime;
10671				} else if ( name.maxDate ) {
10672					max = name.maxDate;
10673				} else if ( name.maxDateTime ) {
10674					max = name.maxDateTime;
10675				}
10676				for ( prop in overrides ) {
10677					if ( Object.prototype.hasOwnProperty.call( overrides, prop ) && name[ prop ] ) {
10678						fns[ prop ] = name[ prop ];
10679					}
10680				}
10681			}
10682			for ( prop in fns ) {
10683				if ( Object.prototype.hasOwnProperty.call( fns, prop ) ) {
10684					overrides[ prop ] = fns[ prop ];
10685					if ( ! name_clone ) {
10686						name_clone = $.extend( {}, name );
10687					}
10688					delete name_clone[ prop ];
10689				}
10690			}
10691			if ( name_clone && isEmptyObject( name_clone ) ) {
10692				return;
10693			}
10694			if ( min ) {
10695				//if min was set
10696				if ( min === 0 ) {
10697					min = new Date();
10698				} else {
10699					min = new Date( min );
10700				}
10701				tp_inst._defaults.minDate = min;
10702				tp_inst._defaults.minDateTime = min;
10703			} else if ( max ) {
10704				//if max was set
10705				if ( max === 0 ) {
10706					max = new Date();
10707				} else {
10708					max = new Date( max );
10709				}
10710				tp_inst._defaults.maxDate = max;
10711				tp_inst._defaults.maxDateTime = max;
10712			} else if ( onselect ) {
10713				tp_inst._defaults.onSelect = onselect;
10714			}
10715
10716			// Datepicker will override our date when we call _base_optionDatepicker when
10717			// calling minDate/maxDate, so we will first grab the value, call
10718			// _base_optionDatepicker, then set our value back.
10719			if ( min || max ) {
10720				$target = $( target );
10721				oldVal = $target.tm_datetimepicker( 'getDate' );
10722				ret = this._base_optionDatepicker.call( $.tm_datepicker, target, name_clone || name, value );
10723				$target.tm_datetimepicker( 'setDate', oldVal );
10724				return ret;
10725			}
10726		}
10727		if ( value === undefined ) {
10728			return this._base_optionDatepicker.call( $.tm_datepicker, target, name );
10729		}
10730		return this._base_optionDatepicker.call( $.tm_datepicker, target, name_clone || name, value );
10731	};
10732
10733	/*
10734	 * Create a Singleton Instance
10735	 */
10736	$.tm_timepicker = new Timepicker();
10737
10738	/**
10739	 * Get the timezone offset as string from a date object (eg '+0530' for UTC+5.5)
10740	 *
10741	 * @param {number}  tzMinutes if not a number, less than -720 (-1200), or greater than 840 (+1400) this value is returned
10742	 * @param {boolean} iso8601   if true formats in accordance to iso8601 "+12:45"
10743	 * @return {string} timezone Offset
10744	 */
10745	$.tm_timepicker.timezoneOffsetString = function( tzMinutes, iso8601 ) {
10746		var off, minutes, hours, iso, tz;
10747
10748		if ( isNaN( tzMinutes ) || tzMinutes > 840 || tzMinutes < -720 ) {
10749			return tzMinutes;
10750		}
10751
10752		off = tzMinutes;
10753		minutes = off % 60;
10754		hours = ( off - minutes ) / 60;
10755		iso = iso8601 ? ':' : '';
10756		tz = ( off >= 0 ? '+' : '-' ) + ( '0' + Math.abs( hours ) ).slice( -2 ) + iso + ( '0' + Math.abs( minutes ) ).slice( -2 );
10757
10758		if ( tz === '+00:00' ) {
10759			return 'Z';
10760		}
10761		return tz;
10762	};
10763
10764	/**
10765	 * Get the number in minutes that represents a timezone string
10766	 *
10767	 * @param {string} tzString formatted like "+0500", "-1245", "Z"
10768	 * @return {number} the offset minutes or the original string if it doesn't match expectations
10769	 */
10770	$.tm_timepicker.timezoneOffsetNumber = function( tzString ) {
10771		var normalized = tzString.toString().replace( ':', '' ); // excuse any iso8601, end up with "+1245"
10772
10773		if ( normalized.toUpperCase() === 'Z' ) {
10774			// if iso8601 with Z, its 0 minute offset
10775			return 0;
10776		}
10777
10778		if ( ! /^(-|\+)\d{4}$/.test( normalized ) ) {
10779			// possibly a user defined tz, so just give it back
10780			return parseInt( tzString, 10 );
10781		}
10782
10783		return (
10784			( normalized.substring( 0, 1 ) === '-' ? -1 : 1 ) * // plus or minus
10785			( ( parseInt( normalized.substring( 1, 3 ), 10 ) * 60 ) + // hours (converted to minutes)
10786				parseInt( normalized.substring( 3, 5 ), 10 ) )
10787		); // minutes
10788	};
10789
10790	/**
10791	 * No way to set timezone in js Date, so we must adjust the minutes to compensate.
10792	 * (think setDate, getDate)
10793	 *
10794	 * @param {Date}   date
10795	 * @param {string} fromTimezone formatted like "+0500", "-1245"
10796	 * @param {string} toTimezone   formatted like "+0500", "-1245"
10797	 * @return {Date} Date
10798	 */
10799	$.tm_timepicker.timezoneAdjust = function( date, fromTimezone, toTimezone ) {
10800		var fromTz = $.timepicker.timezoneOffsetNumber( fromTimezone );
10801		var toTz = $.tm_timepicker.timezoneOffsetNumber( toTimezone );
10802		if ( ! isNaN( toTz ) ) {
10803			date.setMinutes( date.getMinutes() + -fromTz - -toTz );
10804		}
10805		return date;
10806	};
10807
10808	/**
10809	 * Calls `tm_timepicker()` on the `startTime` and `endTime` elements, and configures them to
10810	 * enforce date range limits.
10811	 * n.b. The input value must be correctly formatted (reformatting is not supported)
10812	 *
10813	 * @param {Element} startTime
10814	 * @param {Element} endTime
10815	 * @param {Object}  options   Options for the tm_timepicker() call
10816	 * @return {jQuery} time range
10817	 */
10818	$.tm_timepicker.timeRange = function( startTime, endTime, options ) {
10819		return $.tm_timepicker.handleRange( 'tm_timepicker', startTime, endTime, options );
10820	};
10821
10822	/**
10823	 * Calls `datetimepicker` on the `startTime` and `endTime` elements, and configures them to
10824	 * enforce date range limits.
10825	 *
10826	 * @param {Element} startTime
10827	 * @param {Element} endTime
10828	 * @param {Object}  options   Options for the `tm_timepicker()` call. Also supports `reformat`,
10829	 *                            a boolean value that can be used to reformat the input values to the `dateFormat`.
10830	 */
10831	$.tm_timepicker.datetimeRange = function( startTime, endTime, options ) {
10832		$.tm_timepicker.handleRange( 'tm_datetimepicker', startTime, endTime, options );
10833	};
10834
10835	/**
10836	 * Calls `tm_datepicker` on the `startTime` and `endTime` elements, and configures them to
10837	 * enforce date range limits.
10838	 *
10839	 * @param {Element} startTime
10840	 * @param {Element} endTime
10841	 * @param {Object}  options   Options for the `tm_timepicker()` call. Also supports `reformat`,
10842	 *                            a boolean value that can be used to reformat the input values to the `dateFormat`.
10843	 */
10844	$.tm_timepicker.dateRange = function( startTime, endTime, options ) {
10845		$.tm_timepicker.handleRange( 'tm_datepicker', startTime, endTime, options );
10846	};
10847
10848	/**
10849	 * Calls `method` on the `startTime` and `endTime` elements, and configures them to
10850	 * enforce date range limits.
10851	 *
10852	 * @param {string}  method    Can be used to specify the type of picker to be added
10853	 * @param {Element} startTime
10854	 * @param {Element} endTime
10855	 * @param {Object}  options   Options for the `tm_timepicker()` call. Also supports `reformat`,
10856	 *                            a boolean value that can be used to reformat the input values to the `dateFormat`.
10857	 * @return {jQuery} range
10858	 */
10859	$.tm_timepicker.handleRange = function( method, startTime, endTime, options ) {
10860		var timeOnly = false;
10861		options = $.extend(
10862			{},
10863			{
10864				minInterval: 0, // min allowed interval in milliseconds
10865				maxInterval: 0, // max allowed interval in milliseconds
10866				start: {}, // options for start picker
10867				end: {} // options for end picker
10868			},
10869			options
10870		);
10871
10872		// for the mean time this fixes an issue with calling getDate with tm_timepicker()
10873
10874		if ( method === 'tm_timepicker' ) {
10875			timeOnly = true;
10876			method = 'tm_datetimepicker';
10877		}
10878
10879		function checkDates( changed, other ) {
10880			var startdt = startTime[ method ]( 'getDate' ),
10881				enddt = endTime[ method ]( 'getDate' ),
10882				changeddt = changed[ method ]( 'getDate' );
10883			var minDate;
10884			var maxDate;
10885
10886			if ( startdt !== null ) {
10887				minDate = new Date( startdt.getTime() );
10888				maxDate = new Date( startdt.getTime() );
10889
10890				minDate.setMilliseconds( minDate.getMilliseconds() + options.minInterval );
10891				maxDate.setMilliseconds( maxDate.getMilliseconds() + options.maxInterval );
10892
10893				if ( options.minInterval > 0 && minDate > enddt ) {
10894					// minInterval check
10895					endTime[ method ]( 'setDate', minDate );
10896				} else if ( options.maxInterval > 0 && maxDate < enddt ) {
10897					// max interval check
10898					endTime[ method ]( 'setDate', maxDate );
10899				} else if ( startdt > enddt ) {
10900					other[ method ]( 'setDate', changeddt );
10901				}
10902			}
10903		}
10904
10905		function selected( changed, other, option ) {
10906			var date;
10907			if ( ! changed.val() ) {
10908				return;
10909			}
10910			date = changed[ method ].call( changed, 'getDate' );
10911			if ( date !== null && options.minInterval > 0 ) {
10912				if ( option === 'minDate' ) {
10913					date.setMilliseconds( date.getMilliseconds() + options.minInterval );
10914				}
10915				if ( option === 'maxDate' ) {
10916					date.setMilliseconds( date.getMilliseconds() - options.minInterval );
10917				}
10918			}
10919
10920			if ( date.getTime ) {
10921				other[ method ].call( other, 'option', option, date );
10922			}
10923		}
10924
10925		$.fn[ method ].call(
10926			startTime,
10927			$.extend(
10928				{
10929					timeOnly: timeOnly,
10930					onClose: function() {
10931						checkDates( $( this ), endTime );
10932					},
10933					onSelect: function() {
10934						selected( $( this ), endTime, 'minDate' );
10935					}
10936				},
10937				options,
10938				options.start
10939			)
10940		);
10941		$.fn[ method ].call(
10942			endTime,
10943			$.extend(
10944				{
10945					timeOnly: timeOnly,
10946					onClose: function() {
10947						checkDates( $( this ), startTime );
10948					},
10949					onSelect: function() {
10950						selected( $( this ), startTime, 'maxDate' );
10951					}
10952				},
10953				options,
10954				options.end
10955			)
10956		);
10957
10958		checkDates( startTime, endTime );
10959
10960		selected( startTime, endTime, 'minDate' );
10961		selected( endTime, startTime, 'maxDate' );
10962
10963		return $( [ startTime.get( 0 ), endTime.get( 0 ) ] );
10964	};
10965
10966	/**
10967	 * Log error or data to the console during error or debugging
10968	 *
10969	 * @return {void}
10970	 */
10971	$.tm_timepicker.log = function() {
10972		// Older IE (9, maybe 10) throw error on accessing `window.console.log.apply`, so check first.
10973		if ( window.console && window.console.log && window.console.log.apply ) {
10974			window.console.log.apply( window.console, Array.prototype.slice.call( arguments ) );
10975		}
10976	};
10977
10978	/*
10979	 * Add util object to allow access to private methods for testability.
10980	 */
10981	$.tm_timepicker._util = {
10982		_extendRemove: extendRemove,
10983		_isEmptyObject: isEmptyObject,
10984		_convert24to12: convert24to12,
10985		_detectSupport: detectSupport,
10986		_selectLocalTimezone: selectLocalTimezone,
10987		_computeEffectiveSetting: computeEffectiveSetting,
10988		_splitDateTime: splitDateTime,
10989		_parseDateTimeInternal: parseDateTimeInternal
10990	};
10991
10992	/*
10993	 * Microsecond support
10994	 */
10995	if ( ! Date.prototype.getMicroseconds ) {
10996		Object.defineProperty( Date.prototype, 'microseconds', {
10997			value: 0,
10998			writable: true
10999		} );
11000
11001		Object.defineProperty( Date.prototype, 'getMicroseconds', {
11002			value: function() {
11003				return this.microseconds;
11004			}
11005		} );
11006
11007		Object.defineProperty( Date.prototype, 'setMicroseconds', {
11008			value: function( m ) {
11009				this.setMilliseconds( this.getMilliseconds() + Math.floor( m / 1000 ) );
11010				this.microseconds = m % 1000;
11011				return this;
11012			}
11013		} );
11014	}
11015
11016	/*
11017	 * Keep up with the version
11018	 */
11019	$.tm_timepicker.version = '1.6.3';
11020} ) );
11021;
11022/**
11023 * Owl Carousel v2.3.4
11024 * Copyright 2013-2018 David Deutsch
11025 * Licensed under: SEE LICENSE IN https://github.com/OwlCarousel2/OwlCarousel2/blob/master/LICENSE
11026 * reformatted code by ThemeComplete
11027 */
11028/**
11029 * Owl carousel
11030 *
11031 * @param {jQuery}   $
11032 * @param {Window}   window
11033 * @param {Document} document
11034 * @version 2.3.4
11035 * @author Bartosz Wojciechowski
11036 * @author David Deutsch
11037 * @license The MIT License (MIT)
11038 * @todo Lazy Load Icon
11039 * @todo prevent animationend bubling
11040 * @todo itemsScaleUp
11041 * @todo Test Zepto
11042 * @todo stagePadding calculate wrong active classes
11043 */
11044( function( $, window, document ) {
11045	'use strict';
11046
11047	/**
11048	 * Creates a carousel.
11049	 *
11050	 * @class The Owl Carousel.
11051	 * @public
11052	 * @param {HTMLElement|jQuery} element   - The element to create the carousel for.
11053	 * @param {Object}             [options] - The options
11054	 */
11055	function Owl( element, options ) {
11056		/**
11057		 * Current settings for the carousel.
11058		 *
11059		 * @public
11060		 */
11061		this.settings = null;
11062
11063		/**
11064		 * Current options set by the caller including defaults.
11065		 *
11066		 * @public
11067		 */
11068		this.options = $.extend( {}, Owl.Defaults, options );
11069
11070		/**
11071		 * Plugin element.
11072		 *
11073		 * @public
11074		 */
11075		this.$element = $( element );
11076
11077		/**
11078		 * Proxied event handlers.
11079		 *
11080		 * @protected
11081		 */
11082		this._handlers = {};
11083
11084		/**
11085		 * References to the running plugins of this carousel.
11086		 *
11087		 * @protected
11088		 */
11089		this._plugins = {};
11090
11091		/**
11092		 * Currently suppressed events to prevent them from being retriggered.
11093		 *
11094		 * @protected
11095		 */
11096		this._supress = {};
11097
11098		/**
11099		 * Absolute current position.
11100		 *
11101		 * @protected
11102		 */
11103		this._current = null;
11104
11105		/**
11106		 * Animation speed in milliseconds.
11107		 *
11108		 * @protected
11109		 */
11110		this._speed = null;
11111
11112		/**
11113		 * Coordinates of all items in pixel.
11114		 *
11115		 * @todo The name of this member is missleading.
11116		 * @protected
11117		 */
11118		this._coordinates = [];
11119
11120		/**
11121		 * Current breakpoint.
11122		 *
11123		 * @todo Real media queries would be nice.
11124		 * @protected
11125		 */
11126		this._breakpoint = null;
11127
11128		/**
11129		 * Current width of the plugin element.
11130		 */
11131		this._width = null;
11132
11133		/**
11134		 * All real items.
11135		 *
11136		 * @protected
11137		 */
11138		this._items = [];
11139
11140		/**
11141		 * All cloned items.
11142		 *
11143		 * @protected
11144		 */
11145		this._clones = [];
11146
11147		/**
11148		 * Merge values of all items.
11149		 *
11150		 * @todo Maybe this could be part of a plugin.
11151		 * @protected
11152		 */
11153		this._mergers = [];
11154
11155		/**
11156		 * Widths of all items.
11157		 */
11158		this._widths = [];
11159
11160		/**
11161		 * Invalidated parts within the update process.
11162		 *
11163		 * @protected
11164		 */
11165		this._invalidated = {};
11166
11167		/**
11168		 * Ordered list of workers for the update process.
11169		 *
11170		 * @protected
11171		 */
11172		this._pipe = [];
11173
11174		/**
11175		 * Current state information for the drag operation.
11176		 *
11177		 * @todo #261
11178		 * @protected
11179		 */
11180		this._drag = {
11181			time: null,
11182			target: null,
11183			pointer: null,
11184			stage: {
11185				start: null,
11186				current: null
11187			},
11188			direction: null
11189		};
11190
11191		/**
11192		 * Current state information and their tags.
11193		 *
11194		 * @type {Object}
11195		 * @protected
11196		 */
11197		this._states = {
11198			current: {},
11199			tags: {
11200				initializing: [ 'busy' ],
11201				animating: [ 'busy' ],
11202				dragging: [ 'interacting' ]
11203			}
11204		};
11205
11206		$.each(
11207			[ 'onResize', 'onThrottledResize' ],
11208			$.proxy( function( i, handler ) {
11209				this._handlers[ handler ] = $.proxy( this[ handler ], this );
11210			}, this )
11211		);
11212
11213		$.each(
11214			Owl.Plugins,
11215			$.proxy( function( key, Plugin ) {
11216				this._plugins[ key.charAt( 0 ).toLowerCase() + key.slice( 1 ) ] = new Plugin( this );
11217			}, this )
11218		);
11219
11220		$.each(
11221			Owl.Workers,
11222			$.proxy( function( priority, worker ) {
11223				this._pipe.push( {
11224					filter: worker.filter,
11225					run: $.proxy( worker.run, this )
11226				} );
11227			}, this )
11228		);
11229
11230		this.setup();
11231		this.initialize();
11232	}
11233
11234	/**
11235	 * Default options for the carousel.
11236	 *
11237	 * @public
11238	 */
11239	Owl.Defaults = {
11240		items: 3,
11241		loop: false,
11242		center: false,
11243		rewind: false,
11244		checkVisibility: true,
11245
11246		mouseDrag: true,
11247		touchDrag: true,
11248		pullDrag: true,
11249		freeDrag: false,
11250
11251		margin: 0,
11252		stagePadding: 0,
11253
11254		merge: false,
11255		mergeFit: true,
11256		autoWidth: false,
11257
11258		startPosition: 0,
11259		rtl: false,
11260
11261		smartSpeed: 250,
11262		fluidSpeed: false,
11263		dragEndSpeed: false,
11264
11265		responsive: {},
11266		responsiveRefreshRate: 200,
11267		responsiveBaseElement: window,
11268
11269		fallbackEasing: 'linear',
11270		slideTransition: '',
11271
11272		info: false,
11273
11274		nestedItemSelector: false,
11275		itemElement: 'div',
11276		stageElement: 'div',
11277
11278		refreshClass: 'owl-refresh',
11279		loadedClass: 'owl-loaded',
11280		loadingClass: 'owl-loading',
11281		rtlClass: 'owl-rtl',
11282		responsiveClass: 'owl-responsive',
11283		dragClass: 'owl-drag',
11284		itemClass: 'owl-item',
11285		stageClass: 'owl-stage',
11286		stageOuterClass: 'owl-stage-outer',
11287		grabClass: 'owl-grab'
11288	};
11289
11290	/**
11291	 * Enumeration for width.
11292	 *
11293	 * @public
11294	 * @readonly
11295	 * @enum {string}
11296	 */
11297	Owl.Width = {
11298		Default: 'default',
11299		Inner: 'inner',
11300		Outer: 'outer'
11301	};
11302
11303	/**
11304	 * Enumeration for types.
11305	 *
11306	 * @public
11307	 * @readonly
11308	 * @enum {string}
11309	 */
11310	Owl.Type = {
11311		Event: 'event',
11312		State: 'state'
11313	};
11314
11315	/**
11316	 * Contains all registered plugins.
11317	 *
11318	 * @public
11319	 */
11320	Owl.Plugins = {};
11321
11322	/**
11323	 * List of workers involved in the update process.
11324	 */
11325	Owl.Workers = [
11326		{
11327			filter: [ 'width', 'settings' ],
11328			run: function() {
11329				this._width = this.$element.width();
11330			}
11331		},
11332		{
11333			filter: [ 'width', 'items', 'settings' ],
11334			run: function( cache ) {
11335				cache.current = this._items && this._items[ this.relative( this._current ) ];
11336			}
11337		},
11338		{
11339			filter: [ 'items', 'settings' ],
11340			run: function() {
11341				this.$stage.children( '.cloned' ).remove();
11342			}
11343		},
11344		{
11345			filter: [ 'width', 'items', 'settings' ],
11346			run: function( cache ) {
11347				var margin = this.settings.margin || '',
11348					grid = ! this.settings.autoWidth,
11349					rtl = this.settings.rtl,
11350					css = {
11351						width: 'auto',
11352						'margin-left': rtl ? margin : '',
11353						'margin-right': rtl ? '' : margin
11354					};
11355
11356				if ( ! grid ) {
11357					this.$stage.children().css( css );
11358				}
11359
11360				cache.css = css;
11361			}
11362		},
11363		{
11364			filter: [ 'width', 'items', 'settings' ],
11365			run: function( cache ) {
11366				var width = ( this.width() / this.settings.items ).toFixed( 3 ) - this.settings.margin,
11367					merge = null,
11368					iterator = this._items.length,
11369					grid = ! this.settings.autoWidth,
11370					widths = [];
11371
11372				cache.items = {
11373					merge: false,
11374					width: width
11375				};
11376
11377				while ( iterator > 0 ) {
11378					iterator = iterator - 1;
11379					merge = this._mergers[ iterator ];
11380					merge = ( this.settings.mergeFit && Math.min( merge, this.settings.items ) ) || merge;
11381
11382					cache.items.merge = merge > 1 || cache.items.merge;
11383
11384					widths[ iterator ] = ! grid ? this._items[ iterator ].width() : width * merge;
11385				}
11386
11387				this._widths = widths;
11388			}
11389		},
11390		{
11391			filter: [ 'items', 'settings' ],
11392			run: function() {
11393				var clones = [],
11394					items = this._items,
11395					settings = this.settings,
11396					// TODO: Should be computed from number of min width items in stage
11397					view = Math.max( settings.items * 2, 4 ),
11398					size = Math.ceil( items.length / 2 ) * 2,
11399					repeat = settings.loop && items.length ? ( settings.rewind ? view : Math.max( view, size ) ) : 0,
11400					append = '',
11401					prepend = '';
11402
11403				repeat /= 2;
11404
11405				while ( repeat > 0 ) {
11406					// Switch to only using appended clones
11407					clones.push( this.normalize( clones.length / 2, true ) );
11408					append = append + items[ clones[ clones.length - 1 ] ][ 0 ].outerHTML;
11409					clones.push( this.normalize( items.length - 1 - ( ( clones.length - 1 ) / 2 ), true ) );
11410					prepend = items[ clones[ clones.length - 1 ] ][ 0 ].outerHTML + prepend;
11411					repeat -= 1;
11412				}
11413
11414				this._clones = clones;
11415
11416				$( append ).addClass( 'cloned' ).appendTo( this.$stage );
11417				$( prepend ).addClass( 'cloned' ).prependTo( this.$stage );
11418			}
11419		},
11420		{
11421			filter: [ 'width', 'items', 'settings' ],
11422			run: function() {
11423				var rtl = this.settings.rtl ? 1 : -1,
11424					size = this._clones.length + this._items.length,
11425					iterator = 0,
11426					previous = 0,
11427					current = 0,
11428					coordinates = [];
11429
11430				while ( iterator < size ) {
11431					previous = coordinates[ iterator - 1 ] || 0;
11432					current = this._widths[ this.relative( iterator ) ] + this.settings.margin;
11433					coordinates.push( previous + ( current * rtl ) );
11434					iterator = iterator + 1;
11435				}
11436
11437				this._coordinates = coordinates;
11438			}
11439		},
11440		{
11441			filter: [ 'width', 'items', 'settings' ],
11442			run: function() {
11443				var padding = this.settings.stagePadding,
11444					coordinates = this._coordinates,
11445					css = {
11446						width: Math.ceil( Math.abs( coordinates[ coordinates.length - 1 ] ) ) + ( padding * 2 ),
11447						'padding-left': padding || '',
11448						'padding-right': padding || ''
11449					};
11450
11451				this.$stage.css( css );
11452			}
11453		},
11454		{
11455			filter: [ 'width', 'items', 'settings' ],
11456			run: function( cache ) {
11457				var iterator = this._coordinates.length,
11458					grid = ! this.settings.autoWidth,
11459					items = this.$stage.children();
11460
11461				if ( grid && cache.items.merge ) {
11462					while ( iterator > 0 ) {
11463						iterator = iterator - 1;
11464						cache.css.width = this._widths[ this.relative( iterator ) ];
11465						items.eq( iterator ).css( cache.css );
11466					}
11467				} else if ( grid ) {
11468					cache.css.width = cache.items.width;
11469					items.css( cache.css );
11470				}
11471			}
11472		},
11473		{
11474			filter: [ 'items' ],
11475			run: function() {
11476				if ( this._coordinates.length < 1 ) {
11477					this.$stage.removeAttr( 'style' );
11478				}
11479			}
11480		},
11481		{
11482			filter: [ 'width', 'items', 'settings' ],
11483			run: function( cache ) {
11484				cache.current = cache.current ? this.$stage.children().index( cache.current ) : 0;
11485				cache.current = Math.max( this.minimum(), Math.min( this.maximum(), cache.current ) );
11486				this.reset( cache.current );
11487			}
11488		},
11489		{
11490			filter: [ 'position' ],
11491			run: function() {
11492				this.animate( this.coordinates( this._current ) );
11493			}
11494		},
11495		{
11496			filter: [ 'width', 'position', 'items', 'settings' ],
11497			run: function() {
11498				var rtl = this.settings.rtl ? 1 : -1,
11499					padding = this.settings.stagePadding * 2,
11500					begin = this.coordinates( this.current() ) + padding,
11501					end = begin + ( this.width() * rtl ),
11502					inner,
11503					outer,
11504					matches = [],
11505					i,
11506					n;
11507
11508				for ( i = 0, n = this._coordinates.length; i < n; i += 1 ) {
11509					inner = this._coordinates[ i - 1 ] || 0;
11510					outer = Math.abs( this._coordinates[ i ] ) + ( padding * rtl );
11511
11512					if ( ( this.op( inner, '<=', begin ) && this.op( inner, '>', end ) ) || ( this.op( outer, '<', begin ) && this.op( outer, '>', end ) ) ) {
11513						matches.push( i );
11514					}
11515				}
11516
11517				this.$stage.children( '.active' ).removeClass( 'active' );
11518				this.$stage.children( ':eq(' + matches.join( '), :eq(' ) + ')' ).addClass( 'active' );
11519
11520				this.$stage.children( '.center' ).removeClass( 'center' );
11521				if ( this.settings.center ) {
11522					this.$stage.children().eq( this.current() ).addClass( 'center' );
11523				}
11524			}
11525		}
11526	];
11527
11528	/**
11529	 * Create the stage DOM element
11530	 */
11531	Owl.prototype.initializeStage = function() {
11532		this.$stage = this.$element.find( '.' + this.settings.stageClass );
11533
11534		// if the stage is already in the DOM, grab it and skip stage initialization
11535		if ( this.$stage.length ) {
11536			return;
11537		}
11538
11539		this.$element.addClass( this.options.loadingClass );
11540
11541		// create stage
11542		this.$stage = $( '<' + this.settings.stageElement + '>', {
11543			class: this.settings.stageClass
11544		} ).wrap(
11545			$( '<div/>', {
11546				class: this.settings.stageOuterClass
11547			} )
11548		);
11549
11550		// append stage
11551		this.$element.append( this.$stage.parent() );
11552	};
11553
11554	/**
11555	 * Create item DOM elements
11556	 */
11557	Owl.prototype.initializeItems = function() {
11558		var $items = this.$element.find( '.owl-item' );
11559
11560		// if the items are already in the DOM, grab them and skip item initialization
11561		if ( $items.length ) {
11562			this._items = $items.get().map( function( item ) {
11563				return $( item );
11564			} );
11565
11566			this._mergers = this._items.map( function() {
11567				return 1;
11568			} );
11569
11570			this.refresh();
11571
11572			return;
11573		}
11574
11575		// append content
11576		this.replace( this.$element.children().not( this.$stage.parent() ) );
11577
11578		// check visibility
11579		if ( this.isVisible() ) {
11580			// update view
11581			this.refresh();
11582		} else {
11583			// invalidate width
11584			this.invalidate( 'width' );
11585		}
11586
11587		this.$element.removeClass( this.options.loadingClass ).addClass( this.options.loadedClass );
11588	};
11589
11590	/**
11591	 * Initializes the carousel.
11592	 *
11593	 * @protected
11594	 */
11595	Owl.prototype.initialize = function() {
11596		var imgs, nestedSelector, width;
11597		this.enter( 'initializing' );
11598		this.trigger( 'initialize' );
11599
11600		this.$element.toggleClass( this.settings.rtlClass, this.settings.rtl );
11601
11602		if ( this.settings.autoWidth && ! this.is( 'pre-loading' ) ) {
vendor: 4,759 bytes, lines 11603-11791
11603			imgs = this.$element.find( 'img' );
11604			nestedSelector = this.settings.nestedItemSelector ? '.' + this.settings.nestedItemSelector : undefined;
11605			width = this.$element.children( nestedSelector ).width();
11606
11607			if ( imgs.length && width <= 0 ) {
11608				this.preloadAutoWidthImages( imgs );
11609			}
11610		}
11611
11612		this.initializeStage();
11613		this.initializeItems();
11614
11615		// register event handlers
11616		this.registerEventHandlers();
11617
11618		this.leave( 'initializing' );
11619		this.trigger( 'initialized' );
11620	};
11621
11622	/**
11623	 * @returns {boolean} visibility of $element
11624	 *                    if you know the carousel will always be visible you can set `checkVisibility` to `false` to
11625	 *                    prevent the expensive browser layout forced reflow the $element.is(':visible') does
11626	 */
11627	Owl.prototype.isVisible = function() {
11628		return this.settings.checkVisibility ? this.$element.is( ':visible' ) : true;
11629	};
11630
11631	/**
11632	 * Setups the current settings.
11633	 *
11634	 * @todo Remove responsive classes. Why should adaptive designs be brought into IE8?
11635	 * @todo Support for media queries by using `matchMedia` would be nice.
11636	 * @public
11637	 */
11638	Owl.prototype.setup = function() {
11639		var viewport = this.viewport(),
11640			overwrites = this.options.responsive,
11641			match = -1,
11642			settings = null;
11643
11644		if ( ! overwrites ) {
11645			settings = $.extend( {}, this.options );
11646		} else {
11647			$.each( overwrites, function( breakpoint ) {
11648				if ( breakpoint <= viewport && breakpoint > match ) {
11649					match = Number( breakpoint );
11650				}
11651			} );
11652
11653			settings = $.extend( {}, this.options, overwrites[ match ] );
11654			if ( typeof settings.stagePadding === 'function' ) {
11655				settings.stagePadding = settings.stagePadding();
11656			}
11657			delete settings.responsive;
11658
11659			// responsive class
11660			if ( settings.responsiveClass ) {
11661				this.$element.attr( 'class', this.$element.attr( 'class' ).replace( new RegExp( '(' + this.options.responsiveClass + '-)\\S+\\s', 'g' ), '$1' + match ) );
11662			}
11663		}
11664
11665		this.trigger( 'change', { property: { name: 'settings', value: settings } } );
11666		this._breakpoint = match;
11667		this.settings = settings;
11668		this.invalidate( 'settings' );
11669		this.trigger( 'changed', { property: { name: 'settings', value: this.settings } } );
11670	};
11671
11672	/**
11673	 * Updates option logic if necessery.
11674	 *
11675	 * @protected
11676	 */
11677	Owl.prototype.optionsLogic = function() {
11678		if ( this.settings.autoWidth ) {
11679			this.settings.stagePadding = false;
11680			this.settings.merge = false;
11681		}
11682	};
11683
11684	/**
11685	 * Prepares an item before add.
11686	 *
11687	 * @param {mixed} item
11688	 * @todo Rename event parameter `content` to `item`.
11689	 * @protected
11690	 * @returns {jQuery|HTMLElement} - The item container.
11691	 */
11692	Owl.prototype.prepare = function( item ) {
11693		var event = this.trigger( 'prepare', { content: item } );
11694
11695		if ( ! event.data ) {
11696			event.data = $( '<' + this.settings.itemElement + '/>' )
11697				.addClass( this.options.itemClass )
11698				.append( item );
11699		}
11700
11701		this.trigger( 'prepared', { content: event.data } );
11702
11703		return event.data;
11704	};
11705
11706	/**
11707	 * Updates the view.
11708	 *
11709	 * @public
11710	 */
11711	Owl.prototype.update = function() {
11712		var i = 0,
11713			n = this._pipe.length,
11714			filter = $.proxy( function( p ) {
11715				return this[ p ];
11716			}, this._invalidated ),
11717			cache = {};
11718
11719		while ( i < n ) {
11720			if ( this._invalidated.all || $.grep( this._pipe[ i ].filter, filter ).length > 0 ) {
11721				this._pipe[ i ].run( cache );
11722			}
11723			i += 1;
11724		}
11725
11726		this._invalidated = {};
11727
11728		if ( ! this.is( 'valid' ) ) {
11729			this.enter( 'valid' );
11730		}
11731	};
11732
11733	/**
11734	 * Gets the width of the view.
11735	 *
11736	 * @public
11737	 * @param {Owl.Width} [dimension=Owl.Width.Default] - The dimension to return.
11738	 * @returns {number} - The width of the view in pixel.
11739	 */
11740	Owl.prototype.width = function( dimension ) {
11741		dimension = dimension || Owl.Width.Default;
11742		switch ( dimension ) {
11743			case Owl.Width.Inner:
11744			case Owl.Width.Outer:
11745				return this._width;
11746			default:
11747				return this._width - ( this.settings.stagePadding * 2 ) + this.settings.margin;
11748		}
11749	};
11750
11751	/**
11752	 * Refreshes the carousel primarily for adaptive purposes.
11753	 *
11754	 * @public
11755	 */
11756	Owl.prototype.refresh = function() {
11757		this.enter( 'refreshing' );
11758		this.trigger( 'refresh' );
11759
11760		this.setup();
11761
11762		this.optionsLogic();
11763
11764		this.$element.addClass( this.options.refreshClass );
11765
11766		this.update();
11767
11768		this.$element.removeClass( this.options.refreshClass );
11769
11770		this.leave( 'refreshing' );
11771		this.trigger( 'refreshed' );
11772	};
11773
11774	/**
11775	 * Checks window `resize` event.
11776	 *
11777	 * @protected
11778	 */
11779	Owl.prototype.onThrottledResize = function() {
11780		window.clearTimeout( this.resizeTimer );
11781		this.resizeTimer = window.setTimeout( this._handlers.onResize, this.settings.responsiveRefreshRate );
11782	};
11783
11784	/**
11785	 * Checks window `resize` event.
11786	 *
11787	 * @protected
11788	 */
11789	Owl.prototype.onResize = function() {
11790		if ( ! this._items.length ) {
11791			return false;
vendor: 5,296 bytes, lines 11792-11988
11792		}
11793
11794		if ( this._width === this.$element.width() ) {
11795			return false;
11796		}
11797
11798		if ( ! this.isVisible() ) {
11799			return false;
11800		}
11801
11802		this.enter( 'resizing' );
11803
11804		if ( this.trigger( 'resize' ).isDefaultPrevented() ) {
11805			this.leave( 'resizing' );
11806			return false;
11807		}
11808
11809		this.invalidate( 'width' );
11810
11811		this.refresh();
11812
11813		this.leave( 'resizing' );
11814		this.trigger( 'resized' );
11815	};
11816
11817	/**
11818	 * Registers event handlers.
11819	 *
11820	 * @todo Check `msPointerEnabled`
11821	 * @todo #261
11822	 * @protected
11823	 */
11824	Owl.prototype.registerEventHandlers = function() {
11825		if ( $.support.transition ) {
11826			this.$stage.on( $.support.transition.end + '.owl.core', $.proxy( this.onTransitionEnd, this ) );
11827		}
11828
11829		if ( this.settings.responsive !== false ) {
11830			this.on( window, 'resize', this._handlers.onThrottledResize );
11831		}
11832
11833		if ( this.settings.mouseDrag ) {
11834			this.$element.addClass( this.options.dragClass );
11835			this.$stage.on( 'mousedown.owl.core', $.proxy( this.onDragStart, this ) );
11836			this.$stage.on( 'dragstart.owl.core selectstart.owl.core', function() {
11837				return false;
11838			} );
11839		}
11840
11841		if ( this.settings.touchDrag ) {
11842			this.$stage.on( 'touchstart.owl.core', $.proxy( this.onDragStart, this ) );
11843			this.$stage.on( 'touchcancel.owl.core', $.proxy( this.onDragEnd, this ) );
11844		}
11845	};
11846
11847	/**
11848	 * Handles `touchstart` and `mousedown` events.
11849	 *
11850	 * @todo Horizontal swipe threshold as option
11851	 * @todo #261
11852	 * @protected
11853	 * @param {Event} event - The event arguments.
11854	 */
11855	Owl.prototype.onDragStart = function( event ) {
11856		var stage = null;
11857
11858		if ( event.which === 3 ) {
11859			return;
11860		}
11861
11862		if ( $.support.transform ) {
11863			stage = this.$stage
11864				.css( 'transform' )
11865				.replace( /.*\(|\)| /g, '' )
11866				.split( ',' );
11867			stage = {
11868				x: stage[ stage.length === 16 ? 12 : 4 ],
11869				y: stage[ stage.length === 16 ? 13 : 5 ]
11870			};
11871		} else {
11872			stage = this.$stage.position();
11873			stage = {
11874				x: this.settings.rtl ? stage.left + this.$stage.width() - this.width() + this.settings.margin : stage.left,
11875				y: stage.top
11876			};
11877		}
11878
11879		if ( this.is( 'animating' ) ) {
11880			if ( $.support.transform ) {
11881				this.animate( stage.x );
11882			} else {
11883				this.$stage.stop();
11884			}
11885
11886			this.invalidate( 'position' );
11887		}
11888
11889		this.$element.toggleClass( this.options.grabClass, event.type === 'mousedown' );
11890
11891		this.speed( 0 );
11892
11893		this._drag.time = new Date().getTime();
11894		this._drag.target = $( event.target );
11895		this._drag.stage.start = stage;
11896		this._drag.stage.current = stage;
11897		this._drag.pointer = this.pointer( event );
11898
11899		$( document ).on( 'mouseup.owl.core touchend.owl.core', $.proxy( this.onDragEnd, this ) );
11900
11901		$( document ).one(
11902			'mousemove.owl.core touchmove.owl.core',
11903			$.proxy( function( e ) {
11904				var delta = this.difference( this._drag.pointer, this.pointer( e ) );
11905
11906				$( document ).on( 'mousemove.owl.core touchmove.owl.core', $.proxy( this.onDragMove, this ) );
11907
11908				if ( Math.abs( delta.x ) < Math.abs( delta.y ) && this.is( 'valid' ) ) {
11909					return;
11910				}
11911
11912				e.preventDefault();
11913
11914				this.enter( 'dragging' );
11915				this.trigger( 'drag' );
11916			}, this )
11917		);
11918	};
11919
11920	/**
11921	 * Handles the `touchmove` and `mousemove` events.
11922	 *
11923	 * @todo #261
11924	 * @protected
11925	 * @param {Event} event - The event arguments.
11926	 */
11927	Owl.prototype.onDragMove = function( event ) {
11928		var minimum = null,
11929			maximum = null,
11930			pull = null,
11931			delta = this.difference( this._drag.pointer, this.pointer( event ) ),
11932			stage = this.difference( this._drag.stage.start, delta );
11933
11934		if ( ! this.is( 'dragging' ) ) {
11935			return;
11936		}
11937
11938		event.preventDefault();
11939
11940		if ( this.settings.loop ) {
11941			minimum = this.coordinates( this.minimum() );
11942			maximum = this.coordinates( this.maximum() + 1 ) - minimum;
11943			stage.x = ( ( ( ( stage.x - minimum ) % maximum ) + maximum ) % maximum ) + minimum;
11944		} else {
11945			minimum = this.settings.rtl ? this.coordinates( this.maximum() ) : this.coordinates( this.minimum() );
11946			maximum = this.settings.rtl ? this.coordinates( this.minimum() ) : this.coordinates( this.maximum() );
11947			pull = this.settings.pullDrag ? ( -1 * delta.x ) / 5 : 0;
11948			stage.x = Math.max( Math.min( stage.x, minimum + pull ), maximum + pull );
11949		}
11950
11951		this._drag.stage.current = stage;
11952
11953		this.animate( stage.x );
11954	};
11955
11956	/**
11957	 * Handles the `touchend` and `mouseup` events.
11958	 *
11959	 * @todo #261
11960	 * @todo Threshold for click event
11961	 * @protected
11962	 * @param {Event} event - The event arguments.
11963	 */
11964	Owl.prototype.onDragEnd = function( event ) {
11965		var delta = this.difference( this._drag.pointer, this.pointer( event ) ),
11966			stage = this._drag.stage.current,
11967			direction,
11968			isdeltax = delta.x > 0;
11969		if ( ( isdeltax && ! this.settings.rtl ) || ( this.settings.rtl && ! isdeltax ) ) {
11970			direction = 'left';
11971		} else {
11972			direction = 'right';
11973		}
11974		$( document ).off( '.owl.core' );
11975
11976		this.$element.removeClass( this.options.grabClass );
11977
11978		if ( ( delta.x !== 0 && this.is( 'dragging' ) ) || ! this.is( 'valid' ) ) {
11979			this.speed( this.settings.dragEndSpeed || this.settings.smartSpeed );
11980			this.current( this.closest( stage.x, delta.x !== 0 ? direction : this._drag.direction ) );
11981			this.invalidate( 'position' );
11982			this.update();
11983
11984			this._drag.direction = direction;
11985
11986			if ( Math.abs( delta.x ) > 3 || new Date().getTime() - this._drag.time > 300 ) {
11987				this._drag.target.one( 'click.owl.core', function() {
11988					return false;
11989				} );
11990			}
11991		}
11992
11993		if ( ! this.is( 'dragging' ) ) {
11994			return;
11995		}
11996
11997		this.leave( 'dragging' );
11998		this.trigger( 'dragged' );
11999	};
12000
12001	/**
12002	 * Gets absolute position of the closest item for a coordinate.
12003	 *
12004	 * @todo Setting `freeDrag` makes `closest` not reusable. See #165.
12005	 * @protected
12006	 * @param {number} coordinate - The coordinate in pixel.
12007	 * @param {string} direction  - The direction to check for the closest item. Ether `left` or `right`.
12008	 * @return {number} - The absolute position of the closest item.
12009	 */
12010	Owl.prototype.closest = function( coordinate, direction ) {
12011		var position = -1,
12012			pull = 30,
12013			width = this.width(),
12014			coordinates = this.coordinates();
12015
12016		if ( ! this.settings.freeDrag ) {
12017			// check closest item
12018			$.each(
12019				coordinates,
12020				$.proxy( function( index, value ) {
12021					// on a left pull, check on current index
12022					if ( direction === 'left' && coordinate > value - pull && coordinate < value + pull ) {
12023						position = index;
12024						// on a right pull, check on previous index
12025						// to do so, subtract width from value and set position = index + 1
12026					} else if ( direction === 'right' && coordinate > value - width - pull && coordinate < value - width + pull ) {
12027						position = index + 1;
12028					} else if ( this.op( coordinate, '<', value ) && this.op( coordinate, '>', coordinates[ index + 1 ] !== undefined ? coordinates[ index + 1 ] : value - width ) ) {
12029						position = direction === 'left' ? index + 1 : index;
12030					}
12031					return position === -1;
12032				}, this )
12033			);
12034		}
12035
12036		if ( ! this.settings.loop ) {
12037			// non loop boundries
12038			if ( this.op( coordinate, '>', coordinates[ this.minimum() ] ) ) {
12039				coordinate = this.minimum();
12040				position = coordinate;
12041			} else if ( this.op( coordinate, '<', coordinates[ this.maximum() ] ) ) {
12042				coordinate = this.maximum();
12043				position = coordinate;
12044			}
12045		}
12046
12047		return position;
12048	};
12049
12050	/**
12051	 * Animates the stage.
12052	 *
12053	 * @todo #270
12054	 * @public
12055	 * @param {number} coordinate - The coordinate in pixels.
12056	 */
12057	Owl.prototype.animate = function( coordinate ) {
12058		var animate = this.speed() > 0;
12059
12060		if ( this.is( 'animating' ) ) {
12061			this.onTransitionEnd();
12062		}
12063
12064		if ( animate ) {
12065			this.enter( 'animating' );
12066			this.trigger( 'translate' );
12067		}
12068
12069		if ( $.support.transform3d && $.support.transition ) {
12070			this.$stage.css( {
12071				transform: 'translate3d(' + coordinate + 'px,0px,0px)',
12072				transition: ( this.speed() / 1000 ) + 's' + ( this.settings.slideTransition ? ' ' + this.settings.slideTransition : '' )
12073			} );
12074		} else if ( animate ) {
12075			this.$stage.animate(
12076				{
12077					left: coordinate + 'px'
12078				},
12079				this.speed(),
12080				this.settings.fallbackEasing,
12081				$.proxy( this.onTransitionEnd, this )
12082			);
12083		} else {
12084			this.$stage.css( {
12085				left: coordinate + 'px'
12086			} );
12087		}
12088	};
12089
12090	/**
12091	 * Checks whether the carousel is in a specific state or not.
12092	 *
12093	 * @param {string} state - The state to check.
12094	 * @returns {boolean} - The flag which indicates if the carousel is busy.
12095	 */
12096	Owl.prototype.is = function( state ) {
12097		return this._states.current[ state ] && this._states.current[ state ] > 0;
12098	};
12099
12100	/**
12101	 * Sets the absolute position of the current item.
12102	 *
12103	 * @public
12104	 * @param {number} [position] - The new absolute position or nothing to leave it unchanged.
12105	 * @returns {number} - The absolute position of the current item.
12106	 */
12107	Owl.prototype.current = function( position ) {
12108		var event;
12109		if ( position === undefined ) {
12110			return this._current;
12111		}
12112
12113		if ( this._items.length === 0 ) {
12114			return undefined;
12115		}
12116
12117		position = this.normalize( position );
12118
12119		if ( this._current !== position ) {
12120			event = this.trigger( 'change', { property: { name: 'position', value: position } } );
12121
12122			if ( event.data !== undefined ) {
12123				position = this.normalize( event.data );
12124			}
12125
12126			this._current = position;
12127
12128			this.invalidate( 'position' );
12129
12130			this.trigger( 'changed', { property: { name: 'position', value: this._current } } );
12131		}
12132
12133		return this._current;
12134	};
12135
12136	/**
12137	 * Invalidates the given part of the update routine.
12138	 *
12139	 * @param {string} [part] - The part to invalidate.
12140	 * @returns {Array.<string>} - The invalidated parts.
12141	 */
12142	Owl.prototype.invalidate = function( part ) {
12143		if ( typeof part === 'string' ) {
12144			this._invalidated[ part ] = true;
12145			if ( this.is( 'valid' ) ) {
12146				this.leave( 'valid' );
12147			}
12148		}
12149		return $.map( this._invalidated, function( v, i ) {
12150			return i;
12151		} );
12152	};
12153
12154	/**
12155	 * Resets the absolute position of the current item.
12156	 *
12157	 * @public
12158	 * @param {number} position - The absolute position of the new item.
12159	 */
12160	Owl.prototype.reset = function( position ) {
12161		position = this.normalize( position );
12162
12163		if ( position === undefined ) {
12164			return;
12165		}
12166
12167		this._speed = 0;
12168		this._current = position;
12169
12170		this.suppress( [ 'translate', 'translated' ] );
12171
12172		this.animate( this.coordinates( position ) );
12173
12174		this.release( [ 'translate', 'translated' ] );
12175	};
12176
12177	/**
12178	 * Normalizes an absolute or a relative position of an item.
12179	 *
12180	 * @public
12181	 * @param {number}  position         - The absolute or relative position to normalize.
12182	 * @param {boolean} [relative=false] - Whether the given position is relative or not.
12183	 * @returns {number} - The normalized position.
12184	 */
12185	Owl.prototype.normalize = function( position, relative ) {
12186		var n = this._items.length,
12187			m = relative ? 0 : this._clones.length;
12188
12189		if ( ! this.isNumeric( position ) || n < 1 ) {
12190			position = undefined;
12191		} else if ( position < 0 || position >= n + m ) {
12192			position = ( ( ( ( position - ( m / 2 ) ) % n ) + n ) % n ) + ( m / 2 );
12193		}
12194
12195		return position;
12196	};
12197
12198	/**
12199	 * Converts an absolute position of an item into a relative one.
12200	 *
12201	 * @public
12202	 * @param {number} position - The absolute position to convert.
12203	 * @returns {number} - The converted position.
12204	 */
12205	Owl.prototype.relative = function( position ) {
12206		position -= this._clones.length / 2;
12207		return this.normalize( position, true );
12208	};
12209
12210	/**
12211	 * Gets the maximum position for the current item.
12212	 *
12213	 * @public
12214	 * @param {boolean} [relative=false] - Whether to return an absolute position or a relative position.
12215	 * @returns {number}
12216	 */
12217	Owl.prototype.maximum = function( relative ) {
12218		var settings = this.settings,
12219			maximum = this._coordinates.length,
12220			iterator,
12221			reciprocalItemsWidth,
12222			elementWidth;
12223
12224		if ( settings.loop ) {
12225			maximum = ( this._clones.length / 2 ) + this._items.length - 1;
12226		} else if ( settings.autoWidth || settings.merge ) {
12227			iterator = this._items.length;
12228			if ( iterator ) {
12229				iterator = iterator - 1;
12230				reciprocalItemsWidth = this._items[ iterator ].width();
12231				elementWidth = this.$element.width();
12232				while ( iterator > 0 ) {
12233					reciprocalItemsWidth += this._items[ iterator ].width() + this.settings.margin;
12234					if ( reciprocalItemsWidth > elementWidth ) {
12235						break;
12236					}
12237					iterator = iterator - 1;
12238				}
12239			}
12240			maximum = iterator + 1;
12241		} else if ( settings.center ) {
12242			maximum = this._items.length - 1;
12243		} else {
12244			maximum = this._items.length - settings.items;
12245		}
12246
12247		if ( relative ) {
12248			maximum -= this._clones.length / 2;
12249		}
12250
12251		return Math.max( maximum, 0 );
12252	};
12253
12254	/**
12255	 * Gets the minimum position for the current item.
12256	 *
12257	 * @public
12258	 * @param {boolean} [relative=false] - Whether to return an absolute position or a relative position.
12259	 * @returns {number}
12260	 */
12261	Owl.prototype.minimum = function( relative ) {
12262		return relative ? 0 : this._clones.length / 2;
12263	};
12264
12265	/**
12266	 * Gets an item at the specified relative position.
12267	 *
12268	 * @public
12269	 * @param {number} [position] - The relative position of the item.
12270	 * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given.
12271	 */
12272	Owl.prototype.items = function( position ) {
12273		if ( position === undefined ) {
12274			return this._items.slice();
12275		}
12276
12277		position = this.normalize( position, true );
12278		return this._items[ position ];
12279	};
12280
12281	/**
12282	 * Gets an item at the specified relative position.
12283	 *
12284	 * @public
12285	 * @param {number} [position] - The relative position of the item.
12286	 * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given.
12287	 */
12288	Owl.prototype.mergers = function( position ) {
12289		if ( position === undefined ) {
12290			return this._mergers.slice();
12291		}
12292
12293		position = this.normalize( position, true );
12294		return this._mergers[ position ];
12295	};
12296
12297	/**
12298	 * Gets the absolute positions of clones for an item.
12299	 *
12300	 * @public
12301	 * @param {number} [position] - The relative position of the item.
12302	 * @returns {Array.<number>} - The absolute positions of clones for the item or all if no position was given.
12303	 */
12304	Owl.prototype.clones = function( position ) {
12305		var odd = this._clones.length / 2,
12306			even = odd + this._items.length,
12307			map = function( index ) {
12308				return index % 2 === 0 ? even + ( index / 2 ) : odd - ( ( index + 1 ) / 2 );
12309			};
12310
12311		if ( position === undefined ) {
12312			return $.map( this._clones, function( v, i ) {
12313				return map( i );
12314			} );
12315		}
12316
12317		return $.map( this._clones, function( v, i ) {
12318			return v === position ? map( i ) : null;
12319		} );
12320	};
12321
12322	/**
12323	 * Sets the current animation speed.
12324	 *
12325	 * @public
12326	 * @param {number}
12326 [speed] - The animation speed in milliseconds or nothing to leave it unchanged.
12327	 * @returns {number} - The current animation speed in milliseconds.
12328	 */
12329	Owl.prototype.speed = function( speed ) {
12330		if ( speed !== undefined ) {
12331			this._speed = speed;
12332		}
12333
12334		return this._speed;
12335	};
12336
12337	/**
12338	 * Gets the coordinate of an item.
12339	 *
12340	 * @todo The name of this method is missleanding.
12341	 * @public
12342	 * @param {number} position - The absolute position of the item within `minimum()` and `maximum()`.
12343	 * @returns {number|Array.<number>} - The coordinate of the item in pixel or all coordinates.
12344	 */
12345	Owl.prototype.coordinates = function( position ) {
12346		var multiplier = 1,
12347			newPosition = position - 1,
12348			coordinate;
12349
12350		if ( position === undefined ) {
12351			return $.map(
12352				this._coordinates,
12353				$.proxy( function( scoordinate, index ) {
12354					return this.coordinates( index );
12355				}, this )
12356			);
12357		}
12358
12359		if ( this.settings.center ) {
12360			if ( this.settings.rtl ) {
12361				multiplier = -1;
12362				newPosition = position + 1;
12363			}
12364
12365			coordinate = this._coordinates[ position ];
12366			coordinate += ( ( this.width() - coordinate + ( this._coordinates[ newPosition ] || 0 ) ) / 2 ) * multiplier;
12367		} else {
12368			coordinate = this._coordinates[ newPosition ] || 0;
12369		}
12370
12371		coordinate = Math.ceil( coordinate );
12372
12373		return coordinate;
12374	};
12375
12376	/**
12377	 * Calculates the speed for a translation.
12378	 *
12379	 * @protected
12380	 * @param {number} from               - The absolute position of the start item.
12381	 * @param {number} to                 - The absolute position of the target item.
12382	 * @param {number} [factor=undefined] - The time factor in milliseconds.
12383	 * @returns {number} - The time in milliseconds for the translation.
12384	 */
12385	Owl.prototype.duration = function( from, to, factor ) {
12386		if ( factor === 0 ) {
12387			return 0;
12388		}
12389
12390		return Math.min( Math.max( Math.abs( to - from ), 1 ), 6 ) * Math.abs( factor || this.settings.smartSpeed );
12391	};
12392
12393	/**
12394	 * Slides to the specified item.
12395	 *
12396	 * @public
12397	 * @param {number} position - The position of the item.
12398	 * @param {number} [speed]  - The time in milliseconds for the transition.
12399	 */
12400	Owl.prototype.to = function( position, speed ) {
12401		var current = this.current(),
12402			revert = null,
12403			distance = position - this.relative( current ),
12404			direction = ( distance > 0 ) - ( distance < 0 ),
12405			items = this._items.length,
12406			minimum = this.minimum(),
12407			maximum = this.maximum();
12408
12409		if ( this.settings.loop ) {
12410			if ( ! this.settings.rewind && Math.abs( distance ) > items / 2 ) {
12411				distance += direction * -1 * items;
12412			}
12413
12414			position = current + distance;
12415			revert = ( ( ( ( position - minimum ) % items ) + items ) % items ) + minimum;
12416
12417			if ( revert !== position && revert - distance <= maximum && revert - distance > 0 ) {
12418				current = revert - distance;
12419				position = revert;
12420				this.reset( current );
12421			}
12422		} else if ( this.settings.rewind ) {
12423			maximum += 1;
12424			position = ( ( position % maximum ) + maximum ) % maximum;
12425		} else {
12426			position = Math.max( minimum, Math.min( maximum, position ) );
12427		}
12428
12429		this.speed( this.duration( current, position, speed ) );
12430		this.current( position );
12431
12432		if ( this.isVisible() ) {
12433			this.update();
12434		}
12435	};
12436
12437	/**
12438	 * Slides to the next item.
12439	 *
12440	 * @public
12441	 * @param {number} [speed] - The time in milliseconds for the transition.
12442	 */
12443	Owl.prototype.next = function( speed ) {
12444		speed = speed || false;
12445		this.to( this.relative( this.current() ) + 1, speed );
12446	};
12447
12448	/**
12449	 * Slides to the previous item.
12450	 *
12451	 * @public
12452	 * @param {number} [speed] - The time in milliseconds for the transition.
12453	 */
12454	Owl.prototype.prev = function( speed ) {
12455		speed = speed || false;
12456		this.to( this.relative( this.current() ) - 1, speed );
12457	};
12458
12459	/**
12460	 * Handles the end of an animation.
12461	 *
12462	 * @protected
12463	 * @param {Event} event - The event arguments.
12464	 */
12465	Owl.prototype.onTransitionEnd = function( event ) {
12466		// if css2 animation then event object is undefined
12467		if ( event !== undefined ) {
12468			event.stopPropagation();
12469
12470			// Catch only owl-stage transitionEnd event
12471			if ( ( event.target || event.srcElement || event.originalTarget ) !== this.$stage.get( 0 ) ) {
12472				return false;
12473			}
12474		}
12475
12476		this.leave( 'animating' );
12477		this.trigger( 'translated' );
12478	};
12479
12480	/**
12481	 * Gets viewport width.
12482	 *
12483	 * @protected
12484	 * @return {number} - The width in pixel.
12485	 */
12486	Owl.prototype.viewport = function() {
12487		var width;
12488		if ( this.options.responsiveBaseElement !== window ) {
12489			width = $( this.options.responsiveBaseElement ).width();
12490		} else if ( window.innerWidth ) {
12491			width = window.innerWidth;
12492		} else if ( document.documentElement && document.documentElement.clientWidth ) {
12493			width = document.documentElement.clientWidth;
12494		} else {
12495			//Cannot detect viewport width
12496			return width;
12497		}
12498		return width;
12499	};
12500
12501	/**
12502	 * Replaces the current content.
12503	 *
12504	 * @public
12505	 * @param {HTMLElement|jQuery|string} content - The new content.
12506	 */
12507	Owl.prototype.replace = function( content ) {
12508		this.$stage.empty();
12509		this._items = [];
12510
12511		if ( content ) {
12512			content = content instanceof window.jQuery ? content : $( content );
12513		}
12514
12515		if ( this.settings.nestedItemSelector ) {
12516			content = content.find( '.' + this.settings.nestedItemSelector );
12517		}
12518
12519		content
12520			.filter( function() {
12521				return this.nodeType === 1;
12522			} )
12523			.each(
12524				$.proxy( function( index, item ) {
12525					item = this.prepare( item );
12526					this.$stage.append( item );
12527					this._items.push( item );
12528					this._mergers.push( item.find( '[data-merge]' ).addBack( '[data-merge]' ).attr( 'data-merge' ) * 1 || 1 );
12529				}, this )
12530			);
12531
12532		this.reset( this.isNumeric( this.settings.startPosition ) ? this.settings.startPosition : 0 );
12533
12534		this.invalidate( 'items' );
12535	};
12536
12537	/**
12538	 * Adds an item.
12539	 *
12540	 * @todo Use `item` instead of `content` for the event arguments.
12541	 * @public
12542	 * @param {HTMLElement|jQuery|string} content    - The item content to add.
12543	 * @param {number}                    [position] - The relative position at which to insert the item otherwise the item will be added to the end.
12544	 */
12545	Owl.prototype.add = function( content, position ) {
12546		var current = this.relative( this._current );
12547
12548		position = position === undefined ? this._items.length : this.normalize( position, true );
12549		content = content instanceof window.jQuery ? content : $( content );
12550
12551		this.trigger( 'add', { content: content, position: position } );
12552
12553		content = this.prepare( content );
12554
12555		if ( this._items.length === 0 || position === this._items.length ) {
12556			if ( this._items.length === 0 ) {
12557				this.$stage.append( content );
12558			}
12559			if ( this._items.length !== 0 ) {
12560				this._items[ position - 1 ].after( content );
12561			}
12562			this._items.push( content );
12563			this._mergers.push( content.find( '[data-merge]' ).addBack( '[data-merge]' ).attr( 'data-merge' ) * 1 || 1 );
12564		} else {
12565			this._items[ position ].before( content );
12566			this._items.splice( position, 0, content );
12567			this._mergers.splice( position, 0, content.find( '[data-merge]' ).addBack( '[data-merge]' ).attr( 'data-merge' ) * 1 || 1 );
12568		}
12569
12570		if ( this._items[ current ] ) {
12571			this.reset( this._items[ current ].index() );
12572		}
12573
12574		this.invalidate( 'items' );
12575
12576		this.trigger( 'added', { content: content, position: position } );
12577	};
12578
12579	/**
12580	 * Removes an item by its position.
12581	 *
12582	 * @todo Use `item` instead of `content` for the event arguments.
12583	 * @public
12584	 * @param {number} position - The relative position of the item to remove.
12585	 */
12586	Owl.prototype.remove = function( position ) {
12587		position = this.normalize( position, true );
12588
12589		if ( position === undefined ) {
12590			return;
12591		}
12592
12593		this.trigger( 'remove', { content: this._items[ position ], position: position } );
12594
12595		this._items[ position ].remove();
12596		this._items.splice( position, 1 );
12597		this._mergers.splice( position, 1 );
12598
12599		this.invalidate( 'items' );
12600
12601		this.trigger( 'removed', { content: null, position: position } );
12602	};
12603
12604	/**
12605	 * Preloads images with auto width.
12606	 *
12607	 * @param {mixed} images
12608	 * @todo Replace by a more generic approach
12609	 * @protected
12610	 */
12611	Owl.prototype.preloadAutoWidthImages = function( images ) {
12612		images.each(
12613			$.proxy( function( i, element ) {
12614				this.enter( 'pre-loading' );
12615				element = $( element );
12616				$( new Image() )
12617					.one(
12618						'load',
12619						$.proxy( function( e ) {
12620							element.attr( 'src', e.target.src );
12621							element.css( 'opacity', 1 );
12622							this.leave( 'pre-loading' );
12623							if ( ! this.is( 'pre-loading' ) && ! this.is( 'initializing' ) ) {
12624								this.refresh();
12625							}
12626						}, this )
12627					)
12628					.attr( 'src', element.attr( 'src' ) || element.attr( 'data-src' ) || element.attr( 'data-src-retina' ) );
12629			}, this )
12630		);
12631	};
12632
12633	/**
12634	 * Destroys the carousel.
12635	 *
12636	 * @public
12637	 */
12638	Owl.prototype.destroy = function() {
12639		this.$element.off( '.owl.core' );
12640		this.$stage.off( '.owl.core' );
12641		$( document ).off( '.owl.core' );
12642
12643		if ( this.settings.responsive !== false ) {
12644			window.clearTimeout( this.resizeTimer );
12645			this.off( window, 'resize', this._handlers.onThrottledResize );
12646		}
12647
12648		$.map( this._plugins, function( value ) {
12649			return value;
12650		} ).forEach( function( inst ) {
12651			if ( inst ) {
12652				inst.destroy();
12653			}
12654		} );
12655
12656		this.$stage.children( '.cloned' ).remove();
12657
12658		this.$stage.unwrap();
12659		this.$stage.children().contents().unwrap();
12660		this.$stage.children().unwrap();
12661		this.$stage.remove();
12662		this.$element
12663			.removeClass( this.options.refreshClass )
12664			.removeClass( this.options.loadingClass )
12665			.removeClass( this.options.loadedClass )
12666			.removeClass( this.options.rtlClass )
12667			.removeClass( this.options.dragClass )
12668			.removeClass( this.options.grabClass )
12669			.attr( 'class', this.$element.attr( 'class' ).replace( new RegExp( this.options.responsiveClass + '-\\S+\\s', 'g' ), '' ) )
12670			.removeData( 'owl.carousel' );
12671	};
12672
12673	/**
12674	 * Operators to calculate right-to-left and left-to-right.
12675	 *
12676	 * @protected
12677	 * @param {number} [a] - The left side operand.
12678	 * @param {string} [o] - The operator.
12679	 * @param {number} [b] - The right side operand.
12680	 */
12681	Owl.prototype.op = function( a, o, b ) {
12682		var rtl = this.settings.rtl;
12683		switch ( o ) {
12684			case '<':
12685				return rtl ? a > b : a < b;
12686			case '>':
12687				return rtl ? a < b : a > b;
12688			case '>=':
12689				return rtl ? a <= b : a >= b;
12690			case '<=':
12691				return rtl ? a >= b : a <= b;
12692			default:
12693				break;
12694		}
12695	};
12696
12697	/**
12698	 * Attaches to an internal event.
12699	 *
12700	 * @protected
12701	 * @param {HTMLElement} element  - The event source.
12702	 * @param {string}      event    - The event name.
12703	 * @param {Function}    listener - The event handler to attach.
12704	 * @param {boolean}
vendor: 6,218 bytes, lines 12704-12928
12704     capture  - Wether the event should be handled at the capturing phase or not.
12705	 */
12706	Owl.prototype.on = function( element, event, listener, capture ) {
12707		if ( element.addEventListener ) {
12708			element.addEventListener( event, listener, capture );
12709		} else if ( element.attachEvent ) {
12710			element.attachEvent( 'on' + event, listener );
12711		}
12712	};
12713
12714	/**
12715	 * Detaches from an internal event.
12716	 *
12717	 * @protected
12718	 * @param {HTMLElement} element  - The event source.
12719	 * @param {string}      event    - The event name.
12720	 * @param {Function}    listener - The attached event handler to detach.
12721	 * @param {boolean}     capture  - Wether the attached event handler was registered as a capturing listener or not.
12722	 */
12723	Owl.prototype.off = function( element, event, listener, capture ) {
12724		if ( element.removeEventListener ) {
12725			element.removeEventListener( event, listener, capture );
12726		} else if ( element.detachEvent ) {
12727			element.detachEvent( 'on' + event, listener );
12728		}
12729	};
12730
12731	/**
12732	 * Triggers a public event.
12733	 *
12734	 * @todo Remove `status`, `relatedTarget` should be used instead.
12735	 * @protected
12736	 * @param {string} name                 - The event name.
12737	 * @param {*}      [data=null]          - The event data.
12738	 * @param {string} [namespace=carousel] - The event namespace.
12739	 * @returns {Event} - The event arguments.
12740	 */
12741	Owl.prototype.trigger = function( name, data, namespace ) {
12742		var status = {
12743				item: { count: this._items.length, index: this.current() }
12744			},
12745			handler = $.camelCase(
12746				$.grep( [ 'on', name, namespace ], function( v ) {
12747					return v;
12748				} )
12749					.join( '-' )
12750					.toLowerCase()
12751			),
12752			/*eslint new-cap: [2, {capIsNewExceptions: ["Event"]}]*/
12753			event = $.Event( [ name, 'owl', namespace || 'carousel' ].join( '.' ).toLowerCase(), $.extend( { relatedTarget: this }, status, data ) );
12754
12755		if ( ! this._supress[ name ] ) {
12756			$.each( this._plugins, function( sname, plugin ) {
12757				if ( plugin.onTrigger ) {
12758					plugin.onTrigger( event );
12759				}
12760			} );
12761
12762			this.register( { type: Owl.Type.Event, name: name } );
12763			this.$element.trigger( event );
12764
12765			if ( this.settings && typeof this.settings[ handler ] === 'function' ) {
12766				this.settings[ handler ].call( this, event );
12767			}
12768		}
12769
12770		return event;
12771	};
12772
12773	/**
12774	 * Enters a state.
12775	 *
12776	 * @param {mixed} name - The state name.
12777	 */
12778	Owl.prototype.enter = function( name ) {
12779		$.each(
12780			[ name ].concat( this._states.tags[ name ] || [] ),
12781			$.proxy( function( i, sname ) {
12782				if ( this._states.current[ sname ] === undefined ) {
12783					this._states.current[ sname ] = 0;
12784				}
12785
12786				this._states.current[ sname ] += 1;
12787			}, this )
12788		);
12789	};
12790
12791	/**
12792	 * Leaves a state.
12793	 *
12794	 * @param {mixed} name - The state name.
12795	 */
12796	Owl.prototype.leave = function( name ) {
12797		$.each(
12798			[ name ].concat( this._states.tags[ name ] || [] ),
12799			$.proxy( function( i, sname ) {
12800				this._states.current[ sname ] -= 1;
12801			}, this )
12802		);
12803	};
12804
12805	/**
12806	 * Registers an event or state.
12807	 *
12808	 * @public
12809	 * @param {Object} object - The event or state to register.
12810	 */
12811	Owl.prototype.register = function( object ) {
12812		var _default;
12813		if ( object.type === Owl.Type.Event ) {
12814			if ( ! $.event.special[ object.name ] ) {
12815				$.event.special[ object.name ] = {};
12816			}
12817
12818			if ( ! $.event.special[ object.name ].owl ) {
12819				_default = $.event.special[ object.name ]._default;
12820				$.event.special[ object.name ]._default = function( e ) {
12821					if ( _default && _default.apply && ( ! e.namespace || e.namespace.indexOf( 'owl' ) === -1 ) ) {
12822						return _default.apply( this, arguments );
12823					}
12824					return e.namespace && e.namespace.indexOf( 'owl' ) > -1;
12825				};
12826				$.event.special[ object.name ].owl = true;
12827			}
12828		} else if ( object.type === Owl.Type.State ) {
12829			if ( ! this._states.tags[ object.name ] ) {
12830				this._states.tags[ object.name ] = object.tags;
12831			} else {
12832				this._states.tags[ object.name ] = this._states.tags[ object.name ].concat( object.tags );
12833			}
12834
12835			this._states.tags[ object.name ] = $.grep(
12836				this._states.tags[ object.name ],
12837				$.proxy( function( tag, i ) {
12838					return $.inArray( tag, this._states.tags[ object.name ] ) === i;
12839				}, this )
12840			);
12841		}
12842	};
12843
12844	/**
12845	 * Suppresses events.
12846	 *
12847	 * @protected
12848	 * @param {Array.<string>} events - The events to suppress.
12849	 */
12850	Owl.prototype.suppress = function( events ) {
12851		$.each(
12852			events,
12853			$.proxy( function( index, event ) {
12854				this._supress[ event ] = true;
12855			}, this )
12856		);
12857	};
12858
12859	/**
12860	 * Releases suppressed events.
12861	 *
12862	 * @protected
12863	 * @param {Array.<string>} events - The events to release.
12864	 */
12865	Owl.prototype.release = function( events ) {
12866		$.each(
12867			events,
12868			$.proxy( function( index, event ) {
12869				delete this._supress[ event ];
12870			}, this )
12871		);
12872	};
12873
12874	/**
12875	 * Gets unified pointer coordinates from event.
12876	 *
12877	 * @todo #261
12878	 * @protected
12879	 * @param {Event} event The `mousedown` or `touchstart` event.
12880	 * @returns {Object} - Contains `x` and `y` coordinates of current pointer position.
12881	 */
12882	Owl.prototype.pointer = function( event ) {
12883		var result = { x: null, y: null };
12884
12885		event = event.originalEvent || event || window.event;
12886
12887		event = event.touches && event.touches.length ? event.touches[ 0 ] : event.changedTouches && event.changedTouches.length ? event.changedTouches[ 0 ] : event;
12888
12889		if ( event.pageX ) {
12890			result.x = event.pageX;
12891			result.y = event.pageY;
12892		} else {
12893			result.x = event.clientX;
12894			result.y = event.clientY;
12895		}
12896
12897		return result;
12898	};
12899
12900	/**
12901	 * Determines if the input is a Number or something that can be coerced to a Number
12902	 *
12903	 * @protected
12904	 * @param {number|string|Object|Array|boolean|RegExp|Function|symbol} number The input to be tested
12905	 * @returns {boolean} - An indication if the input is a Number or can be coerced to a Number
12906	 */
12907	Owl.prototype.isNumeric = function( number ) {
12908		return ! isNaN( parseFloat( number ) );
12909	};
12910
12911	/**
12912	 * Gets the difference of two vectors.
12913	 *
12914	 * @todo #261
12915	 * @protected
12916	 * @param {Object} first  The first vector.
12917	 * @param {Object} second The second vector.
12918	 * @returns {Object} - The difference.
12919	 */
12920	Owl.prototype.difference = function( first, second ) {
12921		return {
12922			x: first.x - second.x,
12923			y: first.y - second.y
12924		};
12925	};
12926
12927	/**
12928	 * The jQuery Plugin for the Owl Carousel
12929	 *
12930	 * @param {mixed} option
12931	 * @todo Navigation plugin `next` and `prev`
12932	 * @public
12933	 */
12934	$.fn.tmowlCarousel = function( option ) {
12935		var args = Array.prototype.slice.call( arguments, 1 );
12936
12937		return this.each( function() {
12938			var $this = $( this ),
12939				data = $this.data( 'owl.carousel' );
12940
12941			if ( ! data ) {
12942				data = new Owl( this, typeof option === 'object' && option );
12943				$this.data( 'owl.carousel', data );
12944
12945				$.each( [ 'next', 'prev', 'to', 'destroy', 'refresh', 'replace', 'add', 'remove' ], function( i, event ) {
12946					data.register( { type: Owl.Type.Event, name: event } );
12947					data.$element.on(
12948						event + '.owl.carousel.core',
12949						$.proxy( function( e ) {
12950							if ( e.namespace && e.relatedTarget !== this ) {
12951								this.suppress( [ event ] );
12952								data[ event ].apply( this, [].slice.call( arguments, 1 ) );
12953								this.release( [ event ] );
12954							}
12955						}, data )
12956					);
12957				} );
12958			}
12959
12960			if ( typeof option === 'string' && option.charAt( 0 ) !== '_' ) {
12961				data[ option ].apply( data, args );
12962			}
12963		} );
12964	};
12965
12966	/**
12967	 * The constructor for the jQuery Plugin
12968	 *
12969	 * @public
12970	 */
12971	$.fn.tmowlCarousel.Constructor = Owl;
12972}( window.Zepto || window.jQuery, window, document ) );
12973
12974/**
12975 * AutoRefresh Plugin
12976 *
12977 * @param {jQuery} $
12978 * @param {Window} window
12979 * @version 2.3.4
12980 * @author Artus Kolanowski
12981 * @author David Deutsch
12982 * @license The MIT License (MIT)
12983 */
12984( function( $, window ) {
12985	'use strict';
12986	/**
12987	 * Creates the auto refresh plugin.
12988	 *
12989	 * @class The Auto Refresh Plugin
12990	 * @param {Owl} carousel - The Owl Carousel
12991	 */
12992	var AutoRefresh = function( carousel ) {
12993		/**
12994		 * Reference to the core.
12995		 *
12996		 * @protected
12997		 * @type {Owl}
12998		 */
12999		this._core = carousel;
13000
13001		/**
13002		 * Refresh interval.
13003		 *
13004		 * @protected
13005		 * @type {number}
13006		 */
13007		this._interval = null;
13008
13009		/**
13010		 * Whether the element is currently visible or not.
13011		 *
13012		 * @protected
13013		 * @type {boolean}
13014		 */
13015		this._visible = null;
13016
13017		/**
13018		 * All event handlers.
13019		 *
13020		 * @protected
13021		 * @type {Object}
13022		 */
13023		this._handlers = {
13024			'initialized.owl.carousel': $.proxy( function( e ) {
13025				if ( e.namespace && this._core.settings.autoRefresh ) {
13026					this.watch();
13027				}
13028			}, this )
13029		};
13030
13031		// set default options
13032		this._core.options = $.extend( {}, AutoRefresh.Defaults, this._core.options );
13033
13034		// register event handlers
13035		this._core.$element.on( this._handlers );
13036	};
13037
13038	/**
13039	 * Default options.
13040	 *
13041	 * @public
13042	 */
13043	AutoRefresh.Defaults = {
13044		autoRefresh: true,
13045		autoRefreshInterval: 500
13046	};
13047
13048	/**
13049	 * Watches the element.
13050	 */
13051	AutoRefresh.prototype.watch = function() {
13052		if ( this._interval ) {
13053			return;
13054		}
13055
13056		this._visible = this._core.isVisible();
13057		this._interval = window.setInterval( $.proxy( this.refresh, this ), this._core.settings.autoRefreshInterval );
13058	};
13059
13060	/**
13061	 * Refreshes the element.
13062	 */
13063	AutoRefresh.prototype.refresh = function() {
13064		if ( this._core.isVisible() === this._visible ) {
13065			return;
13066		}
13067
13068		this._visible = ! this._visible;
13069
13070		this._core.$element.toggleClass( 'owl-hidden', ! this._visible );
13071
13072		if ( this._visible && this._core.invalidate( 'width' ) ) {
13073			this._core.refresh();
13074		}
13075	};
13076
13077	/**
13078	 * Destroys the plugin.
13079	 */
13080	AutoRefresh.prototype.destroy = function() {
13081		var handler, property;
13082
13083		window.clearInterval( this._interval );
13084
13085		for ( handler in this._handlers ) {
13086			if ( handler ) {
13087				this._core.$element.off( handler, this._handlers[ handler ] );
13088			}
13089		}
13090		for ( property in Object.getOwnPropertyNames( this ) ) {
13091			if ( typeof this[ property ] !== 'function' ) {
13092				this[ property ] = null;
13093			}
13094		}
13095	};
13096
13097	$.fn.tmowlCarousel.Constructor.Plugins.AutoRefresh = AutoRefresh;
13098}( window.Zepto || window.jQuery, window ) );
13099
13100/**
13101 * Lazy Plugin
13102 *
13103 * @param {jQuery} $
13104 * @param {Window} window
13105 * @version 2.3.4
13106 * @author Bartosz Wojciechowski
13107 * @author David Deutsch
13108 * @license The MIT License (MIT)
13109 */
13110( function( $, window ) {
13111	'use strict';
13112	/**
13113	 * Creates the lazy plugin.
13114	 *
13115	 * @class The Lazy Plugin
13116	 * @param {Owl} carousel - The Owl Carousel
13117	 */
13118	var Lazy = function( carousel ) {
13119		/**
13120		 * Reference to the core.
13121		 *
13122		 * @protected
13123		 * @type {Owl}
13124		 */
13125		this._core = carousel;
13126
13127		/**
13128		 * Already loaded items.
13129		 *
13130		 * @protected
13131		 * @type {Array.<jQuery>}
13132		 */
13133		this._loaded = [];
13134
13135		/**
13136		 * Event handlers.
13137		 *
13138		 * @protected
13139		 * @type {Object}
13140		 */
13141		this._handlers = {
13142			'initialized.owl.carousel change.owl.carousel resized.owl.carousel': $.proxy( function( e ) {
13143				var settings;
13144				var n;
13145				var i;
13146				var position;
13147				var clones;
13148				var load;
13149
13150				if ( ! e.namespace ) {
13151					return;
13152				}
13153
13154				if ( ! this._core.settings || ! this._core.settings.lazyLoad ) {
13155					return;
13156				}
13157
13158				if ( ( e.property && e.property.name === 'position' ) || e.type === 'initialized' ) {
13159					settings = this._core.settings;
13160					n = ( settings.center && Math.ceil( settings.items / 2 ) ) || settings.items;
13161					i = ( settings.center && n * -1 ) || 0;
13162					position = ( e.property && e.property.value !== undefined ? e.property.value : this._core.current() ) + i;
13163					clones = this._core.clones().length;
13164					load = $.proxy( function( ii, v ) {
13165						this.load( v );
13166					}, this );
13167					//TODO: Need documentation for this new option
13168					if ( settings.lazyLoadEager > 0 ) {
13169						n += settings.lazyLoadEager;
13170						// If the carousel is looping also preload images that are to the "left"
13171						if ( settings.loop ) {
13172							position -= settings.lazyLoadEager;
13173							n += 1;
13174						}
13175					}
13176
13177					while ( i < n ) {
13178						i += 1;
13179						this.load( ( clones / 2 ) + this._core.relative( position ) );
13180						if ( clones ) {
13181							$.each( this._core.clones( this._core.relative( position ) ), load );
13182						}
13183						position += 1;
13184					}
13185				}
13186			}, this )
13187		};
13188
13189		// set the default options
13190		this._core.options = $.extend( {}, Lazy.Defaults, this._core.options );
13191
13192		// register event handler
13193		this._core.$element.on( this._handlers );
13194	};
13195
13196	/**
13197	 * Default options.
13198	 *
13199	 * @public
13200	 */
13201	Lazy.Defaults = {
13202		lazyLoad: false,
13203		lazyLoadEager: 0
13204	};
13205
13206	/**
13207	 * Loads all resources of an item at the specified position.
13208	 *
13209	 * @param {number} position - The absolute position of the item.
13210	 * @protected
13211	 */
13212	Lazy.prototype.load = function( position ) {
13213		var $item = this._core.$stage.children().eq( position ),
13214			$elements = $item && $item.find( '.owl-lazy' );
13215
13216		if ( ! $elements || $.inArray( $item.get( 0 ), this._loaded ) > -1 ) {
13217			return;
13218		}
13219
13220		$elements.each(
13221			$.proxy( function( index, element ) {
13222				var $element = $( element ),
13223					image,
13224					url = ( window.devicePixelRatio > 1 && $element.attr( 'data-src-retina' ) ) || $element.attr( 'data-src' ) || $element.attr( 'data-srcset' );
13225
13226				this._core.trigger( 'load', { element: $element, url: url }, 'lazy' );
13227
13228				if ( $element.is( 'img' ) ) {
13229					$element
13230						.one(
13231							'load.owl.lazy',
13232							$.proxy( function() {
13233								$element.css( 'opacity', 1 );
13234								this._core.trigger( 'loaded', { element: $element, url: url }, 'lazy' );
13235							}, this )
13236						)
13237						.attr( 'src', url );
13238				} else if ( $element.is( 'source' ) ) {
13239					$element
13240						.one(
13241							'load.owl.lazy',
13242							$.proxy( function() {
13243								this._core.trigger( 'loaded', { element: $element, url: url }, 'lazy' );
13244							}, this )
13245						)
13246						.attr( 'srcset', url );
13247				} else {
13248					image = new Image();
13249					image.onload = $.proxy( function() {
13250						$element.css( {
13251							'background-image': 'url("' + url + '")',
13252							opacity: '1'
13253						} );
13254						this._core.trigger( 'loaded', { element: $element, url: url }, 'lazy' );
13255					}, this );
13256					image.src = url;
13257				}
13258			}, this )
13259		);
13260
13261		this._loaded.push( $item.get( 0 ) );
13262	};
13263
13264	/**
13265	 * Destroys the plugin.
13266	 *
13267	 * @public
13268	 */
13269	Lazy.prototype.destroy = function() {
13270		var handler, property;
13271
13272		for ( handler in this.handlers ) {
13273			if ( handler ) {
13274				this._core.$element.off( handler, this.handlers[ handler ] );
13275			}
13276		}
13277		for ( property in Object.getOwnPropertyNames( this ) ) {
13278			if ( typeof this[ property ] !== 'function' ) {
13279				this[ property ] = null;
13280			}
13281		}
13282	};
13283
13284	$.fn.tmowlCarousel.Constructor.Plugins.Lazy = Lazy;
13285}( window.Zepto || window.jQuery, window ) );
13286
13287/**
13288 * AutoHeight Plugin
13289 *
13290 * @param {jQuery} $
13291 * @param {Window} window
13292 * @version 2.3.4
13293 * @author Bartosz Wojciechowski
13294 * @author David Deutsch
13295 * @license The MIT License (MIT)
13296 */
13297( function( $, window ) {
13298	'use strict';
13299	/**
13300	 * Creates the auto height plugin.
13301	 *
13302	 * @class The Auto Height Plugin
13303	 * @param {Owl} carousel - The Owl Carousel
13304	 */
13305	var AutoHeight = function( carousel ) {
13306		var refThis = this;
13307		/**
13308		 * Reference to the core.
13309		 *
13310		 * @protected
13311		 * @type {Owl}
13312		 */
13313		this._core = carousel;
13314
13315		this._previousHeight = null;
13316
13317		/**
13318		 * All event handlers.
13319		 *
13320		 * @protected
13321		 * @type {Object}
13322		 */
13323		this._handlers = {
13324			'initialized.owl.carousel refreshed.owl.carousel': $.proxy( function( e ) {
13325				if ( e.namespace && this._core.settings.autoHeight ) {
13326					this.update();
13327				}
13328			}, this ),
13329			'changed.owl.carousel': $.proxy( function( e ) {
13330				if ( e.namespace && this._core.settings.autoHeight && e.property.name === 'position' ) {
13331					this.update();
13332				}
13333			}, this ),
13334			'loaded.owl.lazy': $.proxy( function( e ) {
13335				if ( e.namespace && this._core.settings.autoHeight && e.element.closest( '.' + this._core.settings.itemClass ).index() === this._core.current() ) {
13336					this.update();
13337				}
13338			}, this )
13339		};
13340
13341		// set default options
13342		this._core.options = $.extend( {}, AutoHeight.Defaults, this._core.options );
13343
13344		// register event handlers
13345		this._core.$element.on( this._handlers );
13346		this._intervalId = null;
13347
13348		// These changes have been taken from a PR by gavrochelegnou proposed in #1575
13349		// and have been made compatible with the latest jQuery version
13350		$( window ).on( 'load', function() {
13351			if ( refThis._core.settings.autoHeight ) {
13352				refThis.update();
13353			}
13354		} );
13355
13356		// Autoresize the height of the carousel when window is resized
13357		// When carousel has images, the height is dependent on the width
13358		// and should also change on resize
13359		$( window ).on( 'resize', function() {
13360			if ( refThis._core.settings.autoHeight ) {
13361				if ( refThis._intervalId !== null ) {
13362					clearTimeout( refThis._intervalId );
13363				}
13364
13365				refThis._intervalId = setTimeout( function() {
13366					refThis.update();
13367				}, 250 );
13368			}
13369		} );
13370	};
13371
13372	/**
13373	 * Default options.
13374	 *
13375	 * @public
13376	 */
13377	AutoHeight.Defaults = {
13378		autoHeight: false,
13379		autoHeightClass: 'owl-height'
13380	};
13381
13382	/**
13383	 * Updates the view.
13384	 */
13385	AutoHeight.prototype.update = function() {
13386		var start = this._core._current,
13387			end = start + this._core.settings.items,
13388			lazyLoadEnabled = this._core.settings.lazyLoad,
13389			visible = this._core.$stage.children().toArray().slice( start, end ),
13390			heights = [],
13391			maxheight = 0;
13392
13393		$.each( visible, function( index, item ) {
13394			heights.push( $( item ).height() );
13395		} );
13396
13397		maxheight = Math.max.apply( null, heights );
13398
13399		if ( maxheight <= 1 && lazyLoadEnabled && this._previousHeight ) {
13400			maxheight = this._previousHeight;
13401		}
13402
13403		this._previousHeight = maxheight;
13404
13405		this._core.$stage.parent().height( maxheight ).addClass( this._core.settings.autoHeightClass );
13406	};
13407
13408	AutoHeight.prototype.destroy = function() {
13409		var handler, property;
13410
13411		for ( handler in this._handlers ) {
13412			if ( handler ) {
13413				this._core.$element.off( handler, this._handlers[ handler ] );
13414			}
13415		}
13416		for ( property in Object.getOwnPropertyNames( this ) ) {
13417			if ( typeof this[ property ] !== 'function' ) {
13418				this[ property ] = null;
13419			}
13420		}
13421	};
13422
13423	$.fn.tmowlCarousel.Constructor.Plugins.AutoHeight = AutoHeight;
13424}( window.Zepto || window.jQuery ) );
13425
13426/**
13427 * Video Plugin
13428 *
13429 * @param {jQuery}   $
13430 * @param {Window}   window
13431 * @param {Document} document
13432 * @version 2.3.4
13433 * @author Bartosz Wojciechowski
13434 * @author David Deutsch
13435 * @license The MIT License (MIT)
13436 */
13437( function( $, window, document ) {
13438	'use strict';
13439	/**
13440	 * Creates the video plugin.
13441	 *
13442	 * @class The Video Plugin
13443	 * @param {Owl} carousel - The Owl Carousel
13444	 */
13445	var Video = function( carousel ) {
13446		/**
13447		 * Reference to the core.
13448		 *
13449		 * @protected
13450		 * @type {Owl}
13451		 */
13452		this._core = carousel;
13453
13454		/**
13455		 * Cache all video URLs.
vendor: 4,085 bytes, lines 13456-13598
13456		 *
13457		 * @protected
13458		 * @type {Object}
13459		 */
13460		this._videos = {};
13461
13462		/**
13463		 * Current playing item.
13464		 *
13465		 * @protected
13466		 * @type {jQuery}
13467		 */
13468		this._playing = null;
13469
13470		/**
13471		 * All event handlers.
13472		 *
13473		 * @todo The cloned content removale is too late
13474		 * @protected
13475		 * @type {Object}
13476		 */
13477		this._handlers = {
13478			'initialized.owl.carousel': $.proxy( function( e ) {
13479				if ( e.namespace ) {
13480					this._core.register( { type: 'state', name: 'playing', tags: [ 'interacting' ] } );
13481				}
13482			}, this ),
13483			'resize.owl.carousel': $.proxy( function( e ) {
13484				if ( e.namespace && this._core.settings.video && this.isInFullScreen() ) {
13485					e.preventDefault();
13486				}
13487			}, this ),
13488			'refreshed.owl.carousel': $.proxy( function( e ) {
13489				if ( e.namespace && this._core.is( 'resizing' ) ) {
13490					this._core.$stage.find( '.cloned .owl-video-frame' ).remove();
13491				}
13492			}, this ),
13493			'changed.owl.carousel': $.proxy( function( e ) {
13494				if ( e.namespace && e.property.name === 'position' && this._playing ) {
13495					this.stop();
13496				}
13497			}, this ),
13498			'prepared.owl.carousel': $.proxy( function( e ) {
13499				var $element;
13500				if ( ! e.namespace ) {
13501					return;
13502				}
13503
13504				$element = $( e.content ).find( '.owl-video' );
13505
13506				if ( $element.length ) {
13507					$element.css( 'display', 'none' );
13508					this.fetch( $element, $( e.content ) );
13509				}
13510			}, this )
13511		};
13512
13513		// set default options
13514		this._core.options = $.extend( {}, Video.Defaults, this._core.options );
13515
13516		// register event handlers
13517		this._core.$element.on( this._handlers );
13518
13519		this._core.$element.on(
13520			'click.owl.video',
13521			'.owl-video-play-icon',
13522			$.proxy( function( e ) {
13523				this.play( e );
13524			}, this )
13525		);
13526	};
13527
13528	/**
13529	 * Default options.
13530	 *
13531	 * @public
13532	 */
13533	Video.Defaults = {
13534		video: false,
13535		videoHeight: false,
13536		videoWidth: false
13537	};
13538
13539	/**
13540	 * Gets the video ID and the type (YouTube/Vimeo/vzaar only).
13541	 *
13542	 * @protected
13543	 * @param {jQuery} target - The target containing the video data.
13544	 * @param {jQuery} item   - The item containing the video.
13545	 */
13546	Video.prototype.fetch = function( target, item ) {
13547		var type = ( function() {
13548				if ( target.attr( 'data-vimeo-id' ) ) {
13549					return 'vimeo';
13550				} else if ( target.attr( 'data-vzaar-id' ) ) {
13551					return 'vzaar';
13552				}
13553				return 'youtube';
13554			}() ),
13555			id = target.attr( 'data-vimeo-id' ) || target.attr( 'data-youtube-id' ) || target.attr( 'data-vzaar-id' ),
13556			width = target.attr( 'data-width' ) || this._core.settings.videoWidth,
13557			height = target.attr( 'data-height' ) || this._core.settings.videoHeight,
13558			url = target.attr( 'href' );
13559
13560		if ( url ) {
13561			/*
13562             Parses the id's out of the following urls (and probably more):
13563             https://www.youtube.com/watch?v=:id
13564             https://youtu.be/:id
13565             https://vimeo.com/:id
13566             https://vimeo.com/channels/:channel/:id
13567             https://vimeo.com/groups/:group/videos/:id
13568             https://app.vzaar.com/videos/:id
13569
13570             Visual example: https://regexper.com/#(http%3A%7Chttps%3A%7C)%5C%2F%5C%2F(player.%7Cwww.%7Capp.)%3F(vimeo%5C.com%7Cyoutu(be%5C.com%7C%5C.be%7Cbe%5C.googleapis%5C.com)%7Cvzaar%5C.com)%5C%2F(video%5C%2F%7Cvideos%5C%2F%7Cembed%5C%2F%7Cchannels%5C%2F.%2B%5C%2F%7Cgroups%5C%2F.%2B%5C%2F%7Cwatch%5C%3Fv%3D%7Cv%5C%2F)%3F(%5BA-Za-z0-9._%25-%5D*)(%5C%26%5CS%2B)%3F
13571             */
13572
13573			id = url.match(
13574				// eslint-disable-next-line no-useless-escape
13575				/(http:|https:|)\/\/(player.|www.|app.)?(vimeo\.com|youtu(be\.com|\.be|be\.googleapis\.com|be\-nocookie\.com)|vzaar\.com)\/(video\/|videos\/|embed\/|channels\/.+\/|groups\/.+\/|watch\?v=|v\/)?([A-Za-z0-9._%-]*)(\&\S+)?/
13576			);
13577
13578			if ( id[ 3 ].indexOf( 'youtu' ) > -1 ) {
13579				type = 'youtube';
13580			} else if ( id[ 3 ].indexOf( 'vimeo' ) > -1 ) {
13581				type = 'vimeo';
13582			} else if ( id[ 3 ].indexOf( 'vzaar' ) > -1 ) {
13583				type = 'vzaar';
13584			} else {
13585				throw new Error( 'Video URL not supported.' );
13586			}
13587			id = id[ 6 ];
13588		} else {
13589			throw new Error( 'Missing video URL.' );
13590		}
13591
13592		this._videos[ url ] = {
13593			type: type,
13594			id: id,
13595			width: width,
13596			height: height
13597		};
13598
13599		item.attr( 'data-video', url );
13600
13601		this.thumbnail( target, this._videos[ url ] );
13602	};
13603
13604	/**
13605	 * Creates video thumbnail.
13606	 *
13607	 * @protected
13608	 * @param {jQuery} target The target containing the video data.
13609	 * @param {Object} video  The video info object.
13610	 * @see `fetch`
13611	 */
13612	Video.prototype.thumbnail = function( target, video ) {
13613		var tnLink,
13614			icon,
13615			path,
13616			dimensions = video.width && video.height ? 'width:' + video.width + 'px;height:' + video.height + 'px;' : '',
13617			customTn = target.find( 'img' ),
13618			srcType = 'src',
13619			lazyClass = '',
13620			settings = this._core.settings,
13621			create = function( thispath ) {
13622				icon = '<div class="owl-video-play-icon"></div>';
13623
13624				if ( settings.lazyLoad ) {
13625					tnLink = $( '<div/>', {
13626						class: 'owl-video-tn ' + lazyClass,
13627						srcType: thispath
13628					} );
13629				} else {
13630					tnLink = $( '<div/>', {
13631						class: 'owl-video-tn',
13632						style: 'opacity:1;background-image:url(' + thispath + ')'
13633					} );
13634				}
13635				target.after( tnLink );
13636				target.after( icon );
13637			};
13638
13639		// wrap video content into owl-video-wrapper div
13640		target.wrap(
13641			$( '<div/>', {
13642				class: 'owl-video-wrapper',
13643				style: dimensions
13644			} )
13645		);
13646
13647		if ( this._core.settings.lazyLoad ) {
13648			srcType = 'data-src';
13649			lazyClass = 'owl-lazy';
13650		}
13651
13652		// custom thumbnail
13653		if ( customTn.length ) {
13654			create( customTn.attr( srcType ) );
13655			customTn.remove();
13656			return false;
13657		}
13658
13659		if ( video.type === 'youtube' ) {
13660			path = '//img.youtube.com/vi/' + video.id + '/hqdefault.jpg';
13661			create( path );
13662		} else if ( video.type === 'vimeo' ) {
13663			$.ajax( {
13664				type: 'GET',
13665				url: '//vimeo.com/api/v2/video/' + video.id + '.json',
13666				jsonp: 'callback',
13667				dataType: 'jsonp',
13668				success: function( data ) {
13669					path = data[ 0 ].thumbnail_large;
13670					create( path );
13671				}
13672			} );
13673		} else if ( video.type === 'vzaar' ) {
13674			$.ajax( {
13675				type: 'GET',
13676				url: '//vzaar.com/api/videos/' + video.id + '.json',
13677				jsonp: 'callback',
13678				dataType: 'jsonp',
13679				success: function( data ) {
13680					path = data.framegrab_url;
13681					create( path );
13682				}
13683			} );
13684		}
13685	};
13686
13687	/**
13688	 * Stops the current video.
13689	 *
13690	 * @public
13691	 */
13692	Video.prototype.stop = function() {
13693		this._core.trigger( 'stop', null, 'video' );
13694		this._playing.find( '.owl-video-frame' ).remove();
13695		this._playing.removeClass( 'owl-video-playing' );
13696		this._playing = null;
13697		this._core.leave( 'playing' );
13698		this._core.trigger( 'stopped', null, 'video' );
13699	};
13700
13701	/**
13702	 * Starts the current video.
13703	 *
13704	 * @public
13705	 * @param {Event} event - The event arguments.
13706	 */
13707	Video.prototype.play = function( event ) {
13708		var target = $( event.target ),
13709			item = target.closest( '.' + this._core.settings.itemClass ),
13710			video = this._videos[ item.attr( 'data-video' ) ],
13711			width = video.width || '100%',
13712			height = video.height || this._core.$stage.height(),
13713			html;
13714
13715		if ( this._playing ) {
13716			return;
13717		}
13718
13719		this._core.enter( 'playing' );
13720		this._core.trigger( 'play', null, 'video' );
13721
13722		item = this._core.items( this._core.relative( item.index() ) );
13723
13724		this._core.reset( item.index() );
13725
13726		html = $( '<iframe frameborder="0" allowfullscreen mozallowfullscreen webkitAllowFullScreen ></iframe>' );
13727		html.attr( 'height', height );
13728		html.attr( 'width', width );
13729		if ( video.type === 'youtube' ) {
13730			html.attr( 'src', '//www.youtube.com/embed/' + video.id + '?autoplay=1&rel=0&v=' + video.id );
13731		} else if ( video.type === 'vimeo' ) {
13732			html.attr( 'src', '//player.vimeo.com/video/' + video.id + '?autoplay=1' );
13733		} else if ( video.type === 'vzaar' ) {
13734			html.attr( 'src', '//view.vzaar.com/' + video.id + '/player?autoplay=true' );
13735		}
13736
13737		$( html ).wrap( '<div class="owl-video-frame" />' ).insertAfter( item.find( '.owl-video' ) );
13738
13739		this._playing = item.addClass( 'owl-video-playing' );
13740	};
13741
13742	/**
13743	 * Checks whether an video is currently in full screen mode or not.
13744	 *
13745	 * @todo Bad style because looks like a readonly method but changes members.
13746	 * @protected
13747	 * @returns {boolean}
13748	 */
13749	Video.prototype.isInFullScreen = function() {
13750		var element = document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement;
13751
13752		return element && $( element ).parent().hasClass( 'owl-video-frame' );
13753	};
13754
13755	/**
13756	 * Destroys the plugin.
13757	 */
13758	Video.prototype.destroy = function() {
13759		var handler, property;
13760
13761		this._core.$element.off( 'click.owl.video' );
13762
13763		for ( handler in this._handlers ) {
13764			if ( handler ) {
13765				this._core.$element.off( handler, this._handlers[ handler ] );
13766			}
13767		}
13768		for ( property in Object.getOwnPropertyNames( this ) ) {
13769			if ( typeof this[ property ] !== 'function' ) {
13770				this[ property ] = null;
13771			}
13772		}
13773	};
13774
13775	$.fn.tmowlCarousel.Constructor.Plugins.Video = Video;
13776}( window.Zepto || window.jQuery, window, document ) );
13777
13778/**
13779 * Animate Plugin
13780 *
13781 * @param {jQuery} $
13782 * @version 2.3.4
13783 * @author Bartosz Wojciechowski
13784 * @author David Deutsch
13785 * @license The MIT License (MIT)
13786 */
13787( function( $ ) {
13788	'use strict';
13789	/**
13790	 * Creates the animate plugin.
13791	 *
13792	 * @class The Navigation Plugin
13793	 * @param {Owl} scope - The Owl Carousel
13794	 */
13795	var Animate = function( scope ) {
13796		this.core = scope;
13797		this.core.options = $.extend( {}, Animate.Defaults, this.core.options );
13798		this.swapping = true;
13799		this.previous = undefined;
13800		this.next = undefined;
13801
13802		this.handlers = {
13803			'change.owl.carousel': $.proxy( function( e ) {
13804				if ( e.namespace && e.property.name === 'position' ) {
13805					this.previous = this.core.current();
13806					this.next = e.property.value;
13807				}
13808			}, this ),
13809			'drag.owl.carousel dragged.owl.carousel translated.owl.carousel': $.proxy( function( e ) {
13810				if ( e.namespace ) {
13811					this.swapping = e.type === 'translated';
13812				}
13813			}, this ),
13814			'translate.owl.carousel': $.proxy( function( e ) {
13815				if ( e.namespace && this.swapping && ( this.core.options.animateOut || this.core.options.animateIn ) ) {
13816					this.swap();
13817				}
13818			}, this )
13819		};
13820
13821		this.core.$element.on( this.handlers );
13822	};
13823
13824	/**
13825	 * Default options.
13826	 *
13827	 * @public
13828	 */
13829	Animate.Defaults = {
13830		animateOut: false,
13831		animateIn: false
13832	};
13833
13834	/**
13835	 * Toggles the animation classes whenever an translations starts.
13836	 *
13837	 * @protected
13838	 * @returns {boolean|undefined}
13839	 */
13840	Animate.prototype.swap = function() {
13841		var left, clear, previous, next, incoming, outgoing;
13842		if ( this.core.settings.items !== 1 ) {
13843			return;
13844		}
13845
13846		if ( ! $.support.animation || ! $.support.transition ) {
13847			return;
13848		}
13849
13850		this.core.speed( 0 );
13851
13852		clear = $.proxy( this.clear, this );
13853		previous = this.core.$stage.children().eq( this.previous );
13854		next = this.core.$stage.children().eq( this.next );
13855		incoming = this.core.settings.animateIn;
13856		outgoing = this.core.settings.animateOut;
13857
13858		if ( this.core.current() === this.previous ) {
13859			return;
13860		}
13861
13862		if ( outgoing ) {
13863			left = this.core.coordinates( this.previous ) - this.core.coordinates( this.next );
13864			previous
13865				.one( $.support.animation.end, clear )
13866				.css( { left: left + 'px' } )
13867				.addClass( 'animated owl-animated-out' )
13868				.addClass( outgoing );
13869		}
13870
13871		if ( incoming ) {
13872			next.one( $.support.animation.end, clear ).addClass( 'animated owl-animated-in' ).addClass( incoming );
13873		}
13874	};
13875
13876	Animate.prototype.clear = function( e ) {
13877		$( e.target ).css( { left: '' } ).removeClass( 'animated owl-animated-out owl-animated-in' ).removeClass( this.core.settings.animateIn ).removeClass( this.core.settings.animateOut );
13878		this.core.onTransitionEnd();
13879	};
13880
13881	/**
13882	 * Destroys the plugin.
13883	 *
13884	 * @public
13885	 */
13886	Animate.prototype.destroy = function() {
13887		var handler, property;
13888
13889		for ( handler in this.handlers ) {
13890			if ( handler ) {
13891				this.core.$element.off( handler, this.handlers[ handler ] );
13892			}
13893		}
13894		for ( property in Object.getOwnPropertyNames( this ) ) {
13895			if ( typeof this[ property ] !== 'function' ) {
13896				this[ property ] = null;
13897			}
13898		}
13899	};
13900
13901	$.fn.tmowlCarousel.Constructor.Plugins.Animate = Animate;
13902}( window.Zepto || window.jQuery ) );
13903
13904/**
13905 * Autoplay Plugin
13906 *
13907 * @param {jQuery}   $
13908 * @param {Window}   window
13909 * @param {Document} document
13910 * @version 2.3.4
13911 * @author Bartosz Wojciechowski
13912 * @author Artus Kolanowski
13913 * @author David Deutsch
13914 * @author Tom De Caluwé
13915 * @license The MIT License (MIT)
13916 */
13917( function( $, window, document ) {
13918	'use strict';
13919	/**
13920	 * Creates the autoplay plugin.
13921	 *
13922	 * @class The Autoplay Plugin
13923	 * @param {Owl} carousel The Owl Carousel
13924	 */
13925	var Autoplay = function( carousel ) {
13926		/**
13927		 * Reference to the core.
13928		 *
13929		 * @protected
13930		 * @type {Owl}
13931		 */
13932		this._core = carousel;
13933
13934		/**
13935		 * The autoplay timeout id.
13936		 *
13937		 * @type {number}
13938		 */
13939		this._call = null;
13940
13941		/**
13942		 * Depending on the state of the plugin, this variable contains either
13943		 * the start time of the timer or the current timer value if it's
13944		 * paused. Since we start in a paused state we initialize the timer
13945		 * value.
13946		 *
13947		 * @type {number}
13948		 */
13949		this._time = 0;
13950
13951		/**
13952		 * Stores the timeout currently used.
13953		 *
13954		 * @type {number}
13955		 */
13956		this._timeout = 0;
13957
13958		/**
13959		 * Indicates whenever the autoplay is paused.
13960		 *
13961		 * @type {boolean}
13962		 */
13963		this._paused = true;
13964
13965		/**
13966		 * All event handlers.
13967		 *
13968		 * @protected
13969		 * @type {Object}
13970		 */
13971		this._handlers = {
13972			'changed.owl.carousel': $.proxy( function( e ) {
13973				if ( e.namespace && e.property.name === 'settings' ) {
13974					if ( this._core.settings.autoplay ) {
13975						this.play();
13976					} else {
13977						this.stop();
13978					}
13979				} else if ( e.namespace && e.property.name === 'position' && this._paused ) {
13980					// Reset the timer. This code is triggered when the position
13981					// of the carousel was changed through user interaction.
13982					this._time = 0;
13983				}
13984			}, this ),
13985			'initialized.owl.carousel': $.proxy( function( e ) {
13986				if ( e.namespace && this._core.settings.autoplay ) {
13987					this.play();
13988				}
13989			}, this ),
13990			'play.owl.autoplay': $.proxy( function( e, t, s ) {
13991				if ( e.namespace ) {
13992					this.play( t, s );
13993				}
13994			}, this ),
13995			'stop.owl.autoplay': $.proxy( function( e ) {
13996				if ( e.namespace ) {
13997					this.stop();
13998				}
13999			}, this ),
14000			'mouseover.owl.autoplay': $.proxy( function() {
14001				if ( this._core.settings.autoplayHoverPause && this._core.is( 'rotating' ) ) {
14002					this.pause();
14003				}
14004			}, this ),
14005			'mouseleave.owl.autoplay': $.proxy( function() {
14006				if ( this._core.settings.autoplayHoverPause && this._core.is( 'rotating' ) ) {
14007					this.play();
14008				}
14009			}, this ),
14010			'touchstart.owl.core': $.proxy( function() {
14011				if ( this._core.settings.autoplayHoverPause && this._core.is( 'rotating' ) ) {
14012					this.pause();
14013				}
14014			}, this ),
14015			'touchend.owl.core': $.proxy( function() {
14016				if ( this._core.settings.autoplayHoverPause ) {
14017					this.play();
14018				}
14019			}, this )
14020		};
14021
14022		// register event handlers
14023		this._core.$element.on( this._handlers );
14024
14025		// set default options
14026		this._core.options = $.extend( {}, Autoplay.Defaults, this._core.options );
14027	};
14028
14029	/**
14030	 * Default options.
14031	 *
14032	 * @public
14033	 */
14034	Autoplay.Defaults = {
14035		autoplay: false,
14036		autoplayTimeout: 5000,
14037		autoplayHoverPause: false,
14038		autoplaySpeed: false
14039	};
14040
14041	/**
14042	 * Transition to the next slide and set a timeout for the next transition.
14043	 *
14044	 * @private
14045	 * @param {number}
vendor: 7,927 bytes, lines 14045-14358
14045 [speed] - The animation speed for the animations.
14046	 */
14047	Autoplay.prototype._next = function( speed ) {
14048		this._call = window.setTimeout( $.proxy( this._next, this, speed ), ( this._timeout * ( Math.round( this.read() / this._timeout ) + 1 ) ) - this.read() );
14049
14050		if ( this._core.is( 'interacting' ) || document.hidden ) {
14051			return;
14052		}
14053		this._core.next( speed || this._core.settings.autoplaySpeed );
14054	};
14055
14056	/**
14057	 * Reads the current timer value when the timer is playing.
14058	 *
14059	 * @public
14060	 */
14061	Autoplay.prototype.read = function() {
14062		return new Date().getTime() - this._time;
14063	};
14064
14065	/**
14066	 * Starts the autoplay.
14067	 *
14068	 * @public
14069	 * @param {number} [timeout] - The interval before the next animation starts.
14070	 * @param {number} [speed]   - The animation speed for the animations.
14071	 */
14072	Autoplay.prototype.play = function( timeout, speed ) {
14073		var elapsed;
14074
14075		if ( ! this._core.is( 'rotating' ) ) {
14076			this._core.enter( 'rotating' );
14077		}
14078
14079		timeout = timeout || this._core.settings.autoplayTimeout;
14080
14081		// Calculate the elapsed time since the last transition. If the carousel
14082		// wasn't playing this calculation will yield zero.
14083		elapsed = Math.min( this._time % ( this._timeout || timeout ), timeout );
14084
14085		if ( this._paused ) {
14086			// Start the clock.
14087			this._time = this.read();
14088			this._paused = false;
14089		} else {
14090			// Clear the active timeout to allow replacement.
14091			window.clearTimeout( this._call );
14092		}
14093
14094		// Adjust the origin of the timer to match the new timeout value.
14095		this._time += ( this.read() % timeout ) - elapsed;
14096
14097		this._timeout = timeout;
14098		this._call = window.setTimeout( $.proxy( this._next, this, speed ), timeout - elapsed );
14099	};
14100
14101	/**
14102	 * Stops the autoplay.
14103	 *
14104	 * @public
14105	 */
14106	Autoplay.prototype.stop = function() {
14107		if ( this._core.is( 'rotating' ) ) {
14108			// Reset the clock.
14109			this._time = 0;
14110			this._paused = true;
14111
14112			window.clearTimeout( this._call );
14113			this._core.leave( 'rotating' );
14114		}
14115	};
14116
14117	/**
14118	 * Pauses the autoplay.
14119	 *
14120	 * @public
14121	 */
14122	Autoplay.prototype.pause = function() {
14123		if ( this._core.is( 'rotating' ) && ! this._paused ) {
14124			// Pause the clock.
14125			this._time = this.read();
14126			this._paused = true;
14127
14128			window.clearTimeout( this._call );
14129		}
14130	};
14131
14132	/**
14133	 * Destroys the plugin.
14134	 */
14135	Autoplay.prototype.destroy = function() {
14136		var handler, property;
14137
14138		this.stop();
14139
14140		for ( handler in this._handlers ) {
14141			if ( handler ) {
14142				this._core.$element.off( handler, this._handlers[ handler ] );
14143			}
14144		}
14145		for ( property in Object.getOwnPropertyNames( this ) ) {
14146			if ( typeof this[ property ] !== 'function' ) {
14147				this[ property ] = null;
14148			}
14149		}
14150	};
14151
14152	$.fn.tmowlCarousel.Constructor.Plugins.autoplay = Autoplay;
14153}( window.Zepto || window.jQuery, window, document ) );
14154
14155/**
14156 * Navigation Plugin
14157 *
14158 * @param {jQuery} $
14159 * @version 2.3.4
14160 * @author Artus Kolanowski
14161 * @author David Deutsch
14162 * @license The MIT License (MIT)
14163 */
14164( function( $ ) {
14165	'use strict';
14166
14167	/**
14168	 * Creates the navigation plugin.
14169	 *
14170	 * @class The Navigation Plugin
14171	 * @param {Owl} carousel - The Owl Carousel.
14172	 */
14173	var Navigation = function( carousel ) {
14174		/**
14175		 * Reference to the core.
14176		 *
14177		 * @protected
14178		 * @type {Owl}
14179		 */
14180		this._core = carousel;
14181
14182		/**
14183		 * Indicates whether the plugin is initialized or not.
14184		 *
14185		 * @protected
14186		 * @type {boolean}
14187		 */
14188		this._initialized = false;
14189
14190		/**
14191		 * The current paging indexes.
14192		 *
14193		 * @protected
14194		 * @type {Array}
14195		 */
14196		this._pages = [];
14197
14198		/**
14199		 * All DOM elements of the user interface.
14200		 *
14201		 * @protected
14202		 * @type {Object}
14203		 */
14204		this._controls = {};
14205
14206		/**
14207		 * Markup for an indicator.
14208		 *
14209		 * @protected
14210		 * @type {Array.<string>}
14211		 */
14212		this._templates = [];
14213
14214		/**
14215		 * The carousel element.
14216		 *
14217		 * @type {jQuery}
14218		 */
14219		this.$element = this._core.$element;
14220
14221		/**
14222		 * Overridden methods of the carousel.
14223		 *
14224		 * @protected
14225		 * @type {Object}
14226		 */
14227		this._overrides = {
14228			next: this._core.next,
14229			prev: this._core.prev,
14230			to: this._core.to
14231		};
14232
14233		/**
14234		 * All event handlers.
14235		 *
14236		 * @protected
14237		 * @type {Object}
14238		 */
14239		this._handlers = {
14240			'prepared.owl.carousel': $.proxy( function( e ) {
14241				if ( e.namespace && this._core.settings.dotsData ) {
14242					this._templates.push( '<div class="' + this._core.settings.dotClass + '">' + $( e.content ).find( '[data-dot]' ).addBack( '[data-dot]' ).attr( 'data-dot' ) + '</div>' );
14243				}
14244			}, this ),
14245			'added.owl.carousel': $.proxy( function( e ) {
14246				if ( e.namespace && this._core.settings.dotsData ) {
14247					this._templates.splice( e.position, 0, this._templates.pop() );
14248				}
14249			}, this ),
14250			'remove.owl.carousel': $.proxy( function( e ) {
14251				if ( e.namespace && this._core.settings.dotsData ) {
14252					this._templates.splice( e.position, 1 );
14253				}
14254			}, this ),
14255			'changed.owl.carousel': $.proxy( function( e ) {
14256				if ( e.namespace && e.property.name === 'position' ) {
14257					this.draw();
14258				}
14259			}, this ),
14260			'initialized.owl.carousel': $.proxy( function( e ) {
14261				if ( e.namespace && ! this._initialized ) {
14262					this._core.trigger( 'initialize', null, 'navigation' );
14263					this.initialize();
14264					this.update();
14265					this.draw();
14266					this._initialized = true;
14267					this._core.trigger( 'initialized', null, 'navigation' );
14268				}
14269			}, this ),
14270			'refreshed.owl.carousel': $.proxy( function( e ) {
14271				if ( e.namespace && this._initialized ) {
14272					this._core.trigger( 'refresh', null, 'navigation' );
14273					this.update();
14274					this.draw();
14275					this._core.trigger( 'refreshed', null, 'navigation' );
14276				}
14277			}, this )
14278		};
14279
14280		// set default options
14281		this._core.options = $.extend( {}, Navigation.Defaults, this._core.options );
14282
14283		// register event handlers
14284		this.$element.on( this._handlers );
14285	};
14286
14287	/**
14288	 * Default options.
14289	 *
14290	 * @public
14291	 * @todo Rename `slideBy` to `navBy`
14292	 */
14293	Navigation.Defaults = {
14294		nav: false,
14295		navText: [ '<span aria-label="' + 'Previous' + '">&#x2039;</span>', '<span aria-label="' + 'Next' + '">&#x203a;</span>' ],
14296		navSpeed: false,
14297		navElement: 'button type="button" role="presentation"',
14298		navContainer: false,
14299		navContainerClass: 'owl-nav',
14300		navClass: [ 'owl-prev', 'owl-next' ],
14301		slideBy: 1,
14302		dotClass: 'owl-dot',
14303		dotsClass: 'owl-dots',
14304		dots: true,
14305		dotsEach: false,
14306		dotsData: false,
14307		dotsSpeed: false,
14308		dotsContainer: false
14309	};
14310
14311	/**
14312	 * Initializes the layout of the plugin and extends the carousel.
14313	 *
14314	 * @protected
14315	 */
14316	Navigation.prototype.initialize = function() {
14317		var override,
14318			settings = this._core.settings;
14319
14320		// create DOM structure for relative navigation
14321		this._controls.$relative = ( settings.navContainer ? $( settings.navContainer ) : $( '<div>' ).addClass( settings.navContainerClass ).appendTo( this.$element ) ).addClass( 'disabled' );
14322
14323		this._controls.$previous = $( '<' + settings.navElement + '>' )
14324			.addClass( settings.navClass[ 0 ] )
14325			.html( settings.navText[ 0 ] )
14326			.prependTo( this._controls.$relative )
14327			.on(
14328				'click',
14329				$.proxy( function() {
14330					this.prev( settings.navSpeed );
14331				}, this )
14332			);
14333		this._controls.$next = $( '<' + settings.navElement + '>' )
14334			.addClass( settings.navClass[ 1 ] )
14335			.html( settings.navText[ 1 ] )
14336			.appendTo( this._controls.$relative )
14337			.on(
14338				'click',
14339				$.proxy( function() {
14340					this.next( settings.navSpeed );
14341				}, this )
14342			);
14343
14344		// create DOM structure for absolute navigation
14345		if ( ! settings.dotsData ) {
14346			this._templates = [ $( '<button role="button">' ).addClass( settings.dotClass ).append( $( '<span>' ) ).prop( 'outerHTML' ) ];
14347		}
14348
14349		this._controls.$absolute = ( settings.dotsContainer ? $( settings.dotsContainer ) : $( '<div>' ).addClass( settings.dotsClass ).appendTo( this.$element ) ).addClass( 'disabled' );
14350
14351		this._controls.$absolute.on(
14352			'click',
14353			'button',
14354			$.proxy( function( e ) {
14355				var index = $( e.target ).parent().is( this._controls.$absolute ) ? $( e.target ).index() : $( e.target ).parent().index();
14356
14357				e.preventDefault();
14358
14359				this.to( index, settings.dotsSpeed );
14360			}, this )
14361		);
14362
14363		/*$el.on('focusin', function() {
14364         $(document).off(".carousel");
14365
14366         $(document).on('keydown.carousel', function(e) {
14367         if(e.keyCode == 37) {
14368         $el.trigger('prev.owl')
14369         }
14370         if(e.keyCode == 39) {
14371         $el.trigger('next.owl')
14372         }
14373         });
14374         });*/
14375
14376		// override public methods of the carousel
14377		for ( override in this._overrides ) {
14378			if ( override ) {
14379				this._core[ override ] = $.proxy( this[ override ], this );
14380			}
14381		}
14382	};
14383
14384	/**
14385	 * Destroys the plugin.
14386	 *
14387	 * @protected
14388	 */
14389	Navigation.prototype.destroy = function() {
14390		var handler, control, property, override, settings;
14391		settings = this._core.settings;
14392
14393		for ( handler in this._handlers ) {
14394			if ( handler ) {
14395				this.$element.off( handler, this._handlers[ handler ] );
14396			}
14397		}
14398		for ( control in this._controls ) {
14399			if ( control === '$relative' && settings.navContainer ) {
14400				this._controls[ control ].html( '' );
14401			} else {
14402				this._controls[ control ].remove();
14403			}
14404		}
14405		for ( override in this.overides ) {
14406			if ( override ) {
14407				this._core[ override ] = this._overrides[ override ];
14408			}
14409		}
14410		for ( property in Object.getOwnPropertyNames( this ) ) {
14411			if ( typeof this[ property ] !== 'function' ) {
14412				this[ property ] = null;
14413			}
14414		}
14415	};
14416
14417	/**
14418	 * Updates the internal state.
14419	 *
14420	 * @protected
14421	 */
14422	Navigation.prototype.update = function() {
14423		var i,
14424			j,
14425			lower = this._core.clones().length / 2,
14426			upper = lower + this._core.items().length,
14427			maximum = this._core.maximum( true ),
14428			settings = this._core.settings,
14429			size = settings.center || settings.autoWidth || settings.dotsData ? 1 : settings.dotsEach || settings.items;
14430
14431		if ( settings.slideBy !== 'page' ) {
14432			settings.slideBy = Math.min( settings.slideBy, settings.items );
14433		}
14434
14435		if ( settings.dots || settings.slideBy === 'page' ) {
14436			this._pages = [];
14437
14438			for ( i = lower, j = 0; i < upper; i += 1 ) {
14439				if ( j >= size || j === 0 ) {
14440					this._pages.push( {
14441						start: Math.min( maximum, i - lower ),
14442						end: i - lower + size - 1
14443					} );
14444					if ( Math.min( maximum, i - lower ) === maximum ) {
14445						break;
14446					}
14447					j = 0;
14448				}
14449				j += this._core.mergers( this._core.relative( i ) );
14450			}
14451		}
14452	};
14453
14454	/**
14455	 * Draws the user interface.
14456	 *
14457	 * @todo The option `dotsData` wont work.
14458	 * @protected
14459	 */
14460	Navigation.prototype.draw = function() {
14461		var difference,
14462			settings = this._core.settings,
14463			disabled = this._core.items().length <= settings.items,
14464			index = this._core.relative( this._core.current() ),
14465			loop = settings.loop || settings.rewind;
14466
14467		this._controls.$relative.toggleClass( 'disabled', ! settings.nav || disabled );
14468
14469		if ( settings.nav ) {
14470			this._controls.$previous.toggleClass( 'disabled', ! loop && index <= this._core.minimum( true ) );
14471			this._controls.$next.toggleClass( 'disabled', ! loop && index >= this._core.maximum( true ) );
14472		}
14473
14474		this._controls.$absolute.toggleClass( 'disabled', ! settings.dots || disabled );
14475
14476		if ( settings.dots ) {
14477			difference = this._pages.length - this._controls.$absolute.children().length;
14478
14479			if ( settings.dotsData && difference !== 0 ) {
14480				this._controls.$absolute.html( this._templates.join( '' ) );
14481			} else if ( difference > 0 ) {
14482				this._controls.$absolute.append( new Array( difference + 1 ).join( this._templates[ 0 ] ) );
14483			} else if ( difference < 0 ) {
14484				this._controls.$absolute.children().slice( difference ).remove();
14485			}
14486
14487			this._controls.$absolute.find( '.active' ).removeClass( 'active' );
14488			this._controls.$absolute.children().eq( $.inArray( this.current(), this._pages ) ).addClass( 'active' );
14489		}
14490	};
14491
14492	/**
14493	 * Extends event data.
14494	 *
14495	 * @protected
14496	 * @param {Event} event - The event object which gets thrown.
14497	 */
14498	Navigation.prototype.onTrigger = function( event ) {
14499		var settings = this._core.settings;
14500
14501		event.page = {
14502			index: $.inArray( this.current(), this._pages ),
14503			count: this._pages.length,
14504			size: settings && ( settings.center || settings.autoWidth || settings.dotsData ? 1 : settings.dotsEach || settings.items )
14505		};
14506	};
14507
14508	/**
14509	 * Gets the current page position of the carousel.
14510	 *
14511	 * @protected
14512	 * @returns {number}
14513	 */
14514	Navigation.prototype.current = function() {
14515		var current = this._core.relative( this._core.current() );
14516		return $.grep(
14517			this._pages,
14518			$.proxy( function( page ) {
14519				return page.start <= current && page.end >= current;
14520			}, this )
14521		).pop();
14522	};
14523
14524	/**
14525	 * Gets the current succesor/predecessor position.
14526	 *
14527	 * @param {mixed} successor
14528	 * @protected
14529	 * @returns {number}
14530	 */
14531	Navigation.prototype.getPosition = function( successor ) {
14532		var position,
14533			length,
14534			settings = this._core.settings;
14535
14536		if ( settings.slideBy === 'page' ) {
14537			position = $.inArray( this.current(), this._pages );
14538			length = this._pages.length;
14539			if ( successor ) {
14540				position = position + 1;
14541			} else {
14542				position = position - 1;
14543			}
14544			position = this._pages[ ( ( position % length ) + length ) % length ].start;
14545		} else {
14546			position = this._core.relative( this._core.current() );
14547			length = this._core.items().length;
14548			if ( successor ) {
14549				position += settings.slideBy;
14550			} else {
14551				position -= settings.slideBy;
14552			}
14553		}
14554
14555		return position;
14556	};
14557
14558	/**
14559	 * Retry logic.
14560	 *
14561	 * @public
14562	 * @param {number}  [speed=false]    - The time in milliseconds for the transition.
14563	 * @param {boolean} [nextprev=false] - Determines whether to navigate to the next or previous slide.
14564	 *                                   `true` for next slide, `false` for previous slide.
14565	 */
14566	Navigation.prototype.getAvailableSlide = function( speed, nextprev ) {
14567		var maxTries = this._core._items.length - this._core._current || 100; // Maximum number of attempts to avoid infinite loops
14568		var retryInterval = 50; // Milliseconds between retries
14569		var attempts = 0;
14570
14571		// Define the retry logic as a regular function
14572		var tryNext = function() {
14573			var position = this.getPosition( nextprev );
14574			if ( this._core._items[ position ] && this._core._items[ position ].is( '.tc-hidden-visibility' ) ) {
14575				if ( attempts < maxTries ) {
14576					attempts++;
14577					this._core.current( position );
14578					// Target is still hidden, retrying...
14579					setTimeout( function() {
14580						tryNext.call( this ); // Explicitly bind the `this` context when retrying
14581					}.bind( this ), retryInterval ); // Use .bind(this) to ensure `this` is Navigation
14582				} else {
14583					// Max attempts reached. Navigation failed.
14584				}
14585			} else {
14586				// If the target item is visible, proceed with the navigation
14587				$.proxy( this._overrides.to, this._core )( position, speed );
14588			}
14589		};
14590		// Start the retry logic
14591		tryNext.call( this ); // Ensure the `this` context refers to the correct object
14592	};
14593
14594	/**
14595	 * Slides to the next item or page.
14596	 *
14597	 * @public
14598	 * @param {number} [speed=false] - The time in milliseconds for the transition.
14599	 */
14600	Navigation.prototype.next = function( speed ) {
14601		this.getAvailableSlide( speed, true );
14602	};
14603
14604	/**
14605	 * Slides to the previous item or page.
14606	 *
14607	 * @public
14608	 * @param {number} [speed=false] - The time in milliseconds for the transition.
14609	 */
14610	Navigation.prototype.prev = function( speed ) {
14611		this.getAvailableSlide( speed, false );
14612	};
14613
14614	/**
14615	 * Slides to the specified item or page.
14616	 *
14617	 * @public
14618	 * @param {number}  position         - The position of the item or page.
14619	 * @param {number}  [speed]          - The time in milliseconds for the transition.
14620	 * @param {boolean} [standard=false] - Whether to use the standard behaviour or not.
14621	 */
14622	Navigation.prototype.to = function( position, speed, standard ) {
14623		var length;
14624
14625		if ( ! standard && this._pages.length ) {
14626			length = this._pages.length;
14627			$.proxy( this._overrides.to, this._core )( this._pages[ ( ( position % length ) + length ) % length ].start, speed );
14628		} else {
14629			$.proxy( this._overrides.to, this._core )( position, speed );
14630		}
14631	};
14632
14633	$.fn.tmowlCarousel.Constructor.Plugins.Navigation = Navigation;
14634}( window.Zepto || window.jQuery ) );
14635
14636/**
14637 * Hash Plugin
14638 *
14639 * @param {jQuery} $
14640 * @param {Window} window
14641 * @version 2.3.4
14642 * @author Artus Kolanowski
14643 * @author David Deutsch
14644 * @license The MIT License (MIT)
14645 */
14646( function( $, window ) {
14647	'use strict';
14648
14649	/**
14650	 * Creates the hash plugin.
14651	 *
14652	 * @class The Hash Plugin
14653	 * @param {Owl} carousel - The Owl Carousel
14654	 */
14655	var Hash = function( carousel ) {
14656		/**
14657		 * Reference to the core.
14658		 *
14659		 * @protected
14660		 * @type {Owl}
14661		 */
14662		this._core = carousel;
14663
14664		/**
14665		 * Hash index for the items.
14666		 *
14667		 * @protected
14668		 * @type {Object}
14669		 */
14670		this._hashes = {};
14671
14672		/**
14673		 * The carousel element.
14674		 *
14675		 * @type {jQuery}
14676		 */
14677		this.$element = this._core.$element;
14678
14679		/**
14680		 * All event handlers.
14681		 *
14682		 * @protected
14683		 * @type {Object}
14684		 */
14685		this._handlers = {
14686			'initialized.owl.carousel': $.proxy( function( e ) {
14687				if ( e.namespace && this._core.settings.startPosition === 'URLHash' ) {
14688					$( window ).trigger( 'hashchange.owl.navigation' );
14689				}
14690			}, this ),
14691			'prepared.owl.carousel': $.proxy( function( e ) {
14692				var hash;
14693				if ( e.namespace ) {
14694					hash = $( e.content ).find( '[data-hash]' ).addBack( '[data-hash]' ).attr( 'data-hash' );
14695
14696					if ( ! hash ) {
14697						return;
14698					}
14699
14700					this._hashes[ hash ] = e.content;
14701				}
14702			}, this ),
14703			'changed.owl.carousel': $.proxy( function( e ) {
14704				var current;
14705				var hash;
14706				if ( e.namespace && e.property.name === 'position' ) {
14707					current = this._core.items( this._core.relative( this._core.current() ) );
14708					hash = $.map( this._hashes, function( item, thishash ) {
14709						return item === current ? thishash : null;
14710					} ).join();
14711
14712					if ( ! hash || window.location.hash.slice( 1 ) === hash ) {
14713						return;
14714					}
14715
14716					window.location.hash = hash;
14717				}
14718			}, this )
14719		};
14720
14721		// set default options
14722		this._core.options = $.extend( {}, Hash.Defaults, this._core.options );
14723
14724		// register the event handlers
14725		this.$element.on( this._handlers );
14726
14727		// register event listener for hash navigation
14728		$( window ).on(
14729			'hashchange.owl.navigation',
14730			$.proxy( function() {
14731				var hash = window.location.hash.substring( 1 ),
14732					items = this._core.$stage.children(),
14733					position = this._hashes[ hash ] && items.index( this._hashes[ hash ] );
14734
14735				if ( position === undefined || position === this._core.current() ) {
14736					return;
14737				}
14738
14739				this._core.to( this._core.relative( position ), false, true );
14740			}, this )
14741		);
14742	};
14743
14744	/**
14745	 * Default options.
14746	 *
14747	 * @public
14748	 */
14749	Hash.Defaults = {
14750		URLhashListener: false
14751	};
14752
14753	/**
14754	 * Destroys the plugin.
14755	 *
14756	 * @public
14757	 */
14758	Hash.prototype.destroy = function() {
14759		var handler, property;
14760
14761		$( window ).off( 'hashchange.owl.navigation' );
14762
14763		for ( handler in this._handlers ) {
14764			if ( handler ) {
14765				this._core.$element.off( handler, this._handlers[ handler ] );
14766			}
14767		}
14768		for ( property in Object.getOwnPropertyNames( this ) ) {
14769			if ( typeof this[ property ] !== 'function' ) {
14770				this[ property ] = null;
14771			}
14772		}
14773	};
14774
14775	$.fn.tmowlCarousel.Constructor.Plugins.Hash = Hash;
14776}( window.Zepto || window.jQuery, window ) );
14777
14778/**
14779 * Support Plugin
14780 *
14781 * @param {jQuery} $
14782 * @version 2.3.4
14783 * @author Vivid Planet Software GmbH
14784 * @author Artus Kolanowski
14785 * @author David Deutsch
14786 * @license The MIT License (MIT)
14787 */
14788( function( $ ) {
14789	'use strict';
14790	var style = $( '<support>' ).get( 0 ).style,
14791		prefixes = 'Webkit Moz O ms'.split( ' ' ),
14792		events = {
14793			transition: {
14794				end: {
14795					WebkitTransition: 'webkitTransitionEnd',
14796					MozTransition: 'transitionend',
14797					OTransition: 'oTransitionEnd',
14798					transition: 'transitionend'
14799				}
14800			},
14801			animation: {
14802				end: {
14803					WebkitAnimation: 'webkitAnimationEnd',
14804					MozAnimation: 'animationend',
14805					OAnimation: 'oAnimationEnd',
14806					animation: 'animationend'
14807				}
14808			}
14809		},
14810		tests;
14811
14812	function test( property, thisprefixed ) {
14813		var result = false,
14814			upper = property.charAt( 0 ).toUpperCase() + property.slice( 1 );
14815
14816		$.each( ( property + ' ' + prefixes.join( upper + ' ' ) + upper ).split( ' ' ), function( i, thisproperty ) {
14817			if ( style[ thisproperty ] !== undefined ) {
14818				result = thisprefixed ? thisproperty : true;
14819				return false;
14820			}
14821		} );
14822
14823		return result;
14824	}
14825
14826	function prefixed( property ) {
14827		return test( property, true );
14828	}
14829
14830	tests = {
14831		csstransforms: function() {
14832			return !! test( 'transform' );
14833		},
14834		csstransforms3d: function() {
14835			return !! test( 'perspective' );
14836		},
14837		csstransitions: function() {
14838			return !! test( 'transition' );
14839		},
14840		cssanimations: function() {
14841			return !! test( 'animation' );
14842		}
14843	};
14844	if ( tests.csstransitions() ) {
14845		/* jshint -W053 */
14846		$.support.transition = new String( prefixed( 'transition' ) );
14847		$.support.transition.end = events.transition.end[ $.support.transition ];
14848	}
14849
14850	if ( tests.cssanimations() ) {
14851		/* jshint -W053 */
14852		$.support.animation = new String( prefixed( 'animation' ) );
14853		$.support.animation.end = events.animation.end[ $.support.animation ];
14854	}
14855
14856	if ( tests.csstransforms() ) {
14857		/* jshint -W053 */
14858		$.support.transform = new String( prefixed( 'transform' ) );
14859		$.support.transform3d = tests.csstransforms3d();
14860	}
14861}( window.Zepto || window.jQuery ) );
14862;
14863/**!
14864 * Sortable 1.15.6
14865 * @author	RubaXa   <[email protected]>
14866 * @author	owenm    <[email protected]>
14867 * @license MIT
14868 */
14869
14870/* global define, require, module */
14871( function( global, factory ) {
14872	'use strict';
14873	if ( typeof exports === 'object' && typeof module !== 'undefined' ) {
14874		module.exports = factory();
14875	} else if ( typeof define === 'function' && define.amd ) {
14876		define( factory );
14877	} else {
14878		global = global || self;
14879		global.Sortable = factory();
14880	}
14881}( this, ( function() {
14882	'use strict';
14883
14884	var version = '1.15.6';
14885	var IE11OrLess = userAgent( /(?:Trident.*rv[ :]?11\.|msie|iemobile|Windows Phone)/i );
14886	var Edge = userAgent( /Edge/i );
14887	var FireFox = userAgent( /firefox/i );
14888	var Safari = userAgent( /safari/i ) && ! userAgent( /chrome/i ) && ! userAgent( /android/i );
14889	var IOS = userAgent( /iP(ad|od|hone)/i );
14890	var ChromeForAndroid = userAgent( /chrome/i ) && userAgent( /android/i );
14891	var captureMode = {
14892		capture: false,
14893		passive: false
14894	};
14895	var R_SPACE = /\s+/g;
14896	var _throttleTimeout;
14897	var expando = 'Sortable' + new Date().getTime();
14898	var plugins = [];
14899	var defaults = {
14900		initializeByDefault: true
14901	};
14902	var PluginManager = {
14903		mount: function mount( plugin ) {
14904			var option;
14905			// Set default static properties
14906			for ( option in defaults ) {
14907				if ( defaults.hasOwnProperty( option ) && ! ( option in plugin ) ) {
14908					plugin[ option ] = defaults[ option ];
14909				}
14910			}
14911			plugins.forEach( function( p ) {
14912				if ( p.pluginName === plugin.pluginName ) {
14913					throw 'Sortable: Cannot mount plugin '.concat( plugin.pluginName, ' more than once' );
14914				}
14915			} );
14916			plugins.push( plugin );
14917		},
14918		pluginEvent: function pluginEvent( eventName, sortable, evt ) {
14919			var _this = this;
14920			var eventNameGlobal = eventName + 'Global';
14921			this.eventCanceled = false;
14922			evt.cancel = function() {
14923				_this.eventCanceled = true;
14924			};
14925			plugins.forEach( function( plugin ) {
14926				if ( ! sortable[ plugin.pluginName ] ) {
14927					return;
14928				}
14929				// Fire global events if it exists in this sortable
14930				if ( sortable[ plugin.pluginName ][ eventNameGlobal ] ) {
14931					sortable[ plugin.pluginName ][ eventNameGlobal ]( _objectSpread2( {
14932						sortable: sortable
14933					}, evt ) );
14934				}
14935
14936				// Only fire plugin event if plugin is enabled in this sortable,
14937				// and plugin has event defined
14938				if ( sortable.options[ plugin.pluginName ] && sortable[ plugin.pluginName ][ eventName ] ) {
14939					sortable[ plugin.pluginName ][ eventName ]( _objectSpread2( {
14940						sortable: sortable
14941					}, evt ) );
14942				}
14943			} );
14944		},
14945		initializePlugins: function initializePlugins( sortable, el, _defaults ) {
14946			var option;
14947			var modified;
14948			plugins.forEach( function( plugin ) {
14949				var pluginName = plugin.pluginName;
14950				var initialized;
14951				if ( ! sortable.options[ pluginName ] && ! plugin.initializeByDefault ) {
14952					return;
14953				}
14954				initialized = new plugin( sortable, el, sortable.options );
14955				initialized.sortable = sortable;
14956				initialized.options = sortable.options;
14957				sortable[ pluginName ] = initialized;
14958
14959				// Add default options from plugin
14960				_extends( _defaults, initialized.defaults );
14961			} );
14962			for ( option in sortable.options ) {
14963				if ( ! sortable.options.hasOwnProperty( option ) ) {
14964					continue;
14965				}
14966				modified = this.modifyOption( sortable, option, sortable.options[ option ] );
14967				if ( typeof modified !== 'undefined' ) {
14968					sortable.options[ option ] = modified;
14969				}
14970			}
14971		},
14972		getEventProperties: function getEventProperties( name, sortable ) {
14973			var eventProperties = {};
14974			plugins.forEach( function( plugin ) {
14975				if ( typeof plugin.eventProperties !== 'function' ) {
14976					return;
14977				}
14978				_extends( eventProperties, plugin.eventProperties.call( sortable[ plugin.pluginName ], name ) );
14979			} );
14980			return eventProperties;
14981		},
14982		modifyOption: function modifyOption( sortable, name, value ) {
14983			var modifiedValue;
14984			plugins.forEach( function( plugin ) {
14985				// Plugin must exist on the Sortable
14986				if ( ! sortable[ plugin.pluginName ] ) {
14987					return;
14988				}
14989
14990				// If static option listener exists for this option, call in the context of the Sortable's instance of this plugin
14991				if ( plugin.optionListeners && typeof plugin.optionListeners[ name ] === 'function' ) {
14992					modifiedValue = plugin.optionListeners[ name ].call( sortable[ plugin.pluginName ], value );
14993				}
14994			} );
14995			return modifiedValue;
14996		}
14997	};
14998	var _excluded = [ 'evt' ];
14999	var pluginEvent = function pluginEvent( eventName, sortable ) {
15000		var _ref = arguments.length > 2 && arguments[ 2 ] !== undefined ? arguments[ 2 ] : {},
15001			originalEvent = _ref.evt,
15002			data = _objectWithoutProperties( _ref, _excluded );
15003		PluginManager.pluginEvent.bind( Sortable )( eventName, sortable, _objectSpread2( {
15004			dragEl: dragEl,
15005			parentEl: parentEl,
15006			ghostEl: ghostEl,
15007			rootEl: rootEl,
15008			nextEl: nextEl,
15009			lastDownEl: lastDownEl,
15010			cloneEl: cloneEl,
15011			cloneHidden: cloneHidden,
15012			dragStarted: moved,
15013			putSortable: putSortable,
15014			activeSortable: Sortable.active,
15015			originalEvent: originalEvent,
15016			oldIndex: oldIndex,
15017			oldDraggableIndex: oldDraggableIndex,
15018			newIndex: newIndex,
15019			newDraggableIndex: newDraggableIndex,
15020			hideGhostForTarget: _hideGhostForTarget,
15021			unhideGhostForTarget: _unhideGhostForTarget,
15022			cloneNowHidden: function cloneNowHidden() {
15023				cloneHidden = true;
15024			},
15025			cloneNowShown: function cloneNowShown() {
15026				cloneHidden = false;
15027			},
15028			dispatchSortableEvent: function dispatchSortableEvent( name ) {
15029				_dispatchEvent( {
15030					sortable: sortable,
15031					name: name,
15032					originalEvent: originalEvent
15033				} );
15034			}
15035		}, data ) );
15036	};
15037	var dragEl,
15038		parentEl,
15039		ghostEl,
15040		rootEl,
15041		nextEl,
15042		lastDownEl,
15043		cloneEl,
15044		cloneHidden,
15045		oldIndex,
15046		newIndex,
15047		oldDraggableIndex,
15048		newDraggableIndex,
15049		activeGroup,
15050		putSortable,
15051		awaitingDragStarted = false,
15052		ignoreNextClick = false,
15053		sortables = [],
15054		tapEvt,
15055		touchEvt,
15056		lastDx,
15057		lastDy,
15058		tapDistanceLeft,
15059		tapDistanceTop,
15060		moved,
15061		lastTarget,
15062		lastDirection,
15063		pastFirstInvertThresh = false,
15064		isCircumstantialInvert = false,
15065		targetMoveDistance,
15066		// For positioning ghost absolutely
15067		ghostRelativeParent,
15068		ghostRelativeParentInitialScroll = [],
15069		// (left, top)
15070
15071		_silent = false,
15072		savedInputChecked = [];
15073
15074	var documentExists = typeof document !== 'undefined',
15075		PositionGhostAbsolutely = IOS,
15076		CSSFloatProperty = Edge || IE11OrLess ? 'cssFloat' : 'float',
15077		// This will not pass for IE9, because IE9 DnD only works on anchors
15078		supportDraggable = documentExists && ! ChromeForAndroid && ! IOS && 'draggable' in document.createElement( 'div' ),
15079		supportCssPointerEvents = ( function() {
15080			var el;
15081			if ( ! documentExists ) {
15082				return;
15083			}
15084			// false when <= IE11
15085			if ( IE11OrLess ) {
15086				return false;
15087			}
15088			el = document.createElement( 'x' );
15089			el.style.cssText = 'pointer-events:auto';
15090			return el.style.pointerEvents === 'auto';
15091		}() ),
15092		_detectDirection = function _detectDirection( el, options ) {
15093			var elCSS = css( el ),
15094				elWidth = parseInt( elCSS.width, 10 ) - parseInt( elCSS.paddingLeft, 10 ) - parseInt( elCSS.paddingRight, 10 ) - parseInt( elCSS.borderLeftWidth, 10 ) - parseInt( elCSS.borderRightWidth, 10 ),
15095				child1 = getChild( el, 0, options ),
15096				child2 = getChild( el, 1, options ),
15097				touchingSideChild2,
15098				firstChildCSS = child1 && css( child1 ),
15099				secondChildCSS = child2 && css( child2 ),
15100				firstChildWidth = firstChildCSS && parseInt( firstChildCSS.marginLeft, 10 ) + parseInt( firstChildCSS.marginRight, 10 ) + getRect( child1 ).width,
15101				secondChildWidth = secondChildCSS && parseInt( secondChildCSS.marginLeft, 10 ) + parseInt( secondChildCSS.marginRight, 10 ) + getRect( child2 ).width;
15102			if ( elCSS.display === 'flex' ) {
15103				return elCSS.flexDirection === 'column' || elCSS.flexDirection === 'column-reverse' ? 'vertical' : 'horizontal';
15104			}
15105			if ( elCSS.display === 'grid' ) {
15106				return elCSS.gridTemplateColumns.split( ' ' ).length <= 1 ? 'vertical' : 'horizontal';
15107			}
15108			if ( child1 && firstChildCSS.float && firstChildCSS.float !== 'none' ) {
15109				touchingSideChild2 = firstChildCSS.float === 'left' ? 'left' : 'right';
15110				return child2 && ( secondChildCSS.clear === 'both' || secondChildCSS.clear === touchingSideChild2 ) ? 'vertical' : 'horizontal';
15111			}
15112			return child1 && ( firstChildCSS.display === 'block' || firstChildCSS.display === 'flex' || firstChildCSS.display === 'table' || firstChildCSS.display === 'grid' || firstChildWidth >= elWidth && elCSS[ CSSFloatProperty ] === 'none' || child2 && elCSS[ CSSFloatProperty ] === 'none' && firstChildWidth + secondChildWidth > elWidth ) ? 'vertical' : 'horizontal';
15113		},
15114		_dragElInRowColumn = function _dragElInRowColumn( dragRect, targetRect, vertical ) {
15115			var dragElS1Opp = vertical ? dragRect.left : dragRect.top,
15116				dragElS2Opp = vertical ? dragRect.right : dragRect.bottom,
15117				dragElOppLength = vertical ? dragRect.width : dragRect.height,
15118				targetS1Opp = vertical ? targetRect.left : targetRect.top,
15119				targetS2Opp = vertical ? targetRect.right : targetRect.bottom,
15120				targetOppLength = vertical ? targetRect.width : targetRect.height;
15121			return dragElS1Opp === targetS1Opp || dragElS2Opp === targetS2Opp || dragElS1Opp + dragElOppLength / 2 === targetS1Opp + targetOppLength / 2;
15122		},
15123		/**
15124		 * Detects first nearest empty sortable to X and Y position using emptyInsertThreshold.
15125		 * @param {number} x X position
15126		 * @param {number} y Y position
15127		 * @return {HTMLElement}   Element of the first found nearest Sortable
15128		 */
15129		_detectNearestEmptySortable = function _detectNearestEmptySortable( x, y ) {
15130			var ret;
15131			sortables.some( function( sortable ) {
15132				var threshold = sortable[ expando ].options.emptyInsertThreshold;
15133				var rect;
15134				var insideHorizontally;
15135				var insideVertically;
15136				if ( ! threshold || lastChild( sortable ) ) {
15137					return false; // Explicitly continue searching
15138				}
15139				rect = getRect( sortable );
15140				insideHorizontally = x >= rect.left - threshold && x <= rect.right + threshold;
15141				insideVertically = y >
15141= rect.top - threshold && y <= rect.bottom + threshold;
15142				if ( insideHorizontally && insideVertically ) {
15143					ret = sortable; // Store the matched sortable
15144					return true; // Stop iteration (found a match)
15145				}
15146				return false; // Continue searching
15147			} );
15148			return ret;
15149		},
15150		_prepareGroup = function _prepareGroup( options ) {
15151			var group = {};
15152			var originalGroup = options.group;
15153			function toFn( value, pull ) {
15154				return function( to, from, _dragEl, evt ) {
15155					var otherGroup;
15156					var sameGroup = to.options.group.name && from.options.group.name && to.options.group.name === from.options.group.name;
15157					if ( ( value === null || value === undefined ) && ( pull || sameGroup ) ) {
15158						// Default pull value
15159						// Default pull and put value if same group
15160						return true;
15161					} else if ( value === null || value === undefined || value === false ) {
15162						return false;
15163					} else if ( pull && value === 'clone' ) {
15164						return value;
15165					} else if ( typeof value === 'function' ) {
15166						return toFn( value( to, from, _dragEl, evt ), pull )( to, from, _dragEl, evt );
15167					}
15168					otherGroup = ( pull ? to : from ).options.group.name;
15169					return ( value === true ) || ( typeof value === 'string' && value === otherGroup ) || ( value.join && value.indexOf( otherGroup ) > -1 );
15170				};
15171			}
15172			if ( ! originalGroup || typeof originalGroup !== 'object' ) {
15173				originalGroup = {
15174					name: originalGroup
15175				};
15176			}
15177			group.name = originalGroup.name;
15178			group.checkPull = toFn( originalGroup.pull, true );
15179			group.checkPut = toFn( originalGroup.put );
15180			group.revertClone = originalGroup.revertClone;
15181			options.group = group;
15182		},
15183		_hideGhostForTarget = function _hideGhostForTarget() {
15184			if ( ! supportCssPointerEvents && ghostEl ) {
15185				css( ghostEl, 'display', 'none' );
15186			}
15187		},
15188		_unhideGhostForTarget = function _unhideGhostForTarget() {
15189			if ( ! supportCssPointerEvents && ghostEl ) {
15190				css( ghostEl, 'display', '' );
15191			}
15192		};
15193	var autoScrolls = [],
15194		scrollEl,
15195		scrollRootEl,
15196		scrolling = false,
15197		lastAutoScrollX,
15198		lastAutoScrollY,
15199		touchEvt$1,
15200		pointerElemChangedInterval;
15201	var autoScroll = throttle( function( evt, options, _rootEl, isFallback ) {
15202		var x;
15203		var y;
15204		var sens;
15205		var speed;
15206		var winScroller;
15207		var scrollThisInstance;
15208		var scrollCustomFn;
15209		var layersOut;
15210		var currentParent;
15211		var el, rect,
15212			top,
15213			bottom,
15214			left,
15215			right,
15216			width,
15217			height,
15218			canScrollX,
15219			canScrollY,
15220			scrollWidth,
15221			scrollHeight,
15222			elCSS,
15223			scrollPosX,
15224			scrollPosY,
15225			vx,
15226			vy,
15227			i,
15228			scrollOffsetY,
15229			scrollOffsetX;
15230		// Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521
15231		if ( ! options.scroll ) {
15232			return;
15233		}
15234		x = ( evt.touches ? evt.touches[ 0 ] : evt ).clientX;
15235		y = ( evt.touches ? evt.touches[ 0 ] : evt ).clientY;
15236		sens = options.scrollSensitivity;
15237		speed = options.scrollSpeed;
15238		winScroller = getWindowScrollingElement();
15239		scrollThisInstance = false;
15240		layersOut = 0;
15241		currentParent = scrollEl;
15242
15243		// New scroll root, set scrollEl
15244		if ( scrollRootEl !== _rootEl ) {
15245			scrollRootEl = _rootEl;
15246			clearAutoScrolls();
15247			scrollEl = options.scroll;
15248			scrollCustomFn = options.scrollFn;
15249			if ( scrollEl === true ) {
15250				scrollEl = getParentAutoScrollElement( _rootEl, true );
15251			}
15252		}
15253		do {
15254			el = currentParent;
15255			if ( ! el ) {
15256				continue;
15257			}
15258			rect = getRect( el );
15259			top = rect.top;
15260			bottom = rect.bottom;
15261			left = rect.left;
15262			right = rect.right;
15263			width = rect.width;
15264			height = rect.height;
15265			canScrollX = void 0;
15266			canScrollY = void 0;
15267			scrollWidth = el.scrollWidth;
15268			scrollHeight = el.scrollHeight;
15269			elCSS = css( el );
15270			scrollPosX = el.scrollLeft;
15271			scrollPosY = el.scrollTop;
15272			if ( el === winScroller ) {
15273				canScrollX = width < scrollWidth && ( elCSS.overflowX === 'auto' || elCSS.overflowX === 'scroll' || elCSS.overflowX === 'visible' );
15274				canScrollY = height < scrollHeight && ( elCSS.overflowY === 'auto' || elCSS.overflowY === 'scroll' || elCSS.overflowY === 'visible' );
15275			} else {
15276				canScrollX = width < scrollWidth && ( elCSS.overflowX === 'auto' || elCSS.overflowX === 'scroll' );
15277				canScrollY = height < scrollHeight && ( elCSS.overflowY === 'auto' || elCSS.overflowY === 'scroll' );
15278			}
15279			vx = canScrollX && ( Math.abs( right - x ) <= sens && scrollPosX + width < scrollWidth ) - ( Math.abs( left - x ) <= sens && !! scrollPosX );
15280			vy = canScrollY && ( Math.abs( bottom - y ) <= sens && scrollPosY + height < scrollHeight ) - ( Math.abs( top - y ) <= sens && !! scrollPosY );
15281			if ( ! autoScrolls[ layersOut ] ) {
15282				for ( i = 0; i <= layersOut; i++ ) {
15283					if ( ! autoScrolls[ i ] ) {
15284						autoScrolls[ i ] = {};
15285					}
15286				}
15287			}
15288			if ( autoScrolls[ layersOut ].vx != vx || autoScrolls[ layersOut ].vy != vy || autoScrolls[ layersOut ].el !== el ) {
15289				autoScrolls[ layersOut ].el = el;
15290				autoScrolls[ layersOut ].vx = vx;
15291				autoScrolls[ layersOut ].vy = vy;
15292				clearInterval( autoScrolls[ layersOut ].pid );
15293				if ( vx !== 0 || vy !== 0 ) {
15294					scrollThisInstance = true;
15295					autoScrolls[ layersOut ].pid = setInterval( function() {
15296						// emulate drag over during autoscroll (fallback), emulating native DnD behaviour
15297						if ( isFallback && this.layer === 0 ) {
15298							Sortable.active._onTouchMove( touchEvt$1 ); // To move ghost if it is positioned absolutely
15299						}
15300						scrollOffsetY = autoScrolls[ this.layer ].vy ? autoScrolls[ this.layer ].vy * speed : 0;
15301						scrollOffsetX = autoScrolls[ this.layer ].vx ? autoScrolls[ this.layer ].vx * speed : 0;
15302						if ( typeof scrollCustomFn === 'function' ) {
15303							if ( scrollCustomFn.call( Sortable.dragged.parentNode[ expando ], scrollOffsetX, scrollOffsetY, evt, touchEvt$1, autoScrolls[ this.layer ].el ) !== 'continue' ) {
15304								return;
15305							}
15306						}
15307						scrollBy( autoScrolls[ this.layer ].el, scrollOffsetX, scrollOffsetY );
15308					}.bind( {
15309						layer: layersOut
15310					} ), 24 );
15311				}
15312			}
15313			layersOut++;
15314		} while ( options.bubbleScroll && currentParent !== winScroller && ( currentParent = getParentAutoScrollElement( currentParent, false ) ) );
15315		scrolling = scrollThisInstance; // in case another function catches scrolling as false in between when it is not
15316	}, 30 );
15317
15318	var drop = function drop( _ref ) {
15319		var originalEvent = _ref.originalEvent,
15320			_putSortable = _ref.putSortable,
15321			_dragEl = _ref.dragEl,
15322			activeSortable = _ref.activeSortable,
15323			dispatchSortableEvent = _ref.dispatchSortableEvent,
15324			hideGhostForTarget = _ref.hideGhostForTarget,
15325			unhideGhostForTarget = _ref.unhideGhostForTarget;
15326		var toSortable;
15327		var touch;
15328		var target;
15329		if ( ! originalEvent ) {
15330			return;
15331		}
15332		toSortable = _putSortable || activeSortable;
15333		hideGhostForTarget();
15334		touch = originalEvent.changedTouches && originalEvent.changedTouches.length ? originalEvent.changedTouches[ 0 ] : originalEvent;
15335		target = document.elementFromPoint( touch.clientX, touch.clientY );
15336		unhideGhostForTarget();
15337		if ( toSortable && ! toSortable.el.contains( target ) ) {
15338			dispatchSortableEvent( 'spill' );
15339			this.onSpill( {
15340				dragEl: _dragEl,
15341				putSortable: _putSortable
15342			} );
15343		}
15344	};
15345	var lastSwapEl;
15346	var multiDragElements = [],
15347		multiDragClones = [],
15348		lastMultiDragSelect,
15349		// for selection with modifier key down (SHIFT)
15350		multiDragSortable,
15351		initialFolding = false,
15352		// Initial multi-drag fold when drag started
15353		folding = false,
15354		// Folding any other time
15355		dragStarted = false,
15356		dragEl$1,
15357		clonesFromRect,
15358		clonesHidden;
15359	var nearestEmptyInsertDetectEvent = function nearestEmptyInsertDetectEvent( evt ) {
15360		var nearest;
15361		var event;
15362		var i;
15363		if ( dragEl ) {
15364			evt = evt.touches ? evt.touches[ 0 ] : evt;
15365			nearest = _detectNearestEmptySortable( evt.clientX, evt.clientY );
15366			if ( nearest ) {
15367				// Create imitation event
15368				event = {};
15369				for ( i in evt ) {
15370					if ( evt.hasOwnProperty( i ) ) {
15371						event[ i ] = evt[ i ];
15372					}
15373				}
15374				event.target = event.rootEl = nearest;
15375				event.preventDefault = void 0;
15376				event.stopPropagation = void 0;
15377				nearest[ expando ]._onDragOver( event );
15378			}
15379		}
15380	};
15381	var _checkOutsideTargetEl = function _checkOutsideTargetEl( evt ) {
15382		if ( dragEl ) {
15383			dragEl.parentNode[ expando ]._isOutsideThisEl( evt.target );
15384		}
15385	};
15386	function ownKeys( object, enumerableOnly ) {
15387		var keys = Object.keys( object );
15388		var symbols;
15389		if ( Object.getOwnPropertySymbols ) {
15390			symbols = Object.getOwnPropertySymbols( object );
15391			if ( enumerableOnly ) {
15392				symbols = symbols.filter( function( sym ) {
15393					return Object.getOwnPropertyDescriptor( object, sym ).enumerable;
15394				} );
15395			}
15396			keys.push.apply( keys, symbols );
15397		}
15398		return keys;
15399	}
15400	function _objectSpread2( target ) {
15401		var i, source;
15402		for ( i = 1; i < arguments.length; i++ ) {
15403			source = ( arguments[ i ] !== undefined && arguments[ i ] !== null ) ? arguments[ i ] : {};
15404			if ( i % 2 ) {
15405				ownKeys( Object( source ), true ).forEach( function( key ) {
15406					_defineProperty( target, key, source[ key ] );
15407				} );
15408			} else if ( Object.getOwnPropertyDescriptors ) {
15409				Object.defineProperties( target, Object.getOwnPropertyDescriptors( source ) );
15410			} else {
15411				ownKeys( Object( source ) ).forEach( function( key ) {
15412					Object.defineProperty( target, key, Object.getOwnPropertyDescriptor( source, key ) );
15413				} );
15414			}
15415		}
15416		return target;
15417	}
15418
15419	function _defineProperty( obj, key, value ) {
15420		if ( key in obj ) {
15421			Object.defineProperty( obj, key, {
15422				value: value,
15423				enumerable: true,
15424				configurable: true,
15425				writable: true
15426			} );
15427		} else {
15428			obj[ key ] = value;
15429		}
15430		return obj;
15431	}
15432	function _extends() {
15433		var i;
15434		var source;
15435		var key;
15436		_extends = Object.assign || function( target ) {
15437			for ( i = 1; i < arguments.length; i++ ) {
15438				source = arguments[ i ];
15439				for ( key in source ) {
15440					if ( Object.prototype.hasOwnProperty.call( source, key ) ) {
15441						target[ key ] = source[ key ];
15442					}
15443				}
15444			}
15445			return target;
15446		};
15447		return _extends.apply( this, arguments );
15448	}
15449	function _objectWithoutPropertiesLoose( source, excluded ) {
15450		var target;
15451		var sourceKeys;
15452		var key, i;
15453		if ( source == null ) {
15454			return {};
15455		}
15456		target = {};
15457		sourceKeys = Object.keys( source );
15458		for ( i = 0; i < sourceKeys.length; i++ ) {
15459			key = sourceKeys[ i ];
15460			if ( excluded.indexOf( key ) >= 0 ) {
15461				continue;
15462			}
15463			target[ key ] = source[ key ];
15464		}
15465		return target;
15466	}
15467	function _objectWithoutProperties( source, excluded ) {
15468		var target;
15469		var key, i;
15470		var sourceSymbolKeys;
15471		if ( source == null ) {
15472			return {};
15473		}
15474		target = _objectWithoutPropertiesLoose( source, excluded );
15475		if ( Object.getOwnPropertySymbols ) {
15476			sourceSymbolKeys = Object.getOwnPropertySymbols( source );
15477			for ( i = 0; i < sourceSymbolKeys.length; i++ ) {
15478				key = sourceSymbolKeys[ i ];
15479				if ( excluded.indexOf( key ) >= 0 ) {
15480					continue;
15481				}
15482				if ( ! Object.prototype.propertyIsEnumerable.call( source, key ) ) {
15483					continue;
15484				}
15485				target[ key ] = source[ key ];
15486			}
15487		}
15488		return target;
15489	}
15490	function _toConsumableArray( arr ) {
15491		return _arrayWithoutHoles( arr ) || _iterableToArray( arr ) || _unsupportedIterableToArray( arr ) || _nonIterableSpread();
15492	}
15493	function _arrayWithoutHoles( arr ) {
15494		if ( Array.isArray( arr ) ) {
15495			return _arrayLikeToArray( arr );
15496		}
15497	}
15498	function _iterableToArray( iter ) {
15499		if ( iter !== undefined && iter !== null && typeof iter[ Symbol.iterator ] === 'function' ) {
15500			return Array.from( iter );
15501		}
15502	}
15503	function _unsupportedIterableToArray( o, minLen ) {
15504		var n;
15505		if ( ! o ) {
15506			return;
15507		}
15508		if ( typeof o === 'string' ) {
15509			return _arrayLikeToArray( o, minLen );
15510		}
15511		n = Object.prototype.toString.call( o ).slice( 8, -1 );
15512		if ( n === 'Object' && o.constructor ) {
15513			n = o.constructor.name;
15514		}
15515		if ( n === 'Map' || n === 'Set' ) {
15516			return Array.from( o );
15517		}
15518		if ( n === 'Arguments' || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test( n ) ) {
15519			return _arrayLikeToArray( o, minLen );
15520		}
15521	}
15522	function _arrayLikeToArray( arr, len ) {
15523		var i;
15524		var arr2;
15525		if ( len === undefined || len === null || len > arr.length ) {
15526			len = arr.length;
15527		}
15528		arr2 = new Array( len );
15529		for ( i = 0; i < len; i++ ) {
15530			arr2[ i ] = arr[ i ];
15531		}
15532		return arr2;
15533	}
15534	function _nonIterableSpread() {
15535		throw new TypeError( 'Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.' );
15536	}
15537
15538	function userAgent( pattern ) {
15539		if ( typeof window !== 'undefined' && window.navigator ) {
15540			return !! /*@__PURE__*/navigator.userAgent.match( pattern );
15541		}
15542	}
15543	function on( el, event, fn ) {
15544		el.addEventListener( event, fn, ! IE11OrLess && captureMode );
15545	}
15546	function off( el, event, fn ) {
15547		el.removeEventListener( event, fn, ! IE11OrLess && captureMode );
15548	}
15549	function matches( /**HTMLElement*/el, /**String*/selector ) {
15550		if ( ! selector ) {
15551			return;
15552		}
15553		if ( selector[ 0 ] === '>' ) {
15554			selector = selector.substring( 1 );
15555		}
15556		if ( el ) {
15557			try {
15558				if ( el.matches ) {
15559					return el.matches( selector );
15560				} else if ( el.msMatchesSelector ) {
15561					return el.msMatchesSelector( selector );
15562				} else if ( el.webkitMatchesSelector ) {
15563					return el.webkitMatchesSelector( selector );
15564				}
15565			} catch ( _ ) {
15566				return false;
15567			}
15568		}
15569		return false;
15570	}
15571	function getParentOrHost( el ) {
15572		return el.host && el !== document && el.host.nodeType ? el.host : el.parentNode;
15573	}
15574	function closest( /**HTMLElement*/el, /**String*/selector, /**HTMLElement*/ctx, includeCTX ) {
15575		if ( el ) {
15576			ctx = ctx || document;
15577			do {
15578				if ( ( selector !== undefined && selector !== null && ( selector[ 0 ] === '>' ? el.parentNode === ctx && matches( el, selector ) : matches( el, selector ) ) ) || ( includeCTX && el === ctx ) ) {
15579					return el;
15580				}
15581				if ( el === ctx ) {
15582					break;
15583				}
15584			} while ( ( el = getParentOrHost( el ) ) );
15585		}
15586		return null;
15587	}
15588	function toggleClass( el, name, state ) {
15589		var className;
15590		if ( el && name ) {
15591			if ( el.classList ) {
15592				el.classList[ state ? 'add' : 'remove' ]( name );
15593			} else {
15594				className = ( ' ' + el.className + ' ' ).replace( R_SPACE, ' ' ).replace( ' ' + name + ' ', ' ' );
15595				el.className = ( className + ( state ? ' ' + name : '' ) ).replace( R_SPACE, ' ' );
15596			}
15597		}
15598	}
15599	function css( el, prop, val ) {
15600		var style = el && el.style;
15601		if ( style ) {
15602			if ( val === void 0 ) {
15603				if ( document.defaultView && document.defaultView.getComputedStyle ) {
15604					val = document.defaultView.getComputedStyle( el, '' );
15605				} else if ( el.currentStyle ) {
15606					val = el.currentStyle;
15607				}
15608				return prop === void 0 ? val : val[ prop ];
15609			}
15610			if ( ! ( prop in style ) && prop.indexOf( 'webkit' ) === -1 ) {
15611				prop = '-webkit-' + prop;
15612			}
15613			style[ prop ] = val + ( typeof val === 'string' ? '' : 'px' );
15614		}
15615	}
15616	function matrix( el, selfOnly ) {
15617		var appliedTransforms = '';
15618		var transform;
15619		var matrixFn;
15620		if ( typeof el === 'string' ) {
15621			appliedTransforms = el;
15622		} else {
15623			do {
15624				transform = css( el, 'transform' );
15625				if ( transform && transform !== 'none' ) {
15626					appliedTransforms = transform + ' ' + appliedTransforms;
15627				}
15628			} while ( ! selfOnly && ( el = el.parentNode ) );
15629		}
15630		matrixFn = window.DOMMatrix || window.WebKitCSSMatrix || window.CSSMatrix || window.MSCSSMatrix;
15631		return matrixFn && new matrixFn( appliedTransforms );
15632	}
15633	function find( ctx, tagName, iterator ) {
15634		if ( ctx ) {
15635			var list = ctx.getElementsByTagName( tagName ),
15636				i = 0,
15637				n = list.length;
15638			if ( iterator ) {
15639				for ( ; i < n; i++ ) {
15640					iterator( list[ i ], i );
15641				}
15642			}
15643			return list;
15644		}
15645		return [];
15646	}
15647	function getWindowScrollingElement() {
15648		var scrollingElement = document.scrollingElement;
15649		if ( scrollingElement ) {
15650			return scrollingElement;
15651		}
15652		return document.documentElement;
15653	}
15654
15655	/**
15656	 * Returns the "bounding client rect" of given element
15657	 * @param {HTMLElement}   el                        The element whose boundingClientRect is wanted
15658	 * @param {[boolean]}     relativeToContainingBlock Whether the rect should be relative to the containing block of (including) the container
15659	 * @param {[boolean]}     relativeToNonStaticParent Whether the rect should be relative to the relative parent of (including) the contaienr
15660	 * @param {[boolean]}     undoScale                 Whether the container's scale() should be undone
15661	 * @param {[HTMLElement]} container                 The parent the element will be placed in
15662	 * @return {Object}                               The boundingClientRect of el, with specified adjustments
15663	 */
15664	function getRect( el, relativeToContainingBlock, relativeToNonStaticParent, undoScale, container ) {
15665		if ( ! el || ( ! el.getBoundingClientRect && el !== window ) ) {
15666			return;
15667		}
15668		var elRect, top, left, bottom, right, height, width;
15669		if ( el !== window && el.parentNode && el !== getWindowScrollingElement() ) {
15670			elRect = el.getBoundingClientRect();
15671			top = elRect.top;
15672			left = elRect.left;
15673			bottom = elRect.bottom;
15674			right = elRect.right;
15675			height = elRect.height;
15676			width = elRect.width;
15677		} else {
15678			top = 0;
15679			left = 0;
15680			bottom = window.innerHeight;
15681			right = window.innerWidth;
15682			height = window.innerHeight;
15683			width = window.innerWidth;
15684		}
15685		if ( ( relativeToContainingBlock || relativeToNonStaticParent ) && el !== window ) {
15686			// Adjust for translate()
15687			container = container || el.parentNode;
15688
15689			// solves #1123 (see: https://stackoverflow.com/a/37953806/6088312)
15690			// Not needed on <= IE11
15691			if ( ! IE11OrLess ) {
15692				do {
15693					if ( container && container.getBoundingClientRect && ( css( container, 'transform' ) !== 'none' || relativeToNonStaticParent && css( container, 'position' ) !== 'static' ) ) {
15694						var containerRect = container.getBoundingClientRect();
15695
15696						// Set relative to edges of padding box of container
15697						top -= containerRect.top + parseInt( css( container, 'border-top-width' ), 10 );
15698						left -= containerRect.left + parseInt( css( container, 'border-left-width' ), 10 );
15699						bottom = top + elRect.height;
15700						right = left + elRect.width;
15701						break;
15702					}
15703				} while ( container = container.parentNode );
15704			}
15705		}
15706		if ( undoScale && el !== window ) {
15707			// Adjust for scale()
15708			var elMatrix = matrix( container || el ),
15709				scaleX = elMatrix && elMatrix.a,
15710				scaleY = elMatrix && elMatrix.d;
15711			if ( elMatrix ) {
15712				top /= scaleY;
15713				left /= scaleX;
15714				width /= scaleX;
15715				height /= scaleY;
15716				bottom = top + height;
15717				right = left + width;
15718			}
15719		}
15720		return {
15721			top: top,
15722			left: left,
15723			bottom: bottom,
15724			right: right,
15725			width: width,
15726			height: height
15727		};
15728	}
15729
15730	/**
15731	 * Checks if a side of an element is scrolled past a side of its parents
15732	 * @param {HTMLElement} el         The element who's side being scrolled out of view is in question
15733	 * @param {string}      elSide     Side of the element in question ('top', 'left', 'right', 'bottom')
15734	 * @param {string}      parentSide Side of the parent in question ('top', 'left', 'right', 'bottom')
15735	 * @return {HTMLElement}               The parent scroll element that the el's side is scrolled past, or null if there is no such element
15736	 */
15737	function isScrolledPast( el, elSide, parentSide ) {
15738		var parent = getParentAutoScrollElement( el, true ),
15739			elSideVal = getRect( el )[ elSide ];
15740
15741		while ( parent ) {
15742			var parentSideVal = getRect( parent )[ parentSide ],
15743				visible = void 0;
15744			if ( parentSide === 'top' || parentSide === 'left' ) {
15745				visible = elSideVal >= parentSideVal;
15746			} else {
15747				visible = elSideVal <= parentSideVal;
15748			}
15749			if ( ! visible ) {
15750				return parent;
15751			}
15752			if ( parent === getWindowScrollingElement() ) {
15753				break;
15754			}
15755			parent = getParentAutoScrollElement( parent, false );
15756		}
15757		return false;
15758	}
15759
15760	/**
15761	 * Gets nth child of el, ignoring hidden children, sortable's elements (does not ignore clone if it's visible)
15762	 * and non-draggable elements
15763	 * @param {HTMLElement} el                    The parent element
15764	 * @param {number}      childNum              The index of the child
15765	 * @param {Object}      options               Parent Sortable's options
15766	 * @param {boolean}     [includeDragEl=false] - If `true`, includes the dragged element in the search.
15767	 * @return {HTMLElement}          The child at index childNum, or null if not found
15768	 */
15769	function getChild( el, childNum, options, includeDragEl ) {
15770		var currentChild = 0,
15771			i = 0,
15772			children = el.children;
15773		while ( i < children.length ) {
15774			if ( children[ i ].style.display !== 'none' && children[ i ] !== Sortable.ghost && ( includeDragEl || children[ i ] !== Sortable.dragged ) && closest( children[ i ], options.draggable, el, false ) ) {
15775				if ( currentChild === childNum ) {
15776					return children[ i ];
15777				}
15778				currentChild++;
15779			}
15780			i++;
15781		}
15782		return null;
15783	}
15784
15785	/**
15786	 * Gets the last child in the el, ignoring ghostEl or invisible elements (clones)
15787	 * @param {HTMLElement} el       Parent element
15788	 * @param {selector}    selector Any other elements that should be ignored
15789	 * @return {HTMLElement}          The last child, ignoring ghostEl
15790	 */
15791	function lastChild( el, selector ) {
15792		var last = el.lastElementChild;
15793		while ( last && ( last === Sortable.ghost || css( last, 'display' ) === 'none' || selector && ! matches( last, selector ) ) ) {
15794			last = last.previousElementSibling;
15795		}
15796		return last || null;
15797	}
15798
15799	/**
15800	 * Returns the index of an element within its parent for a selected set of
15801	 * elements
15802	 * @param {HTMLElement} el
15803	 * @param {selector}    selector
15804	 * @return {number}
15805	 */
15806	function index( el, selector ) {
15807		var _index = 0;
15808		if ( ! el || ! el.parentNode ) {
15809			return -1;
15810		}
15811
15812		while ( ( el = el.previousElementSibling ) ) {
15813			if ( el.nodeName.toUpperCase() !== 'TEMPLATE' && el !== Sortable.clone && ( ! selector || matches( el, selector ) ) ) {
15814				_index++;
15815			}
15816		}
15817		return _index;
15818	}
15819
15820	/**
15821	 * Returns the scroll offset of the given element, added with all the scroll offsets of parent elements.
15822	 * The value is returned in real pixels.
15823	 * @param {HTMLElement} el
15824	 * @return {Array}             Offsets in the format of [left, top]
15825	 */
15826	function getRelativeScrollOffset( el ) {
15827		var offsetLeft = 0,
15828			offsetTop = 0,
15829			winScroller = getWindowScrollingElement();
15830		if ( el ) {
15831			do {
15832				var elMatrix = matrix( el ),
15833					scaleX = elMatrix.a,
15834					scaleY = elMatrix.d;
15835				offsetLeft += el.scrollLeft * scaleX;
15836				offsetTop += el.scrollTop * scaleY;
15837			} while ( el !== winScroller && ( el = el.parentNode ) );
15838		}
15839		return [ offsetLeft, offsetTop ];
15840	}
15841
15842	/**
15843	 * Returns the index of the object within the given array
15844	 * @param {Array}  arr Array that may or may not hold the object
15845	 * @param {Object} obj An object that has a key-value pair unique to and identical to a key-value pair in the object you want to find
15846	 * @return {number}      The index of the object in the array, or -1
15847	 */
15848	function indexOfObject( arr, obj ) {
15849		for ( var i in arr ) {
15850			if ( ! arr.hasOwnProperty( i ) ) {
15851				continue;
15852			}
15853			for ( var key in obj ) {
15854				if ( obj.hasOwnProperty( key ) && obj[ key ] === arr[ i ][ key ] ) {
15855					return Number( i );
15856				}
15857			}
15858		}
15859		return -1;
15860	}
15861	function getParentAutoScrollElement( el, includeSelf ) {
15862		// skip to window
15863		if ( ! el || ! el.getBoundingClientRect ) {
15864			return getWindowScrollingElement();
15865		}
15866		var elem = el;
15867		var gotSelf = false;
15868		do {
15869			// we don't need to get elem css if it isn't even overflowing in the first place (performance)
15870			if ( elem.clientWidth < elem.scrollWidth || elem.clientHeight < elem.scrollHeight ) {
15871				var elemCSS = css( elem );
15872				if ( elem.clientWidth < elem.scrollWidth && ( elemCSS.overflowX == 'auto' || elemCSS.overflowX == 'scroll' ) || elem.clientHeight < elem.scrollHeight && ( elemCSS.overflowY == 'auto' || elemCSS.overflowY == 'scroll' ) ) {
15873					if ( ! elem.getBoundingClientRect || elem === document.body ) {
15874						return getWindowScrollingElement();
15875					}
15876					if ( gotSelf || includeSelf ) {
15877						return elem;
15878					}
15879					gotSelf = true;
15880				}
15881			}
15882		} while ( ( elem = elem.parentNode ) );
15883		return getWindowScrollingElement();
15884	}
15885	function extend( dst, src ) {
15886		var key;
15887		if ( dst && src ) {
15888			for ( key in src ) {
15889				if ( src.hasOwnProperty( key ) ) {
15890					dst[ key ] = src[ key ];
15891				}
15892			}
15893		}
15894		return dst;
15895	}
15896	function isRectEqual( rect1, rect2 ) {
15897		return Math.round( rect1.top ) === Math.round( rect2.top ) && Math.round( rect1.left ) === Math.round( rect2.left ) && Math.round( rect1.height ) === Math.round( rect2.height ) && Math.round( rect1.width ) === Math.round( rect2.width );
15898	}
15899	function throttle( callback, ms ) {
15900		return function() {
15901			var args = arguments,
15902				_this = this;
15903			if ( ! _throttleTimeout ) {
15904				if ( args.length === 1 ) {
15905					callback.call( _this, args[ 0 ] );
15906				} else {
15907					callback.apply( _this, args );
15908				}
15909				_throttleTimeout = setTimeout( function() {
15910					_throttleTimeout = void 0;
15911				}, ms );
15912			}
15913		};
15914	}
15915	function cancelThrottle() {
15916		clearTimeout( _throttleTimeout );
15917		_throttleTimeout = void 0;
15918	}
15919	function scrollBy( el, x, y ) {
15920		el.scrollLeft += x;
15921		el.scrollTop += y;
15922	}
15923	function clone( el ) {
15924		var Polymer = window.Polymer;
15925		var $ = window.jQuery || window.Zepto;
15926		if ( Polymer && Polymer.dom ) {
15927			return Polymer.dom( el ).cloneNode( true );
15928		} else if ( $ ) {
15929			return $( el ).clone( true )[ 0 ];
15930		}
15931		return el.cloneNode( true );
15932	}
15933	function setRect( el, rect ) {
15934		css( el, 'position', 'absolute' );
15935		css( el, 'top', rect.top );
15936		css( el, 'left', rect.left );
15937		css( el, 'width', rect.width );
15938		css( el, 'height', rect.height );
15939	}
15940	function unsetRect( el ) {
15941		css( el, 'position', '' );
15942		css( el, 'top', '' );
15943		css( el, 'left', '' );
15944		css( el, 'width', '' );
15945		css( el, 'height', '' );
15946	}
15947	function getChildContainingRectFromElement( container, options, ghostEl ) {
15948		var rect = {};
15949		Array.from( container.children ).forEach( function( child ) {
15950			var _rect$left, _rect$top, _rect$right, _rect$bottom;
15951			if ( ! closest( child, options.draggable, container, false ) || child.animated || child === ghostEl ) {
15952				return;
15953			}
15954			var childRect = getRect( child );
15955			rect.left = Math.min( ( _rect$left = rect.left ) !== null && _rect$left !== void 0 ? _rect$left : Infinity, childRect.left );
15956			rect.top = Math.min( ( _rect$top = rect.top ) !== null && _rect$top !== void 0 ? _rect$top : Infinity, childRect.top );
15957			rect.right = Math.max( ( _rect$right = rect.right ) !== null && _rect$right !== void 0 ? _rect$right : -Infinity, childRect.right );
15958			rect.bottom = Math.max( ( _rect$bottom = rect.bottom ) !== null && _rect$bottom !== void 0 ? _rect$bottom : -Infinity, childRect.bottom );
15959		} );
15960		rect.width = rect.right - rect.left;
15961		rect.height = rect.bottom - rect.top;
15962		rect.x = rect.left;
15963		rect.y = rect.top;
15964		return rect;
15965	}
15966
15967	function AnimationStateManager() {
15968		var animationStates = [],
15969			animationCallbackId;
15970		return {
15971			captureAnimationState: function captureAnimationState() {
15972				animationStates = [];
15973				if ( ! this.options.animation ) {
15974					return;
15975				}
15976				var children = [].slice.call( this.el.children );
vendor: 4,201 bytes, lines 15977-16093
15977				children.forEach( function( child ) {
15978					if ( css( child, 'display' ) === 'none' || child === Sortable.ghost ) {
15979						return;
15980					}
15981					animationStates.push( {
15982						target: child,
15983						rect: getRect( child )
15984					} );
15985					var fromRect = _objectSpread2( {}, animationStates[ animationStates.length - 1 ].rect );
15986
15987					// If animating: compensate for current animation
15988					if ( child.thisAnimationDuration ) {
15989						var childMatrix = matrix( child, true );
15990						if ( childMatrix ) {
15991							fromRect.top -= childMatrix.f;
15992							fromRect.left -= childMatrix.e;
15993						}
15994					}
15995					child.fromRect = fromRect;
15996				} );
15997			},
15998			addAnimationState: function addAnimationState( state ) {
15999				animationStates.push( state );
16000			},
16001			removeAnimationState: function removeAnimationState( target ) {
16002				animationStates.splice( indexOfObject( animationStates, {
16003					target: target
16004				} ), 1 );
16005			},
16006			animateAll: function animateAll( callback ) {
16007				var _this = this;
16008				if ( ! this.options.animation ) {
16009					clearTimeout( animationCallbackId );
16010					if ( typeof callback === 'function' ) {
16011						callback();
16012					}
16013					return;
16014				}
16015				var animating = false,
16016					animationTime = 0;
16017				animationStates.forEach( function( state ) {
16018					var time = 0,
16019						target = state.target,
16020						fromRect = target.fromRect,
16021						toRect = getRect( target ),
16022						prevFromRect = target.prevFromRect,
16023						prevToRect = target.prevToRect,
16024						animatingRect = state.rect,
16025						targetMatrix = matrix( target, true );
16026					if ( targetMatrix ) {
16027						// Compensate for current animation
16028						toRect.top -= targetMatrix.f;
16029						toRect.left -= targetMatrix.e;
16030					}
16031					target.toRect = toRect;
16032					if ( target.thisAnimationDuration ) {
16033						// Could also check if animatingRect is between fromRect and toRect
16034						if ( isRectEqual( prevFromRect, toRect ) && ! isRectEqual( fromRect, toRect ) &&
16035            // Make sure animatingRect is on line between toRect & fromRect
16036            ( animatingRect.top - toRect.top ) / ( animatingRect.left - toRect.left ) === ( fromRect.top - toRect.top ) / ( fromRect.left - toRect.left ) ) {
16037							// If returning to same place as started from animation and on same axis
16038							time = calculateRealTime( animatingRect, prevFromRect, prevToRect, _this.options );
16039						}
16040					}
16041
16042					// if fromRect != toRect: animate
16043					if ( ! isRectEqual( toRect, fromRect ) ) {
16044						target.prevFromRect = fromRect;
16045						target.prevToRect = toRect;
16046						if ( ! time ) {
16047							time = _this.options.animation;
16048						}
16049						_this.animate( target, animatingRect, toRect, time );
16050					}
16051					if ( time ) {
16052						animating = true;
16053						animationTime = Math.max( animationTime, time );
16054						clearTimeout( target.animationResetTimer );
16055						target.animationResetTimer = setTimeout( function() {
16056							target.animationTime = 0;
16057							target.prevFromRect = null;
16058							target.fromRect = null;
16059							target.prevToRect = null;
16060							target.thisAnimationDuration = null;
16061						}, time );
16062						target.thisAnimationDuration = time;
16063					}
16064				} );
16065				clearTimeout( animationCallbackId );
16066				if ( ! animating ) {
16067					if ( typeof callback === 'function' ) {
16068						callback();
16069					}
16070				} else {
16071					animationCallbackId = setTimeout( function() {
16072						if ( typeof callback === 'function' ) {
16073							callback();
16074						}
16075					}, animationTime );
16076				}
16077				animationStates = [];
16078			},
16079			animate: function animate( target, currentRect, toRect, duration ) {
16080				if ( duration ) {
16081					css( target, 'transition', '' );
16082					css( target, 'transform', '' );
16083					var elMatrix = matrix( this.el ),
16084						scaleX = elMatrix && elMatrix.a,
16085						scaleY = elMatrix && elMatrix.d,
16086						translateX = ( currentRect.left - toRect.left ) / ( scaleX || 1 ),
16087						translateY = ( currentRect.top - toRect.top ) / ( scaleY || 1 );
16088					target.animatingX = !! translateX;
16089					target.animatingY = !! translateY;
16090					css( target, 'transform', 'translate3d(' + translateX + 'px,' + translateY + 'px,0)' );
16091					this.forRepaintDummy = repaint( target ); // repaint
16092
16093					css( target, 'transition', 'transform ' + duration + 'ms' + ( this.options.easing ? ' ' + this.options.easing : '' ) );
vendor: 15,223 bytes, lines 16094-16557
16094					css( target, 'transform', 'translate3d(0,0,0)' );
16095					typeof target.animated === 'number' && clearTimeout( target.animated );
16096					target.animated = setTimeout( function() {
16097						css( target, 'transition', '' );
16098						css( target, 'transform', '' );
16099						target.animated = false;
16100						target.animatingX = false;
16101						target.animatingY = false;
16102					}, duration );
16103				}
16104			}
16105		};
16106	}
16107	function repaint( target ) {
16108		return target.offsetWidth;
16109	}
16110	function calculateRealTime( animatingRect, fromRect, toRect, options ) {
16111		return Math.sqrt( Math.pow( fromRect.top - animatingRect.top, 2 ) + Math.pow( fromRect.left - animatingRect.left, 2 ) ) / Math.sqrt( Math.pow( fromRect.top - toRect.top, 2 ) + Math.pow( fromRect.left - toRect.left, 2 ) ) * options.animation;
16112	}
16113
16114	function dispatchEvent( _ref ) {
16115		var sortable = _ref.sortable,
16116			rootEl = _ref.rootEl,
16117			name = _ref.name,
16118			targetEl = _ref.targetEl,
16119			cloneEl = _ref.cloneEl,
16120			toEl = _ref.toEl,
16121			fromEl = _ref.fromEl,
16122			oldIndex = _ref.oldIndex,
16123			newIndex = _ref.newIndex,
16124			oldDraggableIndex = _ref.oldDraggableIndex,
16125			newDraggableIndex = _ref.newDraggableIndex,
16126			originalEvent = _ref.originalEvent,
16127			putSortable = _ref.putSortable,
16128			extraEventProperties = _ref.extraEventProperties;
16129		sortable = sortable || rootEl && rootEl[ expando ];
16130		if ( ! sortable ) {
16131			return;
16132		}
16133		var evt,
16134			options = sortable.options,
16135			onName = 'on' + name.charAt( 0 ).toUpperCase() + name.substr( 1 );
16136		// Support for new CustomEvent feature
16137		if ( window.CustomEvent && ! IE11OrLess && ! Edge ) {
16138			evt = new CustomEvent( name, {
16139				bubbles: true,
16140				cancelable: true
16141			} );
16142		} else {
16143			evt = document.createEvent( 'Event' );
16144			evt.initEvent( name, true, true );
16145		}
16146		evt.to = toEl || rootEl;
16147		evt.from = fromEl || rootEl;
16148		evt.item = targetEl || rootEl;
16149		evt.clone = cloneEl;
16150		evt.oldIndex = oldIndex;
16151		evt.newIndex = newIndex;
16152		evt.oldDraggableIndex = oldDraggableIndex;
16153		evt.newDraggableIndex = newDraggableIndex;
16154		evt.originalEvent = originalEvent;
16155		evt.pullMode = putSortable ? putSortable.lastPutMode : undefined;
16156		var allEventProperties = _objectSpread2( _objectSpread2( {}, extraEventProperties ), PluginManager.getEventProperties( name, sortable ) );
16157		for ( var option in allEventProperties ) {
16158			evt[ option ] = allEventProperties[ option ];
16159		}
16160		if ( rootEl ) {
16161			rootEl.dispatchEvent( evt );
16162		}
16163		if ( options[ onName ] ) {
16164			options[ onName ].call( sortable, evt );
16165		}
16166	}
16167
16168	function _dispatchEvent( info ) {
16169		dispatchEvent( _objectSpread2( {
16170			putSortable: putSortable,
16171			cloneEl: cloneEl,
16172			targetEl: dragEl,
16173			rootEl: rootEl,
16174			oldIndex: oldIndex,
16175			oldDraggableIndex: oldDraggableIndex,
16176			newIndex: newIndex,
16177			newDraggableIndex: newDraggableIndex
16178		}, info ) );
16179	}
16180
16181	// #1184 fix - Prevent click event on fallback if dragged but item not changed position
16182	if ( documentExists && ! ChromeForAndroid ) {
16183		document.addEventListener( 'click', function( evt ) {
16184			if ( ignoreNextClick ) {
16185				evt.preventDefault();
16186				evt.stopPropagation && evt.stopPropagation();
16187				evt.stopImmediatePropagation && evt.stopImmediatePropagation();
16188				ignoreNextClick = false;
16189				return false;
16190			}
16191		}, true );
16192	}
16193
16194	/**
16195	 * @class  Sortable
16196	 * @param {HTMLElement} el
16197	 * @param {Object}      [options]
16198	 */
16199	function Sortable( el, options ) {
16200		var _defaults;
16201		var name;
16202		var fn;
16203		if ( ! ( el && el.nodeType && el.nodeType === 1 ) ) {
16204			throw 'Sortable: `el` must be an HTMLElement, not '.concat( {}.toString.call( el ) );
16205		}
16206		this.el = el; // root element
16207		this.options = options = _extends( {}, options );
16208
16209		// Export instance
16210		el[ expando ] = this;
16211		_defaults = {
16212			group: null,
16213			sort: true,
16214			disabled: false,
16215			store: null,
16216			handle: null,
16217			draggable: /^[uo]l$/i.test( el.nodeName ) ? '>li' : '>*',
16218			swapThreshold: 1,
16219			// percentage; 0 <= x <= 1
16220			invertSwap: false,
16221			// invert always
16222			invertedSwapThreshold: null,
16223			// will be set to same as swapThreshold if default
16224			removeCloneOnHide: true,
16225			direction: function direction() {
16226				return _detectDirection( el, this.options );
16227			},
16228			ghostClass: 'sortable-ghost',
16229			chosenClass: 'sortable-chosen',
16230			dragClass: 'sortable-drag',
16231			ignore: 'a, img',
16232			filter: null,
16233			preventOnFilter: true,
16234			animation: 0,
16235			easing: null,
16236			setData: function setData( dataTransfer, dragEl ) {
16237				dataTransfer.setData( 'Text', dragEl.textContent );
16238			},
16239			dropBubble: false,
16240			dragoverBubble: false,
16241			dataIdAttr: 'data-id',
16242			delay: 0,
16243			delayOnTouchOnly: false,
16244			touchStartThreshold: ( Number.parseInt ? Number : window ).parseInt( window.devicePixelRatio, 10 ) || 1,
16245			forceFallback: false,
16246			fallbackClass: 'sortable-fallback',
16247			fallbackOnBody: false,
16248			fallbackTolerance: 0,
16249			fallbackOffset: {
16250				x: 0,
16251				y: 0
16252			},
16253			// Disabled on Safari: #1571; Enabled on Safari IOS: #2244
16254			supportPointer: Sortable.supportPointer !== false && 'PointerEvent' in window && ( ! Safari || IOS ),
16255			emptyInsertThreshold: 5
16256		};
16257		PluginManager.initializePlugins( this, el, _defaults );
16258
16259		// Set default options
16260		for ( name in _defaults ) {
16261			if ( ! ( name in options ) ) {
16262				options[ name ] = _defaults[ name ];
16263			}
16264		}
16265		_prepareGroup( options );
16266
16267		// Bind all private methods
16268		for ( fn in this ) {
16269			if ( fn.charAt( 0 ) === '_' && typeof this[ fn ] === 'function' ) {
16270				this[ fn ] = this[ fn ].bind( this );
16271			}
16272		}
16273
16274		// Setup drag mode
16275		this.nativeDraggable = options.forceFallback ? false : supportDraggable;
16276		if ( this.nativeDraggable ) {
16277			// Touch start threshold cannot be greater than the native dragstart threshold
16278			this.options.touchStartThreshold = 1;
16279		}
16280
16281		// Bind events
16282		if ( options.supportPointer ) {
16283			on( el, 'pointerdown', this._onTapStart );
16284		} else {
16285			on( el, 'mousedown', this._onTapStart );
16286			on( el, 'touchstart', this._onTapStart );
16287		}
16288		if ( this.nativeDraggable ) {
16289			on( el, 'dragover', this );
16290			on( el, 'dragenter', this );
16291		}
16292		sortables.push( this.el );
16293
16294		// Restore sorting
16295		if ( options.store && options.store.get ) {
16296			this.sort( options.store.get( this ) || [] );
16297		}
16298
16299		// Add animation state manager
16300		_extends( this, AnimationStateManager() );
16301	}
16302	Sortable.prototype = /** @lends Sortable.prototype */{
16303		constructor: Sortable,
16304		_isOutsideThisEl: function _isOutsideThisEl( target ) {
16305			if ( ! this.el.contains( target ) && target !== this.el ) {
16306				lastTarget = null;
16307			}
16308		},
16309		_getDirection: function _getDirection( evt, target ) {
16310			return typeof this.options.direction === 'function' ? this.options.direction.call( this, evt, target, dragEl ) : this.options.direction;
16311		},
16312		_onTapStart: function _onTapStart( /** Event|TouchEvent */evt ) {
16313			if ( ! evt.cancelable ) {
16314				return;
16315			}
16316			var _this = this,
16317				el = this.el,
16318				options = this.options,
16319				preventOnFilter = options.preventOnFilter,
16320				type = evt.type,
16321				touch = evt.touches && evt.touches[ 0 ] || evt.pointerType && evt.pointerType === 'touch' && evt,
16322				target = ( touch || evt ).target,
16323				originalTarget = evt.target.shadowRoot && ( evt.path && evt.path[ 0 ] || evt.composedPath && evt.composedPath()[ 0 ] ) || target,
16324				filter = options.filter;
16325			_saveInputCheckedState( el );
16326
16327			// Don't trigger start event when an element is been dragged, otherwise the evt.oldindex always wrong when set option.group.
16328			if ( dragEl ) {
16329				return;
16330			}
16331			if ( /mousedown|pointerdown/.test( type ) && evt.button !== 0 || options.disabled ) {
16332				return; // only left button and enabled
16333			}
16334
16335			// cancel dnd if original target is content editable
16336			if ( originalTarget.isContentEditable ) {
16337				return;
16338			}
16339
16340			// Safari ignores further event handling after mousedown
16341			if ( ! this.nativeDraggable && Safari && target && target.tagName.toUpperCase() === 'SELECT' ) {
16342				return;
16343			}
16344			target = closest( target, options.draggable, el, false );
16345			if ( target && target.animated ) {
16346				return;
16347			}
16348			if ( lastDownEl === target ) {
16349				// Ignoring duplicate `down`
16350				return;
16351			}
16352
16353			// Get the index of the dragged element within its parent
16354			oldIndex = index( target );
16355			oldDraggableIndex = index( target, options.draggable );
16356
16357			// Check filter
16358			if ( typeof filter === 'function' ) {
16359				if ( filter.call( this, evt, target, this ) ) {
16360					_dispatchEvent( {
16361						sortable: _this,
16362						rootEl: originalTarget,
16363						name: 'filter',
16364						targetEl: target,
16365						toEl: el,
16366						fromEl: el
16367					} );
16368					pluginEvent( 'filter', _this, {
16369						evt: evt
16370					} );
16371					preventOnFilter && evt.preventDefault();
16372					return; // cancel dnd
16373				}
16374			} else if ( filter ) {
16375				filter = filter.split( ',' ).some( function( criteria ) {
16376					criteria = closest( originalTarget, criteria.trim(), el, false );
16377					if ( criteria ) {
16378						_dispatchEvent( {
16379							sortable: _this,
16380							rootEl: criteria,
16381							name: 'filter',
16382							targetEl: target,
16383							fromEl: el,
16384							toEl: el
16385						} );
16386						pluginEvent( 'filter', _this, {
16387							evt: evt
16388						} );
16389						return true;
16390					}
16391				} );
16392				if ( filter ) {
16393					preventOnFilter && evt.preventDefault();
16394					return; // cancel dnd
16395				}
16396			}
16397			if ( options.handle && ! closest( originalTarget, options.handle, el, false ) ) {
16398				return;
16399			}
16400
16401			// Prepare `dragstart`
16402			this._prepareDragStart( evt, touch, target );
16403		},
16404		_prepareDragStart: function _prepareDragStart( /** Event */evt, /** Touch */touch, /** HTMLElement */target ) {
16405			var _this = this,
16406				el = _this.el,
16407				options = _this.options,
16408				ownerDocument = el.ownerDocument,
16409				dragStartFn;
16410			if ( target && ! dragEl && target.parentNode === el ) {
16411				var dragRect = getRect( target );
16412				rootEl = el;
16413				dragEl = target;
16414				parentEl = dragEl.parentNode;
16415				nextEl = dragEl.nextSibling;
16416				lastDownEl = target;
16417				activeGroup = options.group;
16418				Sortable.dragged = dragEl;
16419				tapEvt = {
16420					target: dragEl,
16421					clientX: ( touch || evt ).clientX,
16422					clientY: ( touch || evt ).clientY
16423				};
16424				tapDistanceLeft = tapEvt.clientX - dragRect.left;
16425				tapDistanceTop = tapEvt.clientY - dragRect.top;
16426				this._lastX = ( touch || evt ).clientX;
16427				this._lastY = ( touch || evt ).clientY;
16428				dragEl.style[ 'will-change' ] = 'all';
16429				dragStartFn = function dragStartFn() {
16430					pluginEvent( 'delayEnded', _this, {
16431						evt: evt
16432					} );
16433					if ( Sortable.eventCanceled ) {
16434						_this._onDrop();
16435						return;
16436					}
16437					// Delayed drag has been triggered
16438					// we can re-enable the events: touchmove/mousemove
16439					_this._disableDelayedDragEvents();
16440					if ( ! FireFox && _this.nativeDraggable ) {
16441						dragEl.draggable = true;
16442					}
16443
16444					// Bind the events: dragstart/dragend
16445					_this._triggerDragStart( evt, touch );
16446
16447					// Drag start event
16448					_dispatchEvent( {
16449						sortable: _this,
16450						name: 'choose',
16451						originalEvent: evt
16452					} );
16453
16454					// Chosen item
16455					toggleClass( dragEl, options.chosenClass, true );
16456				};
16457
16458				// Disable "draggable"
16459				options.ignore.split( ',' ).forEach( function( criteria ) {
16460					find( dragEl, criteria.trim(), _disableDraggable );
16461				} );
16462				on( ownerDocument, 'dragover', nearestEmptyInsertDetectEvent );
16463				on( ownerDocument, 'mousemove', nearestEmptyInsertDetectEvent );
16464				on( ownerDocument, 'touchmove', nearestEmptyInsertDetectEvent );
16465				if ( options.supportPointer ) {
16466					on( ownerDocument, 'pointerup', _this._onDrop );
16467					// Native D&D triggers pointercancel
16468					! this.nativeDraggable && on( ownerDocument, 'pointercancel', _this._onDrop );
16469				} else {
16470					on( ownerDocument, 'mouseup', _this._onDrop );
16471					on( ownerDocument, 'touchend', _this._onDrop );
16472					on( ownerDocument, 'touchcancel', _this._onDrop );
16473				}
16474
16475				// Make dragEl draggable (must be before delay for FireFox)
16476				if ( FireFox && this.nativeDraggable ) {
16477					this.options.touchStartThreshold = 4;
16478					dragEl.draggable = true;
16479				}
16480				pluginEvent( 'delayStart', this, {
16481					evt: evt
16482				} );
16483
16484				// Delay is impossible for native DnD in Edge or IE
16485				if ( options.delay && ( ! options.delayOnTouchOnly || touch ) && ( ! this.nativeDraggable || ! ( Edge || IE11OrLess ) ) ) {
16486					if ( Sortable.eventCanceled ) {
16487						this._onDrop();
16488						return;
16489					}
16490					// If the user moves the pointer or let go the click or touch
16491					// before the delay has been reached:
16492					// disable the delayed drag
16493					if ( options.supportPointer ) {
16494						on( ownerDocument, 'pointerup', _this._disableDelayedDrag );
16495						on( ownerDocument, 'pointercancel', _this._disableDelayedDrag );
16496					} else {
16497						on( ownerDocument, 'mouseup', _this._disableDelayedDrag );
16498						on( ownerDocument, 'touchend', _this._disableDelayedDrag );
16499						on( ownerDocument, 'touchcancel', _this._disableDelayedDrag );
16500					}
16501					on( ownerDocument, 'mousemove', _this._delayedDragTouchMoveHandler );
16502					on( ownerDocument, 'touchmove', _this._delayedDragTouchMoveHandler );
16503					options.supportPointer && on( ownerDocument, 'pointermove', _this._delayedDragTouchMoveHandler );
16504					_this._dragStartTimer = setTimeout( dragStartFn, options.delay );
16505				} else {
16506					dragStartFn();
16507				}
16508			}
16509		},
16510		_delayedDragTouchMoveHandler: function _delayedDragTouchMoveHandler( /** TouchEvent|PointerEvent */e ) {
16511			var touch = e.touches ? e.touches[ 0 ] : e;
16512			if ( Math.max( Math.abs( touch.clientX - this._lastX ), Math.abs( touch.clientY - this._lastY ) ) >= Math.floor( this.options.touchStartThreshold / ( this.nativeDraggable && window.devicePixelRatio || 1 ) ) ) {
16513				this._disableDelayedDrag();
16514			}
16515		},
16516		_disableDelayedDrag: function _disableDelayedDrag() {
16517			dragEl && _disableDraggable( dragEl );
16518			clearTimeout( this._dragStartTimer );
16519			this._disableDelayedDragEvents();
16520		},
16521		_disableDelayedDragEvents: function _disableDelayedDragEvents() {
16522			var ownerDocument = this.el.ownerDocument;
16523			off( ownerDocument, 'mouseup', this._disableDelayedDrag );
16524			off( ownerDocument, 'touchend', this._disableDelayedDrag );
16525			off( ownerDocument, 'touchcancel', this._disableDelayedDrag );
16526			off( ownerDocument, 'pointerup', this._disableDelayedDrag );
16527			off( ownerDocument, 'pointercancel', this._disableDelayedDrag );
16528			off( ownerDocument, 'mousemove', this._delayedDragTouchMoveHandler );
16529			off( ownerDocument, 'touchmove', this._delayedDragTouchMoveHandler );
16530			off( ownerDocument, 'pointermove', this._delayedDragTouchMoveHandler );
16531		},
16532		_triggerDragStart: function _triggerDragStart( /** Event */evt, /** Touch */touch ) {
16533			touch = touch || evt.pointerType == 'touch' && evt;
16534			if ( ! this.nativeDraggable || touch ) {
16535				if ( this.options.supportPointer ) {
16536					on( document, 'pointermove', this._onTouchMove );
16537				} else if ( touch ) {
16538					on( document, 'touchmove', this._onTouchMove );
16539				} else {
16540					on( document, 'mousemove', this._onTouchMove );
16541				}
16542			} else {
16543				on( dragEl, 'dragend', this );
16544				on( rootEl, 'dragstart', this._onDragStart );
16545			}
16546			try {
16547				if ( document.selection ) {
16548					_nextTick( function() {
16549						document.selection.empty();
16550					} );
16551				} else {
16552					window.getSelection().removeAllRanges();
16553				}
16554			} catch ( err ) {}
16555		},
16556		_dragStarted: function _dragStarted( fallback, evt ) {
16557			awaitingDragStarted = false;
vendor: 5,416 bytes, lines 16558-16701
16558			if ( rootEl && dragEl ) {
16559				pluginEvent( 'dragStarted', this, {
16560					evt: evt
16561				} );
16562				if ( this.nativeDraggable ) {
16563					on( document, 'dragover', _checkOutsideTargetEl );
16564				}
16565				var options = this.options;
16566
16567				// Apply effect
16568				! fallback && toggleClass( dragEl, options.dragClass, false );
16569				toggleClass( dragEl, options.ghostClass, true );
16570				Sortable.active = this;
16571				fallback && this._appendGhost();
16572
16573				// Drag start event
16574				_dispatchEvent( {
16575					sortable: this,
16576					name: 'start',
16577					originalEvent: evt
16578				} );
16579			} else {
16580				this._nulling();
16581			}
16582		},
16583		_emulateDragOver: function _emulateDragOver() {
16584			if ( touchEvt ) {
16585				this._lastX = touchEvt.clientX;
16586				this._lastY = touchEvt.clientY;
16587				_hideGhostForTarget();
16588				var target = document.elementFromPoint( touchEvt.clientX, touchEvt.clientY );
16589				var parent = target;
16590				while ( target && target.shadowRoot ) {
16591					target = target.shadowRoot.elementFromPoint( touchEvt.clientX, touchEvt.clientY );
16592					if ( target === parent ) {
16593						break;
16594					}
16595					parent = target;
16596				}
16597				dragEl.parentNode[ expando ]._isOutsideThisEl( target );
16598				if ( parent ) {
16599					do {
16600						if ( parent[ expando ] ) {
16601							var inserted = void 0;
16602							inserted = parent[ expando ]._onDragOver( {
16603								clientX: touchEvt.clientX,
16604								clientY: touchEvt.clientY,
16605								target: target,
16606								rootEl: parent
16607							} );
16608							if ( inserted && ! this.options.dragoverBubble ) {
16609								break;
16610							}
16611						}
16612						target = parent; // store last element
16613					}
16614					while ( ( parent = getParentOrHost( parent ) ) );
16615				}
16616				_unhideGhostForTarget();
16617			}
16618		},
16619		_onTouchMove: function _onTouchMove( /**TouchEvent*/evt ) {
16620			if ( tapEvt ) {
16621				var options = this.options,
16622					fallbackTolerance = options.fallbackTolerance,
16623					fallbackOffset = options.fallbackOffset,
16624					touch = evt.touches ? evt.touches[ 0 ] : evt,
16625					ghostMatrix = ghostEl && matrix( ghostEl, true ),
16626					scaleX = ghostEl && ghostMatrix && ghostMatrix.a,
16627					scaleY = ghostEl && ghostMatrix && ghostMatrix.d,
16628					relativeScrollOffset = PositionGhostAbsolutely && ghostRelativeParent && getRelativeScrollOffset( ghostRelativeParent ),
16629					dx = ( touch.clientX - tapEvt.clientX + fallbackOffset.x ) / ( scaleX || 1 ) + ( relativeScrollOffset ? relativeScrollOffset[ 0 ] - ghostRelativeParentInitialScroll[ 0 ] : 0 ) / ( scaleX || 1 ),
16630					dy = ( touch.clientY - tapEvt.clientY + fallbackOffset.y ) / ( scaleY || 1 ) + ( relativeScrollOffset ? relativeScrollOffset[ 1 ] - ghostRelativeParentInitialScroll[ 1 ] : 0 ) / ( scaleY || 1 );
16631
16632				// only set the status to dragging, when we are actually dragging
16633				if ( ! Sortable.active && ! awaitingDragStarted ) {
16634					if ( fallbackTolerance && Math.max( Math.abs( touch.clientX - this._lastX ), Math.abs( touch.clientY - this._lastY ) ) < fallbackTolerance ) {
16635						return;
16636					}
16637					this._onDragStart( evt, true );
16638				}
16639				if ( ghostEl ) {
16640					if ( ghostMatrix ) {
16641						ghostMatrix.e += dx - ( lastDx || 0 );
16642						ghostMatrix.f += dy - ( lastDy || 0 );
16643					} else {
16644						ghostMatrix = {
16645							a: 1,
16646							b: 0,
16647							c: 0,
16648							d: 1,
16649							e: dx,
16650							f: dy
16651						};
16652					}
16653					var cssMatrix = 'matrix('.concat( ghostMatrix.a, ',' ).concat( ghostMatrix.b, ',' ).concat( ghostMatrix.c, ',' ).concat( ghostMatrix.d, ',' ).concat( ghostMatrix.e, ',' ).concat( ghostMatrix.f, ')' );
16654					css( ghostEl, 'webkitTransform', cssMatrix );
16655					css( ghostEl, 'mozTransform', cssMatrix );
16656					css( ghostEl, 'msTransform', cssMatrix );
16657					css( ghostEl, 'transform', cssMatrix );
16658					lastDx = dx;
16659					lastDy = dy;
16660					touchEvt = touch;
16661				}
16662				evt.cancelable && evt.preventDefault();
16663			}
16664		},
16665		_appendGhost: function _appendGhost() {
16666			// Bug if using scale(): https://stackoverflow.com/questions/2637058
16667			// Not being adjusted for
16668			if ( ! ghostEl ) {
16669				var container = this.options.fallbackOnBody ? document.body : rootEl,
16670					rect = getRect( dragEl, true, PositionGhostAbsolutely, true, container ),
16671					options = this.options;
16672
16673				// Position absolutely
16674				if ( PositionGhostAbsolutely ) {
16675					// Get relatively positioned parent
16676					ghostRelativeParent = container;
16677					while ( css( ghostRelativeParent, 'position' ) === 'static' && css( ghostRelativeParent, 'transform' ) === 'none' && ghostRelativeParent !== document ) {
16678						ghostRelativeParent = ghostRelativeParent.parentNode;
16679					}
16680					if ( ghostRelativeParent !== document.body && ghostRelativeParent !== document.documentElement ) {
16681						if ( ghostRelativeParent === document ) {
16682							ghostRelativeParent = getWindowScrollingElement();
16683						}
16684						rect.top += ghostRelativeParent.scrollTop;
16685						rect.left += ghostRelativeParent.scrollLeft;
16686					} else {
16687						ghostRelativeParent = getWindowScrollingElement();
16688					}
16689					ghostRelativeParentInitialScroll = getRelativeScrollOffset( ghostRelativeParent );
16690				}
16691				ghostEl = dragEl.cloneNode( true );
16692				toggleClass( ghostEl, options.ghostClass, false );
16693				toggleClass( ghostEl, options.fallbackClass, true );
16694				toggleClass( ghostEl, options.dragClass, true );
16695				css( ghostEl, 'transition', '' );
16696				css( ghostEl, 'transform', '' );
16697				css( ghostEl, 'box-sizing', 'border-box' );
16698				css( ghostEl, 'margin', 0 );
16699				css( ghostEl, 'top', rect.top );
16700				css( ghostEl, 'left', rect.left );
16701				css( ghostEl, 'width', rect.width );
vendor: 4,303 bytes, lines 16702-16839
16702				css( ghostEl, 'height', rect.height );
16703				css( ghostEl, 'opacity', '0.8' );
16704				css( ghostEl, 'position', PositionGhostAbsolutely ? 'absolute' : 'fixed' );
16705				css( ghostEl, 'zIndex', '100000' );
16706				css( ghostEl, 'pointerEvents', 'none' );
16707				Sortable.ghost = ghostEl;
16708				container.appendChild( ghostEl );
16709
16710				// Set transform-origin
16711				css( ghostEl, 'transform-origin', ( tapDistanceLeft / parseInt( ghostEl.style.width, 10 ) * 100 ) + '% ' + ( tapDistanceTop / parseInt( ghostEl.style.height, 10 ) * 100 ) + '%' );
16712			}
16713		},
16714		_onDragStart: function _onDragStart( /**Event*/evt, /**boolean*/fallback ) {
16715			var _this = this;
16716			var dataTransfer = evt.dataTransfer;
16717			var options = _this.options;
16718			pluginEvent( 'dragStart', this, {
16719				evt: evt
16720			} );
16721			if ( Sortable.eventCanceled ) {
16722				this._onDrop();
16723				return;
16724			}
16725			pluginEvent( 'setupClone', this );
16726			if ( ! Sortable.eventCanceled ) {
16727				cloneEl = clone( dragEl );
16728				cloneEl.removeAttribute( 'id' );
16729				cloneEl.draggable = false;
16730				cloneEl.style[ 'will-change' ] = '';
16731				this._hideClone();
16732				toggleClass( cloneEl, this.options.chosenClass, false );
16733				Sortable.clone = cloneEl;
16734			}
16735
16736			// #1143: IFrame support workaround
16737			_this.cloneId = _nextTick( function() {
16738				pluginEvent( 'clone', _this );
16739				if ( Sortable.eventCanceled ) {
16740					return;
16741				}
16742				if ( ! _this.options.removeCloneOnHide ) {
16743					rootEl.insertBefore( cloneEl, dragEl );
16744				}
16745				_this._hideClone();
16746				_dispatchEvent( {
16747					sortable: _this,
16748					name: 'clone'
16749				} );
16750			} );
16751			! fallback && toggleClass( dragEl, options.dragClass, true );
16752
16753			// Set proper drop events
16754			if ( fallback ) {
16755				ignoreNextClick = true;
16756				_this._loopId = setInterval( _this._emulateDragOver, 50 );
16757			} else {
16758				// Undo what was set in _prepareDragStart before drag started
16759				off( document, 'mouseup', _this._onDrop );
16760				off( document, 'touchend', _this._onDrop );
16761				off( document, 'touchcancel', _this._onDrop );
16762				if ( dataTransfer ) {
16763					dataTransfer.effectAllowed = 'move';
16764					options.setData && options.setData.call( _this, dataTransfer, dragEl );
16765				}
16766				on( document, 'drop', _this );
16767
16768				// #1276 fix:
16769				css( dragEl, 'transform', 'translateZ(0)' );
16770			}
16771			awaitingDragStarted = true;
16772			_this._dragStartId = _nextTick( _this._dragStarted.bind( _this, fallback, evt ) );
16773			on( document, 'selectstart', _this );
16774			moved = true;
16775			window.getSelection().removeAllRanges();
16776			if ( Safari ) {
16777				css( document.body, 'user-select', 'none' );
16778			}
16779		},
16780		// Returns true - if no further action is needed (either inserted or another condition)
16781		_onDragOver: function _onDragOver( /**Event*/evt ) {
16782			var el = this.el,
16783				target = evt.target,
16784				dragRect,
16785				targetRect,
16786				revert,
16787				options = this.options,
16788				group = options.group,
16789				activeSortable = Sortable.active,
16790				isOwner = activeGroup === group,
16791				canSort = options.sort,
16792				fromSortable = putSortable || activeSortable,
16793				vertical,
16794				_this = this,
16795				completedFired = false;
16796			if ( _silent ) {
16797				return;
16798			}
16799			function dragOverEvent( name, extra ) {
16800				pluginEvent( name, _this, _objectSpread2( {
16801					evt: evt,
16802					isOwner: isOwner,
16803					axis: vertical ? 'vertical' : 'horizontal',
16804					revert: revert,
16805					dragRect: dragRect,
16806					targetRect: targetRect,
16807					canSort: canSort,
16808					fromSortable: fromSortable,
16809					target: target,
16810					completed: completed,
16811					onMove: function onMove( target, after ) {
16812						return _onMove( rootEl, el, dragEl, dragRect, target, getRect( target ), evt, after );
16813					},
16814					changed: changed
16815				}, extra ) );
16816			}
16817
16818			// Capture animation state
16819			function capture() {
16820				dragOverEvent( 'dragOverAnimationCapture' );
16821				_this.captureAnimationState();
16822				if ( _this !== fromSortable ) {
16823					fromSortable.captureAnimationState();
16824				}
16825			}
16826
16827			// Return invocation when dragEl is inserted (or completed)
16828			function completed( insertion ) {
16829				dragOverEvent( 'dragOverCompleted', {
16830					insertion: insertion
16831				} );
16832				if ( insertion ) {
16833					// Clones must be hidden before folding animation to capture dragRectAbsolute properly
16834					if ( isOwner ) {
16835						activeSortable._hideClone();
16836					} else {
16837						activeSortable._showClone( _this );
16838					}
16839					if ( _this !== fromSortable ) {
vendor: 5,070 bytes, lines 16840-16979
16840						// Set ghost class to new sortable's ghost class
16841						toggleClass( dragEl, putSortable ? putSortable.options.ghostClass : activeSortable.options.ghostClass, false );
16842						toggleClass( dragEl, options.ghostClass, true );
16843					}
16844					if ( putSortable !== _this && _this !== Sortable.active ) {
16845						putSortable = _this;
16846					} else if ( _this === Sortable.active && putSortable ) {
16847						putSortable = null;
16848					}
16849
16850					// Animation
16851					if ( fromSortable === _this ) {
16852						_this._ignoreWhileAnimating = target;
16853					}
16854					_this.animateAll( function() {
16855						dragOverEvent( 'dragOverAnimationComplete' );
16856						_this._ignoreWhileAnimating = null;
16857					} );
16858					if ( _this !== fromSortable ) {
16859						fromSortable.animateAll();
16860						fromSortable._ignoreWhileAnimating = null;
16861					}
16862				}
16863
16864				// Null lastTarget if it is not inside a previously swapped element
16865				if ( target === dragEl && ! dragEl.animated || target === el && ! target.animated ) {
16866					lastTarget = null;
16867				}
16868
16869				// no bubbling and not fallback
16870				if ( ! options.dragoverBubble && ! evt.rootEl && target !== document ) {
16871					dragEl.parentNode[ expando ]._isOutsideThisEl( evt.target );
16872
16873					// Do not detect for empty insert if already inserted
16874					! insertion && nearestEmptyInsertDetectEvent( evt );
16875				}
16876				! options.dragoverBubble && evt.stopPropagation && evt.stopPropagation();
16877				return completedFired = true;
16878			}
16879
16880			// Call when dragEl has been inserted
16881			function changed() {
16882				newIndex = index( dragEl );
16883				newDraggableIndex = index( dragEl, options.draggable );
16884				_dispatchEvent( {
16885					sortable: _this,
16886					name: 'change',
16887					toEl: el,
16888					newIndex: newIndex,
16889					newDraggableIndex: newDraggableIndex,
16890					originalEvent: evt
16891				} );
16892			}
16893			if ( evt.preventDefault !== void 0 ) {
16894				evt.cancelable && evt.preventDefault();
16895			}
16896			target = closest( target, options.draggable, el, true );
16897			dragOverEvent( 'dragOver' );
16898			if ( Sortable.eventCanceled ) {
16899				return completedFired;
16900			}
16901			if ( dragEl.contains( evt.target ) || target.animated && target.animatingX && target.animatingY || _this._ignoreWhileAnimating === target ) {
16902				return completed( false );
16903			}
16904			ignoreNextClick = false;
16905			if ( activeSortable && ! options.disabled && ( isOwner ? canSort || ( revert = parentEl !== rootEl ) // Reverting item into the original list
16906				: putSortable === this || ( this.lastPutMode = activeGroup.checkPull( this, activeSortable, dragEl, evt ) ) && group.checkPut( this, activeSortable, dragEl, evt ) ) ) {
16907				vertical = this._getDirection( evt, target ) === 'vertical';
16908				dragRect = getRect( dragEl );
16909				dragOverEvent( 'dragOverValid' );
16910				if ( Sortable.eventCanceled ) {
16911					return completedFired;
16912				}
16913				if ( revert ) {
16914					parentEl = rootEl; // actualization
16915					capture();
16916					this._hideClone();
16917					dragOverEvent( 'revert' );
16918					if ( ! Sortable.eventCanceled ) {
16919						if ( nextEl ) {
16920							rootEl.insertBefore( dragEl, nextEl );
16921						} else {
16922							rootEl.appendChild( dragEl );
16923						}
16924					}
16925					return completed( true );
16926				}
16927				var elLastChild = lastChild( el, options.draggable );
16928				if ( ! elLastChild || _ghostIsLast( evt, vertical, this ) && ! elLastChild.animated ) {
16929					// Insert to end of list
16930
16931					// If already at end of list: Do not insert
16932					if ( elLastChild === dragEl ) {
16933						return completed( false );
16934					}
16935
16936					// if there is a last element, it is the target
16937					if ( elLastChild && el === evt.target ) {
16938						target = elLastChild;
16939					}
16940					if ( target ) {
16941						targetRect = getRect( target );
16942					}
16943					if ( _onMove( rootEl, el, dragEl, dragRect, target, targetRect, evt, !! target ) !== false ) {
16944						capture();
16945						if ( elLastChild && elLastChild.nextSibling ) {
16946							// the last draggable element is not the last node
16947							el.insertBefore( dragEl, elLastChild.nextSibling );
16948						} else {
16949							el.appendChild( dragEl );
16950						}
16951						parentEl = el; // actualization
16952
16953						changed();
16954						return completed( true );
16955					}
16956				} else if ( elLastChild && _ghostIsFirst( evt, vertical, this ) ) {
16957					// Insert to start of list
16958					var firstChild = getChild( el, 0, options, true );
16959					if ( firstChild === dragEl ) {
16960						return completed( false );
16961					}
16962					target = firstChild;
16963					targetRect = getRect( target );
16964					if ( _onMove( rootEl, el, dragEl, dragRect, target, targetRect, evt, false ) !== false ) {
16965						capture();
16966						el.insertBefore( dragEl, firstChild );
16967						parentEl = el; // actualization
16968
16969						changed();
16970						return completed( true );
16971					}
16972				} else if ( target.parentNode === el ) {
16973					targetRect = getRect( target );
16974					var direction = 0,
16975						targetBeforeFirstSwap,
16976						differentLevel = dragEl.parentNode !== el,
16977						differentRowCol = ! _dragElInRowColumn( dragEl.animated && dragEl.toRect || dragRect, target.animated && target.toRect || targetRect, vertical ),
16978						side1 = vertical ? 'top' : 'left',
16979						scrolledPastTop = isScrolledPast( target, 'top', 'top' ) || isScrolledPast( dragEl, 'top', 'top' ),
vendor: 1,709 bytes, lines 16980-17018
16980						scrollBefore = scrolledPastTop ? scrolledPastTop.scrollTop : void 0;
16981					if ( lastTarget !== target ) {
16982						targetBeforeFirstSwap = targetRect[ side1 ];
16983						pastFirstInvertThresh = false;
16984						isCircumstantialInvert = ! differentRowCol && options.invertSwap || differentLevel;
16985					}
16986					direction = _getSwapDirection( evt, target, targetRect, vertical, differentRowCol ? 1 : options.swapThreshold, options.invertedSwapThreshold == null ? options.swapThreshold : options.invertedSwapThreshold, isCircumstantialInvert, lastTarget === target );
16987					var sibling;
16988					if ( direction !== 0 ) {
16989						// Check if target is beside dragEl in respective direction (ignoring hidden elements)
16990						var dragIndex = index( dragEl );
16991						do {
16992							dragIndex -= direction;
16993							sibling = parentEl.children[ dragIndex ];
16994						} while ( sibling && ( css( sibling, 'display' ) === 'none' || sibling === ghostEl ) );
16995					}
16996					// If dragEl is already beside target: Do not insert
16997					if ( direction === 0 || sibling === target ) {
16998						return completed( false );
16999					}
17000					lastTarget = target;
17001					lastDirection = direction;
17002					var nextSibling = target.nextElementSibling,
17003						after = false;
17004					after = direction === 1;
17005					var moveVector = _onMove( rootEl, el, dragEl, dragRect, target, targetRect, evt, after );
17006					if ( moveVector !== false ) {
17007						if ( moveVector === 1 || moveVector === -1 ) {
17008							after = moveVector === 1;
17009						}
17010						_silent = true;
17011						setTimeout( _unsilent, 30 );
17012						capture();
17013						if ( after && ! nextSibling ) {
17014							el.appendChild( dragEl );
17015						} else {
17016							target.parentNode.insertBefore( dragEl, after ? nextSibling : target );
17017						}
17018
vendor: 5,178 bytes, lines 17019-17196
17019						// Undo chrome's scroll adjustment (has no effect on other browsers)
17020						if ( scrolledPastTop ) {
17021							scrollBy( scrolledPastTop, 0, scrollBefore - scrolledPastTop.scrollTop );
17022						}
17023						parentEl = dragEl.parentNode; // actualization
17024
17025						// must be done before animation
17026						if ( targetBeforeFirstSwap !== undefined && ! isCircumstantialInvert ) {
17027							targetMoveDistance = Math.abs( targetBeforeFirstSwap - getRect( target )[ side1 ] );
17028						}
17029						changed();
17030						return completed( true );
17031					}
17032				}
17033				if ( el.contains( dragEl ) ) {
17034					return completed( false );
17035				}
17036			}
17037			return false;
17038		},
17039		_ignoreWhileAnimating: null,
17040		_offMoveEvents: function _offMoveEvents() {
17041			off( document, 'mousemove', this._onTouchMove );
17042			off( document, 'touchmove', this._onTouchMove );
17043			off( document, 'pointermove', this._onTouchMove );
17044			off( document, 'dragover', nearestEmptyInsertDetectEvent );
17045			off( document, 'mousemove', nearestEmptyInsertDetectEvent );
17046			off( document, 'touchmove', nearestEmptyInsertDetectEvent );
17047		},
17048		_offUpEvents: function _offUpEvents() {
17049			var ownerDocument = this.el.ownerDocument;
17050			off( ownerDocument, 'mouseup', this._onDrop );
17051			off( ownerDocument, 'touchend', this._onDrop );
17052			off( ownerDocument, 'pointerup', this._onDrop );
17053			off( ownerDocument, 'pointercancel', this._onDrop );
17054			off( ownerDocument, 'touchcancel', this._onDrop );
17055			off( document, 'selectstart', this );
17056		},
17057		_onDrop: function _onDrop( /**Event*/evt ) {
17058			var el = this.el,
17059				options = this.options;
17060
17061			// Get the index of the dragged element within its parent
17062			newIndex = index( dragEl );
17063			newDraggableIndex = index( dragEl, options.draggable );
17064			pluginEvent( 'drop', this, {
17065				evt: evt
17066			} );
17067			parentEl = dragEl && dragEl.parentNode;
17068
17069			// Get again after plugin event
17070			newIndex = index( dragEl );
17071			newDraggableIndex = index( dragEl, options.draggable );
17072			if ( Sortable.eventCanceled ) {
17073				this._nulling();
17074				return;
17075			}
17076			awaitingDragStarted = false;
17077			isCircumstantialInvert = false;
17078			pastFirstInvertThresh = false;
17079			clearInterval( this._loopId );
17080			clearTimeout( this._dragStartTimer );
17081			_cancelNextTick( this.cloneId );
17082			_cancelNextTick( this._dragStartId );
17083
17084			// Unbind events
17085			if ( this.nativeDraggable ) {
17086				off( document, 'drop', this );
17087				off( el, 'dragstart', this._onDragStart );
17088			}
17089			this._offMoveEvents();
17090			this._offUpEvents();
17091			if ( Safari ) {
17092				css( document.body, 'user-select', '' );
17093			}
17094			css( dragEl, 'transform', '' );
17095			if ( evt ) {
17096				if ( moved ) {
17097					evt.cancelable && evt.preventDefault();
17098					! options.dropBubble && evt.stopPropagation();
17099				}
17100				ghostEl && ghostEl.parentNode && ghostEl.parentNode.removeChild( ghostEl );
17101				if ( rootEl === parentEl || putSortable && putSortable.lastPutMode !== 'clone' ) {
17102					// Remove clone(s)
17103					cloneEl && cloneEl.parentNode && cloneEl.parentNode.removeChild( cloneEl );
17104				}
17105				if ( dragEl ) {
17106					if ( this.nativeDraggable ) {
17107						off( dragEl, 'dragend', this );
17108					}
17109					_disableDraggable( dragEl );
17110					dragEl.style[ 'will-change' ] = '';
17111
17112					// Remove classes
17113					// ghostClass is added in dragStarted
17114					if ( moved && ! awaitingDragStarted ) {
17115						toggleClass( dragEl, putSortable ? putSortable.options.ghostClass : this.options.ghostClass, false );
17116					}
17117					toggleClass( dragEl, this.options.chosenClass, false );
17118
17119					// Drag stop event
17120					_dispatchEvent( {
17121						sortable: this,
17122						name: 'unchoose',
17123						toEl: parentEl,
17124						newIndex: null,
17125						newDraggableIndex: null,
17126						originalEvent: evt
17127					} );
17128					if ( rootEl !== parentEl ) {
17129						if ( newIndex >= 0 ) {
17130							// Add event
17131							_dispatchEvent( {
17132								rootEl: parentEl,
17133								name: 'add',
17134								toEl: parentEl,
17135								fromEl: rootEl,
17136								originalEvent: evt
17137							} );
17138
17139							// Remove event
17140							_dispatchEvent( {
17141								sortable: this,
17142								name: 'remove',
17143								toEl: parentEl,
17144								originalEvent: evt
17145							} );
17146
17147							// drag from one list and drop into another
17148							_dispatchEvent( {
17149								rootEl: parentEl,
17150								name: 'sort',
17151								toEl: parentEl,
17152								fromEl: rootEl,
17153								originalEvent: evt
17154							} );
17155							_dispatchEvent( {
17156								sortable: this,
17157								name: 'sort',
17158								toEl: parentEl,
17159								originalEvent: evt
17160							} );
17161						}
17162						putSortable && putSortable.save();
17163					} else if ( newIndex !== oldIndex ) {
17164						if ( newIndex >= 0 ) {
17165							// drag & drop within the same list
17166							_dispatchEvent( {
17167								sortable: this,
17168								name: 'update',
17169								toEl: parentEl,
17170								originalEvent: evt
17171							} );
17172							_dispatchEvent( {
17173								sortable: this,
17174								name: 'sort',
17175								toEl: parentEl,
17176								originalEvent: evt
17177							} );
17178						}
17179					}
17180					if ( Sortable.active ) {
17181						if ( newIndex == null || newIndex === -1 ) {
17182							newIndex = oldIndex;
17183							newDraggableIndex = oldDraggableIndex;
17184						}
17185						_dispatchEvent( {
17186							sortable: this,
17187							name: 'end',
17188							toEl: parentEl,
17189							originalEvent: evt
17190						} );
17191
17192						// Save sorting
17193						this.save();
17194					}
17195				}
17196			}
vendor: 4,930 bytes, lines 17197-17363
17197			this._nulling();
17198		},
17199		_nulling: function _nulling() {
17200			pluginEvent( 'nulling', this );
17201			rootEl = dragEl = parentEl = ghostEl = nextEl = cloneEl = lastDownEl = cloneHidden = tapEvt = touchEvt = moved = newIndex = newDraggableIndex = oldIndex = oldDraggableIndex = lastTarget = lastDirection = putSortable = activeGroup = Sortable.dragged = Sortable.ghost = Sortable.clone = Sortable.active = null;
17202			savedInputChecked.forEach( function( el ) {
17203				el.checked = true;
17204			} );
17205			savedInputChecked.length = lastDx = lastDy = 0;
17206		},
17207		handleEvent: function handleEvent( /**Event*/evt ) {
17208			switch ( evt.type ) {
17209				case 'drop':
17210				case 'dragend':
17211					this._onDrop( evt );
17212					break;
17213				case 'dragenter':
17214				case 'dragover':
17215					if ( dragEl ) {
17216						this._onDragOver( evt );
17217						_globalDragOver( evt );
17218					}
17219					break;
17220				case 'selectstart':
17221					evt.preventDefault();
17222					break;
17223			}
17224		},
17225		/**
17226		 * Serializes the item into an array of string.
17227		 * @returns {string[]}
17228		 */
17229		toArray: function toArray() {
17230			var order = [],
17231				el,
17232				children = this.el.children,
17233				i = 0,
17234				n = children.length,
17235				options = this.options;
17236			for ( ; i < n; i++ ) {
17237				el = children[ i ];
17238				if ( closest( el, options.draggable, this.el, false ) ) {
17239					order.push( el.getAttribute( options.dataIdAttr ) || _generateId( el ) );
17240				}
17241			}
17242			return order;
17243		},
17244		/**
17245		 * Sorts the elements according to the array.
17246		 * @param {string[]} order        Order of the items
17247		 * @param {boolean}  useAnimation Whether to use animations
17248		 */
17249		sort: function sort( order, useAnimation ) {
17250			var items = {},
17251				rootEl = this.el;
17252			this.toArray().forEach( function( id, i ) {
17253				var el = rootEl.children[ i ];
17254				if ( closest( el, this.options.draggable, rootEl, false ) ) {
17255					items[ id ] = el;
17256				}
17257			}, this );
17258			useAnimation && this.captureAnimationState();
17259			order.forEach( function( id ) {
17260				if ( items[ id ] ) {
17261					rootEl.removeChild( items[ id ] );
17262					rootEl.appendChild( items[ id ] );
17263				}
17264			} );
17265			useAnimation && this.animateAll();
17266		},
17267		/**
17268		 * Save the current sorting
17269		 */
17270		save: function save() {
17271			var store = this.options.store;
17272			store && store.set && store.set( this );
17273		},
17274		/**
17275		 * For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.
17276		 * @param {HTMLElement} el
17277		 * @param {string}      [selector] default: `options.draggable`
17278		 * @returns {HTMLElement|null}
17279		 */
17280		closest: function closest$1( el, selector ) {
17281			return closest( el, selector || this.options.draggable, this.el, false );
17282		},
17283		/**
17284		 * Set/get option
17285		 * @param {string} name
17286		 * @param {*}      [value]
17287		 * @returns {*}
17288		 */
17289		option: function option( name, value ) {
17290			var options = this.options;
17291			if ( value === void 0 ) {
17292				return options[ name ];
17293			}
17294			var modifiedValue = PluginManager.modifyOption( this, name, value );
17295			if ( typeof modifiedValue !== 'undefined' ) {
17296				options[ name ] = modifiedValue;
17297			} else {
17298				options[ name ] = value;
17299			}
17300			if ( name === 'group' ) {
17301				_prepareGroup( options );
17302			}
17303		},
17304		/**
17305		 * Destroy
17306		 */
17307		destroy: function destroy() {
17308			pluginEvent( 'destroy', this );
17309			var el = this.el;
17310			el[ expando ] = null;
17311			off( el, 'mousedown', this._onTapStart );
17312			off( el, 'touchstart', this._onTapStart );
17313			off( el, 'pointerdown', this._onTapStart );
17314			if ( this.nativeDraggable ) {
17315				off( el, 'dragover', this );
17316				off( el, 'dragenter', this );
17317			}
17318			// Remove draggable attributes
17319			Array.prototype.forEach.call( el.querySelectorAll( '[draggable]' ), function( el ) {
17320				el.removeAttribute( 'draggable' );
17321			} );
17322			this._onDrop();
17323			this._disableDelayedDragEvents();
17324			sortables.splice( sortables.indexOf( this.el ), 1 );
17325			this.el = el = null;
17326		},
17327		_hideClone: function _hideClone() {
17328			if ( ! cloneHidden ) {
17329				pluginEvent( 'hideClone', this );
17330				if ( Sortable.eventCanceled ) {
17331					return;
17332				}
17333				css( cloneEl, 'display', 'none' );
17334				if ( this.options.removeCloneOnHide && cloneEl.parentNode ) {
17335					cloneEl.parentNode.removeChild( cloneEl );
17336				}
17337				cloneHidden = true;
17338			}
17339		},
17340		_showClone: function _showClone( putSortable ) {
17341			if ( putSortable.lastPutMode !== 'clone' ) {
17342				this._hideClone();
17343				return;
17344			}
17345			if ( cloneHidden ) {
17346				pluginEvent( 'showClone', this );
17347				if ( Sortable.eventCanceled ) {
17348					return;
17349				}
17350
17351				// show clone at dragEl or original position
17352				if ( dragEl.parentNode == rootEl && ! this.options.group.revertClone ) {
17353					rootEl.insertBefore( cloneEl, dragEl );
17354				} else if ( nextEl ) {
17355					rootEl.insertBefore( cloneEl, nextEl );
17356				} else {
17357					rootEl.appendChild( cloneEl );
17358				}
17359				if ( this.options.group.revertClone ) {
17360					this.animate( dragEl, cloneEl );
17361				}
17362				css( cloneEl, 'display', '' );
17363				cloneHidden = false;
vendor: 5,091 bytes, lines 17364-17491
17364			}
17365		}
17366	};
17367	function _globalDragOver( /**Event*/evt ) {
17368		if ( evt.dataTransfer ) {
17369			// evt.dataTransfer.dropEffect = 'move';
17370		}
17371		evt.cancelable && evt.preventDefault();
17372	}
17373	function _onMove( fromEl, toEl, dragEl, dragRect, targetEl, targetRect, originalEvent, willInsertAfter ) {
17374		var evt,
17375			sortable = fromEl[ expando ],
17376			onMoveFn = sortable.options.onMove,
17377			retVal;
17378		// Support for new CustomEvent feature
17379		if ( window.CustomEvent && ! IE11OrLess && ! Edge ) {
17380			evt = new CustomEvent( 'move', {
17381				bubbles: true,
17382				cancelable: true
17383			} );
17384		} else {
17385			evt = document.createEvent( 'Event' );
17386			evt.initEvent( 'move', true, true );
17387		}
17388		evt.to = toEl;
17389		evt.from = fromEl;
17390		evt.dragged = dragEl;
17391		evt.draggedRect = dragRect;
17392		evt.related = targetEl || toEl;
17393		evt.relatedRect = targetRect || getRect( toEl );
17394		evt.willInsertAfter = willInsertAfter;
17395		evt.originalEvent = originalEvent;
17396		fromEl.dispatchEvent( evt );
17397		if ( onMoveFn ) {
17398			retVal = onMoveFn.call( sortable, evt, originalEvent );
17399		}
17400		return retVal;
17401	}
17402	function _disableDraggable( el ) {
17403		el.draggable = false;
17404	}
17405	function _unsilent() {
17406		_silent = false;
17407	}
17408	function _ghostIsFirst( evt, vertical, sortable ) {
17409		var firstElRect = getRect( getChild( sortable.el, 0, sortable.options, true ) );
17410		var childContainingRect = getChildContainingRectFromElement( sortable.el, sortable.options, ghostEl );
17411		var spacer = 10;
17412		return vertical ? evt.clientX < childContainingRect.left - spacer || evt.clientY < firstElRect.top && evt.clientX < firstElRect.right : evt.clientY < childContainingRect.top - spacer || evt.clientY < firstElRect.bottom && evt.clientX < firstElRect.left;
17413	}
17414	function _ghostIsLast( evt, vertical, sortable ) {
17415		var lastElRect = getRect( lastChild( sortable.el, sortable.options.draggable ) );
17416		var childContainingRect = getChildContainingRectFromElement( sortable.el, sortable.options, ghostEl );
17417		var spacer = 10;
17418		return vertical ? evt.clientX > childContainingRect.right + spacer || evt.clientY > lastElRect.bottom && evt.clientX > lastElRect.left : evt.clientY > childContainingRect.bottom + spacer || evt.clientX > lastElRect.right && evt.clientY > lastElRect.top;
17419	}
17420	function _getSwapDirection( evt, target, targetRect, vertical, swapThreshold, invertedSwapThreshold, invertSwap, isLastTarget ) {
17421		var mouseOnAxis = vertical ? evt.clientY : evt.clientX,
17422			targetLength = vertical ? targetRect.height : targetRect.width,
17423			targetS1 = vertical ? targetRect.top : targetRect.left,
17424			targetS2 = vertical ? targetRect.bottom : targetRect.right,
17425			invert = false;
17426		if ( ! invertSwap ) {
17427			// Never invert or create dragEl shadow when target movemenet causes mouse to move past the end of regular swapThreshold
17428			if ( isLastTarget && targetMoveDistance < targetLength * swapThreshold ) {
17429				// multiplied only by swapThreshold because mouse will already be inside target by (1 - threshold) * targetLength / 2
17430				// check if past first invert threshold on side opposite of lastDirection
17431				if ( ! pastFirstInvertThresh && ( lastDirection === 1 ? mouseOnAxis > targetS1 + targetLength * invertedSwapThreshold / 2 : mouseOnAxis < targetS2 - targetLength * invertedSwapThreshold / 2 ) ) {
17432					// past first invert threshold, do not restrict inverted threshold to dragEl shadow
17433					pastFirstInvertThresh = true;
17434				}
17435				if ( ! pastFirstInvertThresh ) {
17436					// dragEl shadow (target move distance shadow)
17437					if ( lastDirection === 1 ? mouseOnAxis < targetS1 + targetMoveDistance // over dragEl shadow
17438						: mouseOnAxis > targetS2 - targetMoveDistance ) {
17439						return -lastDirection;
17440					}
17441				} else {
17442					invert = true;
17443				}
17444			} else {
17445				// Regular
17446				if ( mouseOnAxis > targetS1 + targetLength * ( 1 - swapThreshold ) / 2 && mouseOnAxis < targetS2 - targetLength * ( 1 - swapThreshold ) / 2 ) {
17447					return _getInsertDirection( target );
17448				}
17449			}
17450		}
17451		invert = invert || invertSwap;
17452		if ( invert ) {
17453			// Invert of regular
17454			if ( mouseOnAxis < targetS1 + targetLength * invertedSwapThreshold / 2 || mouseOnAxis > targetS2 - targetLength * invertedSwapThreshold / 2 ) {
17455				return mouseOnAxis > targetS1 + targetLength / 2 ? 1 : -1;
17456			}
17457		}
17458		return 0;
17459	}
17460
17461	/**
17462	 * Gets the direction dragEl must be swapped relative to target in order to make it
17463	 * seem that dragEl has been "inserted" into that element's position
17464	 * @param {HTMLElement} target The target whose position dragEl is being inserted at
17465	 * @return {number}                   Direction dragEl must be swapped
17466	 */
17467	function _getInsertDirection( target ) {
17468		if ( index( dragEl ) < index( target ) ) {
17469			return 1;
17470		}
17471		return -1;
17472	}
17473
17474	/**
17475	 * Generate id
17476	 * @param {HTMLElement} el
17477	 * @returns {string}
17478	 * @private
17479	 */
17480	function _generateId( el ) {
17481		var str = el.tagName + el.className + el.src + el.href + el.textContent,
17482			i = str.length,
17483			sum = 0;
17484		while ( i-- ) {
17485			sum += str.charCodeAt( i );
17486		}
17487		return sum.toString( 36 );
17488	}
17489	function _saveInputCheckedState( root ) {
17490		savedInputChecked.length = 0;
17491		var inputs = root.getElementsByTagName( 'input' );
vendor: 3,647 bytes, lines 17492-17625
17492		var idx = inputs.length;
17493		while ( idx-- ) {
17494			var el = inputs[ idx ];
17495			el.checked && savedInputChecked.push( el );
17496		}
17497	}
17498	function _nextTick( fn ) {
17499		return setTimeout( fn, 0 );
17500	}
17501	function _cancelNextTick( id ) {
17502		return clearTimeout( id );
17503	}
17504
17505	// Fixed #973:
17506	if ( documentExists ) {
17507		on( document, 'touchmove', function( evt ) {
17508			if ( ( Sortable.active || awaitingDragStarted ) && evt.cancelable ) {
17509				evt.preventDefault();
17510			}
17511		} );
17512	}
17513
17514	// Export utils
17515	Sortable.utils = {
17516		on: on,
17517		off: off,
17518		css: css,
17519		find: find,
17520		is: function is( el, selector ) {
17521			return !! closest( el, selector, el, false );
17522		},
17523		extend: extend,
17524		throttle: throttle,
17525		closest: closest,
17526		toggleClass: toggleClass,
17527		clone: clone,
17528		index: index,
17529		nextTick: _nextTick,
17530		cancelNextTick: _cancelNextTick,
17531		detectDirection: _detectDirection,
17532		getChild: getChild,
17533		expando: expando
17534	};
17535
17536	/**
17537	 * Get the Sortable instance of an element
17538	 * @param {HTMLElement} element The element
17539	 * @return {Sortable|undefined}         The instance of Sortable
17540	 */
17541	Sortable.get = function( element ) {
17542		return element[ expando ];
17543	};
17544
17545	/**
17546	 * Mount a plugin to Sortable
17547	 */
17548	Sortable.mount = function() {
17549		var _plugins = Array.from( arguments );
17550
17551		if ( _plugins[ 0 ].constructor === Array ) {
17552			_plugins = _plugins[ 0 ];
17553		}
17554		_plugins.forEach( function( plugin ) {
17555			if ( ! plugin.prototype || ! plugin.prototype.constructor ) {
17556				throw 'Sortable: Mounted plugin must be a constructor function, not '.concat( {}.toString.call( plugin ) );
17557			}
17558			if ( plugin.utils ) {
17559				Sortable.utils = _objectSpread2( _objectSpread2( {}, Sortable.utils ), plugin.utils );
17560			}
17561			PluginManager.mount( plugin );
17562		} );
17563	};
17564
17565	/**
17566	 * Create sortable instance
17567	 * @param {HTMLElement} el
17568	 * @param {Object}      [options]
17569	 */
17570	Sortable.create = function( el, options ) {
17571		return new Sortable( el, options );
17572	};
17573
17574	// Export
17575	Sortable.version = version;
17576
17577	function AutoScrollPlugin() {
17578		function AutoScroll() {
17579			this.defaults = {
17580				scroll: true,
17581				forceAutoScrollFallback: false,
17582				scrollSensitivity: 30,
17583				scrollSpeed: 10,
17584				bubbleScroll: true
17585			};
17586
17587			// Bind all private methods
17588			for ( var fn in this ) {
17589				if ( fn.charAt( 0 ) === '_' && typeof this[ fn ] === 'function' ) {
17590					this[ fn ] = this[ fn ].bind( this );
17591				}
17592			}
17593		}
17594		AutoScroll.prototype = {
17595			dragStarted: function dragStarted( _ref ) {
17596				var originalEvent = _ref.originalEvent;
17597				if ( this.sortable.nativeDraggable ) {
17598					on( document, 'dragover', this._handleAutoScroll );
17599				} else if ( this.options.supportPointer ) {
17600					on( document, 'pointermove', this._handleFallbackAutoScroll );
17601				} else if ( originalEvent.touches ) {
17602					on( document, 'touchmove', this._handleFallbackAutoScroll );
17603				} else {
17604					on( document, 'mousemove', this._handleFallbackAutoScroll );
17605				}
17606			},
17607			dragOverCompleted: function dragOverCompleted( _ref2 ) {
17608				var originalEvent = _ref2.originalEvent;
17609				// For when bubbling is canceled and using fallback (fallback 'touchmove' always reached)
17610				if ( ! this.options.dragOverBubble && ! originalEvent.rootEl ) {
17611					this._handleAutoScroll( originalEvent );
17612				}
17613			},
17614			drop: function drop() {
17615				if ( this.sortable.nativeDraggable ) {
17616					off( document, 'dragover', this._handleAutoScroll );
17617				} else {
17618					off( document, 'pointermove', this._handleFallbackAutoScroll );
17619					off( document, 'touchmove', this._handleFallbackAutoScroll );
17620					off( document, 'mousemove', this._handleFallbackAutoScroll );
17621				}
17622				clearPointerElemChangedInterval();
17623				clearAutoScrolls();
17624				cancelThrottle();
17625			},
vendor: 4,190 bytes, lines 17626-17750
17626			nulling: function nulling() {
17627				touchEvt$1 = scrollRootEl = scrollEl = scrolling = pointerElemChangedInterval = lastAutoScrollX = lastAutoScrollY = null;
17628				autoScrolls.length = 0;
17629			},
17630			_handleFallbackAutoScroll: function _handleFallbackAutoScroll( evt ) {
17631				this._handleAutoScroll( evt, true );
17632			},
17633			_handleAutoScroll: function _handleAutoScroll( evt, fallback ) {
17634				var _this = this;
17635				var x = ( evt.touches ? evt.touches[ 0 ] : evt ).clientX,
17636					y = ( evt.touches ? evt.touches[ 0 ] : evt ).clientY,
17637					elem = document.elementFromPoint( x, y );
17638				touchEvt$1 = evt;
17639
17640				// IE does not seem to have native autoscroll,
17641				// Edge's autoscroll seems too conditional,
17642				// MACOS Safari does not have autoscroll,
17643				// Firefox and Chrome are good
17644				if ( fallback || this.options.forceAutoScrollFallback || Edge || IE11OrLess || Safari ) {
17645					autoScroll( evt, this.options, elem, fallback );
17646
17647					// Listener for pointer element change
17648					var ogElemScroller = getParentAutoScrollElement( elem, true );
17649					if ( scrolling && ( ! pointerElemChangedInterval || x !== lastAutoScrollX || y !== lastAutoScrollY ) ) {
17650						pointerElemChangedInterval && clearPointerElemChangedInterval();
17651						// Detect for pointer elem change, emulating native DnD behaviour
17652						pointerElemChangedInterval = setInterval( function() {
17653							var newElem = getParentAutoScrollElement( document.elementFromPoint( x, y ), true );
17654							if ( newElem !== ogElemScroller ) {
17655								ogElemScroller = newElem;
17656								clearAutoScrolls();
17657							}
17658							autoScroll( evt, _this.options, newElem, fallback );
17659						}, 10 );
17660						lastAutoScrollX = x;
17661						lastAutoScrollY = y;
17662					}
17663				} else {
17664					// if DnD is enabled (and browser has good autoscrolling), first autoscroll will already scroll, so get parent autoscroll of first autoscroll
17665					if ( ! this.options.bubbleScroll || getParentAutoScrollElement( elem, true ) === getWindowScrollingElement() ) {
17666						clearAutoScrolls();
17667						return;
17668					}
17669					autoScroll( evt, this.options, getParentAutoScrollElement( elem, false ), false );
17670				}
17671			}
17672		};
17673		return _extends( AutoScroll, {
17674			pluginName: 'scroll',
17675			initializeByDefault: true
17676		} );
17677	}
17678	function clearAutoScrolls() {
17679		autoScrolls.forEach( function( _autoScroll ) {
17680			clearInterval( _autoScroll.pid );
17681		} );
17682		autoScrolls = [];
17683	}
17684	function clearPointerElemChangedInterval() {
17685		clearInterval( pointerElemChangedInterval );
17686	}
17687	function Revert() {}
17688	Revert.prototype = {
17689		startIndex: null,
17690		dragStart: function dragStart( _ref2 ) {
17691			var oldDraggableIndex = _ref2.oldDraggableIndex;
17692			this.startIndex = oldDraggableIndex;
17693		},
17694		onSpill: function onSpill( _ref3 ) {
17695			var dragEl = _ref3.dragEl,
17696				putSortable = _ref3.putSortable;
17697			this.sortable.captureAnimationState();
17698			if ( putSortable ) {
17699				putSortable.captureAnimationState();
17700			}
17701			var nextSibling = getChild( this.sortable.el, this.startIndex, this.options );
17702			if ( nextSibling ) {
17703				this.sortable.el.insertBefore( dragEl, nextSibling );
17704			} else {
17705				this.sortable.el.appendChild( dragEl );
17706			}
17707			this.sortable.animateAll();
17708			if ( putSortable ) {
17709				putSortable.animateAll();
17710			}
17711		},
17712		drop: drop
17713	};
17714	_extends( Revert, {
17715		pluginName: 'revertOnSpill'
17716	} );
17717	function Remove() {}
17718	Remove.prototype = {
17719		onSpill: function onSpill( _ref4 ) {
17720			var dragEl = _ref4.dragEl,
17721				putSortable = _ref4.putSortable;
17722			var parentSortable = putSortable || this.sortable;
17723			parentSortable.captureAnimationState();
17724			dragEl.parentNode && dragEl.parentNode.removeChild( dragEl );
17725			parentSortable.animateAll();
17726		},
17727		drop: drop
17728	};
17729	_extends( Remove, {
17730		pluginName: 'removeOnSpill'
17731	} );
17732
17733	function SwapPlugin() {
17734		function Swap() {
17735			this.defaults = {
17736				swapClass: 'sortable-swap-highlight'
17737			};
17738		}
17739		Swap.prototype = {
17740			dragStart: function dragStart( _ref ) {
17741				var dragEl = _ref.dragEl;
17742				lastSwapEl = dragEl;
17743			},
17744			dragOverValid: function dragOverValid( _ref2 ) {
17745				var completed = _ref2.completed,
17746					target = _ref2.target,
17747					onMove = _ref2.onMove,
17748					activeSortable = _ref2.activeSortable,
17749					changed = _ref2.changed,
17750					cancel = _ref2.cancel;
17751				if ( ! activeSortable.options.swap ) {
17752					return;
17753				}
17754				var el = this.sortable.el,
17755					options = this.options;
17756				if ( target && target !== el ) {
17757					var prevSwapEl = lastSwapEl;
17758					if ( onMove( target ) !== false ) {
17759						toggleClass( target, options.swapClass, true );
17760						lastSwapEl = target;
17761					} else {
17762						lastSwapEl = null;
17763					}
17764					if ( prevSwapEl && prevSwapEl !== lastSwapEl ) {
17765						toggleClass( prevSwapEl, options.swapClass, false );
17766					}
17767				}
17768				changed();
17769				completed( true );
17770				cancel();
17771			},
17772			drop: function drop( _ref3 ) {
17773				var activeSortable = _ref3.activeSortable,
17774					putSortable = _ref3.putSortable,
17775					dragEl = _ref3.dragEl;
17776				var toSortable = putSortable || this.sortable;
17777				var options = this.options;
17778				if ( lastSwapEl ) {
17779					toggleClass( lastSwapEl, options.swapClass, false );
17780				}
17781				if ( lastSwapEl && ( options.swap || putSortable && putSortable.options.swap ) ) {
17782					if ( dragEl !== lastSwapEl ) {
17783						toSortable.captureAnimationState();
17784						if ( toSortable !== activeSortable ) {
17785							activeSortable.captureAnimationState();
17786						}
17787						swapNodes( dragEl, lastSwapEl );
17788						toSortable.animateAll();
17789						if ( toSortable !== activeSortable ) {
17790							activeSortable.animateAll();
17791						}
17792					}
17793				}
17794			},
vendor: 3,449 bytes, lines 17795-17909
17795			nulling: function nulling() {
17796				lastSwapEl = null;
17797			}
17798		};
17799		return _extends( Swap, {
17800			pluginName: 'swap',
17801			eventProperties: function eventProperties() {
17802				return {
17803					swapItem: lastSwapEl
17804				};
17805			}
17806		} );
17807	}
17808	function swapNodes( n1, n2 ) {
17809		var p1 = n1.parentNode,
17810			p2 = n2.parentNode,
17811			i1,
17812			i2;
17813		if ( ! p1 || ! p2 || p1.isEqualNode( n2 ) || p2.isEqualNode( n1 ) ) {
17814			return;
17815		}
17816		i1 = index( n1 );
17817		i2 = index( n2 );
17818		if ( p1.isEqualNode( p2 ) && i1 < i2 ) {
17819			i2++;
17820		}
17821		p1.insertBefore( n2, p1.children[ i1 ] );
17822		p2.insertBefore( n1, p2.children[ i2 ] );
17823	}
17824
17825	function MultiDragPlugin() {
17826		function MultiDrag( sortable ) {
17827			// Bind all private methods
17828			for ( var fn in this ) {
17829				if ( fn.charAt( 0 ) === '_' && typeof this[ fn ] === 'function' ) {
17830					this[ fn ] = this[ fn ].bind( this );
17831				}
17832			}
17833			if ( ! sortable.options.avoidImplicitDeselect ) {
17834				if ( sortable.options.supportPointer ) {
17835					on( document, 'pointerup', this._deselectMultiDrag );
17836				} else {
17837					on( document, 'mouseup', this._deselectMultiDrag );
17838					on( document, 'touchend', this._deselectMultiDrag );
17839				}
17840			}
17841			on( document, 'keydown', this._checkKeyDown );
17842			on( document, 'keyup', this._checkKeyUp );
17843			this.defaults = {
17844				selectedClass: 'sortable-selected',
17845				multiDragKey: null,
17846				avoidImplicitDeselect: false,
17847				setData: function setData( dataTransfer, dragEl ) {
17848					var data = '';
17849					if ( multiDragElements.length && multiDragSortable === sortable ) {
17850						multiDragElements.forEach( function( multiDragElement, i ) {
17851							data += ( ! i ? '' : ', ' ) + multiDragElement.textContent;
17852						} );
17853					} else {
17854						data = dragEl.textContent;
17855					}
17856					dataTransfer.setData( 'Text', data );
17857				}
17858			};
17859		}
17860		MultiDrag.prototype = {
17861			multiDragKeyDown: false,
17862			isMultiDrag: false,
17863			delayStartGlobal: function delayStartGlobal( _ref ) {
17864				var dragged = _ref.dragEl;
17865				dragEl$1 = dragged;
17866			},
17867			delayEnded: function delayEnded() {
17868				this.isMultiDrag = multiDragElements.indexOf( dragEl$1 ) !== -1;
17869			},
17870			setupClone: function setupClone( _ref2 ) {
17871				var sortable = _ref2.sortable,
17872					cancel = _ref2.cancel;
17873				if ( ! this.isMultiDrag ) {
17874					return;
17875				}
17876				for ( var i = 0; i < multiDragElements.length; i++ ) {
17877					multiDragClones.push( clone( multiDragElements[ i ] ) );
17878					multiDragClones[ i ].sortableIndex = multiDragElements[ i ].sortableIndex;
17879					multiDragClones[ i ].draggable = false;
17880					multiDragClones[ i ].style[ 'will-change' ] = '';
17881					toggleClass( multiDragClones[ i ], this.options.selectedClass, false );
17882					multiDragElements[ i ] === dragEl$1 && toggleClass( multiDragClones[ i ], this.options.chosenClass, false );
17883				}
17884				sortable._hideClone();
17885				cancel();
17886			},
17887			clone: function clone( _ref3 ) {
17888				var sortable = _ref3.sortable,
17889					rootEl = _ref3.rootEl,
17890					dispatchSortableEvent = _ref3.dispatchSortableEvent,
17891					cancel = _ref3.cancel;
17892				if ( ! this.isMultiDrag ) {
17893					return;
17894				}
17895				if ( ! this.options.removeCloneOnHide ) {
17896					if ( multiDragElements.length && multiDragSortable === sortable ) {
17897						insertMultiDragClones( true, rootEl );
17898						dispatchSortableEvent( 'clone' );
17899						cancel();
17900					}
17901				}
17902			},
17903			showClone: function showClone( _ref4 ) {
17904				var cloneNowShown = _ref4.cloneNowShown,
17905					rootEl = _ref4.rootEl,
17906					cancel = _ref4.cancel;
17907				if ( ! this.isMultiDrag ) {
17908					return;
17909				}
17910				insertMultiDragClones( false, rootEl );
17911				multiDragClones.forEach( function( _clone ) {
17912					css( _clone, 'display', '' );
17913				} );
17914				cloneNowShown();
17915				clonesHidden = false;
17916				cancel();
17917			},
17918			hideClone: function hideClone( _ref5 ) {
17919				var _this = this;
17920				var sortable = _ref5.sortable,
17921					cloneNowHidden = _ref5.cloneNowHidden,
17922					cancel = _ref5.cancel;
17923				if ( ! this.isMultiDrag ) {
17924					return;
17925				}
17926				multiDragClones.forEach( function( _clone ) {
17927					css( _clone, 'display', 'none' );
17928					if ( _clone.options.removeCloneOnHide && _clone.parentNode ) {
17929						clone.parentNode.removeChild( _clone );
17930					}
17931				} );
17932				cloneNowHidden();
17933				clonesHidden = true;
17934				cancel();
17935			},
17936			dragStartGlobal: function dragStartGlobal( _ref6 ) {
17937				var sortable = _ref6.sortable;
17938				if ( ! this.isMultiDrag && multiDragSortable ) {
17939					multiDragSortable.multiDrag._deselectMultiDrag();
17940				}
17941				multiDragElements.forEach( function( multiDragElement ) {
17942					multiDragElement.sortableIndex = index( multiDragElement );
17943				} );
17944
17945				// Sort multi-drag elements
17946				multiDragElements = multiDragElements.sort( function( a, b ) {
17947					return a.sortableIndex - b.sortableIndex;
17948				} );
17949				dragStarted = true;
17950			},
17951			dragStarted: function( _ref7 ) {
17952				var _this2 = this;
17953				var sortable = _ref7.sortable;
17954				var dragRect;
17955				if ( ! this.isMultiDrag ) {
17956					return;
17957				}
17958				if ( this.options.sort ) {
17959					// Capture rects,
17960					// hide multi drag elements (by positioning them absolute),
17961					// set multi drag elements rects to dragRect,
17962					// show multi drag elements,
17963					// animate to rects,
17964					// unset rects & remove from DOM
17965
17966					sortable.captureAnimationState();
17967					if ( this.options.animation ) {
17968						multiDragElements.forEach( function( multiDragElement ) {
17969							if ( multiDragElement === dragEl$1 ) {
17970								return;
17971							}
17972							css( multiDragElement, 'position', 'absolute' );
17973						} );
17974						dragRect = getRect( dragEl$1, false, true, true );
17975						multiDragElements.forEach( function( multiDragElement ) {
17976							if ( multiDragElement === dragEl$1 ) {
17977								return;
17978							}
17979							setRect( multiDragElement, dragRect );
17980						} );
17981						folding = true;
17982						initialFolding = true;
17983					}
17984				}
17985				sortable.animateAll( function() {
17986					folding = false;
17987					initialFolding = false;
17988					if ( _this2.options.animation ) {
17989						multiDragElements.forEach( function( multiDragElement ) {
17990							unsetRect( multiDragElement );
17991						} );
17992					}
17993
17994					// Remove all auxiliary multidrag items from el, if sorting enabled
17995					if ( _this2.options.sort ) {
17996						removeMultiDragElements();
17997					}
17998				} );
17999			},
18000			dragOver: function dragOver( _ref8 ) {
18001				var target = _ref8.target,
18002					completed = _ref8.completed,
18003					cancel = _ref8.cancel;
18004				if ( folding && multiDragElements.indexOf( target ) !== -1 ) {
18005					completed( false );
18006					cancel();
18007				}
18008			},
18009			revert: function revert( _ref9 ) {
18010				var fromSortable = _ref9.fromSortable,
18011					_rootEl = _ref9.rootEl,
18012					sortable = _ref9.sortable,
18013					dragRect = _ref9.dragRect;
18014				if ( multiDragElements.length > 1 ) {
18015					// Setup unfold animation
18016					multiDragElements.forEach( function( multiDragElement ) {
18017						sortable.addAnimationState( {
18018							target: multiDragElement,
18019							rect: folding ? getRect( multiDragElement ) : dragRect
18020						} );
18021						unsetRect( multiDragElement );
18022						multiDragElement.fromRect = dragRect;
18023						fromSortable.removeAnimationState( multiDragElement );
18024					} );
18025					folding = false;
18026					insertMultiDragElements( ! this.options.removeCloneOnHide, _rootEl );
18027				}
18028			},
18029			dragOverCompleted: function dragOverCompleted( _ref10 ) {
18030				var sortable = _ref10.sortable,
18031					isOwner = _ref10.isOwner,
18032					insertion = _ref10.insertion,
18033					activeSortable = _ref10.activeSortable,
18034					_parentEl = _ref10.parentEl,
18035					_putSortable = _ref10.putSortable;
18036				var options = this.options;
18037				var dragRectAbsolute;
18038				var clonesHiddenBefore;
18039				if ( insertion ) {
18040					// Clones must be hidden before folding animation to capture dragRectAbsolute properly
18041					if ( isOwner ) {
18042						activeSortable._hideClone();
18043					}
18044					initialFolding = false;
18045					// If leaving sort:false root, or already folding - Fold to new location
18046					if ( options.animation && multiDragElements.length > 1 && ( folding || ( ! isOwner && ! activeSortable.options.sort && ! _putSortable ) ) ) {
18047						// Fold: Set all multi drag elements's rects to dragEl's rect when multi-drag elements are invisible
18048						dragRectAbsolute = getRect( dragEl$1, false, true, true );
18049						multiDragElements.forEach( function( multiDragElement ) {
18050							if ( multiDragElement === dragEl$1 ) {
18051								return;
18052							}
18053							setRect( multiDragElement, dragRectAbsolute );
18054
18055							// Move element(s) to end of _parentEl so that it does not interfere with multi-drag clones insertion if they are inserted
18056							// while folding, and so that we can capture them again because old sortable will no longer be fromSortable
18057							_parentEl.appendChild( multiDragElement );
18058						} );
18059						folding = true;
18060					}
18061
18062					// Clones must be shown (and check to remove multi drags) after folding when interfering multiDragElements are moved out
18063					if ( ! isOwner ) {
18064						// Only remove if not folding (folding will remove them anyways)
18065						if ( ! folding ) {
18066							removeMultiDragElements();
18067						}
18068						if ( multiDragElements.length > 1 ) {
18069							clonesHiddenBefore = clonesHidden;
18070							activeSortable._showClone( sortable );
18071
18072							// Unfold animation for clones if showing from hidden
18073							if ( activeSortable.options.animation && ! clonesHidden && clonesHiddenBefore ) {
18074								multiDragClones.forEach( function( _clone ) {
18075									activeSortable.addAnimationState( {
18076										target: _clone,
18077										rect: clonesFromRect
18078									} );
18079									_clone.fromRect = clonesFromRect;
18080									_clone.thisAnimationDuration = null;
18081								} );
18082							}
18083						} else {
18084							activeSortable._showClone( sortable );
18085						}
18086					}
18087				}
18088			},
18089			dragOverAnimationCapture: function dragOverAnimationCapture( _ref11 ) {
18090				var dragRect = _ref11.dragRect,
18091					isOwner = _ref11.isOwner,
18092					activeSortable = _ref11.activeSortable,
18093					dragMatrix;
18094				multiDragElements.forEach( function( multiDragElement ) {
18095					multiDragElement.thisAnimationDuration = null;
18096				} );
18097				if ( activeSortable.options.animation && ! isOwner && activeSortable.multiDrag.isMultiDrag ) {
18098					clonesFromRect = _extends( {}, dragRect );
18099					dragMatrix = matrix( dragEl$1, true );
18100					clonesFromRect.top -= dragMatrix.f;
18101					clonesFromRect.left -= dragMatrix.e;
18102				}
18103			},
18104			dragOverAnimationComplete: function dragOverAnimationComplete() {
18105				if ( folding ) {
18106					folding = false;
18107					removeMultiDragElements();
18108				}
18109			},
18110			drop: function( _ref12 ) {
18111				var evt = _ref12.originalEvent,
18112					_rootEl = _ref12.rootEl,
18113					_parentEl = _ref12.parentEl,
18114					sortable = _ref12.sortable,
18115					dispatchSortableEvent = _ref12.dispatchSortableEvent,
18116					_oldIndex = _ref12.oldIndex,
18117					_putSortable = _ref12.putSortable;
18118				var toSortable = _putSortable || this.sortable;
18119				var options;
18120				var children;
18121				var update;
18122				var lastIndex;
18123				var currentIndex;
18124				var filter;
18125				var filtered;
18126				var dragRect;
18127				var multiDragIndex;
18128				var rect;
18129				if ( ! evt ) {
18130					return;
18131				}
18132				options = this.options;
18133				children = _parentEl.children;
18134
18135				// Multi-drag selection
18136				if ( ! dragStarted ) {
18137					if ( options.multiDragKey && ! this.multiDragKeyDown ) {
18138						this._deselectMultiDrag();
18139					}
18140					toggleClass( dragEl$1, options.selectedClass, multiDragElements.indexOf( dragEl$1 ) === -1 );
18141					if ( multiDragElements.indexOf( dragEl$1 ) === -1 ) {
18142						multiDragElements.push( dragEl$1 );
18143						dispatchEvent( {
18144							sortable: sortable,
18145							rootEl: _rootEl,
18146							name: 'select',
18147							targetEl: dragEl$1,
18148							originalEvent: evt
18149						} );
18150
18151						// Modifier activated, select from last to dragEl
18152						if ( evt.shiftKey && lastMultiDragSelect && sortable.el.contains( lastMultiDragSelect ) ) {
18153							lastIndex = index( lastMultiDragSelect );
18154							currentIndex = index( dragEl$1 );
18155							if ( lastIndex !== -1 && currentIndex !== -1 && lastIndex !== currentIndex ) {
18156								( function() {
18157									// Must include lastMultiDragSelect (select it), in case modified selection from no selection
18158									// (but previous selection existed)
18159									var n, i;
18160									if ( currentIndex > lastIndex ) {
18161										i = lastIndex;
18162										n = currentIndex;
18163									} else {
18164										i = currentIndex;
18165										n = lastIndex + 1;
18166									}
18167									filter = options.filter;
18168									for ( ; i < n; i++ ) {
18169										if ( multiDragElements.indexOf( children[ i ] ) !== -1 ) {
18170											continue;
18171										}
18172										// Check if element is draggable
18173										if ( ! closest( children[ i ], options.draggable, _parentEl, false ) ) {
18174											continue;
18175										}
18176										// Check if element is filtered
18177										filtered = filter && ( typeof filter === 'function' ? filter.call( sortable, evt, children[ i ], sortable ) : filter.split( ',' ).some( function( criteria ) {
18178											return closest( children[ i ], criteria.trim(), _parentEl, false );
18179										} ) );
18180										if ( filtered ) {
18181											continue;
18182										}
18183										toggleClass( children[ i ], options.selectedClass, true );
18184										multiDragElements.push( children[ i ] );
18185										dispatchEvent( {
18186											sortable: sortable,
18187											rootEl: _rootEl,
18188											name: 'select',
18189											targetEl: children[ i ],
18190											originalEvent: evt
18191										} );
18192									}
18193								}() );
18194							}
18195						} else {
18196							lastMultiDragSelect = dragEl$1;
18197						}
18198						multiDragSortable = toSortable;
18199					} else {
18200						multiDragElements.splice( multiDragElements.indexOf( dragEl$1 ), 1 );
18201						lastMultiDragSelect = null;
18202						dispatchEvent( {
18203							sortable: sortable,
18204							rootEl: _rootEl,
18205							name: 'deselect',
18206							targetEl: dragEl$1,
18207							originalEvent: evt
18208						} );
18209					}
18210				}
18211
18212				// Multi-drag drop
18213				if ( dragStarted && this.isMultiDrag ) {
18214					folding = false;
18215					// Do not "unfold" after around dragEl if reverted
18216					if ( ( _parentEl[ expando ].options.sort || _parentEl !== _rootEl ) && multiDragElements.length > 1 ) {
18217						dragRect = getRect( dragEl$1 );
18218						multiDragIndex = index( dragEl$1, ':not(.' + this.options.selectedClass + ')' );
18219						if ( ! initialFolding && options.animation ) {
18220							dragEl$1.thisAnimationDuration = null;
18221						}
18222						toSortable.captureAnimationState();
18223						if ( ! initialFolding ) {
18224							if ( options.animation ) {
18225								dragEl$1.fromRect = dragRect;
18226								multiDragElements.forEach( function( multiDragElement ) {
18227									multiDragElement.thisAnimationDuration = null;
18228									if ( multiDragElement !== dragEl$1 ) {
18229										rect = folding ? getRect( multiDragElement ) : dragRect;
18230										multiDragElement.fromRect = rect;
18231
18232										// Prepare unfold animation
18233										toSortable.addAnimationState( {
18234											target: multiDragElement,
18235											rect: rect
18236										} );
18237									}
18238								} );
18239							}
18240
18241							// Multi drag elements are not necessarily removed from the DOM on drop, so to reinsert
18242							// properly they must all be removed
18243							removeMultiDragElements();
18244							multiDragElements.forEach( function( multiDragElement ) {
18245								if ( children[ multiDragIndex ] ) {
18246									_parentEl.insertBefore( multiDragElement, children[ multiDragIndex ] );
18247								} else {
18248									_parentEl.appendChild( multiDragElement );
18249								}
18250								multiDragIndex++;
18251							} );
18252
18253							// If initial folding is done, the elements may have changed position because they are now
18254							// unfolding around dragEl, even though dragEl may not have his index changed, so update event
18255							// must be fired here as Sortable will not.
18256							if ( _oldIndex === index( dragEl$1 ) ) {
18257								update = false;
18258								multiDragElements.forEach( function( multiDragElement ) {
18259									if ( multiDragElement.sortableIndex !== index( multiDragElement ) ) {
18260										update = true;
18261									}
18262								} );
18263								if ( update ) {
18264									dispatchSortableEvent( 'update' );
18265									dispatchSortableEvent( 'sort' );
18266								}
18267							}
18268						}
18269
18270						// Must be done after capturing individual rects (scroll bar)
18271						multiDragElements.forEach( function( multiDragElement ) {
18272							unsetRect( multiDragElement );
18273						} );
18274						toSortable.animateAll();
18275					}
18276					multiDragSortable = toSortable;
18277				}
18278
18279				// Remove clones if necessary
18280				if ( _rootEl === _parentEl || ( _putSortable && _putSortable.lastPutMode !== 'clone' ) ) {
18281					multiDragClones.forEach( function( _clone ) {
18282						if ( _clone.parentNode ) {
18283							_clone.parentNode.removeChild( _clone );
18284						}
18285					} );
18286				}
18287			},
18288			nullingGlobal: function nullingGlobal() {
18289				this.isMultiDrag = dragStarted = false;
18290				multiDragClones.length = 0;
18291			},
18292			destroyGlobal: function destroyGlobal() {
18293				this._deselectMultiDrag();
18294				off( document, 'pointerup', this._deselectMultiDrag );
18295				off( document, 'mouseup', this._deselectMultiDrag );
18296				off( document, 'touchend', this._deselectMultiDrag );
18297				off( document, 'keydown', this._checkKeyDown );
18298				off( document, 'keyup', this._checkKeyUp );
18299			},
18300			_deselectMultiDrag: function _deselectMultiDrag( evt ) {
18301				var el;
18302				if ( typeof dragStarted !== 'undefined' && dragStarted ) {
18303					return;
18304				}
18305
18306				// Only deselect if selection is in this sortable
18307				if ( multiDragSortable !== this.sortable ) {
18308					return;
18309				}
18310
18311				// Only deselect if target is not item in this sortable
18312				if ( evt && closest( evt.target, this.options.draggable, this.sortable.el, false ) ) {
18313					return;
18314				}
18315
18316				// Only deselect if left click
18317				if ( evt && evt.button !== 0 ) {
18318					return;
18319				}
18320				while ( multiDragElements.length ) {
18321					el = multiDragElements[ 0 ];
18322					toggleClass( el, this.options.selectedClass, false );
18323					multiDragElements.shift();
18324					dispatchEvent( {
18325						sortable: this.sortable,
18326						rootEl: this.sortable.el,
18327						name: 'deselect',
18328						targetEl: el,
18329						originalEvent: evt
18330					} );
18331				}
18332			},
18333			_checkKeyDown: function _checkKeyDown( evt ) {
18334				if ( evt.key === this.options.multiDragKey ) {
18335					this.multiDragKeyDown = true;
18336				}
18337			},
18338			_checkKeyUp: function _checkKeyUp( evt ) {
18339				if ( evt.key === this.options.multiDragKey ) {
18340					this.multiDragKeyDown = false;
18341				}
18342			}
18343		};
18344		return _extends( MultiDrag, {
18345			// Static methods & properties
18346			pluginName: 'multiDrag',
18347			utils: {
18348				/**
18349				 * Selects the provided multi-drag item
18350				 * @param {HTMLElement} el The element to be selected
18351				 */
18352				select: function select( el ) {
18353					var sortable = el.parentNode[ expando ];
18354					if ( ! sortable || ! sortable.options.multiDrag || multiDragElements.indexOf( el ) !== -1 ) {
18355						return;
18356					}
18357					if ( multiDragSortable && multiDragSortable !== sortable ) {
18358						multiDragSortable.multiDrag._deselectMultiDrag();
18359						multiDragSortable = sortable;
18360					}
18361					toggleClass( el, sortable.options.selectedClass, true );
18362					multiDragElements.push( el );
18363				},
18364				/**
18365				 * Deselects the provided multi-drag item
18366				 * @param {HTMLElement} el The element to be deselected
18367				 */
18368				deselect: function deselect( el ) {
18369					var sortable = el.parentNode[ expando ],
18370						_index = multiDragElements.indexOf( el );
18371					if ( ! sortable || ! sortable.options.multiDrag || _index === -1 ) {
18372						return;
18373					}
18374					toggleClass( el, sortable.options.selectedClass, false );
18375					multiDragElements.splice( _index, 1 );
18376				}
18377			},
18378			eventProperties: function eventProperties() {
18379				var _this3 = this;
18380				var oldIndicies = [],
18381					newIndicies = [];
18382				var _newIndex;
18383				multiDragElements.forEach( function( multiDragElement ) {
18384					oldIndicies.push( {
18385						multiDragElement: multiDragElement,
18386						index: multiDragElement.sortableIndex
18387					} );
18388
18389					// multiDragElements will already be sorted if folding
18390					if ( folding && multiDragElement !== dragEl$1 ) {
18391						_newIndex = -1;
18392					} else if ( folding ) {
18393						_newIndex = index( multiDragElement, ':not(.' + _this3.options.selectedClass + ')' );
18394					} else {
18395						_newIndex = index( multiDragElement );
18396					}
18397					newIndicies.push( {
18398						multiDragElement: multiDragElement,
18399						index: _newIndex
18400					} );
18401				} );
18402				return {
18403					items: _toConsumableArray( multiDragElements ),
18404					clones: [].concat( multiDragClones ),
18405					oldIndicies: oldIndicies,
18406					newIndicies: newIndicies
18407				};
18408			},
18409			optionListeners: {
18410				multiDragKey: function multiDragKey( key ) {
18411					key = key.toLowerCase();
18412					if ( key === 'ctrl' ) {
18413						key = 'Control';
18414					} else if ( key.length > 1 ) {
18415						key = key.charAt( 0 ).toUpperCase() + key.substr( 1 );
18416					}
18417					return key;
18418				}
18419			}
18420		} );
18421	}
18422	function insertMultiDragElements( clonesInserted, _rootEl ) {
18423		multiDragElements.forEach( function( multiDragElement, i ) {
18424			var target = _rootEl.children[ multiDragElement.sortableIndex + ( clonesInserted ? Number( i ) : 0 ) ];
18425			if ( target ) {
18426				_rootEl.insertBefore( multiDragElement, target );
18427			} else {
18428				_rootEl.appendChild( multiDragElement );
18429			}
18430		} );
18431	}
18432
18433	/**
18434	 * Insert multi-drag clones
18435	 * @param {[boolean]}   elementsInserted Whether the multi-drag elements are inserted
18436	 * @param {HTMLElement} _rootEl
18437	 */
18438	function insertMultiDragClones( elementsInserted, _rootEl ) {
18439		multiDragClones.forEach( function( _clone, i ) {
18440			var target = _rootEl.children[ clone.sortableIndex + ( elementsInserted ? Number( i ) : 0 ) ];
18441			if ( target ) {
18442				_rootEl.insertBefore( _clone, target );
18443			} else {
18444				_rootEl.appendChild( _clone );
18445			}
18446		} );
18447	}
18448	function removeMultiDragElements() {
18449		multiDragElements.forEach( function( multiDragElement ) {
18450			if ( multiDragElement === dragEl$1 ) {
18451				return;
18452			}
18453			if ( multiDragElement && multiDragElement.parentNode ) {
18454				multiDragElement.parentNode.removeChild( multiDragElement );
18455			}
18456		} );
18457	}
18458
18459	Sortable.mount( new AutoScrollPlugin() );
18460	Sortable.mount( Remove, Revert );
18461
18462	Sortable.mount( new SwapPlugin() );
18463	Sortable.mount( new MultiDragPlugin() );
18464
18465	return Sortable;
18466} ) ) );
18467
18468( function( factory ) {
18469	'use strict';
18470	var sortable,
18471		jq;
18472
18473	if ( typeof define === 'function' && define.amd ) {
18474		try {
18475			define( [ 'sortablejs', 'jquery' ], function( Sortable, $ ) {
18476				sortable = Sortable;
18477				jq = $;
18478				checkErrors();
18479				factory( Sortable, $ );
18480			} );
18481		} catch ( err ) {
18482			checkErrors();
18483		}
18484		return;
18485	} else if ( typeof exports === 'object' ) {
18486		try {
18487			sortable = require( 'sortablejs' );
18488			jq = require( 'jquery' );
18489		} catch ( err ) { }
18490	}
18491
18492	if ( typeof window.jQuery === 'function' || typeof $ === 'function' ) {
18493		jq = window.jQuery || window.$;
18494	}
18495
18496	if ( typeof window.Sortable !== 'undefined' ) {
18497		sortable = window.Sortable;
18498	}
18499
18500	function checkErrors() {
18501		if ( ! jq ) {
18502			throw new Error( 'jQuery is required for jquery-sortablejs' );
18503		}
18504
18505		if ( ! sortable ) {
18506			throw new Error( 'SortableJS is required for jquery-sortablejs (https://github.com/SortableJS/Sortable)' );
18507		}
18508	}
18509	checkErrors();
18510	factory( sortable, jq );
18511}( function( Sortable, $ ) {
18512	'use strict';
18513
18514	$.fn.sortablejs = function( options ) {
18515		var retVal,
18516			args = arguments;
18517
18518		this.each( function() {
18519			var $el = $( this ),
18520				sortable = $el.data( 'sortable' );
18521
18522			if ( ! sortable && ( options instanceof Object || ! options ) ) {
18523				sortable = new Sortable( this, options );
18524				$el.data( 'sortable', sortable );
18525				$el.addClass( 'ui-sortable' );
18526			} else if ( sortable ) {
18527				if ( options === 'destroy' ) {
18528					sortable.destroy();
18529					$el.removeData( 'sortable' );
18530					$el.removeClass( 'ui-sortable' );
18531				} else if ( options === 'widget' ) {
18532					retVal = sortable;
18533				} else if ( typeof sortable[ options ] === 'function' ) {
18534					retVal = sortable[ options ].apply( sortable, [].slice.call( args, 1 ) );
18535				}
18535 else if ( options in sortable.options ) {
18536					retVal = sortable.option.apply( sortable, args );
18537				}
18538			}
18539		} );
18540
18541		return ( retVal === void 0 ) ? this : retVal;
18542	};
18543} ) );
18544;
18545/**
18546 * jQuery Tabs plugin
18547 * Creates a tabular interface
18548 *
18549 * @param {Object} $ The jQuery object
18550 * @version: v1.1
18551 * @author: ThemeComplete
18552 *
18553 * Copyright 2022 ThemeComplete
18554 */
18555( function( $ ) {
18556	'use strict';
18557
18558	var localStorage = $.epoAPI.util.getStorage( 'localStorage' );
18559	var confirm = window.confirm;
18560
18561	var Tabs = function( dom, options ) {
18562		this.elements = $( dom );
18563		this.last = [];
18564		this.current = [];
18565		this.enableEvents = true;
18566		this.backup = [];
18567
18568		this.options = $.extend( {}, $.fn.tcTabs.defaults, options );
18569
18570		if ( this.elements.length > 0 ) {
18571			this.init();
18572			return this;
18573		}
18574
18575		return false;
18576	};
18577
18578	Tabs.prototype = {
18579		constructor: Tabs,
18580
18581		set: function( options ) {
18582			this.options = $.extend( {}, this.options, options );
18583			this.init( true );
18584		},
18585
18586		addHeaderEvents: function( t, index, tc, header, theaderswrap, theaders, scrollLeftButton, scrollRightButton, state ) {
18587			var additional_events = '';
18588			var ithis = this;
18589
18590			header.off( 'closetab.tmtabs' ).on( 'closetab.tmtabs', function() {
18591				var $this = $( this );
18592				var _tab = t.find( $this.data( 'tab' ) );
18593
18594				$this.removeClass( 'closed open' ).addClass( 'closed' );
18595				$this
18596					.find( '.tm-arrow' )
18597					.removeClass( ithis.options.classdown + ' ' + ithis.options.classup )
18598					.addClass( ithis.options.classdown );
18599				if ( ithis.options.useclasstohide ) {
18600					_tab.addClass( 'tm-hide' ).removeClass( 'tm-show' );
18601				} else {
18602					_tab.hide();
18603				}
18604				_tab.removeClass( 'tm-animated ' + ithis.options.animationclass );
18605				$( window ).trigger( 'tc-closetab.tmtabs', {
18606					header: $this,
18607					tab: _tab
18608				} );
18609			} );
18610
18611			header.off( 'opentab.tmtabs' ).on( 'opentab.tmtabs', function() {
18612				var $this = $( this );
18613				var _tab = t.find( $this.data( 'tab' ) );
18614
18615				$this.removeClass( 'closed open' ).addClass( 'open' );
18616				$this
18617					.find( '.tm-arrow' )
18618					.removeClass( ithis.options.classdown + ' ' + ithis.options.classup )
18619					.addClass( ithis.options.classup );
18620
18621				if ( ithis.options.useclasstohide ) {
18622					_tab.removeClass( 'tm-hide' ).addClass( 'tm-show' );
18623				} else {
18624					_tab.show();
18625				}
18626				_tab.removeClass( 'tm-animated ' + ithis.options.animationclass ).addClass( 'tm-animated ' + ithis.options.animationclass );
18627				ithis.current[ index ] = $this.data( 'tab' );
18628				$( window ).trigger( 'tc-opentab.tmtabs', {
18629					header: $this,
18630					tab: ithis.current[ index ],
18631					table: _tab
18632				} );
18633				ithis.scrollIntoView( t, $this, state );
18634			} );
18635
18636			if ( ithis.options.showonhover === true || typeof ithis.options.showonhover === 'function' ) {
18637				additional_events = ' mouseover';
18638			}
18639
18640			header.off( 'keydown.tmtabs' ).on( 'keydown.tmtabs', function( e ) {
18641				var $this = $( this );
18642				var prevnext;
18643
18644				if ( e.keyCode === 13 ) {
18645					$this.trigger( 'click.tmtabs' );
18646				}
18647				if ( e.keyCode === 40 ) {
18648					prevnext = $this.closest( '.tm-box' ).next().find( '.' + ithis.options.header );
18649				}
18650				if ( e.keyCode === 38 ) {
18651					prevnext = $this.closest( '.tm-box' ).prev().find( '.' + ithis.options.header );
18652				}
18653				if ( prevnext && prevnext.length ) {
18654					$this.trigger( 'blur' );
18655					prevnext.trigger( ' focus' ).trigger( 'click' );
18656					e.preventDefault();
18657				}
18658			} );
18659
18660			header.off( 'click.tmtabs' ).on( 'click.tmtabs' + additional_events, function( e ) {
18661				var $this = $( this );
18662				e.preventDefault();
18663				if ( e.type === 'mouseover' && typeof ithis.options.showonhover === 'function' && ! ithis.options.showonhover.call() ) {
18664					return;
18665				}
18666				if ( ithis.current[ index ] === $this.data( 'tab' ) ) {
18667					$( window ).trigger( 'tc-isopentab.tmtabs', {
18668						header: $this,
18669						tab: ithis.current[ index ],
18670						table: t.find( ithis.current[ index ] )
18671					} );
18672					return;
18673				}
18674				if ( ithis.last[ index ] ) {
18675					$( ithis.last[ index ] ).trigger( 'closetab.tmtabs' );
18676				}
18677				$this.trigger( 'opentab.tmtabs' );
18678				ithis.last[ index ] = $this;
18679				if ( localStorage ) {
18680					localStorage.setItem( 'tmadmintab-' + tc, $this.attr( ithis.options.dataopenattribute ) );
18681				}
18682				$( window ).trigger( 'tc-tmtabs-clicked', {
18683					tc: tc,
18684					options: ithis.options,
18685					header: $this,
18686					tab: ithis.current[ index ],
18687					table: t.find( ithis.current[ index ] )
18688				} );
18689			} );
18690
18691			header.find( '.' + this.options.deleteheader ).remove();
18692			if ( this.options.deletebutton ) {
18693				header.append( this.options.deletebuttonhtml );
18694				header
18695					.find( '.' + this.options.deleteheader )
18696					.off( 'click.tmtabs' )
18697					.on( 'click.tmtabs', function( e ) {
18698						var $t = $( this );
18699						var $tmbox = $t.closest( '.tm-box' );
18700						var $header = $tmbox.find( '.' + ithis.options.header );
18701						var $tab = t.find( '.' + $header.attr( ithis.options.dataattribute ) );
18702
18703						e.stopPropagation();
18704
18705						if ( t.find( '.' + ithis.options.headers + ' ' + '.' + ithis.options.header ).length < 2 ) {
18706							return;
18707						}
18708						if ( ithis.options.deleteconfirm ) {
18709							if ( ! confirm( window.TMEPOGLOBALADMINJS.i18n_builder_delete ) ) {
18710								return;
18711							}
18712						}
18713
18714						if ( typeof ithis.options.beforedeletetab === 'function' ) {
18715							ithis.options.beforedeletetab.call( t, $t, $tab );
18716						}
18717
18718						$tab.remove();
18719						if ( $header.is( '.open' ) ) {
18720							if ( $tmbox.next().find( '.' + ithis.options.header ).is( '.closed' ) ) {
18721								$tmbox.next().find( '.' + ithis.options.header ).trigger( 'click.tmtabs' );
18722							} else if ( $tmbox.prev().find( '.' + ithis.options.header ).is( '.closed' ) ) {
18723								$tmbox.prev().find( '.' + ithis.options.header ).trigger( 'click.tmtabs' );
18724							}
18725						}
18726						$tmbox.remove();
18727
18728						ithis.checkSize( theaderswrap, theaders, scrollLeftButton, scrollRightButton, state, true );
18729						if ( typeof ithis.options.afterdeletetab === 'function' ) {
18730							ithis.options.afterdeletetab.call( t );
18731						}
18732					} );
18733			}
18734
18735			header.find( '.' + this.options.editheader ).remove();
18736			if ( this.options.editbutton ) {
18737				header.append( ithis.options.editbuttonhtml );
18738				header
18739					.find( '.' + ithis.options.editheader )
18740					.off( 'click.tmtabs' )
18741					.on( 'click.tmtabs', function( event ) {
18742						var $t;
18743						var $tab;
18744						if ( ithis.enableEvents && typeof ithis.options.oneditbutton === 'function' ) {
18745							event.stopPropagation();
18746							$t = $( this );
18747							$tab = t.find( '.' + $t.closest( '.tm-box' ).find( '.' + ithis.options.header ).attr( ithis.options.dataattribute ) );
18748							ithis.options.oneditbutton.call( t, $t, $tab );
18749						}
18750					} );
18751			}
18752		},
18753
18754		initHeader: function( header, t, index, theaderswrap, theaders, scrollLeftButton, scrollRightButton, state ) {
18755			var id;
18756			var init_open = 0;
18757			var tc = t.attr( 'class' );
18758
18759			header = $( header );
18760			id = '.' + header.attr( this.options.dataattribute );
18761			header.data( 'tab', id );
18762			if ( this.options.useclasstohide ) {
18763				t.find( id ).addClass( 'tm-hide' ).removeClass( 'tm-show' );
18764			} else {
18765				t.find( id ).hide();
18766			}
18767			t.find( id ).data( 'state', 'closed' );
18768			if ( ! init_open && header.is( '.open' ) ) {
18769				header.removeClass( 'closed open' ).addClass( 'open' ).data( 'state', 'open' );
18770				header
18771					.find( '.tm-arrow' )
18772					.removeClass( this.options.classdown + ' ' + this.options.classup )
18773					.addClass( this.options.classup );
18774				if ( this.options.useclasstohide ) {
18775					t.find( id ).removeClass( 'tm-hide' ).addClass( 'tm-show' );
18776				} else {
18777					t.find( id ).show();
18778				}
18779				t.find( id ).data( 'state', 'open' );
18780				init_open = 1;
18781				this.current[ index ] = id;
18782				this.last[ index ] = header;
18783			} else {
18784				header.removeClass( 'closed open' ).addClass( 'closed' ).data( 'state', 'closed' );
18785			}
18786
18787			this.addHeaderEvents( t, index, tc, header, theaderswrap, theaders, scrollLeftButton, scrollRightButton, state );
18788		},
18789
18790		checkSize: function( theaderswrap, theaders, scrollLeftButton, scrollRightButton, state, setScrollWidth ) {
18791			var panelWidth;
18792			var openedHeader;
18793			var props = [];
18794			var hiddenParents = theaders.parents().addBack().not( ':visible' );
18795
18796			// When tabs element is hidden its width cannot be calcualted
18797			// so we revert the visibility for this procedure only
18798			hiddenParents.each( function() {
18799				var $this = $( this );
18800				var styleDisplay = this.style.display;
18801				var cssDisplay = $this.css( 'display' );
18802				var styleVisibility = this.style.visibility;
18803				var cssVisibility = $this.css( 'visibility' );
18804				var obj;
18805				var did = false;
18806				obj = {
18807					styleDisplay: styleDisplay,
18808					cssDisplay: cssDisplay,
18809					styleVisibility: styleVisibility,
18810					cssVisibility: cssVisibility,
18811					display: null,
18812					visibility: null,
18813					element: $this
18814				};
18815				if ( styleDisplay === 'none' ) {
18816					this.style.display = '';
18817					obj.display = true;
18818					if ( $this.css( 'display' ) === 'none' ) {
18819						this.style.display = 'block';
18820					}
18821					did = true;
18822				}
18823				if ( styleVisibility === 'hidden' ) {
18824					this.style.visibility = '';
18825					obj.visibility = true;
18826					if ( $this.css( 'visibility' ) === 'hidden' ) {
18827						this.style.visibility = 'visible';
18828					}
18829					did = true;
18830				}
18831
18832				if ( did ) {
18833					props.push( obj );
18834				}
18835			} );
18836
18837			panelWidth = Math.round( theaders.outerWidth() );
18838
18839			if ( theaders.is( '.has-scroll-arrows' ) ) {
18840				theaders.removeClass( '.has-scroll-arrows' );
18841				panelWidth = Math.round( theaderswrap.outerWidth() );
18842			}
18843
18844			if ( setScrollWidth ) {
18845				openedHeader = theaders.find( '.' + this.options.header + '.open' );
18846				if ( openedHeader.length ) {
18847					openedHeader.removeClass( 'open' );
18848				}
18849				state.scrollWidth = theaders[ 0 ].scrollWidth;
18850				if ( openedHeader.length ) {
18851					openedHeader.addClass( 'open' );
18852				}
18853			}
18854
18855			if ( state.scrollWidth > panelWidth ) {
18856				scrollRightButton.addClass( 'scroll-arrow-show' );
18857				scrollLeftButton.addClass( 'scroll-arrow-show' );
18858				theaders.addClass( 'has-scroll-arrows' );
18859
18860				if ( state.scrollWidth - panelWidth === theaders.scrollLeft() ) {
18861					scrollRightButton.addClass( 'scroll-arrow-disabled scroll-arrow-right-disabled' );
18862				} else {
18863					scrollRightButton.removeClass( 'scroll-arrow-disabled scroll-arrow-right-disabled' );
18864				}
18865				if ( theaders.scrollLeft() === 0 ) {
18866					scrollLeftButton.addClass( 'scroll-arrow-disabled scroll-arrow-left-disabled' );
18867				} else {
18868					scrollLeftButton.removeClass( 'scroll-arrow-disabled scroll-arrow-left-disabled' );
18869				}
18870			} else {
18871				scrollRightButton.removeClass( 'scroll-arrow-show' );
18872				scrollLeftButton.removeClass( 'scroll-arrow-show' );
18873				theaders[ 0 ].scrollLeft = 0;
18874				theaders.removeClass( 'has-scroll-arrows' );
18875			}
18876
18877			props.forEach( function( o ) {
18878				if ( o.display ) {
18879					o.element[ 0 ].style.display = o.styleDisplay;
18880				}
18881				if ( o.visibility ) {
18882					o.element[ 0 ].style.visibility = o.styleVisibility;
18883				}
18884			} );
18885		},
18886
18887		scrollIntoView: function( t, header, state ) {
18888			var left = state.scrollPos;
18889			var theaders = t.find( '.' + this.options.headers );
18890			var scrollWidth = theaders.width();
18891
18892			if ( theaders.is( '.has-scroll-arrows' ) && header && typeof ( header ) !== 'undefined' && header.position() && typeof ( header.position() ) !== 'undefined' ) {
18893				if ( header.position().left < 0 ) {
18894					state.scrollPos = Math.max( left + header.position().left + 1, 0 );
18895					theaders.animate( { scrollLeft: ( state.scrollPos + 1 ) + 'px' }, this.options.scrollDuration );
18896				} else if ( ( header.position().left + header.outerWidth() ) > scrollWidth ) {
18897					state.scrollPos = Math.min( left + ( ( header.position().left + header.outerWidth() ) - scrollWidth ), theaders[ 0 ].scrollWidth - theaders.outerWidth() );
18898					theaders.animate( { scrollLeft: ( state.scrollPos - 1 ) + 'px' }, this.options.scrollDuration );
18899				}
18900			}
18901		},
18902
18903		initElement: function( t, index, state, uniqid ) {
18904			var ithis = this;
18905			var tc = t.attr( 'class' );
18906			var sortableHeader;
18907			var theaders;
18908			var theaderswrap;
18909			var scrollLeftButton;
18910			var scrollRightButton;
18911			var headers = t.find( '.' + this.options.headers + ' ' + '.' + this.options.header );
18912			var ohp = 0;
18913			var ohpid = '';
18914			var _selected_tab;
18915			var vars = {};
18916			var initialIndex;
18917			var pressHoldTimeout;
18918
18919			if ( headers.length === 0 ) {
18920				return;
18921			}
18922
18923			theaders = t.find( '.' + this.options.headers );
18924			theaders.wrap( '<div class="' + this.options.headersWrap + '"></div>' );
18925			theaderswrap = t.find( '.' + this.options.headersWrap );
18926
18927			if ( this.options.scroll ) {
18928				theaderswrap.html( '<div class="tc-scroll-left-arrow"></div>' + theaderswrap.html() + '<div class="tc-scroll-right-arrow"></div>' );
18929				headers = t.find( '.' + this.options.headers + ' ' + '.' + this.options.header );
18930				theaders = t.find( '.' + this.options.headers );
18931				scrollLeftButton = t.find( '.tc-scroll-left-arrow' );
18932				scrollRightButton = t.find( '.tc-scroll-right-arrow' );
18933
18934				// Mousewheel support, if present.
18935				if ( typeof $.fn.mousewheel === 'function' ) {
18936					theaders.mousewheel( function( event, delta ) {
18937						// Only do mousewheel scrolling if scrolling is necessary
18938						if ( scrollRightButton.css( 'display' ) !== 'none' ) {
18939							this.scrollLeft -= ( delta * 30 );
18940							state.scrollPos = this.scrollLeft;
18941							event.preventDefault();
18942						}
18943					} );
18944				}
18945
18946				// Set initial scroll position
18947				theaders.animate( { scrollLeft: state.scrollPos + 'px' }, 0 );
18948
18949				this.checkSize( theaderswrap, theaders, scrollLeftButton, scrollRightButton, state, true );
18950				$( window ).off( 'resize.tmtabs' + uniqid ).on( 'resize.tmtabs' + uniqid, function() {
18951					ithis.checkSize( theaderswrap, theaders, scrollLeftButton, scrollRightButton, state, true );
18952				} );
18953
18954				t.off( 'refresh.tmtabs' + uniqid ).on( 'refresh.tmtabs' + uniqid, function() {
18955					ithis.checkSize( theaderswrap, theaders, scrollLeftButton, scrollRightButton, state, true );
18956				} );
18957
18958				// Document load
18959				$( function() {
18960					ithis.checkSize( theaderswrap, theaders, scrollLeftButton, scrollRightButton, state, true );
18961				} );
18962
18963				scrollRightButton.off( 'mousedown.tmtabs mouseup.tmtabs mouseleave.tmtabs mouseover.tmtabs mouseout.tmtabs' )
18964					.on( 'mousedown.tmtabs', function( e ) {
18965						var scrollRightFunc = function() {
18966							var left = theaders.scrollLeft();
18967							state.scrollPos = Math.min( left + ithis.options.scrollDistance, state.scrollWidth - theaders.outerWidth() );
18968							theaders.animate(
18969								{
18970									scrollLeft: state.scrollPos + 'px'
18971								},
18972								ithis.options.scrollDuration,
18973								function() {
18974									ithis.checkSize( theaderswrap, theaders, scrollLeftButton, scrollRightButton, state );
18975								}
18976							);
18977						};
18978						e.stopPropagation();
18979						scrollRightFunc();
18980
18981						pressHoldTimeout = setInterval( function() {
18982							scrollRightFunc();
18983						}, ithis.options.scrollDuration );
18984					} ).on( 'mouseup.tmtabs mouseleave.tmtabs', function() {
18985						clearInterval( pressHoldTimeout );
18986					} ).on( 'mouseover.tmtabs', function() {
18987						$( this ).addClass( 'scroll-arrow-over scroll-arrow-right-over' );
18988					} ).on( 'mouseout.tmtabs', function() {
18989						$( this ).removeClass( 'scroll-arrow-over scroll-arrow-right-over' );
18990					} );
18991
18992				scrollLeftButton.off( 'mousedown.tmtabs mouseup.tmtabs mouseleave.tmtabs mouseover.tmtabs mouseout.tmtabs' )
18993					.on( 'mousedown.tmtabs', function( e ) {
18994						var scrollLeftFunc = function() {
18995							var left = theaders.scrollLeft();
18996							state.scrollPos = Math.max( left - ithis.options.scrollDistance, 0 );
18997							theaders.animate(
18998								{
18999									scrollLeft: state.scrollPos + 'px'
19000								},
19001								ithis.options.scrollDuration,
19002								function() {
19003									ithis.checkSize( theaderswrap, theaders, scrollLeftButton, scrollRightButton, state );
19004								}
19005							);
19006						};
19007						e.stopPropagation();
19008						scrollLeftFunc();
19009
19010						pressHoldTimeout = setInterval( function() {
19011							scrollLeftFunc();
19012						}, ithis.options.scrollDuration );
19013					} ).on( 'mouseup.tmtabs mouseleave.tmtabs', function() {
19014						clearInterval( pressHoldTimeout );
19015					} ).on( 'mouseover.tmtabs', function() {
19016						$( this ).addClass( 'scroll-arrow-over scroll-arrow-left-over' );
19017					} ).on( 'mouseout.tmtabs', function() {
19018						$( this ).removeClass( 'scroll-arrow-over scroll-arrow-left-over' );
19019					} );
19020			}
19021
19022			window.location.href.replace( /[?&]+([^=&]+)=([^&]*)/gi, function( m, key, value ) {
19023				vars[ key ] = value;
19024			} );
19025
19026			this.last[ index ] = false;
19027			this.current[ index ] = '';
19028
19029			t.data( 'tm-has-tmtabs', 1 );
19030
19031			headers.each( function( i, header ) {
19032				ithis.initHeader( header, t, index, theaderswrap, theaders, scrollLeftButton, scrollRightButton, state );
19033			} );
19034
19035			if ( this.options.sortabletabs ) {
19036				sortableHeader = '.' + this.options.headers;
19037				if ( t.find( sortableHeader + ' .' + this.options.headers + '-inner' ).length ) {
19038					sortableHeader = '.' + this.options.headers + '-inner';
19039				}
19040				t.find( sortableHeader + ':not(.section_elements ' + sortableHeader + ',.tm-settings-wrap ' + sortableHeader + ',.builder-element-wrap ' + sortableHeader + ')' ).sortablejs( {
19041					draggable: '.tm-box:not(.tm-add-box)',
19042					handle: '.tm-box:not(.tm-add-box)',
19043					onStart: function( evt ) {
19044						var ui = $( evt.item );
19045						var $tab;
19046						$tab = t.find( '.' + ui.closest( '.tm-box' ).find( '.' + ithis.options.header ).attr( ithis.options.dataattribute ) );
19047						ohp = ui.index();
19048						ohpid = ui.find( '.' + ithis.options.header ).attr( ithis.options.dataopenattribute );
19049
19050						initialIndex = $.tmEPOAdmin.find_index( true, $tab.find( '.bitem' ).first() );
19051
19052						if ( typeof ithis.options.beforemovetab === 'function' ) {
19053							ithis.options.beforemovetab.call( this, ohp, $tab, initialIndex );
19054						}
19055						ithis.enableEvents = false;
19056					},
19057					onEnd: function( evt ) {
19058						var ui = $( evt.item );
19059						var original_item;
19060						var new_index;
19061						var replaced_item;
19062						var $tab;
19063						var originalItemCopy;
19064
19065						$tab = t.find( '.' + ui.closest( '.tm-box' ).find( '.' + ithis.options.header ).attr( ithis.options.dataattribute ) );
19066						original_item = t.find( '.' + ithis.options.slide + '.' + ohpid );
19067						new_index = t
19068							.find( '.' + ithis.options.headers + ' ' + '.' + ithis.options.header + '[' + ithis.options.dataopenattribute + '=\'' + ohpid + '\']' )
19069							.parent()
19070							.index();
19071						if ( new_index !== ohp ) {
19072							replaced_item = t.find( '.' + ithis.options.slide ).eq( new_index );
19073							originalItemCopy = original_item.tcClone();
19074							originalItemCopy.find( '.bitem' ).remove();
19075							if ( new_index > ohp ) {
19076								replaced_item.after( originalItemCopy );
19077							} else if ( new_index < ohp ) {
19078								replaced_item.before( originalItemCopy );
19079							}
19080							original_item.find( '.bitem' ).toArray().forEach( function( el ) {
19081								el = $( el );
19082								if ( typeof ithis.options.beforemovebitem === 'function' ) {
19083									ithis.options.beforemovebitem.call( this, new_index, ohp, $tab, initialIndex, el );
19084								}
19085								originalItemCopy.append( el );
19086								if ( typeof ithis.options.aftermovebitem === 'function' ) {
19087									ithis.options.aftermovebitem.call( this, new_index, ohp, $tab, initialIndex, el );
19088								}
19089							} );
19090							original_item.remove();
19091							if ( typeof ithis.options.aftermovetab === 'function' ) {
19092								ithis.options.aftermovetab.call( this, new_index, ohp, $tab, initialIndex );
19093							}
19094						}
19095						ithis.enableEvents = true;
19096					},
19097					filter: '.tm-add-box',
19098					ghostClass: 'sortable-ghost',
19099					dragClass: 'sortable-drag'
19100
19101				} );
19102			}
19103
19104			if ( this.options.addbutton ) {
19105				theaderswrap.append( this.options.addbuttonhtml );
19106				t.addClass( 'has-add-button' ).find( '.' + this.options.addheader ).off( 'click.tmtabs' ).on( 'click.tmtabs', function( e ) {
19107					var allHeaders = t.find( '.' + ithis.options.headers + ' ' + '.' + ithis.options.header );
19108					var last_header = allHeaders.last();
19109					var id = last_header.attr( ithis.options.dataattribute );
19110					var last_tab = t.find( '.' + id );
19111					var new_header = last_header.tcClone().off( 'closetab.tmtabs opentab.tmtabs click.tmtabs' );
19112					var new_tab = last_tab.tcClone().empty();
19113					var newid = ithis.options.slide + allHeaders.length;
19114
19115					e.preventDefault();
19116
19117					new_header
19118						.html( '<span class="tab-text">' + ( t.find( '.' + ithis.options.headers + ' ' + '.' + ithis.options.header ).length + 1 ) + '</span>' )
19119						.removeClass( 'closed open' )
19120						.addClass( 'closed' )
19121						.data( 'tab', '.' + newid )
19122						.data( 'state', 'closed' )
19123						.attr( ithis.options.dataattribute, newid );
19124					new_tab.removeClass( id ).addClass( newid );
19125					if ( ithis.options.useclasstohide ) {
19126						new_tab.addClass( 'tm-hide' ).removeClass( '.tm-show' );
19127					} else {
19128						new_tab.hide();
19129					}
19130					new_tab.removeClass( 'tm-animated ' + ithis.options.animationclass );
19131
19132					last_header.closest( '.tm-box' ).after( new_header );
19133
19134					new_header.wrap( '<div class="tm-box"></div>' );
19135
19136					ithis.addHeaderEvents( t, index, tc, new_header, theaderswrap, theaders, scrollLeftButton, scrollRightButton, state );
19137					last_tab.after( new_tab );
19138					if ( ithis.options.scroll ) {
19139						theaders.animate(
19140							{
19141								scrollLeft: new_header.width() + 'px'
19142							},
19143							ithis.options.scrollDuration,
19144							function() {
19145								ithis.checkSize( theaderswrap, theaders, scrollLeftButton, scrollRightButton, state, true );
19146							}
19147						);
19148					}
19149					if ( typeof ithis.options.afteraddtab === 'function' ) {
19150						ithis.options.afteraddtab.call( this, new_header, new_tab );
19151					}
19152				} );
19153			} else {
19154				theaderswrap.find( '.' + this.options.addheader ).remove();
19155			}
19156
19157			if ( this.options.selectedtab === 'auto' ) {
19158				if ( localStorage ) {
19159					_selected_tab = localStorage.getItem( 'tmadmintab-' + tc );
19160				}
19161				if ( vars.selected_tab !== undefined ) {
19162					_selected_tab = vars.selected_tab;
19163				}
19164				if ( vars.menu !== undefined ) {
19165					_selected_tab = vars.menu;
19166				}
19167				if ( _selected_tab === undefined || _selected_tab === null ) {
19168					_selected_tab = t.find( '.' + this.options.header ).eq( 0 ).attr( this.options.dataopenattribute );
19169				}
19170				if ( ! t.find( '.' + this.options.header + '[' + this.options.dataopenattribute + '="' + _selected_tab + '"]' ).is( ':visible' ) ) {
19171					t.find( '.' + this.options.header ).eq( 0 ).trigger( 'click.tmtabs' );
19172				} else {
19173					t.find( '.' + this.options.header + '[' + this.options.dataopenattribute + '="' + _selected_tab + '"]' ).trigger( 'click.tmtabs' );
19174				}
19175			} else if ( this.options.selectedtab !== false ) {
19176				_selected_tab = parseInt( this.options.selectedtab, 10 );
19177				t.find( '.' + this.options.header + ':eq(' + _selected_tab + ')' ).trigger( 'click.tmtabs' );
19178			}
19179		},
19180
19181		init: function( nobackup ) {
19182			var ithis = this;
19183			this.elements.each( function( index ) {
19184				var t = $( this );
19185				var display;
19186				var state = {};
19187				var backup = t.html();
19188				var tmSettingsWrap = $( ithis.options.animateDiv );
19189
19190				if ( ! nobackup && ithis.backup[ index ] ) {
19191					t.html( ithis.backup[ index ] );
19192				} else {
19193					ithis.backup[ index ] = backup;
19194				}
19195				state.scrollPos = 0;
19196				state.scrollWidth = 0;
19197				display = t.css( 'display' );
19198				tmSettingsWrap.show();
19199				t.show();
19200				ithis.initElement( t, index, state, $.epoAPI.math.uniqueid( '' ) );
19201				t.css( 'display', display );
19202				tmSettingsWrap.addClass( 'appear' );
19203			} );
19204		}
19205	};
19206
19207	$.fn.tcTabs = function( option ) {
19208		var methodReturn;
19209		var targets = $( this );
19210		var data = targets.data( 'tctabs' );
19211		var options;
19212		var ret;
19213		var args = $.makeArray( arguments );
19214		args.splice( 0, 1 );
19215
19216		if ( typeof option === 'object' ) {
19217			options = option;
19218		} else {
19219			options = {};
19220		}
19221
19222		if ( ! data ) {
19223			data = new Tabs( this, options );
19224			targets.data( 'tctabs', data );
19225		}
19226
19227		if ( typeof option === 'string' ) {
19228			methodReturn = data[ option ].apply( data, args );
19229		}
19230
19231		if ( methodReturn === undefined ) {
19232			ret = targets;
19233		} else {
19234			ret = methodReturn;
19235		}
19236
19237		return ret;
19238	};
19239
19240	$.fn.tcTabs.defaults = {
19241		headers: 'tm-tab-headers',
19242		headersWrap: 'tc-tab-headers-wrap',
19243		header: 'tab-header',
19244		slide: 'tc-tab-slide',
19245
19246		classdown: 'tcfa-angle-down',
19247		classup: 'tcfa-angle-up',
19248		animationclass: 'appear',
19249
19250		dataattribute: 'data-id',
19251		dataopenattribute: 'data-id',
19252		selectedtab: 'auto',
19253		showonhover: false,
19254		useclasstohide: true,
19255		sortabletabs: true,
19256
19257		addbutton: false,
19258		addheader: 'tm-add-tab',
19259		addbuttonhtml: '<div class="tm-add-tab"><span class="tmicon tcfa tcfa-plus"></span></div>',
19260		afteraddtab: null,
19261
19262		beforemovetab: null,
19263		aftermovetab: null,
19264		beforemovebitem: null,
19265		aftermovebitem: null,
19266
19267		deletebutton: false,
19268		deletebuttonhtml: '<div class="tm-del-tab"><span class="tcfa tcfa-times"></span></div>',
19269		deleteheader: 'tm-del-tab',
19270		deleteconfirm: false,
19271		beforedeletetab: null,
19272		afterdeletetab: null,
19273
19274		editbutton: false,
19275		editbuttonhtml: '<span class="tm-edit-tab"><span class="tcfa tcfa-edit"></span></span>',
19276		editheader: 'tm-edit-tab',
19277		oneditbutton: null,
19278
19279		scroll: true,
19280		scrollDistance: 300,
19281		scrollDuration: 300,
19282		animateDiv: ''
19283	};
19284
19285	$.fn.tcTabs.Constructor = Tabs;
19286
19287	$.fn.tmtabs = $.fn.tcTabs;
19288}( window.jQuery ) );
19289;
19290// TinyColor v1.4.1
19291// https://github.com/bgrins/TinyColor
19292// Brian Grinstead, MIT License
19293// reformatted code by ThemeComplete
19294( function( Math ) {
19295	'use strict';
19296
19297	var trimLeft = /^\s+/,
19298		trimRight = /\s+$/,
19299		tinyCounter = 0,
19300		mathRound = Math.round,
19301		mathMin = Math.min,
19302		mathMax = Math.max,
19303		mathRandom = Math.random;
19304
19305	var names = {
19306		aliceblue: 'f0f8ff',
19307		antiquewhite: 'faebd7',
19308		aqua: '0ff',
19309		aquamarine: '7fffd4',
19310		azure: 'f0ffff',
19311		beige: 'f5f5dc',
19312		bisque: 'ffe4c4',
19313		black: '000',
19314		blanchedalmond: 'ffebcd',
19315		blue: '00f',
19316		blueviolet: '8a2be2',
19317		brown: 'a52a2a',
19318		burlywood: 'deb887',
19319		burntsienna: 'ea7e5d',
19320		cadetblue: '5f9ea0',
19321		chartreuse: '7fff00',
19322		chocolate: 'd2691e',
19323		coral: 'ff7f50',
19324		cornflowerblue: '6495ed',
19325		cornsilk: 'fff8dc',
19326		crimson: 'dc143c',
19327		cyan: '0ff',
19328		darkblue: '00008b',
19329		darkcyan: '008b8b',
19330		darkgoldenrod: 'b8860b',
19331		darkgray: 'a9a9a9',
19332		darkgreen: '006400',
19333		darkgrey: 'a9a9a9',
19334		darkkhaki: 'bdb76b',
19335		darkmagenta: '8b008b',
19336		darkolivegreen: '556b2f',
19337		darkorange: 'ff8c00',
19338		darkorchid: '9932cc',
19339		darkred: '8b0000',
19340		darksalmon: 'e9967a',
19341		darkseagreen: '8fbc8f',
19342		darkslateblue: '483d8b',
19343		darkslategray: '2f4f4f',
19344		darkslategrey: '2f4f4f',
19345		darkturquoise: '00ced1',
19346		darkviolet: '9400d3',
19347		deeppink: 'ff1493',
19348		deepskyblue: '00bfff',
19349		dimgray: '696969',
19350		dimgrey: '696969',
19351		dodgerblue: '1e90ff',
19352		firebrick: 'b22222',
19353		floralwhite: 'fffaf0',
19354		forestgreen: '228b22',
19355		fuchsia: 'f0f',
19356		gainsboro: 'dcdcdc',
19357		ghostwhite: 'f8f8ff',
19358		gold: 'ffd700',
19359		goldenrod: 'daa520',
19360		gray: '808080',
19361		green: '008000',
19362		greenyellow: 'adff2f',
19363		grey: '808080',
19364		honeydew: 'f0fff0',
19365		hotpink: 'ff69b4',
19366		indianred: 'cd5c5c',
19367		indigo: '4b0082',
19368		ivory: 'fffff0',
19369		khaki: 'f0e68c',
19370		lavender: 'e6e6fa',
19371		lavenderblush: 'fff0f5',
19372		lawngreen: '7cfc00',
19373		lemonchiffon: 'fffacd',
19374		lightblue: 'add8e6',
19375		lightcoral: 'f08080',
19376		lightcyan: 'e0ffff',
19377		lightgoldenrodyellow: 'fafad2',
19378		lightgray: 'd3d3d3',
19379		lightgreen: '90ee90',
19380		lightgrey: 'd3d3d3',
19381		lightpink: 'ffb6c1',
19382		lightsalmon: 'ffa07a',
19383		lightseagreen: '20b2aa',
19384		lightskyblue: '87cefa',
19385		lightslategray: '789',
19386		lightslategrey: '789',
19387		lightsteelblue: 'b0c4de',
19388		lightyellow: 'ffffe0',
19389		lime: '0f0',
19390		limegreen: '32cd32',
19391		linen: 'faf0e6',
19392		magenta: 'f0f',
19393		maroon: '800000',
19394		mediumaquamarine: '66cdaa',
19395		mediumblue: '0000cd',
19396		mediumorchid: 'ba55d3',
19397		mediumpurple: '9370db',
19398		mediumseagreen: '3cb371',
19399		mediumslateblue: '7b68ee',
19400		mediumspringgreen: '00fa9a',
19401		mediumturquoise: '48d1cc',
19402		mediumvioletred: 'c71585',
19403		midnightblue: '191970',
19404		mintcream: 'f5fffa',
19405		mistyrose: 'ffe4e1',
19406		moccasin: 'ffe4b5',
19407		navajowhite: 'ffdead',
19408		navy: '000080',
19409		oldlace: 'fdf5e6',
19410		olive: '808000',
19411		olivedrab: '6b8e23',
19412		orange: 'ffa500',
19413		orangered: 'ff4500',
19414		orchid: 'da70d6',
19415		palegoldenrod: 'eee8aa',
19416		palegreen: '98fb98',
19417		paleturquoise: 'afeeee',
19418		palevioletred: 'db7093',
19419		papayawhip: 'ffefd5',
19420		peachpuff: 'ffdab9',
19421		peru: 'cd853f',
19422		pink: 'ffc0cb',
19423		plum: 'dda0dd',
19424		powderblue: 'b0e0e6',
19425		purple: '800080',
19426		rebeccapurple: '663399',
19427		red: 'f00',
19428		rosybrown: 'bc8f8f',
19429		royalblue: '4169e1',
19430		saddlebrown: '8b4513',
19431		salmon: 'fa8072',
19432		sandybrown: 'f4a460',
19433		seagreen: '2e8b57',
19434		seashell: 'fff5ee',
19435		sienna: 'a0522d',
19436		silver: 'c0c0c0',
19437		skyblue: '87ceeb',
19438		slateblue: '6a5acd',
19439		slategray: '708090',
19440		slategrey: '708090',
19441		snow: 'fffafa',
19442		springgreen: '00ff7f',
19443		steelblue: '4682b4',
19444		tan: 'd2b48c',
19445		teal: '008080',
19446		thistle: 'd8bfd8',
19447		tomato: 'ff6347',
19448		turquoise: '40e0d0',
19449		violet: 'ee82ee',
19450		wheat: 'f5deb3',
19451		white: 'fff',
19452		whitesmoke: 'f5f5f5',
19453		yellow: 'ff0',
19454		yellowgreen: '9acd32'
19455	};
19456
19457	// `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`
19458	var flip = function( o ) {
19459		var flipped = {};
19460		var i;
19461		for ( i in o ) {
19462			if ( Object.prototype.hasOwnProperty.call( o, i ) ) {
19463				flipped[ o[ i ] ] = i;
19464			}
19465		}
19466		return flipped;
19467	};
19468
19469	var hexNames = flip( names );
19470
19471	var matchers = ( function() {
19472		// <http://www.w3.org/TR/css3-values/#integers>
19473		var CSS_INTEGER = '[-\\+]?\\d+%?';
19474
19475		// <http://www.w3.org/TR/css3-values/#number-value>
19476		var CSS_NUMBER = '[-\\+]?\\d*\\.\\d+%?';
19477
19478		// Allow positive/negative integer/number.  Don't capture the either/or, just the entire outcome.
19479		var CSS_UNIT = '(?:' + CSS_NUMBER + ')|(?:' + CSS_INTEGER + ')';
19480
19481		// Actual matching.
19482		// Parentheses and commas are optional, but not required.
19483		// Whitespace can take the place of commas or opening paren
19484		var PERMISSIVE_MATCH3 = '[\\s|\\(]+(' + CSS_UNIT + ')[,|\\s]+(' + CSS_UNIT + ')[,|\\s]+(' + CSS_UNIT + ')\\s*\\)?';
19485		var PERMISSIVE_MATCH4 = '[\\s|\\(]+(' + CSS_UNIT + ')[,|\\s]+(' + CSS_UNIT + ')[,|\\s]+(' + CSS_UNIT + ')[,|\\s]+(' + CSS_UNIT + ')\\s*\\)?';
19486
19487		return {
19488			CSS_UNIT: new RegExp( CSS_UNIT ),
19489			rgb: new RegExp( 'rgb' + PERMISSIVE_MATCH3 ),
19490			rgba: new RegExp( 'rgba' + PERMISSIVE_MATCH4 ),
19491			hsl: new RegExp( 'hsl' + PERMISSIVE_MATCH3 ),
19492			hsla: new RegExp( 'hsla' + PERMISSIVE_MATCH4 ),
19493			hsv: new RegExp( 'hsv' + PERMISSIVE_MATCH3 ),
19494			hsva: new RegExp( 'hsva' + PERMISSIVE_MATCH4 ),
19495			hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
19496			hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
19497			hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
19498			hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/
19499		};
19500	}() );
19501
19502	// `isValidCSSUnit`
19503	// Take in a single string / number and check to see if it looks like a CSS unit
19504	// (see `matchers` above for definition).
19505	function isValidCSSUnit( color ) {
19506		return !! matchers.CSS_UNIT.exec( color );
19507	}
19508
19509	// Parse a base-16 hex value into a base-10 integer
19510	function parseIntFromHex( val ) {
19511		return parseInt( val, 16 );
19512	}
19513
19514	// Converts a hex value to a decimal
19515	function convertHexToDecimal( h ) {
19516		return parseIntFromHex( h ) / 255;
19517	}
19518
19519	// `stringInputToObject`
19520	// Permissive string parsing.  Take in a number of formats, and output an object
19521	// based on detected format.  Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`
19522	function stringInputToObject( color ) {
19523		var named = false;
19524		var match;
19525		color = color.replace( trimLeft, '' ).replace( trimRight, '' ).toLowerCase();
19526
19527		if ( names[ color ] ) {
19528			color = names[ color ];
19529			named = true;
19530		} else if ( color === 'transparent' ) {
19531			return { r: 0, g: 0, b: 0, a: 0, format: 'name' };
19532		}
19533
19534		// Try to match string input using regular expressions.
19535		// Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]
19536		// Just return an object and let the conversion functions handle that.
19537		// This way the result will be the same whether the TinyColor is initialized with string or object.
19538
19539		if ( ( match = matchers.rgb.exec( color ) ) ) {
19540			return { r: match[ 1 ], g: match[ 2 ], b: match[ 3 ] };
19541		}
19542		if ( ( match = matchers.rgba.exec( color ) ) ) {
19543			return { r: match[ 1 ], g: match[ 2 ], b: match[ 3 ], a: match[ 4 ] };
19544		}
19545		if ( ( match = matchers.hsl.exec( color ) ) ) {
19546			return { h: match[ 1 ], s: match[ 2 ], l: match[ 3 ] };
19547		}
19548		if ( ( match = matchers.hsla.exec( color ) ) ) {
19549			return { h: match[ 1 ], s: match[ 2 ], l: match[ 3 ], a: match[ 4 ] };
19550		}
19551		if ( ( match = matchers.hsv.exec( color ) ) ) {
19552			return { h: match[ 1 ], s: match[ 2 ], v: match[ 3 ] };
19553		}
19554		if ( ( match = matchers.hsva.exec( color ) ) ) {
19555			return { h: match[ 1 ], s: match[ 2 ], v: match[ 3 ], a: match[ 4 ] };
19556		}
19557		if ( ( match = matchers.hex8.exec( color ) ) ) {
19558			return {
19559				r: parseIntFromHex( match[ 1 ] ),
19560				g: parseIntFromHex( match[ 2 ] ),
19561				b: parseIntFromHex( match[ 3 ] ),
19562				a: convertHexToDecimal( match[ 4 ] ),
19563				format: named ? 'name' : 'hex8'
19564			};
19565		}
19566		if ( ( match = matchers.hex6.exec( color ) ) ) {
19567			return {
19568				r: parseIntFromHex( match[ 1 ] ),
19569				g: parseIntFromHex( match[ 2 ] ),
19570				b: parseIntFromHex( match[ 3 ] ),
19571				format: named ? 'name' : 'hex'
19572			};
19573		}
19574		if ( ( match = matchers.hex4.exec( color ) ) ) {
19575			return {
19576				r: parseIntFromHex( match[ 1 ] + '' + match[ 1 ] ),
19577				g: parseIntFromHex( match[ 2 ] + '' + match[ 2 ] ),
19578				b: parseIntFromHex( match[ 3 ] + '' + match[ 3 ] ),
19579				a: convertHexToDecimal( match[ 4 ] + '' + match[ 4 ] ),
19580				format: named ? 'name' : 'hex8'
19581			};
19582		}
19583		if ( ( match = matchers.hex3.exec( color ) ) ) {
19584			return {
19585				r: parseIntFromHex( match[ 1 ] + '' + match[ 1 ] ),
19586				g: parseIntFromHex( match[ 2 ] + '' + match[ 2 ] ),
19587				b: parseIntFromHex( match[ 3 ] + '' + match[ 3 ] ),
19588				format: named ? 'name' : 'hex'
19589			};
19590		}
19591
19592		return false;
19593	}
19594
19595	function validateWCAG2Parms( parms ) {
19596		// return valid WCAG2 parms for isReadable.
19597		// If input parms are invalid, return {"level":"AA", "size":"small"}
19598		var level, size;
19599		parms = parms || { level: 'AA', size: 'small' };
19600		level = ( parms.level || 'AA' ).toUpperCase();
19601		size = ( parms.size || 'small' ).toLowerCase();
19602		if ( level !== 'AA' && level !== 'AAA' ) {
19603			level = 'AA';
19604		}
19605		if ( size !== 'small' && size !== 'large' ) {
19606			size = 'small';
19607		}
19608		return { level: level, size: size };
19609	}
19610
19611	// Utilities
19612	// ---------
19613
19614	// Return a valid alpha value [0,1] with all invalid values being set to 1
19615	function boundAlpha( a ) {
19616		a = parseFloat( a );
19617
19618		if ( isNaN( a ) || a < 0 || a > 1 ) {
19619			a = 1;
19620		}
19621
19622		return a;
19623	}
19624
19625	// Check to see if string passed in is a percentage
19626	function isPercentage( n ) {
19627		return typeof n === 'string' && n.indexOf( '%' ) !== -1;
19628	}
19629
19630	// Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
19631	// <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
19632	function isOnePointZero( n ) {
19633		return typeof n === 'string' && n.indexOf( '.' ) !== -1 && parseFloat( n ) === 1;
19634	}
19635
19636	// Take input from [0, n] and return it as [0, 1]
19637	function bound01( n, max ) {
19638		var processPercent;
19639		if ( isOnePointZero( n ) ) {
19640			n = '100%';
19641		}
19642
19643		processPercent = isPercentage( n );
19644		n = mathMin( max, mathMax( 0, parseFloat( n ) ) );
19645
19646		// Automatically convert percentage into number
19647		if ( processPercent ) {
19648			n = parseInt( n * max, 10 ) / 100;
19649		}
19650
19651		// Handle floating point rounding errors
19652		if ( Math.abs( n - max ) < 0.000001 ) {
19653			return 1;
19654		}
19655
19656		// Convert into [0, 1] range if it isn't already
19657		return ( n % max ) / parseFloat( max );
19658	}
19659
19660	// Force a number between 0 and 1
19661	function clamp01( val ) {
19662		return mathMin( 1, mathMax( 0, val ) );
19663	}
19664
19665	// Force a hex value to have 2 characters
19666	function pad2( c ) {
19667		return c.length === 1 ? '0' + c : '' + c;
19668	}
19669
19670	// Replace a decimal with it's percentage value
19671	function convertToPercentage( n ) {
19672		if ( n <= 1 ) {
19673			n = ( n * 100 ) + '%';
19674		}
19675
19676		return n;
19677	}
19678
19679	// Converts a decimal to a hex value
19680	function convertDecimalToHex( d ) {
19681		return Math.round( parseFloat( d ) * 255 ).toString( 16 );
19682	}
19683
19684	// Conversion Functions
19685	// --------------------
19686
19687	// `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:
19688	// <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>
19689
19690	// `rgbToRgb`
19691	// Handle bounds / percentage checking to conform to CSS color spec
19692	// <http://www.w3.org/TR/css3-color/>
19693	// *Assumes:* r, g, b in [0, 255] or [0, 1]
19694	// *Returns:* { r, g, b } in [0, 255]
19695	function rgbToRgb( r, g, b ) {
19696		return {
19697			r: bound01( r, 255 ) * 255,
19698			g: bound01( g, 255 ) * 255,
19699			b: bound01( b, 255 ) * 255
19700		};
19701	}
19702
19703	// `rgbToHsl`
19704	// Converts an RGB color value to HSL.
19705	// *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]
19706	// *Returns:* { h, s, l } in [0,1]
19707	function rgbToHsl( r, g, b ) {
19708		var max;
19709		var min;
19710		var h;
19711		var s;
19712		var l;
19713		var d;
19714
19715		r = bound01( r, 255 );
19716		g = bound01( g, 255 );
19717		b = bound01( b, 255 );
19718
19719		max = mathMax( r, g, b );
19720		min = mathMin( r, g, b );
19721		l = ( max + min ) / 2;
19722
19723		if ( max === min ) {
19724			h = s = 0; // achromatic
19725		} else {
19726			d = max - min;
19727			s = l > 0.5 ? d / ( 2 - max - min ) : d / ( max + min );
19728			switch ( max ) {
19729				case r:
19730					h = ( ( g - b ) / d ) + ( g < b ? 6 : 0 );
19731					break;
19732				case g:
19733					h = ( ( b - r ) / d ) + 2;
19734					break;
19735				case b:
19736					h = ( ( r - g ) / d ) + 4;
19737					break;
19738			}
19739
19740			h /= 6;
19741		}
19742
19743		return { h: h, s: s, l: l };
19744	}
19745
19746	// `hslToRgb`
19747	// Converts an HSL color value to RGB.
19748	// *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]
19749	// *Returns:* { r, g, b } in the set [0, 255]
19750	function hslToRgb( h, s, l ) {
19751		var r;
19752		var g;
19753		var b;
19754		var q;
19755		var p;
19756
19757		h = bound01( h, 360 );
19758		s = bound01( s, 100 );
19759		l = bound01( l, 100 );
19760
19761		function hue2rgb( pp, qq, t ) {
19762			if ( t < 0 ) {
19763				t += 1;
19764			}
19765			if ( t > 1 ) {
19766				t -= 1;
19767			}
19768			if ( t < 1 / 6 ) {
19769				return pp + ( ( qq - pp ) * 6 * t );
19770			}
19771			if ( t < 1 / 2 ) {
19772				return qq;
19773			}
19774			if ( t < 2 / 3 ) {
19775				return pp + ( ( qq - pp ) * ( ( 2 / 3 ) - t ) * 6 );
19776			}
19777			return pp;
19778		}
19779
19780		if ( s === 0 ) {
19781			r = g = b = l; // achromatic
19782		} else {
19783			q = l < 0.5 ? l * ( 1 + s ) : l + s - ( l * s );
19784			p = ( 2 * l ) - q;
19785			r = hue2rgb( p, q, h + ( 1 / 3 ) );
19786			g = hue2rgb( p, q, h );
19787			b = hue2rgb( p, q, h - ( 1 / 3 ) );
19788		}
19789
19790		return { r: r * 255, g: g * 255, b: b * 255 };
19791	}
19792
19793	// `rgbToHsv`
19794	// Converts an RGB color value to HSV
19795	// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
19796	// *Returns:* { h, s, v } in [0,1]
19797	function rgbToHsv( r, g, b ) {
19798		var max;
19799		var min;
19800		var h;
19801		var s;
19802		var v;
19803		var d;
19804
19805		r = bound01( r, 255 );
19806		g = bound01( g, 255 );
19807		b = bound01( b, 255 );
19808
19809		max = mathMax( r, g, b );
19810		min = mathMin( r, g, b );
19811		v = max;
19812
19813		d = max - min;
19814		s = max === 0 ? 0 : d / max;
19815
19816		if ( max === min ) {
19817			h = 0; // achromatic
19818		} else {
19819			switch ( max ) {
19820				case r:
19821					h = ( ( g - b ) / d ) + ( g < b ? 6 : 0 );
19822					break;
19823				case g:
19824					h = ( ( b - r ) / d ) + 2;
19825					break;
19826				case b:
19827					h = ( ( r - g ) / d ) + 4;
19828					break;
19829			}
19830			h /= 6;
19831		}
19832		return { h: h, s: s, v: v };
19833	}
19834
19835	// `hsvToRgb`
19836	// Converts an HSV color value to RGB.
19837	// *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
19838	// *Returns:* { r, g, b } in the set [0, 255]
19839	function hsvToRgb( h, s, v ) {
19840		var i;
19841		var f;
19842		var p;
19843		var q;
19844		var t;
19845		var mod;
19846		var r;
19847		var g;
19848		var b;
19849
19850		h = bound01( h, 360 ) * 6;
19851		s = bound01( s, 100 );
19852		v = bound01( v, 100 );
19853
19854		i = Math.floor( h );
19855		f = h - i;
19856		p = v * ( 1 - s );
19857		q = v * ( 1 - ( f * s ) );
19858		t = v * ( 1 - ( ( 1 - f ) * s ) );
19859		mod = i % 6;
19860		r = [ v, q, p, p, t, v ][ mod ];
19861		g = [ t, v, v, q, p, p ][ mod ];
19862		b = [ p, p, t, v, v, q ][ mod ];
19863
19864		return { r: r * 255, g: g * 255, b: b * 255 };
19865	}
19866
19867	// `rgbToHex`
19868	// Converts an RGB color to hex
19869	// Assumes r, g, and b are contained in the set [0, 255]
19870	// Returns a 3 or 6 character hex
19871	function rgbToHex( r, g, b, allow3Char ) {
19872		var hex = [ pad2( mathRound( r ).toString( 16 ) ), pad2( mathRound( g ).toString( 16 ) ), pad2( mathRound( b ).toString( 16 ) ) ];
19873
19874		// Return a 3 character hex if possible
19875		if ( allow3Char && hex[ 0 ].charAt( 0 ) === hex[ 0 ].charAt( 1 ) && hex[ 1 ].charAt( 0 ) === hex[ 1 ].charAt( 1 ) && hex[ 2 ].charAt( 0 ) === hex[ 2 ].charAt( 1 ) ) {
19876			return hex[ 0 ].charAt( 0 ) + hex[ 1 ].charAt( 0 ) + hex[ 2 ].charAt( 0 );
19877		}
19878
19879		return hex.join( '' );
19880	}
19881
19882	// `rgbaToHex`
19883	// Converts an RGBA color plus alpha transparency to hex
19884	// Assumes r, g, b are contained in the set [0, 255] and
19885	// a in [0, 1]. Returns a 4 or 8 character rgba hex
19886	function rgbaToHex( r, g, b, a, allow4Char ) {
19887		var hex = [ pad2( mathRound( r ).toString( 16 ) ), pad2( mathRound( g ).toString( 16 ) ), pad2( mathRound( b ).toString( 16 ) ), pad2( convertDecimalToHex( a ) ) ];
19888
19889		// Return a 4 character hex if possible
19890		if ( allow4Char && hex[ 0 ].charAt( 0 ) === hex[ 0 ].charAt( 1 ) && hex[ 1 ].charAt( 0 ) === hex[ 1 ].charAt( 1 ) && hex[ 2 ].charAt( 0 ) === hex[ 2 ].charAt( 1 ) && hex[ 3 ].charAt( 0 ) === hex[ 3 ].charAt( 1 ) ) {
19891			return hex[ 0 ].charAt( 0 ) + hex[ 1 ].charAt( 0 ) + hex[ 2 ].charAt( 0 ) + hex[ 3 ].charAt( 0 );
19892		}
19893
19894		return hex.join( '' );
19895	}
19896
19897	// `rgbaToArgbHex`
19898	// Converts an RGBA color to an ARGB Hex8 string
19899	// Rarely used, but required for "toFilter()"
19900	function rgbaToArgbHex( r, g, b, a ) {
19901		var hex = [ pad2( convertDecimalToHex( a ) ), pad2( mathRound( r ).toString( 16 ) ), pad2( mathRound( g ).toString( 16 ) ), pad2( mathRound( b ).toString( 16 ) ) ];
19902
19903		return hex.join( '' );
19904	}
19905
19906	// Given a string or object, convert that input to RGB
19907	// Possible string inputs:
19908	//
19909	//     "red"
19910	//     "#f00" or "f00"
19911	//     "#ff0000" or "ff0000"
19912	//     "#ff000000" or "ff000000"
19913	//     "rgb 255 0 0" or "rgb (255, 0, 0)"
19914	//     "rgb 1.0 0 0" or "rgb (1, 0, 0)"
19915	//     "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
19916	//     "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
19917	//     "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
19918	//     "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
19919	//     "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
19920	//
19921	function inputToRGB( color ) {
19922		var rgb = { r: 0, g: 0, b: 0 };
19923		var a = 1;
19924		var s = null;
19925		var v = null;
19926		var l = null;
19927		var ok = false;
19928		var format = false;
19929
19930		if ( typeof color === 'string' ) {
19931			color = stringInputToObject( color );
19932		}
19933
19934		if ( typeof color === 'object' ) {
19935			if ( isValidCSSUnit( color.r ) && isValidCSSUnit( color.g ) && isValidCSSUnit( color.b ) ) {
19936				rgb = rgbToRgb( color.r, color.g, color.b );
19937				ok = true;
19938				format = String( color.r ).substring( String( color.r ).length - 1 ) === '%' ? 'prgb' : 'rgb';
19939			} else if ( isValidCSSUnit( color.h ) && isValidCSSUnit( color.s ) && isValidCSSUnit( color.v ) ) {
19940				s = convertToPercentage( color.s );
19941				v = convertToPercentage( color.v );
19942				rgb = hsvToRgb( color.h, s, v );
19943				ok = true;
19944				format = 'hsv';
19945			} else if ( isValidCSSUnit( color.h ) && isValidCSSUnit( color.s ) && isValidCSSUnit( color.l ) ) {
19946				s = convertToPercentage( color.s );
19947				l = convertToPercentage( color.l );
19948				rgb = hslToRgb( color.h, s, l );
19949				ok = true;
19950				format = 'hsl';
19951			}
19952
19953			if ( Object.prototype.hasOwnProperty.call( color, 'a' ) ) {
19954				a = color.a;
19955			}
19956		}
19957
19958		a = boundAlpha( a );
19959
19960		return {
19961			ok: ok,
19962			format: color.format || format,
19963			r: mathMin( 255, mathMax( rgb.r, 0 ) ),
19964			g: mathMin( 255, mathMax( rgb.g, 0 ) ),
19965			b: mathMin( 255, mathMax( rgb.b, 0 ) ),
19966			a: a
19967		};
19968	}
19969
19970	function TinyColor( color, opts ) {
19971		var rgb;
19972		color = color ? color : '';
19973		opts = opts || {};
19974
19975		// If input is already a TinyColor, return itself
19976		if ( color instanceof TinyColor ) {
19977			return color;
19978		}
19979		// If we are called as a function, call using new instead
19980		if ( ! ( this instanceof TinyColor ) ) {
19981			return new TinyColor( color, opts );
19982		}
19983
19984		rgb = inputToRGB( color );
19985		this._originalInput = color;
19986		this._r = rgb.r;
19987		this._g = rgb.g;
19988		this._b = rgb.b;
19989		this._a = rgb.a;
19990		this._roundA = mathRound( 100 * this._a ) / 100;
19991		if ( opts.format ) {
19992			this._format = opts.format;
19993		} else {
19994			this._format = rgb.format;
19995		}
19996
19997		this._gradientType = opts.gradientType;
19998
19999		// Don't let the range of [0,255] come back in [0,1].
20000		// Potentially lose a little bit of precision here, but will fix issues where
20001		// .5 gets interpreted as half of the total, instead of half of 1
20002		// If it was supposed to be 128, this was already taken care of by `inputToRgb`
20003		if ( this._r < 1 ) {
20004			this._r = mathRound( this._r );
20005		}
20006		if ( this._g < 1 ) {
20007			this._g = mathRound( this._g );
20008		}
20009		if ( this._b < 1 ) {
20010			this._b = mathRound( this._b );
20011		}
20012
20013		this._ok = rgb.ok;
20014		tinyCounter = tinyCounter + 1;
20015		this._tc_id = tinyCounter;
20016	}
20017
20018	function newTinyColor( color, opts ) {
20019		return new TinyColor( color, opts );
20020	}
20021
20022	// Modification Functions
20023	// ----------------------
20024	// Thanks to less.js for some of the basics here
20025	// <https://github.com/cloudhead/less.js/blob/master/lib/less/functions.js>
20026
20027	function desaturate( color, amount ) {
20028		var hsl;
20029		amount = amount === 0 ? 0 : amount || 10;
20030		hsl = newTinyColor( color ).toHsl();
20031		hsl.s -= amount / 100;
20032		hsl.s = clamp01( hsl.s );
20033		return newTinyColor( hsl );
20034	}
20035
20036	function saturate( color, amount ) {
20037		var hsl;
20038		amount = amount === 0 ? 0 : amount || 10;
20039		hsl = newTinyColor( color ).toHsl();
20040		hsl.s += amount / 100;
20041		hsl.s = clamp01( hsl.s );
20042		return newTinyColor( hsl );
20043	}
20044
20045	function greyscale( color ) {
20046		return newTinyColor( color ).desaturate( 100 );
20047	}
20048
20049	function lighten( color, amount ) {
20050		var hsl;
20051		amount = amount === 0 ? 0 : amount || 10;
20052		hsl = newTinyColor( color ).toHsl();
20053		hsl.l += amount / 100;
20054		hsl.l = clamp01( hsl.l );
20055		return newTinyColor( hsl );
20056	}
20057
20058	function brighten( color, amount ) {
20059		var rgb;
20060		amount = amount === 0 ? 0 : amount || 10;
20061		rgb = newTinyColor( color ).toRgb();
20062		rgb.r = mathMax( 0, mathMin( 255, rgb.r - mathRound( 255 * -( amount / 100 ) ) ) );
20063		rgb.g = mathMax( 0, mathMin( 255, rgb.g - mathRound( 255 * -( amount / 100 ) ) ) );
20064		rgb.b = mathMax( 0, mathMin( 255, rgb.b - mathRound( 255 * -( amount / 100 ) ) ) );
20065		return newTinyColor( rgb );
20066	}
20067
20068	function darken( color, amount ) {
20069		var hsl;
20070		amount = amount === 0 ? 0 : amount || 10;
20071		hsl = newTinyColor( color ).toHsl();
20072		hsl.l -= amount / 100;
20073		hsl.l = clamp01( hsl.l );
20074		return newTinyColor( hsl );
20075	}
20076
20077	// Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.
20078	// Values outside of this range will be wrapped into this range.
20079	function spin( color, amount ) {
20080		var hsl = newTinyColor( color ).toHsl();
20081		var hue = ( hsl.h + amount ) % 360;
20082		hsl.h = hue < 0 ? 360 + hue : hue;
20083		return newTinyColor( hsl );
20084	}
20085
20086	// Combination Functions
20087	// ---------------------
20088	// Thanks to jQuery xColor for some of the ideas behind these
20089	// <https://github.com/infusion/jQuery-xcolor/blob/master/jquery.xcolor.js>
20090
20091	function complement( color ) {
20092		var hsl = newTinyColor( color ).toHsl();
20093		hsl.h = ( hsl.h + 180 ) % 360;
20094		return newTinyColor( hsl );
20095	}
20096
20097	function triad( color ) {
20098		var hsl = newTinyColor( color ).toHsl();
vendor: 5,466 bytes, lines 20099-20265
20099		var h = hsl.h;
20100		return [ newTinyColor( color ), newTinyColor( { h: ( h + 120 ) % 360, s: hsl.s, l: hsl.l } ), newTinyColor( { h: ( h + 240 ) % 360, s: hsl.s, l: hsl.l } ) ];
20101	}
20102
20103	function tetrad( color ) {
20104		var hsl = newTinyColor( color ).toHsl();
20105		var h = hsl.h;
20106		return [ newTinyColor( color ), newTinyColor( { h: ( h + 90 ) % 360, s: hsl.s, l: hsl.l } ), newTinyColor( { h: ( h + 180 ) % 360, s: hsl.s, l: hsl.l } ), newTinyColor( { h: ( h + 270 ) % 360, s: hsl.s, l: hsl.l } ) ];
20107	}
20108
20109	function splitcomplement( color ) {
20110		var hsl = newTinyColor( color ).toHsl();
20111		var h = hsl.h;
20112		return [ newTinyColor( color ), newTinyColor( { h: ( h + 72 ) % 360, s: hsl.s, l: hsl.l } ), newTinyColor( { h: ( h + 216 ) % 360, s: hsl.s, l: hsl.l } ) ];
20113	}
20114
20115	/*function analogous(color, results, slices) {
20116     results = results || 6;
20117     slices = slices || 30;
20118
20119     var hsl = newTinyColor(color).toHsl();
20120     var part = 360 / slices;
20121     var ret = [newTinyColor(color)];
20122
20123     for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results;) {
20124     hsl.h = (hsl.h + part) % 360;
20125     ret.push(newTinyColor(hsl));
20126     }
20127     return ret;
20128     }
20129
20130     function monochromatic(color, results) {
20131     var hsv;
20132     var h;
20133     var s;
20134     var v;
20135     var ret;
20136     var modification;
20137     results = results || 6;
20138     hsv = newTinyColor(color).toHsv();
20139     h = hsv.h;
20140     s = hsv.s;
20141     v = hsv.v;
20142     ret = [];
20143     modification = 1 / results;
20144
20145     while (results--) {
20146     ret.push(newTinyColor({h: h, s: s, v: v}));
20147     v = (v + modification) % 1;
20148     }
20149
20150     return ret;
20151     }*/
20152
20153	TinyColor.prototype = {
20154		isDark: function() {
20155			return this.getBrightness() < 128;
20156		},
20157		isLight: function() {
20158			return ! this.isDark();
20159		},
20160		isValid: function() {
20161			return this._ok;
20162		},
20163		getOriginalInput: function() {
20164			return this._originalInput;
20165		},
20166		getFormat: function() {
20167			return this._format;
20168		},
20169		getAlpha: function() {
20170			return this._a;
20171		},
20172		getBrightness: function() {
20173			//http://www.w3.org/TR/AERT#color-contrast
20174			var rgb = this.toRgb();
20175			return ( ( rgb.r * 299 ) + ( rgb.g * 587 ) + ( rgb.b * 114 ) ) / 1000;
20176		},
20177		getLuminance: function() {
20178			//http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
20179			var rgb = this.toRgb();
20180			var RsRGB, GsRGB, BsRGB, R, G, B;
20181			RsRGB = rgb.r / 255;
20182			GsRGB = rgb.g / 255;
20183			BsRGB = rgb.b / 255;
20184
20185			if ( RsRGB <= 0.03928 ) {
20186				R = RsRGB / 12.92;
20187			} else {
20188				R = Math.pow( ( RsRGB + 0.055 ) / 1.055, 2.4 );
20189			}
20190			if ( GsRGB <= 0.03928 ) {
20191				G = GsRGB / 12.92;
20192			} else {
20193				G = Math.pow( ( GsRGB + 0.055 ) / 1.055, 2.4 );
20194			}
20195			if ( BsRGB <= 0.03928 ) {
20196				B = BsRGB / 12.92;
20197			} else {
20198				B = Math.pow( ( BsRGB + 0.055 ) / 1.055, 2.4 );
20199			}
20200			return ( 0.2126 * R ) + ( 0.7152 * G ) + ( 0.0722 * B );
20201		},
20202		setAlpha: function( value ) {
20203			this._a = boundAlpha( value );
20204			this._roundA = mathRound( 100 * this._a ) / 100;
20205			return this;
20206		},
20207		toHsv: function() {
20208			var hsv = rgbToHsv( this._r, this._g, this._b );
20209			return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a };
20210		},
20211		toHsvString: function() {
20212			var hsv = rgbToHsv( this._r, this._g, this._b );
20213			var h = mathRound( hsv.h * 360 ),
20214				s = mathRound( hsv.s * 100 ),
20215				v = mathRound( hsv.v * 100 );
20216			return this._a === 1 ? 'hsv(' + h + ', ' + s + '%, ' + v + '%)' : 'hsva(' + h + ', ' + s + '%, ' + v + '%, ' + this._roundA + ')';
20217		},
20218		toHsl: function() {
20219			var hsl = rgbToHsl( this._r, this._g, this._b );
20220			return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a };
20221		},
20222		toHslString: function() {
20223			var hsl = rgbToHsl( this._r, this._g, this._b );
20224			var h = mathRound( hsl.h * 360 ),
20225				s = mathRound( hsl.s * 100 ),
20226				l = mathRound( hsl.l * 100 );
20227			return this._a === 1 ? 'hsl(' + h + ', ' + s + '%, ' + l + '%)' : 'hsla(' + h + ', ' + s + '%, ' + l + '%, ' + this._roundA + ')';
20228		},
20229		toHex: function( allow3Char ) {
20230			return rgbToHex( this._r, this._g, this._b, allow3Char );
20231		},
20232		toHexString: function( allow3Char ) {
20233			return '#' + this.toHex( allow3Char );
20234		},
20235		toHex8: function( allow4Char ) {
20236			return rgbaToHex( this._r, this._g, this._b, this._a, allow4Char );
20237		},
20238		toHex8String: function( allow4Char ) {
20239			return '#' + this.toHex8( allow4Char );
20240		},
20241		toRgb: function() {
20242			return { r: mathRound( this._r ), g: mathRound( this._g ), b: mathRound( this._b ), a: this._a };
20243		},
20244		toRgbString: function() {
20245			return this._a === 1 ? 'rgb(' + mathRound( this._r ) + ', ' + mathRound( this._g ) + ', ' + mathRound( this._b ) + ')' : 'rgba(' + mathRound( this._r ) + ', ' + mathRound( this._g ) + ', ' + mathRound( this._b ) + ', ' + this._roundA + ')';
20246		},
20247		toPercentageRgb: function() {
20248			return {
20249				r: mathRound( bound01( this._r, 255 ) * 100 ) + '%',
20250				g: mathRound( bound01( this._g, 255 ) * 100 ) + '%',
20251				b: mathRound( bound01( this._b, 255 ) * 100 ) + '%',
20252				a: this._a
20253			};
20254		},
20255		toPercentageRgbString: function() {
20256			return this._a === 1 ? 'rgb(' + mathRound( bound01( this._r, 255 ) * 100 ) + '%, ' + mathRound( bound01( this._g, 255 ) * 100 ) + '%, ' + mathRound( bound01( this._b, 255 ) * 100 ) + '%)'
20257				: 'rgba(' + mathRound( bound01( this._r, 255 ) * 100 ) + '%, ' + mathRound( bound01( this._g, 255 ) * 100 ) + '%, ' + mathRound( bound01( this._b, 255 ) * 100 ) + '%, ' + this._roundA + ')';
20258		},
20259		toName: function() {
20260			if ( this._a === 0 ) {
20261				return 'transparent';
20262			}
20263
20264			if ( this._a < 1 ) {
20265				return false;
20266			}
20267
20268			return hexNames[ rgbToHex( this._r, this._g, this._b, true ) ] || false;
20269		},
20270		toFilter: function( secondColor ) {
20271			var hex8String = '#' + rgbaToArgbHex( this._r, this._g, this._b, this._a );
20272			var secondHex8String = hex8String;
20273			var gradientType = this._gradientType ? 'GradientType = 1, ' : '';
20274			var s;
20275			if ( secondColor ) {
20276				s = newTinyColor( secondColor );
20277				secondHex8String = '#' + rgbaToArgbHex( s._r, s._g, s._b, s._a );
20278			}
20279
20280			return 'progid:DXImageTransform.Microsoft.gradient(' + gradientType + 'startColorstr=' + hex8String + ',endColorstr=' + secondHex8String + ')';
20281		},
20282		toString: function( format ) {
20283			var formatSet = !! format;
20284			var formattedString;
20285			var hasAlpha;
20286			var needsAlphaFormat;
20287			format = format || this._format;
20288
20289			formattedString = false;
20290			hasAlpha = this._a < 1 && this._a >= 0;
20291			needsAlphaFormat = ! formatSet && hasAlpha && ( format === 'hex' || format === 'hex6' || format === 'hex3' || format === 'hex4' || format === 'hex8' || format === 'name' );
20292
20293			if ( needsAlphaFormat ) {
20294				// Special case for "transparent", all other non-alpha formats
20295				// will return rgba when there is transparency.
20296				if ( format === 'name' && this._a === 0 ) {
20297					return this.toName();
20298				}
20299				return this.toRgbString();
20300			}
20301			if ( format === 'rgb' ) {
20302				formattedString = this.toRgbString();
20303			}
20304			if ( format === 'prgb' ) {
20305				formattedString = this.toPercentageRgbString();
20306			}
20307			if ( format === 'hex' || format === 'hex6' ) {
20308				formattedString = this.toHexString();
20309			}
20310			if ( format === 'hex3' ) {
20311				formattedString = this.toHexString( true );
20312			}
20313			if ( format === 'hex4' ) {
20314				formattedString = this.toHex8String( true );
20315			}
20316			if ( format === 'hex8' ) {
20317				formattedString = this.toHex8String();
20318			}
20319			if ( format === 'name' ) {
20320				formattedString = this.toName();
20321			}
20322			if ( format === 'hsl' ) {
20323				formattedString = this.toHslString();
20324			}
20325			if ( format === 'hsv' ) {
20326				formattedString = this.toHsvString();
20327			}
20328
20329			return formattedString || this.toHexString();
20330		},
20331		clone: function() {
20332			return newTinyColor( this.toString() );
20333		},
20334
20335		_applyModification: function( fn, args ) {
20336			var color = fn.apply( null, [ this ].concat( [].slice.call( args ) ) );
20337			this._r = color._r;
20338			this._g = color._g;
20339			this._b = color._b;
20340			this.setAlpha( color._a );
20341			return this;
20342		},
20343		lighten: function() {
20344			return this._applyModification( lighten, arguments );
20345		},
20346		brighten: function() {
20347			return this._applyModification( brighten, arguments );
20348		},
20349		darken: function() {
20350			return this._applyModification( darken, arguments );
20351		},
20352		desaturate: function() {
20353			return this._applyModification( desaturate, arguments );
20354		},
20355		saturate: function() {
20356			return this._applyModification( saturate, arguments );
20357		},
20358		greyscale: function() {
20359			return this._applyModification( greyscale, arguments );
20360		},
20361		spin: function() {
20362			return this._applyModification( spin, arguments );
20363		},
20364
20365		_applyCombination: function( fn, args ) {
20366			return fn.apply( null, [ this ].concat( [].slice.call( args ) ) );
20367		},
20368		/*analogous: function () {
20369         return this._applyCombination(analogous, arguments);
20370         },*/
20371		complement: function() {
20372			return this._applyCombination( complement, arguments );
20373		},
20374		/*monochromatic: function () {
20375         return this._applyCombination(monochromatic, arguments);
20376         },*/
20377		splitcomplement: function() {
20378			return this._applyCombination( splitcomplement, arguments );
20379		},
20380		triad: function() {
20381			return this._applyCombination( triad, arguments );
20382		},
20383		tetrad: function() {
20384			return this._applyCombination( tetrad, arguments );
20385		}
20386	};
20387
20388	// If input is an object, force 1 into "1.0" to handle ratios properly
20389	// String input requires "1.0" as input, so 1 will be treated as 1
20390	TinyColor.fromRatio = function( color, opts ) {
20391		var newColor;
20392		var i;
20393		if ( typeof color === 'object' ) {
20394			newColor = {};
20395			for ( i in color ) {
20396				if ( Object.prototype.hasOwnProperty.call( color, i ) ) {
20397					if ( i === 'a' ) {
20398						newColor[ i ] = color[ i ];
20399					} else {
20400						newColor[ i ] = convertToPercentage( color[ i ] );
20401					}
20402				}
20403			}
20404			color = newColor;
20405		}
20406
20407		return newTinyColor( color, opts );
20408	};
20409
20410	// `equals`
20411	// Can be called with any TinyColor input
20412	TinyColor.equals = function( color1, color2 ) {
20413		if ( ! color1 || ! color2 ) {
20414			return false;
20415		}
20416		return newTinyColor( color1 ).toRgbString() === newTinyColor( color2 ).toRgbString();
20417	};
20418
20419	TinyColor.random = function() {
20420		return TinyColor.fromRatio( {
20421			r: mathRandom(),
20422			g: mathRandom(),
20423			b: mathRandom()
20424		} );
20425	};
20426
20427	// Utility Functions
20428	// ---------------------
20429
20430	TinyColor.mix = function( color1, color2, amount ) {
20431		var rgb1;
20432		var rgb2;
20433		var p;
20434		var rgba;
20435
20436		amount = amount === 0 ? 0 : amount || 50;
20437
20438		rgb1 = newTinyColor( color1 ).toRgb();
20439		rgb2 = newTinyColor( color2 ).toRgb();
20440
20441		p = amount / 100;
20442
20443		rgba = {
20444			r: ( ( rgb2.r - rgb1.r ) * p ) + rgb1.r,
20445			g: ( ( rgb2.g - rgb1.g ) * p ) + rgb1.g,
20446			b: ( ( rgb2.b - rgb1.b ) * p ) + rgb1.b,
20447			a: ( ( rgb2.a - rgb1.a ) * p ) + rgb1.a
20448		};
20449
20450		return newTinyColor( rgba );
20451	};
20452
20453	// Readability Functions
20454	// ---------------------
20455	// <http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef (WCAG Version 2)
20456
20457	// `contrast`
20458	// Analyze the 2 colors and returns the color contrast defined by (WCAG Version 2)
20459	TinyColor.readability = function( color1, color2 ) {
20460		var c1 = newTinyColor( color1 );
20461		var c2 = newTinyColor( color2 );
20462		return ( Math.max( c1.getLuminance(), c2.getLuminance() ) + 0.05 ) / ( Math.min( c1.getLuminance(), c2.getLuminance() ) + 0.05 );
20463	};
20464
20465	// `isReadable`
20466	// Ensure that foreground and background color combinations meet WCAG2 guidelines.
20467	// The third argument is an optional Object.
20468	//      the 'level' property states 'AA' or 'AAA' - if missing or invalid, it defaults to 'AA';
20469	//      the 'size' property states 'large' or 'small' - if missing or invalid, it defaults to 'small'.
20470	// If the entire object is absent, isReadable defaults to {level:"AA",size:"small"}.
20471
20472	// *Example*
20473	//    TinyColor.isReadable("#000", "#111") => false
20474	//    TinyColor.isReadable("#000", "#111",{level:"AA",size:"large"}) => false
20475	TinyColor.isReadable = function( color1, color2, wcag2 ) {
20476		var readability = TinyColor.readability( color1, color2 );
20477		var wcag2Parms, out;
20478
20479		out = false;
20480
20481		wcag2Parms = validateWCAG2Parms( wcag2 );
20482		switch ( wcag2Parms.level + wcag2Parms.size ) {
20483			case 'AAsmall':
20484			case 'AAAlarge':
20485				out = readability >= 4.5;
20486				break;
20487			case 'AAlarge':
20488				out = readability >= 3;
20489				break;
20490			case 'AAAsmall':
20491				out = readability >= 7;
20492				break;
20493		}
20494		return out;
20495	};
20496
20497	// `mostReadable`
20498	// Given a base color and a list of possible foreground or background
20499	// colors for that base, returns the most readable color.
20500	// Optionally returns Black or White if the most readable color is unreadable.
20501	// *Example*
20502	//    TinyColor.mostReadable(TinyColor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:false}).toHexString(); // "#112255"
20503	//    TinyColor.mostReadable(TinyColor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:true}).toHexString();  // "#ffffff"
20504	//    TinyColor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"large"}).toHexString(); // "#faf3f3"
20505	//    TinyColor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"small"}).toHexString(); // "#ffffff"
20506	TinyColor.mostReadable = function( baseColor, colorList, args ) {
20507		var bestColor = null;
20508		var bestScore = 0;
20509		var readability;
20510		var includeFallbackColors;
20511		var level;
20512		var size;
20513		var i;
20514		args = args || {};
20515		includeFallbackColors = args.includeFallbackColors;
20516		level = args.level;
20517		size = args.size;
20518
20519		for ( i = 0; i < colorList.length; i += 1 ) {
20520			readability = TinyColor.readability( baseColor, colorList[ i ] );
20521			if ( readability > bestScore ) {
20522				bestScore = readability;
20523				bestColor = newTinyColor( colorList[ i ] );
20524			}
20525		}
20526
20527		if ( TinyColor.isReadable( baseColor, bestColor, { level: level, size: size } ) || ! includeFallbackColors ) {
20528			return bestColor;
20529		}
20530		args.includeFallbackColors = false;
20531		return TinyColor.mostReadable( baseColor, [ '#fff', '#000' ], args );
20532	};
20533
20534	// Big List of Colors
20535	// ------------------
20536	// <http://www.w3.org/TR/css3-color/#svg-color>
20537	TinyColor.names = names;
20538
20539	// Make it easy to access colors via `hexNames[hex]`
20540	TinyColor.hexNames = hexNames;
20541
20542	// Node: Export function
20543	if ( typeof window.module !== 'undefined' && window.module.exports ) {
20544		window.module.exports = TinyColor;
20545	} else if ( typeof window.define === 'function' && window.define.amd ) { // AMD/requirejs: Define the module
20546		window.define( function() {
20547			return TinyColor;
20548		} );
20549	} else { // Browser: Expose to window
20550		window.tinycolor = TinyColor;
20551	}
20552}( Math ) );
20553
20554// Spectrum Colorpicker v2.0.10
20555// https://github.com/seballot/spectrum
20556// Author: Brian Grinstead
20557// License: MIT
20558
20559( function( factory ) {
20560	'use strict';
20561
20562	if ( typeof window.define === 'function' && window.define.amd ) {
20563		// AMD
20564		window.define( [ 'jquery' ], factory );
20565	} else if ( typeof exports === 'object' && typeof module === 'object' ) {
20566		// CommonJS
20567		window.module.exports = factory( window.require( 'jquery' ) );
20568	} else {
20569		// Browser
20570		factory( window.jQuery );
20571	}
20572}( function( $ ) {
20573	'use strict';
20574
20575	var dataID = 'spectrum.id';
20576	var tinycolor = window.tinycolor;
20577	var defaultOpts = {
20578			// Callbacks
20579			beforeShow: noop,
20580			move: noop,
20581			change: noop,
20582			show: noop,
20583			hide: noop,
20584
20585			// Options
20586			color: false,
20587			flat: false, // Deprecated - use type instead
20588			type: 'color', // text, color, component or flat
20589			showInput: false,
20590			allowEmpty: false,
20591			showButtons: true,
20592			clickoutFiresChange: true,
20593			showInitial: false,
20594			showPalette: false,
20595			showPaletteOnly: false,
20596			hideAfterPaletteSelect: false,
20597			togglePaletteOnly: false,
20598			showSelectionPalette: true,
20599			localStorageKey: false,
20600			appendTo: 'body',
20601			maxSelectionSize: 8,
20602			locale: 'en',
20603			cancelText: 'cancel',
20604			chooseText: 'choose',
20605			togglePaletteMoreText: 'more',
20606			togglePaletteLessText: 'less',
20607			clearText: 'Clear Color Selection',
20608			preferredFormat: 'hex',
20609			className: '', // Deprecated - use containerClassName and replacerClassName instead.
20610			containerClassName: '',
20611			replacerClassName: '',
20612			showAlpha: false,
20613			theme: 'epo',
20614			palette: [
20615				[ '#000000', '#444444', '#5b5b5b', '#999999', '#bcbcbc', '#eeeeee', '#f3f6f4', '#ffffff' ],
20616				[ '#f44336', '#744700', '#ce7e00', '#8fce00', '#2986cc', '#16537e', '#6a329f', '#c90076' ],
20617				[ '#f4cccc', '#fce5cd', '#fff2cc', '#d9ead3', '#d0e0e3', '#cfe2f3', '#d9d2e9', '#ead1dc' ],
20618				[ '#ea9999', '#f9cb9c', '#ffe599', '#b6d7a8', '#a2c4c9', '#9fc5e8', '#b4a7d6', '#d5a6bd' ],
20619				[ '#e06666', '#f6b26b', '#ffd966', '#93c47d', '#76a5af', '#6fa8dc', '#8e7cc3', '#c27ba0' ],
20620				[ '#cc0000', '#e69138', '#f1c232', '#6aa84f', '#45818e', '#3d85c6', '#674ea7', '#a64d79' ],
20621				[ '#990000', '#b45f06', '#bf9000', '#38761d', '#134f5c', '#0b5394', '#351c75', '#741b47' ],
20622				[ '#660000', '#783f04', '#7f6000', '#274e13', '#0c343d', '#073763', '#20124d', '#4c1130' ]
20623			],
20624			selectionPalette: [],
20625			disabled: false,
20626			offset: null
20627		},
20628		spectrums = [],
20629		IE = !! /msie/i.exec( window.navigator.userAgent ),
20630		rgbaSupport = ( function() {
20631			var contains = function( str, substr ) {
20632				return ( '' + str ).indexOf( substr ) !== 1;
20633			};
20634			var elem = document.createElement( 'div' );
20635			var style = elem.style;
20636			style.cssText = 'background-color:rgba(0,0,0,.5)';
20637			return contains( style.backgroundColor, 'rgba' ) || contains( style.backgroundColor, 'hsla' );
20638		}() ),
20639		replaceInput = [
20640			'<button type="button" class="sp-replacer">',
20641			'<span class="sp-preview"><span class="sp-preview-inner"></span></span>',
20642			'<span class="sp-dd">&#9660;</span>',
20643			'</button>'
20644		].join( '' ),
20645		markup = ( function() {
20646			var i;
20647			// IE does not support gradients with multiple stops, so we need to simulate
20648			//  that for the rainbow slider with 8 divs that each have a single gradient
20649			var gradientFix = '';
20650			if ( IE ) {
20651				for ( i = 1; i <= 6; i++ ) {
20652					gradientFix += "<div class='sp-" + i + "'></div>";
20653				}
20654			}
20655
20656			return [
20657				"<div class='sp-container sp-hidden'>",
20658				"<div class='sp-palette-container'>",
20659				"<div class='sp-palette sp-thumb sp-cf'></div>",
20660				"<div class='sp-palette-button-container sp-cf'>",
20661				"<button type='button' class='sp-palette-toggle'></button>",
20662				'</div>',
20663				'</div>',
20664				"<div class='sp-picker-container'>",
20665				"<div class='sp-top sp-cf'>",
20666				"<div class='sp-fill'></div>",
20667				"<div class='sp-top-inner'>",
20668				"<div class='sp-color'>",
20669				"<div class='sp-sat'>",
20670				"<div class='sp-val'>",
20671				"<div class='sp-dragger' tabindex='0'></div>",
20672				'</div>',
20673				'</div>',
20674				'</div>',
20675				"<div class='sp-clear sp-clear-display'>",
20676				'</div>',
20677				"<div class='sp-hue'>",
20678				"<div class='sp-slider' tabindex='0'></div>",
20679				gradientFix,
20680				'</div>',
20681				'</div>',
20682				"<div class='sp-alpha'><div class='sp-alpha-inner'><div class='sp-alpha-handle' tabindex='0'></div></div></div>",
20683				'</div>',
20684				"<div class='sp-input-container sp-cf'>",
20685				"<input class='sp-input' type='text' spellcheck='false'>",
20686				'</div>',
20687				"<div class='sp-initial sp-thumb sp-cf'></div>",
20688				"<div class='sp-button-container sp-cf'>",
20689				"<button type='button' class='sp-cancel'></button>",
20690				"<button type='button' class='sp-choose'></button>",
20691				'</div>',
20692				'</div>',
20693				'</div>'
20694			].join( '' );
20695		}() );
20696
20697	function paletteTemplate( p, color, className, opts ) {
20698		var html = [];
20699		var i;
20700		var current;
20701		var tiny;
20702		var c;
20703		var formattedString;
20704		var swatchStyle;
20705		for ( i = 0; i < p.length; i++ ) {
20706			current = p[ i ];
20707			if ( current ) {
20708				tiny = tinycolor( current );
20709				c = tiny.toHsl().l < 0.5 ? 'sp-thumb-el sp-thumb-dark' : 'sp-thumb-el sp-thumb-light';
20710				c += tinycolor.equals( color, current ) ? ' sp-thumb-active' : '';
20711				formattedString = tiny.toString( opts.preferredFormat || 'rgb' );
20712				swatchStyle = rgbaSupport ? 'background-color:' + tiny.toRgbString() : 'filter:' + tiny.toFilter();
20713				html.push( '<span title="' + formattedString + '" data-color="' + tiny.toRgbString() + '" class="' + c + '" role="button" tabindex="0"><span class="sp-thumb-inner" style="' + swatchStyle + ';"></span></span>' );
20714			} else {
20715				html.push( '<span class="sp-thumb-el sp-clear-display" role="button" tabindex="0"><span class="sp-clear-palette-only" style="background-color: transparent;"></span></span>' );
20716			}
20717		}
20718		return "<div class='sp-cf " + className + "'>" + html.join( '' ) + '</div>';
20719	}
20720
20721	function hideAll() {
20722		var i;
20723		for ( i = 0; i < spectrums.length; i++ ) {
20724			if ( spectrums[ i ] ) {
20725				spectrums[ i ].hide();
20726			}
20727		}
20728	}
20729
20730	function instanceOptions( o, callbackContext ) {
20731		var opts;
20732		o.locale = o.locale || window.navigator.language;
20733		if ( o.locale ) {
20734			o.locale = o.locale.split( '-' )[ 0 ].toLowerCase();
20735		} // handle locale like "fr-FR"
20736		if ( o.locale !== 'en' && $.spectrum.localization[ o.locale ] ) {
20737			o = $.extend( {}, $.spectrum.localization[ o.locale ], o );
20738		}
20739		opts = $.extend( {}, defaultOpts, o );
20740
20741		opts.callbacks = {
20742			move: bind( opts.move, callbackContext ),
20743			change: bind( opts.change, callbackContext ),
20744			show: bind( opts.show, callbackContext ),
20745			hide: bind( opts.hide, callbackContext ),
20746			beforeShow: bind( opts.beforeShow, callbackContext )
20747		};
20748
20749		return opts;
20750	}
20751
20752	function spectrum( element, o ) {
20753		var spect;
20754
20755		var opts = instanceOptions( o, element ),
20756			type = opts.type,
20757			flat = type === 'flat',
20758			showSelectionPalette = opts.showSelectionPalette,
20759			localStorageKey = opts.localStorageKey,
20760			theme = opts.theme,
20761			callbacks = opts.callbacks,
20762			resize = throttle( reflow, 10 ),
20763			visible = false,
20764			isDragging = false,
20765			dragWidth = 0,
20766			dragHeight = 0,
20767			dragHelperHeight = 0,
20768			slideHeight = 0,
20769			alphaWidth = 0,
20770			alphaSlideHelperWidth = 0,
20771			slideHelperHeight = 0,
20772			currentHue = 0,
20773			currentSaturation = 0,
20774			currentValue = 0,
20775			currentAlpha = 1,
20776			palette = [],
20777			paletteArray = [],
20778			paletteLookup = {},
20779			selectionPalette = opts.selectionPalette.slice( 0 ),
20780			maxSelectionSize = opts.maxSelectionSize,
20781			draggingClass = 'sp-dragging',
20782			abortNextInputChange = false,
20783			shiftMovementDirection = null;
20784
20785		var doc = element.ownerDocument,
20786			boundElement = $( element ),
20787			disabled = false,
20788			container = $( markup, doc ).addClass( theme ),
20789			pickerContainer = container.find( '.sp-picker-container' ),
20790			dragger = container.find( '.sp-color' ),
20791			dragHelper = container.find( '.sp-dragger' ),
20792			slider = container.find( '.sp-hue' ),
20793			slideHelper = container.find( '.sp-slider' ),
20794			alphaSliderInner = container.find( '.sp-alpha-inner' ),
20795			alphaSlider = container.find( '.sp-alpha' ),
20796			alphaSlideHelper = container.find( '.sp-alpha-handle' ),
20797			textInput = container.find( '.sp-input' ),
20798			paletteContainer = container.find( '.sp-palette' ),
20799			initialColorContainer = container.find( '.sp-initial' ),
20800			cancelButton = container.find( '.sp-cancel' ),
20801			clearButton = container.find( '.sp-clear' ),
20802			chooseButton = container.find( '.sp-choose' ),
20803			toggleButton = container.find( '.sp-palette-toggle' ),
20804			isInput = boundElement.is( 'input' ),
20805			shouldReplace = isInput && type === 'color',
20806			replacer = shouldReplace ? $( replaceInput ).addClass( theme ).addClass( opts.className ).addClass( opts.replacerClassName ) : $( [] ),
20807			offsetElement = shouldReplace ? replacer : boundElement,
20808			previewElement = replacer.find( '.sp-preview-inner' ),
20809			initialColor = opts.color || ( isInput && boundElement.val() ),
20810			colorOnShow = false,
20811			currentPreferredFormat = opts.preferredFormat,
20812			clickoutFiresChange = ! opts.showButtons || opts.clickoutFiresChange,
20813			isEmpty = ! initialColor,
20814			allowEmpty = opts.allowEmpty;
20815
20816		// Element to be updated with the input color. Populated in initialize method
20817		var originalInputContainer = null,
20818			colorizeElement = null,
20819			colorizeElementInitialColor = null,
20820			colorizeElementInitialBackground = null;
20821
20822		//If there is a label for this element, when clicked on, show the colour picker
20823		var thisId = boundElement.attr( 'id' );
20824		var label;
20825
20826		if ( thisId !== undefined && thisId.length > 0 ) {
20827			label = $( 'label[for="' + thisId + '"]' );
20828			if ( label.length ) {
20829				label.on( 'click', function( e ) {
20830					e.preventDefault();
20831					boundElement.spectrum( 'show' );
20832					return false;
20833				} );
20834			}
20835		}
20836
20837		function applyOptions() {
20838			var i;
20839			var j;
20840			var rgb;
20841
20842			if ( opts.showPaletteOnly ) {
20843				opts.showPalette = true;
20844			}
20845
20846			toggleButton.text( opts.showPaletteOnly ? opts.togglePaletteMoreText : opts.togglePaletteLessText );
20847
20848			if ( opts.palette ) {
20849				palette = opts.palette.slice( 0 );
20850				paletteArray = Array.isArray( palette[ 0 ] ) ? palette : [ palette ];
20851				paletteLookup = {};
20852				for ( i = 0; i < paletteArray.length; i++ ) {
20853					for ( j = 0; j < paletteArray[ i ].length; j++ ) {
20854						rgb = tinycolor( paletteArray[ i ][ j ] ).toRgbString();
20855						paletteLookup[ rgb ] = true;
20856					}
20857				}
20858
20859				// if showPaletteOnly and didn't set initialcolor
20860				// set initialcolor to first palette
20861				if ( opts.showPaletteOnly && ! initialColor ) {
20862					initialColor = palette[ 0 ][ 0 ] === '' ? palette[ 0 ][ 0 ] : Object.keys( paletteLookup )[ 0 ];
20863				}
20864			}
20865
20866			container.toggleClass( 'sp-flat', flat );
20867			container.toggleClass( 'sp-input-disabled', ! opts.showInput );
20868			container.toggleClass( 'sp-alpha-enabled', opts.showAlpha );
20869			container.toggleClass( 'sp-clear-enabled', allowEmpty );
20870			container.toggleClass( 'sp-buttons-disabled', ! opts.showButtons );
20871			container.toggleClass( 'sp-palette-buttons-disabled', ! opts.togglePaletteOnly );
20872			container.toggleClass( 'sp-palette-disabled', ! opts.showPalette );
20873			container.toggleClass( 'sp-palette-only', opts.showPaletteOnly );
20874			container.toggleClass( 'sp-initial-disabled', ! opts.showInitial );
20875			container.addClass( opts.className ).addClass( opts.containerClassName );
20876
20877			reflow();
20878		}
20879
20880		function initialize() {
20881			var addOn;
20882			var appendTo;
20883			var paletteEvent;
20884
20885			if ( IE ) {
20886				container.find( '*:not(input)' ).attr( 'unselectable', 'on' );
20887			}
20888
20889			applyOptions();
20890
20891			originalInputContainer = $( '<span class="sp-original-input-container"></span>' );
20892			[ 'margin' ].forEach( function( cssProp ) {
20893				originalInputContainer.css( cssProp, boundElement.css( cssProp ) );
20894			} );
20895			// inline-flex by default, switching to flex if needed
20896			if ( boundElement.css( 'display' ) === 'block' ) {
20897				originalInputContainer.css( 'display', 'flex' );
20898			}
20899
20900			if ( shouldReplace ) {
20901				boundElement.after( replacer ).hide();
20902			} else if ( type === 'text' ) {
20903				originalInputContainer.addClass( 'sp-colorize-container' );
20904				boundElement.addClass( 'spectrum sp-colorize' ).wrap( originalInputContainer );
20905			} else if ( type === 'component' ) {
20906				boundElement.addClass( 'spectrum' ).wrap( originalInputContainer );
20907				addOn = $( [ "<div class='sp-colorize-container sp-add-on'>", "<div class='sp-colorize'></div> ", '</div>' ].join( '' ) );
20908				addOn
20909					.width( boundElement.outerHeight() + 'px' )
20910					.css( 'border-radius', boundElement.css( 'border-radius' ) )
20911					.css( 'border', boundElement.css( 'border' ) );
20912				boundElement.addClass( 'with-add-on' ).before( addOn );
20913			}
20914
20915			colorizeElement = boundElement.parent().find( '.sp-colorize' );
20916			colorizeElementInitialColor = colorizeElement.css( 'color' );
20917			colorizeElementInitialBackground = colorizeElement.css( 'background-color' );
20918
20919			if ( ! allowEmpty ) {
20920				clearButton.hide();
20921			}
20922
20923			if ( flat ) {
20924				boundElement.after( container ).hide();
20925			} else {
20926				appendTo = opts.appendTo === 'parent' ? boundElement.parent() : $( opts.appendTo );
20927				if ( appendTo.length !== 1 ) {
20928					appendTo = $( 'body' );
20929				}
20930
20931				appendTo.append( container );
20932			}
20933
20934			updateSelectionPaletteFromStorage();
20935
20936			offsetElement.on( 'click.spectrum touchstart.spectrum', function( e ) {
20937				if ( ! disabled ) {
20938					show();
20939				}
20940
20941				e.stopPropagation();
20942
20943				if ( ! $( e.target ).is( 'input' ) ) {
20944					e.preventDefault();
20945				}
20946			} );
20947
20948			if ( boundElement.is( ':disabled' ) || opts.disabled === true ) {
20949				disable();
20950			}
20951
20952			// Prevent clicks from bubbling up to document.  This would cause it to be hidden.
20953			container.on( 'click', stopPropagation );
20954
20955			container.on( 'keydown', 'div[role="button"]', function( e ) {
20956				if ( e.keyCode === 13 ) {
20957					e.preventDefault();
20958					$( this ).click();
20959				}
20960			} );
20961
20962			// Handle user typed input
20963			[ textInput, boundElement ].forEach( function( input ) {
20964				input.on( 'change', function() {
20965					setFromTextInput( input.val() );
20966				} );
20967				input.on( 'paste', function() {
20968					setTimeout( function() {
20969						setFromTextInput( input.val() );
20970					}, 1 );
20971				} );
20972				input.on( 'keydown', function( e ) {
20973					if ( e.keyCode === 13 ) {
20974						setFromTextInput( $( input ).val() );
20975						// eslint-disable-next-line eqeqeq
20976						if ( input == boundElement ) {
20977							hide();
20978						}
20979					}
20980				} );
20981			} );
20982
20983			cancelButton.text( opts.cancelText );
20984			cancelButton.on( 'click.spectrum', function( e ) {
20985				e.stopPropagation();
20986				e.preventDefault();
20987				revert();
20988				hide();
20989			} );
20990
20991			clearButton.attr( 'title', opts.clearText );
20992			clearButton.on( 'click.spectrum', function( e ) {
20993				e.stopPropagation();
20994				e.preventDefault();
20995				isEmpty = true;
20996				move();
20997
20998				if ( flat ) {
20999					//for the flat style, this is a change event
21000					updateOriginalInput( true );
21001				}
21002			} );
21003
21004			chooseButton.text( opts.chooseText );
21005			chooseButton.on( 'click.spectrum', function( e ) {
21006				e.stopPropagation();
21007				e.preventDefault();
21008
21009				if ( IE && textInput.is( ':focus' ) ) {
21010					textInput.trigger( 'change' );
21011				}
21012
21013				if ( isValid() ) {
21014					updateOriginalInput( true );
21015					hide();
21016				}
21017			} );
21018
21019			toggleButton.text( opts.showPaletteOnly ? opts.togglePaletteMoreText : opts.togglePaletteLessText );
21020			toggleButton.on( 'click.spectrum', function( e ) {
21021				e.stopPropagation();
21022				e.preventDefault();
21023
21024				opts.showPaletteOnly = ! opts.showPaletteOnly;
21025
21026				// To make sure the Picker area is drawn on the right, next to the
21027				// Palette area (and not below the palette), first move the Palette
21028				// to the left to make space for the picker, plus 5px extra.
21029				// The 'applyOptions' function puts the whole container back into place
21030				// and takes care of the button-text and the sp-palette-only CSS class.
21031				if ( ! opts.showPaletteOnly && ! flat ) {
21032					container.css( 'left', '-=' + ( pickerContainer.outerWidth( true ) + 5 ) );
21033				}
21034				applyOptions();
21035			} );
21036
21037			draggable(
21038				alphaSlider,
21039				function( dragX, dragY, e ) {
21040					currentAlpha = dragX / alphaWidth;
21041					isEmpty = false;
21042					if ( e.shiftKey ) {
21043						currentAlpha = Math.round( currentAlpha * 10 ) / 10;
21044					}
21045
21046					move();
21047				},
21048				dragStart,
21049				dragStop
21050			);
21051
21052			draggable(
21053				slider,
21054				function( dragX, dragY ) {
21055					currentHue = parseFloat( dragY / slideHeight );
21056					isEmpty = false;
21057					if ( ! opts.showAlpha ) {
21058						currentAlpha = 1;
21059					}
21060					move();
21061				},
21062				dragStart,
21063				dragStop
21064			);
21065
21066			draggable(
21067				dragger,
21068				function( dragX, dragY, e ) {
21069					var oldDragX;
21070					var oldDragY;
21071					var furtherFromX;
21072					var setSaturation;
21073					var setValue;
21074					// shift+drag should snap the movement to either the x or y axis.
21075					if ( ! e.shiftKey ) {
21076						shiftMovementDirection = null;
21077					} else if ( ! shiftMovementDirection ) {
21078						oldDragX = currentSaturation * dragWidth;
21079						oldDragY = dragHeight - ( currentValue * dragHeight );
21080						furtherFromX = Math.abs( dragX - oldDragX ) > Math.abs( dragY - oldDragY );
21081
21082						shiftMovementDirection = furtherFromX ? 'x' : 'y';
21083					}
21084
21085					setSaturation = ! shiftMovementDirection || shiftMovementDirection === 'x';
21086					setValue = ! shiftMovementDirection || shiftMovementDirection === 'y';
21087
21088					if ( setSaturation ) {
21089						currentSaturation = parseFloat( dragX / dragWidth );
21090					}
21091					if ( setValue ) {
21092						currentValue = parseFloat( ( dragHeight - dragY ) / dragHeight );
21093					}
21094
21095					isEmpty = false;
21096					if ( ! opts.showAlpha ) {
21097						currentAlpha = 1;
21098					}
21099
21100					move();
21101				},
21102				dragStart,
21103				dragStop
21104			);
21105
21106			if ( initialColor !== false && initialColor !== '' ) {
21107				set( initialColor );
21108
21109				// In case color was black - update the preview UI and set the format
21110				// since the set function will not run (default color is black).
21111				updateUI();
21112				currentPreferredFormat = tinycolor( initialColor ).format || opts.preferredFormat;
21113				addColorToSelectionPalette( initialColor );
21114			} else if ( initialColor === '' ) {
21115				set( initialColor );
21116				updateUI();
21117			} else {
21118				updateUI();
21119			}
21120
21121			if ( flat ) {
21122				show();
21123			}
21124
21125			function paletteElementClick( e ) {
21126				if ( e.data && e.data.ignore ) {
21127					set( $( e.target ).closest( '.sp-thumb-el' ).data( 'color' ) );
21128					move();
21129				} else {
21130					set( $( e.target ).closest( '.sp-thumb-el' ).data( 'color' ) );
21131					move();
21132
21133					// If the picker is going to close immediately, a palette selection
21134					// is a change.  Otherwise, it's a move only.
21135					if ( opts.hideAfterPaletteSelect ) {
21136						updateOriginalInput( true );
21137						hide();
21138					} else {
21139						updateOriginalInput();
21140					}
21141				}
21142
21143				return false;
21144			}
21145
21146			paletteEvent = IE ? 'mousedown.spectrum' : 'click.spectrum touchstart.spectrum';
21147			paletteContainer.on( paletteEvent, '.sp-thumb-el', paletteElementClick );
21148			initialColorContainer.on( paletteEvent, '.sp-thumb-el:nth-child(1)', { ignore: true }, paletteElementClick );
21149		}
21150
21151		function updateSelectionPaletteFromStorage() {
21152			var localStorage;
21153			var oldPalette;
21154
21155			if ( localStorageKey ) {
21156				// Migrate old palettes over to new format.  May want to remove this eventually.
21157				try {
21158					localStorage = window.localStorage;
21159					oldPalette = localStorage[ localStorageKey ].split( ',#' );
21160					if ( oldPalette.length > 1 ) {
21161						delete localStorage[ localStorageKey ];
21162						$.each( oldPalette, function( i, c ) {
21163							addColorToSelectionPalette( c );
21164						} );
21165					}
21166				} catch ( e ) {
21167					window.console.log( e );
21168				}
21169
21170				try {
21171					selectionPalette = window.localStorage[ localStorageKey ].split( ';' );
21172				} catch ( e ) {
21173					window.console.log( e );
21174				}
21175			}
21176		}
21177
21178		function addColorToSelectionPalette( color ) {
21179			var rgb;
21180			if ( showSelectionPalette ) {
21181				rgb = tinycolor( color ).toRgbString();
21182				if ( ! paletteLookup[ rgb ] && $.inArray( rgb, selectionPalette ) === -1 ) {
21183					selectionPalette.push( rgb );
21184					while ( selectionPalette.length > maxSelectionSize ) {
21185						selectionPalette.shift();
21186					}
21187				}
21188
21189				if ( localStorageKey ) {
21190					try {
21191						window.localStorage[ localStorageKey ] = selectionPalette.join( ';' );
21192					} catch ( e ) {
21193						window.console.log( e );
21194					}
21195				}
21196			}
21197		}
21198
21199		function getUniqueSelectionPalette() {
21200			var unique = [];
21201			var i;
21202			var rgb;
21203			if ( opts.showPalette ) {
21204				for ( i = 0; i < selectionPalette.length; i++ ) {
21205					rgb = tinycolor( selectionPalette[ i ] ).toRgbString();
21206
21207					if ( ! paletteLookup[ rgb ] ) {
21208						unique.push( selectionPalette[ i ] );
21209					}
21210				}
21211			}
21212
21213			return unique.reverse().slice( 0, opts.maxSelectionSize );
21214		}
21215
21216		function drawPalette() {
21217			var currentColor = get();
21218
21219			var html = $.map( paletteArray, function( thispalette, i ) {
21220				return paletteTemplate( thispalette, currentColor, 'sp-palette-row sp-palette-row-' + i, opts );
21221			} );
21222
21223			updateSelectionPaletteFromStorage();
21224
21225			if ( selectionPalette ) {
21226				html.push( paletteTemplate( getUniqueSelectionPalette(), currentColor, 'sp-palette-row sp-palette-row-selection', opts ) );
21227			}
21228
21229			paletteContainer.html( html.join( '' ) );
21230		}
21231
21232		function drawInitial() {
21233			var initial;
21234			var current;
21235			if ( opts.showInitial ) {
21236				initial = colorOnShow;
21237				current = get();
21238				initialColorContainer.html( paletteTemplate( [ initial, current ], current, 'sp-palette-row-initial', opts ) );
21239			}
21240		}
21241
21242		function dragStart() {
21243			if ( dragHeight <= 0 || dragWidth <= 0 || slideHeight <= 0 ) {
21244				reflow();
21245			}
21246			isDragging = true;
21247			container.addClass( draggingClass );
21248			shiftMovementDirection = null;
21249			boundElement.trigger( 'dragstart.spectrum', [ get() ] );
21250		}
21251
21252		function dragStop() {
21253			isDragging = false;
vendor: 5,558 bytes, lines 21254-21488
21254			container.removeClass( draggingClass );
21255			boundElement.trigger( 'dragstop.spectrum', [ get() ] );
21256		}
21257
21258		function setFromTextInput( value ) {
21259			var tiny;
21260			if ( abortNextInputChange ) {
21261				abortNextInputChange = false;
21262				return;
21263			}
21264			if ( ( value === null || value === '' ) && allowEmpty ) {
21265				set( null );
21266				move();
21267				updateOriginalInput();
21268			} else {
21269				tiny = tinycolor( value );
21270				if ( tiny.isValid() ) {
21271					set( tiny );
21272					move();
21273					updateOriginalInput();
21274				} else {
21275					textInput.addClass( 'sp-validation-error' );
21276				}
21277			}
21278		}
21279
21280		function toggle() {
21281			if ( visible ) {
21282				hide();
21283			} else {
21284				show();
21285			}
21286		}
21287
21288		function show() {
21289			// debugger;
21290			var event = $.Event( 'beforeShow.spectrum' );
21291
21292			if ( visible ) {
21293				reflow();
21294				return;
21295			}
21296
21297			boundElement.trigger( event, [ get() ] );
21298
21299			if ( callbacks.beforeShow( get() ) === false || event.isDefaultPrevented() ) {
21300				return;
21301			}
21302
21303			hideAll();
21304			visible = true;
21305
21306			$( doc ).on( 'keydown.spectrum', onkeydown );
21307			$( doc ).on( 'click.spectrum', clickout );
21308			$( window ).on( 'resize.spectrum', resize );
21309			replacer.attr( 'aria-expanded', true );
21310			replacer.addClass( 'sp-active' );
21311			container.removeClass( 'sp-hidden' );
21312
21313			reflow();
21314			updateUI();
21315
21316			colorOnShow = get();
21317
21318			drawInitial();
21319			callbacks.show( colorOnShow );
21320			boundElement.trigger( 'show.spectrum', [ colorOnShow ] );
21321		}
21322
21323		function onkeydown( e ) {
21324			// Close on ESC
21325			if ( e.keyCode === 27 ) {
21326				hide();
21327			}
21328		}
21329
21330		function clickout( e ) {
21331			// Return on right click.
21332			if ( e.button === 2 ) {
21333				return;
21334			}
21335
21336			// If a drag event was happening during the mouseup, don't hide
21337			// on click.
21338			if ( isDragging ) {
21339				return;
21340			}
21341
21342			if ( clickoutFiresChange ) {
21343				updateOriginalInput( true );
21344			} else {
21345				revert();
21346			}
21347			hide();
21348		}
21349
21350		function hide() {
21351			// Return if hiding is unnecessary
21352			if ( ! visible || flat ) {
21353				return;
21354			}
21355			visible = false;
21356
21357			$( doc ).off( 'keydown.spectrum', onkeydown );
21358			$( doc ).off( 'click.spectrum', clickout );
21359			$( window ).off( 'resize.spectrum', resize );
21360
21361			replacer.attr( 'aria-expanded', false );
21362			replacer.removeClass( 'sp-active' );
21363			container.addClass( 'sp-hidden' );
21364
21365			callbacks.hide( get() );
21366			boundElement.trigger( 'hide.spectrum', [ get() ] );
21367		}
21368
21369		function revert() {
21370			set( colorOnShow, true );
21371			updateOriginalInput( true );
21372		}
21373
21374		function set( color, ignoreFormatChange ) {
21375			var newColor, newHsv;
21376			if ( tinycolor.equals( color, get() ) ) {
21377				// Update UI just in case a validation error needs
21378				// to be cleared.
21379				updateUI();
21380				return;
21381			}
21382
21383			if ( ( ! color || color === undefined ) && allowEmpty ) {
21384				isEmpty = true;
21385			} else {
21386				isEmpty = false;
21387				newColor = tinycolor( color );
21388				newHsv = newColor.toHsv();
21389
21390				currentHue = ( newHsv.h % 360 ) / 360;
21391				currentSaturation = newHsv.s;
21392				currentValue = newHsv.v;
21393				currentAlpha = newHsv.a;
21394			}
21395			updateUI();
21396
21397			if ( newColor && newColor.isValid() && ! ignoreFormatChange ) {
21398				currentPreferredFormat = opts.preferredFormat || newColor.getFormat();
21399			}
21400		}
21401
21402		function get( thisopts ) {
21403			thisopts = thisopts || {};
21404
21405			if ( allowEmpty && isEmpty ) {
21406				return null;
21407			}
21408
21409			return tinycolor.fromRatio(
21410				{
21411					h: currentHue,
21412					s: currentSaturation,
21413					v: currentValue,
21414					a: Math.round( currentAlpha * 1000 ) / 1000
21415				},
21416				{ format: thisopts.format || currentPreferredFormat }
21417			);
21418		}
21419
21420		function isValid() {
21421			return ! textInput.hasClass( 'sp-validation-error' );
21422		}
21423
21424		function move() {
21425			updateUI();
21426
21427			callbacks.move( get() );
21428			boundElement.trigger( 'move.spectrum', [ get() ] );
21429		}
21430
21431		function updateUI() {
21432			var flatColor;
21433			var format;
21434			var realColor;
21435			var displayColor;
21436			var realHex;
21437			var realRgb;
21438			var rgb;
21439			var realAlpha;
21440			var gradient;
21441			var color;
21442			var textColor;
21443
21444			textInput.removeClass( 'sp-validation-error' );
21445
21446			updateHelperLocations();
21447
21448			// Update dragger background color (gradients take care of saturation and value).
21449			flatColor = tinycolor.fromRatio( { h: currentHue, s: 1, v: 1 } );
21450			dragger.css( 'background-color', flatColor.toHexString() );
21451
21452			// Get a format that alpha will be included in (hex and names ignore alpha)
21453			format = currentPreferredFormat;
21454			if ( currentAlpha < 1 && ! ( currentAlpha === 0 && format === 'name' ) ) {
21455				if ( format === 'hex' || format === 'hex3' || format === 'hex6' || format === 'name' ) {
21456					format = 'rgb';
21457				}
21458			}
21459
21460			realColor = get( { format: format } );
21461			displayColor = '';
21462
21463			//reset background info for preview element
21464			previewElement.removeClass( 'sp-clear-display' );
21465			previewElement.css( 'background-color', 'transparent' );
21466
21467			if ( ! realColor && allowEmpty ) {
21468				// Update the replaced elements background with icon indicating no color selection
21469				previewElement.addClass( 'sp-clear-display' );
21470			} else {
21471				realHex = realColor.toHexString();
21472				realRgb = realColor.toRgbString();
21473
21474				// Update the replaced elements background color (with actual selected color)
21475				if ( rgbaSupport || realColor.alpha === 1 ) {
21476					previewElement.css( 'background-color', realRgb );
21477				} else {
21478					previewElement.css( 'background-color', 'transparent' );
21479					previewElement.css( 'filter', realColor.toFilter() );
21480				}
21481
21482				if ( opts.showAlpha ) {
21483					rgb = realColor.toRgb();
21484					rgb.a = 0;
21485					realAlpha = tinycolor( rgb ).toRgbString();
21486					gradient = 'linear-gradient(left, ' + realAlpha + ', ' + realHex + ')';
21487
21488					if ( IE ) {
21489						alphaSliderInner.css( 'filter', tinycolor( realAlpha ).toFilter( { gradientType: 1 }, realHex ) );
21490					} else {
21491						alphaSliderInner.css( 'background', '-webkit-' + gradient );
21492						alphaSliderInner.css( 'background', '-moz-' + gradient );
21493						alphaSliderInner.css( 'background', '-ms-' + gradient );
21494						// Use current syntax gradient on unprefixed property.
21495						alphaSliderInner.css( 'background', 'linear-gradient(to right, ' + realAlpha + ', ' + realHex + ')' );
21496					}
21497				}
21498
21499				displayColor = realColor.toString( format );
21500			}
21501
21502			// Update the text entry input as it changes happen
21503			if ( opts.showInput ) {
21504				textInput.val( displayColor );
21505			}
21506			boundElement.val( displayColor );
21507			if ( opts.type === 'text' || opts.type === 'component' ) {
21508				color = realColor;
21509				if ( color && colorizeElement ) {
21510					textColor = color.isLight() || color.getAlpha() < 0.4 ? 'black' : 'white';
21511					colorizeElement.css( 'background-color', color.toRgbString() ).css( 'color', textColor );
21512				} else {
21513					colorizeElement.css( 'background-color', colorizeElementInitialBackground ).css( 'color', colorizeElementInitialColor );
21514				}
21515			}
21516
21517			if ( opts.showPalette ) {
21518				drawPalette();
21519			}
21520
21521			drawInitial();
21522		}
21523
21524		function updateHelperLocations() {
21525			var s = currentSaturation;
21526			var v = currentValue;
21527			var dragX;
21528			var dragY;
21529			var alphaX;
21530			var slideY;
21531
21532			if ( allowEmpty && isEmpty ) {
21533				//if selected color is empty, hide the helpers
21534				alphaSlideHelper.hide();
21535				slideHelper.hide();
21536				dragHelper.hide();
21537			} else {
21538				//make sure helpers are visible
21539				alphaSlideHelper.show();
21540				slideHelper.show();
21541				dragHelper.show();
21542
21543				// Where to show the little circle in that displays your current selected color
21544				dragX = s * dragWidth;
21545				dragY = dragHeight - ( v * dragHeight );
21546				dragX = Math.max( -dragHelperHeight, Math.min( dragWidth - dragHelperHeight, dragX - dragHelperHeight ) );
21547				dragY = Math.max( -dragHelperHeight, Math.min( dragHeight - dragHelperHeight, dragY - dragHelperHeight ) );
21548				dragHelper.css( {
21549					top: dragY + 'px',
21550					left: dragX + 'px'
21551				} );
21552
21553				alphaX = currentAlpha * alphaWidth;
21554				alphaSlideHelper.css( {
21555					left: alphaX - ( alphaSlideHelperWidth / 2 ) + 'px'
21556				} );
21557
21558				// Where to show the bar that displays your current selected hue
21559				slideY = currentHue * slideHeight;
21560				slideHelper.css( {
21561					top: slideY - slideHelperHeight + 'px'
21562				} );
21563			}
21564		}
21565
21566		function updateOriginalInput( fireCallback ) {
21567			var color = get(),
21568				hasChanged = ! tinycolor.equals( color, colorOnShow );
21569
21570			if ( color ) {
21571				// Update the selection palette with the current color
21572				addColorToSelectionPalette( color );
21573			}
21574
21575			if ( fireCallback && hasChanged ) {
21576				callbacks.change( color );
21577				// we trigger the change event or input, but the input change event is also binded
21578				// to some spectrum processing, that we do no need
21579				abortNextInputChange = true;
21580				boundElement.trigger( 'change', [ color ] );
21581			}
21582		}
21583
21584		function reflow() {
21585			if ( ! visible ) {
21586				return; // Calculations would be useless and wouldn't be reliable anyways
21587			}
21588			dragWidth = dragger.width();
21589			dragHeight = dragger.height();
21590			dragHelperHeight = dragHelper.height();
21591			slideHeight = slider.height();
21592			slideHelperHeight = slideHelper.height();
21593			alphaWidth = alphaSlider.width();
21594			alphaSlideHelperWidth = alphaSlideHelper.width();
21595
21596			if ( ! flat ) {
21597				container.attr( 'aria-haspopup', true );
21598				container.css( 'position', 'absolute' );
21599				if ( opts.offset ) {
21600					container.offset( opts.offset );
21601				} else {
21602					container.offset( getOffset( container, offsetElement ) );
21603				}
21604			}
21605
21606			updateHelperLocations();
21607
21608			if ( opts.showPalette ) {
21609				drawPalette();
21610			}
21611
21612			boundElement.trigger( 'reflow.spectrum' );
21613		}
21614
21615		function destroy() {
21616			var oInputContainer;
21617			boundElement.show().removeClass( 'spectrum with-add-on sp-colorize' );
21618			offsetElement.off( 'click.spectrum touchstart.spectrum' );
21619			container.remove();
21620			replacer.remove();
21621			if ( colorizeElement ) {
21622				colorizeElement.css( 'background-color', colorizeElementInitialBackgroun
21622d ).css( 'color', colorizeElementInitialColor );
21623			}
21624			oInputContainer = boundElement.closest( '.sp-original-input-container' );
21625			if ( oInputContainer.length > 0 ) {
21626				oInputContainer.after( boundElement ).remove();
21627			}
21628			spectrums[ spect.id ] = null;
21629		}
21630
21631		function option( optionName, optionValue ) {
21632			if ( optionName === undefined ) {
21633				return $.extend( {}, opts );
21634			}
21635			if ( optionValue === undefined ) {
21636				return opts[ optionName ];
21637			}
21638
21639			opts[ optionName ] = optionValue;
21640
21641			if ( optionName === 'preferredFormat' ) {
21642				currentPreferredFormat = opts.preferredFormat;
21643			}
21644			applyOptions();
21645		}
21646
21647		function enable() {
21648			disabled = false;
21649			boundElement.attr( 'disabled', false );
21650			offsetElement.removeClass( 'sp-disabled' );
21651		}
21652
21653		function disable() {
21654			hide();
21655			disabled = true;
21656			boundElement.attr( 'disabled', true );
21657			offsetElement.addClass( 'sp-disabled' );
21658		}
21659
21660		function setOffset( coord ) {
21661			opts.offset = coord;
21662			reflow();
21663		}
21664
21665		initialize();
21666
21667		spect = {
21668			show: show,
21669			hide: hide,
21670			toggle: toggle,
21671			reflow: reflow,
21672			option: option,
21673			enable: enable,
21674			disable: disable,
21675			offset: setOffset,
21676			set: function( c ) {
21677				set( c );
21678				updateOriginalInput();
21679			},
21680			get: get,
21681			destroy: destroy,
21682			container: container
21683		};
21684
21685		spect.id = spectrums.push( spect ) - 1;
21686
21687		return spect;
21688	}
21689
21690	// checkOffset - get the offset below/above and left/right element depending on screen position
21691	// Thanks https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.datepicker.js
21692	function getOffset( picker, input ) {
21693		var extraY = 0;
21694		var dpWidth = picker.outerWidth();
21695		var dpHeight = picker.outerHeight();
21696		var inputHeight = input.outerHeight();
21697		var doc = picker[ 0 ].ownerDocument;
21698		var docElem = doc.documentElement;
21699		var viewWidth = docElem.clientWidth + $( doc ).scrollLeft();
21700		var viewHeight = docElem.clientHeight + $( doc ).scrollTop();
21701		var offset = input.offset();
21702		var offsetLeft = offset.left;
21703		var offsetTop = offset.top;
21704
21705		offsetTop += inputHeight;
21706
21707		offsetLeft -= Math.min( offsetLeft, offsetLeft + dpWidth > viewWidth && viewWidth > dpWidth ? Math.abs( offsetLeft + dpWidth - viewWidth ) : 0 );
21708
21709		offsetTop -= Math.min( offsetTop, offsetTop + dpHeight > viewHeight && viewHeight > dpHeight ? Math.abs( dpHeight + inputHeight - extraY ) : extraY );
21710
21711		return {
21712			top: offsetTop,
21713			bottom: offset.bottom,
21714			left: offsetLeft,
21715			right: offset.right,
21716			width: offset.width,
21717			height: offset.height
21718		};
21719	}
21720
21721	// noop - do nothing
21722	function noop() {}
21723
21724	// stopPropagation - makes the code only doing this a little easier to read in line
21725	function stopPropagation( e ) {
21726		e.stopPropagation();
21727	}
21728
21729	// Create a function bound to a given object
21730	// Thanks to underscore.js
21731	function bind( func, obj ) {
21732		var slice = Array.prototype.slice;
21733		var args = slice.call( arguments, 2 );
21734		return function() {
21735			return func.apply( obj, args.concat( slice.call( arguments ) ) );
21736		};
21737	}
21738
21739	// Lightweight drag helper.  Handles containment within the element, so that
21740	// when dragging, the x is within [0,element.width] and y is within [0,element.height]
21741	function draggable( element, onmove, onstart, onstop ) {
21742		var doc = document;
21743		var dragging = false;
21744		var offset = {};
21745		var maxHeight = 0;
21746		var maxWidth = 0;
21747		var hasTouch = 'ontouchstart' in window;
21748		var duringDragEvents = {};
21749
21750		onmove = onmove || function() {};
21751		onstart = onstart || function() {};
21752		onstop = onstop || function() {};
21753
21754		duringDragEvents.selectstart = prevent;
21755		duringDragEvents.dragstart = prevent;
21756		duringDragEvents[ 'touchmove mousemove' ] = move;
21757		duringDragEvents[ 'touchend mouseup' ] = stop;
21758
21759		function prevent( e ) {
21760			if ( e.stopPropagation ) {
21761				e.stopPropagation();
21762			}
21763			if ( e.preventDefault ) {
21764				e.preventDefault();
21765			}
21766			e.returnValue = false;
21767		}
21768
21769		function move( e ) {
21770			var t0;
21771			var pageX;
21772			var pageY;
21773			var dragX;
21774			var dragY;
21775			if ( dragging ) {
21776				// Mouseup happened outside of window
21777				if ( IE && doc.documentMode < 9 && ! e.button ) {
21778					return stop();
21779				}
21780
21781				t0 = e.originalEvent && e.originalEvent.touches && e.originalEvent.touches[ 0 ];
21782				pageX = ( t0 && t0.pageX ) || e.pageX;
21783				pageY = ( t0 && t0.pageY ) || e.pageY;
21784
21785				dragX = Math.max( 0, Math.min( pageX - offset.left, maxWidth ) );
21786				dragY = Math.max( 0, Math.min( pageY - offset.top, maxHeight ) );
21787
21788				if ( hasTouch ) {
21789					// Stop scrolling in iOS
21790					prevent( e );
21791				}
21792
21793				onmove.apply( element, [ dragX, dragY, e ] );
21794			}
21795		}
21796
21797		function start( e ) {
21798			var rightclick = e.which ? e.which === 3 : e.button === 2;
21799
21800			if ( ! rightclick && ! dragging ) {
21801				if ( onstart.apply( element, arguments ) !== false ) {
21802					dragging = true;
21803					maxHeight = $( element ).height();
21804					maxWidth = $( element ).width();
21805					offset = $( element ).offset();
21806
21807					$( doc ).on( duringDragEvents );
21808					$( doc.body ).addClass( 'sp-dragging' );
21809
21810					move( e );
21811
21812					prevent( e );
21813				}
21814			}
21815		}
21816
21817		function stop() {
21818			if ( dragging ) {
21819				$( doc ).off( duringDragEvents );
21820				$( doc.body ).removeClass( 'sp-dragging' );
21821
21822				// Wait a tick before notifying observers to allow the click event
21823				// to fire in Chrome.
21824				setTimeout( function() {
21825					onstop.apply( element, arguments );
21826				}, 0 );
21827			}
21828			dragging = false;
21829		}
21830
21831		$( element ).on( 'touchstart mousedown', start );
21832	}
21833
21834	function throttle( func, wait, debounce ) {
21835		var timeout;
21836		return function() {
21837			var context = this,
21838				args = arguments;
21839			var throttler = function() {
21840				timeout = null;
21841				func.apply( context, args );
21842			};
21843			if ( debounce ) {
21844				clearTimeout( timeout );
21845			}
21846			if ( debounce || ! timeout ) {
21847				timeout = setTimeout( throttler, wait );
21848			}
21849		};
21850	}
21851
21852	function inputTypeColorSupport() {
21853		return $.fn.spectrum.inputTypeColorSupport();
21854	}
21855
21856	// Define a jQuery plugin
21857	$.fn.spectrum = function( opts ) {
21858		var returnValue;
21859		var args;
21860		var spect;
21861		var method;
21862
21863		if ( typeof opts === 'string' ) {
21864			returnValue = this;
21865			args = Array.prototype.slice.call( arguments, 1 );
21866
21867			this.each( function() {
21868				spect = spectrums[ $( this ).data( dataID ) ];
21869				if ( spect ) {
21870					method = spect[ opts ];
21871					if ( ! method ) {
21872						throw new Error( "Spectrum: no such method: '" + opts + "'" );
21873					}
21874
21875					if ( opts === 'get' ) {
21876						returnValue = spect.get();
21877					} else if ( opts === 'container' ) {
21878						returnValue = spect.container;
21879					} else if ( opts === 'option' ) {
21880						returnValue = spect.option.apply( spect, args );
21881					} else if ( opts === 'destroy' ) {
21882						spect.destroy();
21883						$( this ).removeData( dataID );
21884					} else {
21885						method.apply( spect, args );
21886					}
21887				}
21888			} );
21889
21890			return returnValue;
21891		}
21892
21893		// Initializing a new instance of spectrum
21894		return this.spectrum( 'destroy' ).each( function() {
21895			var options = $.extend( {}, $( this ).data(), opts );
21896			var thisspect;
21897			// Infer default type from input params and deprecated options
21898			if ( ! $( this ).is( 'input' ) ) {
21899				options.type = 'noInput';
21900			} else if ( options.flat || options.type === 'flat' ) {
21901				options.type = 'flat';
21902			} else if ( $( this ).attr( 'type' ) === 'color' ) {
21903				options.type = 'color';
21904			}
21905
21906			thisspect = spectrum( this, options );
21907			$( this ).data( dataID, thisspect.id );
21908		} );
21909	};
21910
21911	$.fn.spectrum.load = true;
21912	$.fn.spectrum.loadOpts = {};
21913	$.fn.spectrum.draggable = draggable;
21914	$.fn.spectrum.defaults = defaultOpts;
21915	$.fn.spectrum.inputTypeColorSupport = function() {
21916		var colorInput;
21917		if ( typeof inputTypeColorSupport._cachedResult === 'undefined' ) {
21918			colorInput = $( "<input type='color'>" )[ 0 ]; // if color element is supported, value will default to not null
21919			inputTypeColorSupport._cachedResult = colorInput.type === 'color' && colorInput.value !== '';
21920		}
21921		return inputTypeColorSupport._cachedResult;
21922	};
21923
21924	$.spectrum = {};
21925	$.spectrum.localization = {};
21926	$.spectrum.palettes = {};
21927
21928	$.fn.spectrum.processNativeColorInputs = function() {
21929		var colorInputs = $( 'input[type=color]' );
21930		if ( colorInputs.length && ! inputTypeColorSupport() ) {
21931			colorInputs.spectrum( {
21932				preferredFormat: 'hex6'
21933			} );
21934		}
21935	};
21936
21937	$( function() {
21938		if ( $.fn.spectrum.load ) {
21939			$.fn.spectrum.processNativeColorInputs();
21940		}
21941	} );
21942} ) );
21943;
21944/**
21945 * jquery.mask.js
21946 *
21947 * @version: v1.14.16
21948 * @author: Igor Escobar
21949 *
21950 * Created by Igor Escobar on 2012-03-10. Please report any bug at github.com/igorescobar/jQuery-Mask-Plugin
21951 *
21952 * Copyright (c) 2012 Igor Escobar http://igorescobar.com
21953 *
21954 * The MIT License (http://www.opensource.org/licenses/mit-license.php)
21955 *
21956 * Permission is hereby granted, free of charge, to any person
21957 * obtaining a copy of this software and associated documentation
21958 * files (the "Software"), to deal in the Software without
21959 * restriction, including without limitation the rights to use,
21960 * copy, modify, merge, publish, distribute, sublicense, and/or sell
21961 * copies of the Software, and to permit persons to whom the
21962 * Software is furnished to do so, subject to the following
21963 * conditions:
21964 *
21965 * The above copyright notice and this permission notice shall be
21966 * included in all copies or substantial portions of the Software.
21967 *
21968 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21969 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
21970 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21971 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21972 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21973 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21974 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21975 * OTHER DEALINGS IN THE SOFTWARE.
21976 */
21977
21978/* jshint laxbreak: true */
21979/* jshint maxcomplexity:17 */
21980/* global define */
21981
21982// UMD (Universal Module Definition) patterns for JavaScript modules that work everywhere.
21983// https://github.com/umdjs/umd/blob/master/templates/jqueryPlugin.js
21984// reformatted code by ThemeComplete
21985( function( factory, jQuery, Zepto ) {
21986	'use strict';
21987
21988	if ( typeof define === 'function' && define.amd ) {
21989		define( [ 'jquery' ], factory );
21990	} else if ( typeof exports === 'object' && typeof Meteor === 'undefined' ) {
21991		window.module.exports = factory( window.require( 'jquery' ) );
21992	} else {
21993		factory( jQuery || Zepto );
21994	}
21995}(
21996	function( $ ) {
21997		'use strict';
21998
21999		var JSON = window.JSON;
22000		var globals;
22001		var Mask = function( el, mask, options ) {
22002			var jMask = this;
22003			var regexMask;
22004			var oldValue;
22005
22006			var p = {
22007				invalid: [],
22008				getCaret: function() {
22009					var sel,
22010						pos = 0,
22011						ctrl,
22012						dSel,
22013						cSelStart;
22014					try {
22015						ctrl = el.get( 0 );
22016						dSel = document.selection;
22017						cSelStart = ctrl.selectionStart;
22018
22019						// IE Support
22020						if ( dSel && navigator.appVersion.indexOf( 'MSIE 10' ) === -1 ) {
22021							sel = dSel.createRange();
22022							sel.moveStart( 'character', -p.val().length );
22023							pos = sel.text.length;
22024						} else if ( cSelStart || cSelStart === '0' ) { // Firefox support
22025							pos = cSelStart;
22026						}
22027
22028						return pos;
22029					} catch ( err ) {
22030						window.console.log( err );
22031					}
22032				},
22033				setCaret: function( pos ) {
22034					var range;
22035					var ctrl;
22036					try {
22037						if ( el.is( ':focus' ) ) {
22038							ctrl = el.get( 0 );
22039
22040							// Firefox, WebKit, etc..
22041							if ( ctrl.setSelectionRange ) {
22042								ctrl.setSelectionRange( pos, pos );
22043							} else {
22044								// IE
22045								range = ctrl.createTextRange();
22046								range.collapse( true );
22047								range.moveEnd( 'character', pos );
22048								range.moveStart( 'character', pos );
22049								range.select();
22050							}
22051						}
22052					} catch ( err ) {
22053						window.console.log( err );
22054					}
22055				},
22056				events: function() {
22057					el.on( 'keydown.mask', function( e ) {
22058						el.data( 'mask-keycode', e.keyCode || e.which );
22059						el.data( 'mask-previus-value', el.val() );
22060						el.data( 'mask-previus-caret-pos', p.getCaret() );
22061						p.maskDigitPosMapOld = p.maskDigitPosMap;
22062					} )
22063						.on( $.jMaskGlobals.useInput ? 'input.mask' : 'keyup.mask', p.behaviour )
22064						.on( 'paste.mask drop.mask', function() {
22065							setTimeout( function() {
22066								el.keydown().keyup();
22067							}, 100 );
22068						} )
22069						.on( 'change.mask', function() {
22070							el.data( 'changed', true );
22071						} )
22072						.on( 'blur.mask', function() {
22073							if ( oldValue !== p.val() && ! el.data( 'changed' ) ) {
22074								el.trigger( 'change' );
22075							}
22076							el.data( 'changed', false );
22077						} )
22078						// it's very important that this callback remains in this position
22079						// otherwhise oldValue it's going to work buggy
22080						.on( 'blur.mask', function() {
22081							oldValue = p.val();
22082						} )
22083						// select all text on focus
22084						.on( 'focus.mask', function( e ) {
22085							if ( options.selectOnFocus === true ) {
22086								$( e.target ).select();
22087							}
22088						} )
22089						// clear the value if it not complete the mask
22090						.on( 'focusout.mask', function() {
22091							if ( options.clearIfNotMatch && ! regexMask.test( p.val() ) ) {
22092								p.val( '' );
22093							}
22094						} );
22095				},
22096				getRegexMask: function() {
22097					var maskChunks = [],
22098						translation,
22099						pattern,
22100						optional,
22101						recursive,
22102						oRecursive,
22103						r;
22104					var i;
22105
22106					for ( i = 0; i < mask.length; i += 1 ) {
22107						translation = jMask.translation[ mask.charAt( i ) ];
22108
22109						if ( translation ) {
22110							pattern = translation.pattern.toString().replace( /.{1}$|^.{1}/g, '' );
22111							optional = translation.optional;
22112							recursive = translation.recursive;
22113
22114							if ( recursive ) {
22115								maskChunks.push( mask.charAt( i ) );
22116								oRecursive = {
22117									digit: mask.charAt( i ),
22118									pattern: pattern
22119								};
22120							} else {
22121								maskChunks.push( ! optional && ! recursive ? pattern : pattern + '?' );
22122							}
22123						} else {
22124							// eslint-disable-next-line no-useless-escape
22125							maskChunks.push( mask.charAt( i ).replace( /[-\/\\^$*+?.()|[\]{}]/g, '\\$&' ) );
22126						}
22127					}
22128
22129					r = maskChunks.join( '' );
22130
22131					if ( oRecursive ) {
22132						r = r.replace( new RegExp( '(' + oRecursive.digit + '(.*' + oRecursive.digit + ')?)' ), '($1)?' ).replace( new RegExp( oRecursive.digit, 'g' ), oRecursive.pattern );
22133					}
22134
22135					return new RegExp( r );
22136				},
22137				destroyEvents: function() {
22138					el.off( [ 'input', 'keydown', 'keyup', 'paste', 'drop', 'blur', 'focusout', '' ].join( '.mask ' ) );
22139				},
22140				val: function( v ) {
22141					var isInput = el.is( 'input' ),
22142						method = isInput ? 'val' : 'text',
22143						r;
22144
22145					if ( arguments.length > 0 ) {
22146						if ( el[ method ]() !== v ) {
22147							el[ method ]( v );
22148						}
22149						r = el;
22150					} else {
22151						r = el[ method ]();
22152					}
22153
22154					return r;
22155				},
22156				calculateCaretPosition: function( oldVal ) {
22157					var newVal = p.getMasked(),
22158						caretPosNew = p.getCaret();
22159					var caretPosOld,
22160						newValL,
22161						oldValL,
22162						maskDigitsBeforeCaret = 0,
22163						maskDigitsAfterCaret = 0,
22164						maskDigitsBeforeCaretAll = 0,
22165						maskDigitsBeforeCaretAllOld = 0,
22166						i = 0;
22167					var caretPos;
22168					if ( oldVal !== newVal ) {
22169						caretPosOld = el.data( 'mask-previus-caret-pos' ) || 0;
22170						newValL = newVal.length;
22171						oldValL = oldVal.length;
22172
22173						for ( i = caretPosNew; i < newValL; i += 1 ) {
22174							if ( ! p.maskDigitPosMap[ i ] ) {
22175								break;
22176							}
22177							maskDigitsAfterCaret = maskDigitsAfterCaret + 1;
22178						}
22179
22180						for ( i = caretPosNew - 1; i >= 0; i -= 1 ) {
22181							if ( ! p.maskDigitPosMap[ i ] ) {
22182								break;
22183							}
22184							maskDigitsBeforeCaret = maskDigitsBeforeCaret + 1;
22185						}
22186
22187						for ( i = caretPosNew - 1; i >= 0; i -= 1 ) {
22188							if ( p.maskDigitPosMap[ i ] ) {
22189								maskDigitsBeforeCaretAll = maskDigitsBeforeCaretAll + 1;
22190							}
22191						}
22192
22193						for ( i = caretPosOld - 1; i >= 0; i -= 1 ) {
22194							if ( p.maskDigitPosMapOld[ i ] ) {
22195								maskDigitsBeforeCaretAllOld = maskDigitsBeforeCaretAllOld + 1;
22196							}
22197						}
22198
22199						// if the cursor is at the end keep it there
22200						if ( caretPosNew > oldValL ) {
22201							caretPosNew = newValL * 10;
22202						} else if ( caretPosOld >
22202= caretPosNew && caretPosOld !== oldValL ) {
22203							if ( ! p.maskDigitPosMapOld[ caretPosNew ] ) {
22204								caretPos = caretPosNew;
22205								caretPosNew -= maskDigitsBeforeCaretAllOld - maskDigitsBeforeCaretAll;
22206								caretPosNew -= maskDigitsBeforeCaret;
22207								if ( p.maskDigitPosMap[ caretPosNew ] ) {
22208									caretPosNew = caretPos;
22209								}
22210							}
22211						} else if ( caretPosNew > caretPosOld ) {
22212							caretPosNew += maskDigitsBeforeCaretAll - maskDigitsBeforeCaretAllOld;
22213							caretPosNew += maskDigitsAfterCaret;
22214						}
22215					}
22216					return caretPosNew;
22217				},
22218				behaviour: function( e ) {
22219					var keyCode = el.data( 'mask-keycode' );
22220					var newVal;
22221					var oldVal;
22222					var caretPos;
22223					e = e || window.event;
22224					p.invalid = [];
22225
22226					if ( $.inArray( keyCode, jMask.byPassKeys ) === -1 ) {
22227						newVal = p.getMasked();
22228						caretPos = p.getCaret();
22229						oldVal = el.data( 'mask-previus-value' ) || '';
22230
22231						// this is a compensation to devices/browsers that don't compensate
22232						// caret positioning the right way
22233						setTimeout( function() {
22234							p.setCaret( p.calculateCaretPosition( oldVal ) );
22235						}, $.jMaskGlobals.keyStrokeCompensation );
22236
22237						p.val( newVal );
22238						p.setCaret( caretPos );
22239						return p.callbacks( e );
22240					}
22241				},
22242				getMasked: function( skipMaskChars, val ) {
22243					var buf = [],
22244						value = val === undefined ? p.val() : val + '',
22245						m = 0,
22246						maskLen = mask.length,
22247						v = 0,
22248						valLen = value.length,
22249						offset = 1,
22250						addMethod = 'push',
22251						resetPos = -1,
22252						maskDigitCount = 0,
22253						maskDigitPosArr = [],
22254						lastMaskChar,
22255						check;
22256					var lastUntranslatedMaskChar;
22257					var maskDigit;
22258					var valDigit;
22259					var translation;
22260					var lastMaskCharDigit;
22261					var newVal;
22262					if ( options.reverse ) {
22263						addMethod = 'unshift';
22264						offset = -1;
22265						lastMaskChar = 0;
22266						m = maskLen - 1;
22267						v = valLen - 1;
22268						check = function() {
22269							return m > -1 && v > -1;
22270						};
22271					} else {
22272						lastMaskChar = maskLen - 1;
22273						check = function() {
22274							return m < maskLen && v < valLen;
22275						};
22276					}
22277
22278					while ( check() ) {
22279						maskDigit = mask.charAt( m );
22280						valDigit = value.charAt( v );
22281						translation = jMask.translation[ maskDigit ];
22282
22283						if ( translation ) {
22284							if ( valDigit.match( translation.pattern ) ) {
22285								buf[ addMethod ]( valDigit );
22286								if ( translation.recursive ) {
22287									if ( resetPos === -1 ) {
22288										resetPos = m;
22289									} else if ( m === lastMaskChar && m !== resetPos ) {
22290										m = resetPos - offset;
22291									}
22292
22293									if ( lastMaskChar === resetPos ) {
22294										m -= offset;
22295									}
22296								}
22297								m += offset;
22298							} else if ( valDigit === lastUntranslatedMaskChar ) {
22299								// matched the last untranslated (raw) mask character that we encountered
22300								// likely an insert offset the mask character from the last entry; fall
22301								// through and only increment v
22302								maskDigitCount = maskDigitCount - 1;
22303								lastUntranslatedMaskChar = undefined;
22304							} else if ( translation.optional ) {
22305								m += offset;
22306								v -= offset;
22307							} else if ( translation.fallback ) {
22308								buf[ addMethod ]( translation.fallback );
22309								m += offset;
22310								v -= offset;
22311							} else {
22312								p.invalid.push( {
22313									p: v,
22314									v: valDigit,
22315									e: translation.pattern
22316								} );
22317							}
22318							v += offset;
22319						} else {
22320							if ( ! skipMaskChars ) {
22321								buf[ addMethod ]( maskDigit );
22322							}
22323
22324							if ( valDigit === maskDigit ) {
22325								maskDigitPosArr.push( v );
22326								v += offset;
22327							} else {
22328								lastUntranslatedMaskChar = maskDigit;
22329								maskDigitPosArr.push( v + maskDigitCount );
22330								maskDigitCount = maskDigitCount + 1;
22331							}
22332
22333							m += offset;
22334						}
22335					}
22336
22337					lastMaskCharDigit = mask.charAt( lastMaskChar );
22338					if ( maskLen === valLen + 1 && ! jMask.translation[ lastMaskCharDigit ] ) {
22339						buf.push( lastMaskCharDigit );
22340					}
22341
22342					newVal = buf.join( '' );
22343					p.mapMaskdigitPositions( newVal, maskDigitPosArr, valLen );
22344					return newVal;
22345				},
22346				mapMaskdigitPositions: function( newVal, maskDigitPosArr, valLen ) {
22347					var maskDiff = options.reverse ? newVal.length - valLen : 0;
22348					var i;
22349					p.maskDigitPosMap = {};
22350					for ( i = 0; i < maskDigitPosArr.length; i += 1 ) {
22351						p.maskDigitPosMap[ maskDigitPosArr[ i ] + maskDiff ] = 1;
22352					}
22353				},
22354				callbacks: function( e ) {
22355					var val = p.val(),
22356						changed = val !== oldValue,
22357						defaultArgs = [ val, e, el, options ],
22358						callback = function( name, criteria, args ) {
22359							if ( typeof options[ name ] === 'function' && criteria ) {
22360								options[ name ].apply( this, args );
22361							}
22362						};
22363
22364					callback( 'onChange', changed === true, defaultArgs );
22365					callback( 'onKeyPress', changed === true, defaultArgs );
22366					callback( 'onComplete', val.length === mask.length, defaultArgs );
22367					callback( 'onInvalid', p.invalid.length > 0, [ val, e, el, p.invalid, options ] );
22368				}
22369			};
22370
22371			el = $( el );
22372
22373			oldValue = p.val();
22374
22375			mask = typeof mask === 'function' ? mask( p.val(), undefined, el, options ) : mask;
22376
22377			// public methods
22378			jMask.mask = mask;
22379			jMask.options = options;
22380			jMask.remove = function() {
22381				var caret = p.getCaret();
22382				if ( jMask.options.placeholder ) {
22383					el.removeAttr( 'placeholder' );
22384				}
22385				if ( el.data( 'mask-maxlength' ) ) {
22386					el.removeAttr( 'maxlength' );
22387				}
22388				p.destroyEvents();
22389				p.val( jMask.getCleanVal() );
22390				p.setCaret( caret );
22391				return el;
22392			};
22393
22394			// get value without mask
22395			jMask.getCleanVal = function() {
22396				return p.getMasked( true );
22397			};
22398
22399			// get masked value without the value being in the input or element
22400			jMask.getMaskedVal = function( val ) {
22401				return p.getMasked( false, val );
22402			};
22403
22404			jMask.init = function( onlyMask ) {
22405				var i;
22406				var translation;
22407				var caret;
22408				var maxlength;
22409
22410				onlyMask = onlyMask || false;
22411				options = options || {};
22412
22413				jMask.clearIfNotMatch = $.jMaskGlobals.clearIfNotMatch;
22414				jMask.byPassKeys = $.jMaskGlobals.byPassKeys;
22415				jMask.translation = $.extend( {}, $.jMaskGlobals.translation, options.translation );
22416
22417				jMask = $.extend( true, {}
22417, jMask, options );
22418
22419				regexMask = p.getRegexMask();
22420
22421				if ( onlyMask ) {
22422					p.events();
22423					p.val( p.getMasked() );
22424				} else {
22425					if ( options.placeholder ) {
22426						el.attr( 'placeholder', options.placeholder );
22427					}
22428
22429					// this is necessary, otherwise if the user submit the form
22430					// and then press the "back" button, the autocomplete will erase
22431					// the data. Works fine on IE9+, FF, Opera, Safari.
22432					if ( el.data( 'mask' ) ) {
22433						el.attr( 'autocomplete', 'off' );
22434					}
22435
22436					// detect if is necessary let the user type freely.
22437					// for is a lot faster than forEach.
22438					for ( i = 0, maxlength = true; i < mask.length; i += 1 ) {
22439						translation = jMask.translation[ mask.charAt( i ) ];
22440						if ( translation && translation.recursive ) {
22441							maxlength = false;
22442							break;
22443						}
22444					}
22445
22446					if ( maxlength ) {
22447						el.attr( 'maxlength', mask.length ).data( 'mask-maxlength', true );
22448					}
22449
22450					p.destroyEvents();
22451					p.events();
22452
22453					caret = p.getCaret();
22454					p.val( p.getMasked() );
22455					p.setCaret( caret );
22456				}
22457			};
22458
22459			jMask.init( ! el.is( 'input' ) );
22460		};
22461
22462		var notSameMaskObject = function( field, mask, options ) {
22463				var maskObject = $( field ).data( 'mask' ),
22464					stringify = JSON.stringify,
22465					value = $( field ).val() || $( field ).text();
22466				options = options || {};
22467				try {
22468					if ( typeof mask === 'function' ) {
22469						mask = mask( value );
22470					}
22471					return typeof maskObject !== 'object' || stringify( maskObject.options ) !== stringify( options ) || maskObject.mask !== mask;
22472				} catch ( err ) {
22473					window.console.log( err );
22474				}
22475			},
22476			HTMLAttributes = function() {
22477				var input = $( this ),
22478					options = {},
22479					prefix = 'data-mask-',
22480					mask = input.attr( 'data-mask' );
22481
22482				if ( input.attr( prefix + 'reverse' ) ) {
22483					options.reverse = true;
22484				}
22485
22486				if ( input.attr( prefix + 'clearifnotmatch' ) ) {
22487					options.clearIfNotMatch = true;
22488				}
22489
22490				if ( input.attr( prefix + 'selectonfocus' ) === 'true' ) {
22491					options.selectOnFocus = true;
22492				}
22493
22494				if ( input.attr( prefix + 'placeholder' ) ) {
22495					options.placeholder = input.attr( prefix + 'placeholder' );
22496				}
22497
22498				if ( notSameMaskObject( input, mask, options ) ) {
22499					return input.data( 'mask', new Mask( this, mask, options ) );
22500				}
22501			},
22502			eventSupported = function( eventName ) {
22503				var el = document.createElement( 'div' ),
22504					isSupported;
22505
22506				eventName = 'on' + eventName;
22507				isSupported = eventName in el;
22508
22509				if ( ! isSupported ) {
22510					el.setAttribute( eventName, 'return;' );
22511					isSupported = typeof el[ eventName ] === 'function';
22512				}
22513				el = null;
22514
22515				return isSupported;
22516			};
22517
22518		$.maskWatchers = {};
22519
22520		$.fn.mask = function( mask, options ) {
22521			var selector = this.selector,
22522				maskGlobals = $.jMaskGlobals,
22523				interval = maskGlobals.watchInterval,
22524				watchInputs,
22525				maskFunction;
22526			options = options || {};
22527
22528			watchInputs = options.watchInputs || maskGlobals.watchInputs;
22529			maskFunction = function() {
22530				if ( notSameMaskObject( this, mask, options ) ) {
22531					return $( this ).data( 'mask', new Mask( this, mask, options ) );
22532				}
22533			};
22534
22535			$( this ).each( maskFunction );
22536
22537			if ( selector && selector !== '' && watchInputs ) {
22538				clearInterval( $.maskWatchers[ selector ] );
22539				$.maskWatchers[ selector ] = setInterval( function() {
22540					$( document ).find( selector ).each( maskFunction );
22541				}, interval );
22542			}
22543			return this;
22544		};
22545
22546		$.fn.masked = function( val ) {
22547			return this.data( 'mask' ).getMaskedVal( val );
22548		};
22549
22550		$.fn.unmask = function() {
22551			clearInterval( $.maskWatchers[ this.selector ] );
22552			delete $.maskWatchers[ this.selector ];
22553			return this.each( function() {
22554				var dataMask = $( this ).data( 'mask' );
22555				if ( dataMask ) {
22556					dataMask.remove().removeData( 'mask' );
22557				}
22558			} );
22559		};
22560
22561		$.fn.cleanVal = function() {
22562			return this.data( 'mask' ).getCleanVal();
22563		};
22564
22565		$.applyDataMask = function( selector ) {
22566			var $selector;
22567			selector = selector || $.jMaskGlobals.maskElements;
22568			$selector = selector instanceof $ ? selector : $( selector );
22569			$selector.filter( $.jMaskGlobals.dataMaskAttr ).each( HTMLAttributes );
22570		};
22571
22572		globals = {
22573			maskElements: 'input,td,span,div',
22574			dataMaskAttr: '*[data-mask]',
22575			dataMask: true,
22576			watchInterval: 300,
22577			watchInputs: true,
22578			keyStrokeCompensation: 10,
22579			// old versions of chrome dont work great with input event
22580			useInput: ! /Chrome\/[2-4][0-9]|SamsungBrowser/.test( window.navigator.userAgent ) && eventSupported( 'input' ),
22581			watchDataMask: false,
22582			byPassKeys: [ 9, 16, 17, 18, 36, 37, 38, 39, 40, 91 ],
22583			translation: {
22584				0: { pattern: /\d/ },
22585				9: { pattern: /\d/, optional: true },
22586				'#': { pattern: /\d/, recursive: true },
22587				A: { pattern: /[a-zA-Z0-9]/ },
22588				S: { pattern: /[a-zA-Z]/ }
22589			}
22590		};
22591
22592		$.jMaskGlobals = $.jMaskGlobals || {};
22593		globals = $.jMaskGlobals = $.extend( true, {}, globals, $.jMaskGlobals );
22594
22595		// looking for inputs with data-mask attribute
22596		if ( globals.dataMask ) {
22597			$.applyDataMask();
22598		}
22599
22600		setInterval( function() {
22601			if ( $.jMaskGlobals.watchDataMask ) {
22602				$.applyDataMask();
22603			}
22604		}, globals.watchInterval );
22605	},
22606	window.jQuery,
22607	window.Zepto
22608) );
22609;
22610
22611/*jshint esversion: 6 */
22612( function( window ) {
22613	'use strict';
22614
22615	var isNumber = function( str ) {
22616		return String( str ).isNumeric() && ! String( str ).startsWith( '0' );
22617	};
22618
22619	var empty = function( mixedVar ) {
22620		var undef;
22621		var key;
22622		var i;
22623		var len;
22624		var emptyValues = [ undef, null, false, 0, '', '0' ];
22625		for ( i = 0, len = emptyValues.length; i < len; i++ ) {
22626			if ( mixedVar === emptyValues[ i ] ) {
22627				return true;
22628			}
22629		}
22630		if ( typeof mixedVar === 'object' ) {
22631			for ( key in mixedVar ) {
22632				if ( mixedVar.hasOwnProperty( key ) ) {
22633					return false;
22634				}
22635			}
22636			return true;
22637		}
22638		return false;
22639	};
22640
22641	var array_key_exists = function( key, search ) {
22642		if ( ! search || ( search.constructor !== Array && search.constructor !== Object ) ) {
22643			return false;
22644		}
22645		return key in search;
22646	};
22647
22648	var getNumberOfParameters = function( func ) {
22649		const ARROW = true;
22650		const FUNC_ARGS = ARROW ? /^(function)?\s*[^\(]*\(\s*([^\)]*)\)/m : /^(function)\s*[^\(]*\(\s*([^\)]*)\)/m;
22651		const FUNC_ARG_SPLIT = /,/;
22652		const FUNC_ARG = /^\s*(_?)(.+?)\1\s*$/;
22653		const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
22654
22655		return ( ( func || '' )
22656			.toString()
22657			.replace( STRIP_COMMENTS, '' )
22658			.match( FUNC_ARGS ) || [ '', '', '' ] )[ 2 ]
22659			.split( FUNC_ARG_SPLIT )
22660			.map( function( arg ) {
22661				return arg.replace( FUNC_ARG, function( all, underscore, name ) {
22662					return name.split( '=' )[ 0 ].trim();
22663				} );
22664			} )
22665			.filter( String )
22666			.length;
22667	};
22668
22669	var getNumberOfRequiredParameters = function( func ) {
22670		return func.length;
22671	};
22672
22673	var is_scalar = function( mixedVar ) {
22674		return ( /boolean|number|string/ ).test( typeof mixedVar );
22675	};
22676
22677	var gettype = function( obj ) {
22678		return window.jQuery.epoAPI.util.getType( obj );
22679	};
22680
22681	var floatval = function( s, d ) {
22682		var n;
22683
22684		if ( ! ( typeof s === 'string' || typeof s === 'number' ) || isNaN( s ) ) {
22685			if ( d !== undefined ) {
22686				return d;
22687			}
22688			return 0;
22689		}
22690		n = parseFloat( s );
22691		if ( isNaN( n ) ) {
22692			if ( d !== undefined ) {
22693				return d;
22694			}
22695			return s;
22696		}
22697
22698		return n;
22699	};
22700
22701	var strcmp = function( str1, str2 ) {
22702		return ( ( str1 === str2 ) ? 0 : ( ( str1 > str2 ) ? 1 : -1 ) );
22703	};
22704
22705	var bindec = function( binaryString ) {
22706		binaryString = ( binaryString + '' ).replace( /[^01]/gi, '' );
22707		return parseInt( binaryString, 2 );
22708	};
22709
22710	var decbin = function( number ) {
22711		if ( number < 0 ) {
22712			number = 0xFFFFFFFF + number + 1;
22713		}
22714		return parseInt( number, 10 ).toString( 2 );
22715	};
22716
22717	var dechex = function( number ) {
22718		if ( number < 0 ) {
22719			number = 0xFFFFFFFF + number + 1;
22720		}
22721		return parseInt( number, 10 ).toString( 16 );
22722	};
22723
22724	var decoct = function( number ) {
22725		if ( number < 0 ) {
22726			number = 0xFFFFFFFF + number + 1;
22727		}
22728		return parseInt( number, 10 ).toString( 8 );
22729	};
22730
22731	var deg2rad = function( angle ) {
22732		return angle * 0.017453292519943295; // (angle / 180) * Math.PI;
22733	};
22734
22735	var roundWithPrecision = function( num, dec = 0 ) {
22736		var num_sign = num >= 0 ? 1 : -1;
22737		return dec === 0 ? Math.round( num ) : parseFloat( ( Math.round( ( num * Math.pow( 10, dec ) ) + ( num_sign * 0.0001 ) ) / Math.pow( 10, dec ) ).toFixed( dec ) );
22738	};
22739
22740	var hexdec = function( hexString ) {
22741		hexString = ( hexString + '' ).replace( /[^a-f0-9]/gi, '' );
22742		return parseInt( hexString, 16 );
22743	};
22744
22745	var octdec = function( octString ) {
22746		octString = ( octString + '' ).replace( /[^0-7]/gi, '' );
22747		return parseInt( octString, 8 );
22748	};
22749
22750	var hypot = function( x, y ) {
22751		var t;
22752		x = Math.abs( x );
22753		y = Math.abs( y );
22754		t = Math.min( x, y );
22755		x = Math.max( x, y );
22756		t = t / x;
22757		return x * Math.sqrt( 1 + ( t * t ) ) || null;
22758	};
22759
22760	var THEMECOMPLETE_EPO_MATH = {
22761		variables: {},
22762		on_var_not_found: undefined,
22763		on_var_validation: undefined,
22764		operators: {},
22765		functions: {},
22766		cache: {},
22767		construct: function() {
22768			this.add_defaults();
22769			this.set_division_by_zero_to_zero();
22770			return this;
22771		},
22772		add_operator: function( $operator ) {
22773			this.operators[ $operator.operator ] = $operator;
22774			return this;
22775		},
22776		evaluate: function( $expression, $cache = true ) {
22777			return this.execute( $expression, $cache );
22778		},
22779		parse: function( $expression ) {
22780			var $tokens;
22781			var result;
22782			var $calculator;
22783			var count = 0;
22784			var char;
22785
22786			for ( char of $expression ) {
22787				if ( char === '(' ) {
22788					count++;
22789				} else if ( char === ')' ) {
22790					count--;
22791				}
22792
22793				// If count becomes negative at any point, return false
22794				if ( count < 0 ) {
22795					return false;
22796				}
22797			}
22798
22799			// If count is not zero at the end, parentheses are not balanced
22800			if ( count !== 0 ) {
22801				return false;
22802			}
22803
22804			$tokens = new THEMECOMPLETE_EPO_MATH_Tokenizer( $expression, this.operators );
22805			$tokens = $tokens.tokenize().build_reverse_polish_notation();
22806			$calculator = new THEMECOMPLETE_EPO_MATH_Calculator( this.functions, this.operators );
22807
22808			result = $calculator.calculate( $tokens, this.variables, this.on_var_not_found, this );
22809			if ( 'number' !== gettype( result ) || isNaN( result ) ) {
22810				return false;
22811			}
22812			return result;
22813		},
22814		execute: function( $expression, $cache = true, $canreturnstring = false ) {
22815			var $cache_key = $expression;
22816			var $calculator;
22817			var $tokens;
22818			var result;
22819
22820			if ( ! array_key_exists( $cache_key, this.cache ) ) {
22821				$tokens = new THEMECOMPLETE_EPO_MATH_Tokenizer( $expression, this.operators );
22822				$tokens = $tokens.tokenize().build_reverse_polish_notation();
22823				if ( $cache ) {
22824					this.cache[ $cache_key ] = $tokens;
22825				}
22826			} else {
22827				$tokens = this.cache[ $cache_key ];
22828			}
22829
22830			$calculator = new THEMECOMPLETE_EPO_MATH_Calculator( this.functions, this.operators );
22831
22832			result = $calculator.calculate( $tokens, this.variables, this.on_var_not_found, this );
22833
22834			if ( false === $canreturnstring && ( 'number' !== gettype( result ) || isNaN( result ) ) ) {
22835				result = 0;
22836			}
22837
22838			return result;
22839		},
22840		add_function: function( $name, $function ) {
22841			this.functions[ $name ] = new THEMECOMPLETE_EPO_MATH_CustomFunction( $name, $function );
22842			return this;
22843		},
22844		get_vars: function() {
22845			return this.variables;
22846		},
22847		get_var: function( $variable ) {
22848			if ( ! array_key_exists( $variable, this.variables ) ) {
22849				if ( this.on_var_not_found && 'function' === typeof this.on_var_not_found ) {
22850					this.on_var_not_found( $variable );
22851				}
22852				return THEMECOMPLETE_EPO_MATH_Error.trigger( 'Variable (' + $variable + ') not set', 'UnknownVariableError', 0 );
22853			}
22854			return this.variables[ $variable ];
22855		},
22856		set_var: function( $variable, $value ) {
22857			if ( this.on_var_validation && 'function' === typeof this.on_var_validation ) {
22858				$value = this.on_var_validation( $variable, $value );
22859			}
22860			this.variables[ $variable ] = $value;
22861			return this;
22862		},
22863		var_exists: function( $variable ) {
22864			return array_key_exists( $variable, this.variables );
22865		},
22866		set_vars: function( $variables, $clear = true ) {
22867			var $this = this;
22868			if ( $clear ) {
22869				this.remove_vars();
22870			}
22871			$variables.forFach( function( $value, $name ) {
22872				$this.set_var( $name, $value );
22873			} );
22874			return this;
22875		},
22876		set_var_not_found_handler: function( $handler ) {
22877			this.on_var_not_found = $handler;
22878			return this;
22879		},
22880		set_var_validation_handler: function( $handler ) {
22881			this.on_var_validation = $handler;
22882			return this;
22883		},
22884		remove_var: function( $variable ) {
22885			delete this.variables[ $variable ];
22886			return this;
22887		},
22888		remove_vars: function() {
22889			this.variables = {};
22890			this.on_var_not_found = null;
22891			return this;
22892		},
22893		get_operators: function() {
22894			return this.operators;
22895		},
22896		get_functions: function() {
22897			return this.functions;
22898		},
22899		remove_operator: function( $operator ) {
22900			delete this.operators[ $operator ];
22901		},
22902		set_division_by_zero_to_zero: function() {
22903			this.add_operator(
22904				new THEMECOMPLETE_EPO_MATH_Operator(
22905					'/',
22906					false,
22907					180,
22908					function( $a, $b ) {
22909						$a = Number( $a );
22910						$b = Number( $b );
22911						return 0 == $b ? 0 : $a / $b; // eslint-disable-line eqeqeq
22912					}
22913				)
22914			);
22915			return this;
22916		},
22917		get_cache: function() {
22918			return this.cache;
22919		},
22920		clear_cache: function() {
22921			this.cache = [];
22922		},
22923		add_defaults: function() {
22924			var $this = this;
22925			var default_operators = this.default_operators();
22926			var default_functions = this.default_functions();
22927			Object.keys( default_operators ).forEach( function( $name ) {
22928				var $operator = default_operators[ $name ];
22929				$this.add_operator( new THEMECOMPLETE_EPO_MATH_Operator( $name, $operator[ 2 ], $operator[ 1 ], $operator[ 0 ] ) );
22930			} );
22931			Object.keys( default_functions ).forEach( function( $name ) {
22932				var $callable = default_functions[ $name ];
22933				$this.add_function( $name, $callable );
22934			} );
22935			this.on_var_validation = this.default_var_validation;
22936			this.variables = this.default_vars();
22937			return this;
22938		},
22939		default_operators: function() {
22940			return {
22941				'+': [
22942					function( $a, $b ) {
22943						$a = floatval( $a, 0 );
22944						$b = floatval( $b, 0 );
22945						return $a + $b;
22946					},
22947					170,
22948					false
22949				],
22950				'-': [
22951					function( $a, $b ) {
22952						$a = floatval( $a, 0 );
22953						$b = floatval( $b, 0 );
22954						return $a - $b;
22955					},
22956					170,
22957					false
22958				],
22959				// unary positive token.
22960				uPos: [
22961					function( $a ) {
22962						$a = floatval( $a, 0 );
22963						return $a;
22964					},
22965					200,
22966					false
22967				],
22968				// unary minus token.
22969				uNeg: [
22970					function( $a ) {
22971						$a = floatval( $a, 0 );
22972						return 0 - $a;
22973					},
22974					200,
22975					false
22976				],
22977				'*': [
22978					function( $a, $b ) {
22979						$a = floatval( $a, 0 );
22980						$b = floatval( $b, 0 );
22981						return $a * $b;
22982					},
22983					180,
22984					false
22985				],
22986				'/': [
22987					function( $a, $b ) {
22988						$a = floatval( $a, 0 );
22989						$b = floatval( $b, 0 );
22990						if ( empty( $b ) ) {
22991							return THEMECOMPLETE_EPO_MATH_Error.trigger( 'Division By Zero', 'DivisionByZeroError', 0 );
22992						}
22993						return $a / $b;
22994					},
22995					180,
22996					false
22997				],
22998				'^': [
22999					function( $a, $b ) {
23000						$a = floatval( $a, 0 );
23001						$b = floatval( $b, 0 );
23002						return Math.pow( $a, $b );
23003					},
23004					220,
23005					true
23006				],
23007				'%': [
23008					function( $a, $b ) {
23009						$a = floatval( $a, 0 );
23010						$b = floatval( $b, 0 );
23011						return $a % $b;
23012					},
23013					180,
23014					false
23015				],
23016				'&&': [
23017					function( $a, $b ) {
23018						$a = String( $a );
23019						$b = String( $b );
23020						if ( $a.isNumeric() ) {
23021							$a = floatval( $a, 0 );
23022						}
23023						if ( $b.isNumeric() ) {
23024							$b = floatval( $b, 0 );
23025						}
23026						return $a && $b ? 1 : 0;
23027					},
23028					100,
23029					false
23030				],
23031				'||': [
23032					function( $a, $b ) {
23033						$a = String( $a );
23034						$b = String( $b );
23035						if ( $a.isNumeric() ) {
23036							$a = floatval( $a, 0 );
23037						}
23038						if ( $b.isNumeric() ) {
23039							$b = floatval( $b, 0 );
23040						}
23041						return $a || $b ? 1 : 0;
23042					},
23043					90,
23044					false
23045				],
23046				'==': [
23047					function( $a, $b ) {
23048						$a = String( $a );
23049						$b = String( $b );
23050						if ( $a.isNumeric() && $b.isNumeric() ) {
23051							return floatval( $a ) === floatval( $b ) ? 1 : 0;
23052						}
23053						return 0 === strcmp( $a, $b ) ? 1 : 0;
23054					},
23055					140,
23056					false
23057				],
23058				'!=': [
23059					function( $a, $b ) {
23060						$a = String( $a );
23061						$b = String( $b );
23062						if ( $a.isNumeric() && $b.isNumeric() ) {
23063							return floatval( $a ) !== floatval( $b ) ? 1 : 0;
23064						}
23065						return 0 !== strcmp( $a, $b ) ? 1 : 0;
23066					},
23067					140,
23068					false
23069				],
23070				'>=': [
23071					function( $a, $b ) {
23072						$a = String( $a );
23073						$b = String( $b );
23074						if ( $a.isNumeric() ) {
23075							$a = floatval( $a, 0 );
23076						}
23077						if ( $b.isNumeric() ) {
23078							$b = floatval( $b, 0 );
23079						}
23080						return $a >= $b ? 1 : 0;
23081					},
23082					150,
23083					false
23084				],
23085				'>': [
23086					function( $a, $b ) {
23087						$a = String( $a );
23088						$b = String( $b );
23089						if ( $a.isNumeric() ) {
23090							$a = floatval( $a, 0 );
23091						}
23092						if ( $b.isNumeric() ) {
23093							$b = floatval( $b, 0 );
23094						}
23095						return $a > $b ? 1 : 0;
23096					},
23097					150,
23098					false
23099				],
23100				'<=': [
23101					function( $a, $b ) {
23102						$a = String( $a );
23103						$b = String( $b );
23104						if ( $a.isNumeric() ) {
23105							$a = floatval( $a, 0 );
23106						}
23107						if ( $b.isNumeric() ) {
23108							$b = floatval( $b, 0 );
23109						}
23110						return $a <= $b ? 1 : 0;
23111					},
23112					150,
23113					false
23114				],
23115				'<': [
23116					function( $a, $b ) {
23117						$a = String( $a );
23118						$b = String( $b );
23119						if ( $a.isNumeric() ) {
23120							$a = floatval( $a, 0 );
23121						}
23122						if ( $b.isNumeric() ) {
23123							$b = floatval( $b, 0 );
23124						}
23125						return $a < $b ? 1 : 0;
23126					},
23127					150,
23128					false
23129				]
23130			};
23131		},
23132		default_functions: function() {
23133			var $this = this;
23134			return {
23135				abs: Math.abs,
23136				acos: Math.acos,
23137				acosh: Math.acosh,
23138				arcsin: Math.asin,
23139				arcctg: function( $arg ) {
23140					return ( Math.PI / 2 ) - Math.atan( $arg );
23141				},
23142				arccot: function( $arg ) {
23143					return ( Math.PI / 2 ) - Math.atan( $arg );
23144				},
23145				arccotan: function( $arg ) {
23146					return ( Math.PI / 2 ) - Math.atan( $arg );
23147				},
23148				arcsec: function( $arg ) {
23149					return Math.acos( 1 / $arg );
23150				},
23151				arccosec: function( $arg ) {
23152					return Math.asin( 1 / $arg );
23153				},
23154				arccsc: function( $arg ) {
23155					return Math.asin( 1 / $arg );
23156				},
23157				arccos: Math.acos,
23158				arctan: Math.atan,
23159				arctg: Math.atan,
23160				array: function( ...$args ) {
23161					return $args;
23162				},
23163				asin: Math.asin,
23164				atan: Math.atan,
23165				atan2: Math.atan2,
23166				atanh: Math.atanh,
23167				atn: Math.atan,
23168
23169				avg: function( $arg1, ...$args ) {
23170					var sum;
23171					if ( Array.isArray( $arg1 ) ) {
23172						if ( 0 === $arg1.length ) {
23173							return THEMECOMPLETE_EPO_MATH_Error.trigger( 'Array must contain at least one element!', 'InvalidArgumentError', 0 );
23174						}
23175					}
23176					$args = [].concat.apply( [], [ $arg1, ...$args ] );
23177
23178					sum = $args.reduce( function( previousValue, currentValue ) {
23179						return floatval( currentValue, 0 ) + floatval( previousValue, 0 );
23180					} );
23181					return sum / $args.length;
23182				},
23183				average: function( $arg1, ...$args ) {
23184					var sum;
23185					if ( Array.isArray( $arg1 ) ) {
23186						if ( 0 === $arg1.length ) {
23187							return THEMECOMPLETE_EPO_MATH_Error.trigger( 'Array must contain at least one element!', 'InvalidArgumentError', 0 );
23188						}
23189					}
23190					$args = [].concat.apply( [], [ $arg1, ...$args ] );
23191
23192					sum = $args.reduce( function( previousValue, currentValue ) {
23193						return floatval( currentValue, 0 ) + floatval( previousValue, 0 );
23194					} );
23195					return sum / $args.length;
23196				},
23197				bindec: bindec,
23198				ceil: Math.ceil,
23199				cos: Math.cos,
23200				cosec: function( $arg ) {
23201					return Math.sin( 1 / $arg );
23202				},
23203				csc: function( $arg ) {
23204					return Math.sin( 1 / $arg );
23205				},
23206				cosh: Math.cosh,
23207				ctg: function( $arg ) {
23208					return Math.cos( $arg ) / Math.sin( $arg );
23209				},
23210				cot: function( $arg ) {
23211					return Math.cos( $arg ) / Math.sin( $arg );
23212				},
23213				cotan: function( $arg ) {
23214					return Math.cos( $arg ) / Math.sin( $arg );
23215				},
23216				cotg: function( $arg ) {
23217					return Math.cos( $arg ) / Math.sin( $arg );
23218				},
23219				ctn: function( $arg ) {
23220					return Math.cos( $arg ) / Math.sin( $arg );
23221				},
23222				decbin: decbin,
23223				dechex: dechex,
23224				decoct: decoct,
23225				deg2rad: deg2rad,
23226				exp: Math.exp,
23227				expm1: Math.expm1,
23228				floor: Math.floor,
23229				int: Math.floor,
23230				fmod: function( $arg1, $arg2 ) {
23231					return $arg1 % $arg2;
23232				},
23233				hexdec: hexdec,
23234				hypot: hypot,
23235				if: function( ...$args ) {
23236					var $exres;
23237					var $expr;
23238					var $trueval;
23239					var $falseval;
23240					var $canreturnstring;
23241					$expr = $args[ 0 ];
23242					$trueval = $args[ 1 ];
23243					$falseval = $args[ 2 ];
23244					if ( 'string' === gettype( $trueval ) ) {
23245						$trueval = "'" + $trueval.replace( /'/g, "\\'" ) + "'";
23246					}
23247					if ( 'string' === gettype( $falseval ) ) {
23248						$falseval = "'" + $falseval.replace( /'/g, "\\'" ) + "'";
23249					}
23250					$canreturnstring = $args[ 3 ] !== undefined ? $args[ 3 ] : false;
23251					if ( 0 === $expr || 1 === $expr || true === $expr || false === $expr ) {
23252						$exres = $expr;
23253					} else {
23254						$exres = $this.execute( $expr, true, $canreturnstring );
23255					}
23256					if ( $exres ) {
23257						return $this.execute( $trueval, true, $canreturnstring );
23258					}
23259					return $this.execute( $falseval, true, $canreturnstring );
23260				},
23261				intdiv: function( $arg1, $arg2 ) {
23262					return Math.trunc( Math.trunc( $arg1 ) / Math.trunc( $arg2 ) );
23263				},
23264				ln: Math.log,
23265				lg: Math.log10,
23266				log: Math.log,
23267				log1p: Math.log1p,
23268				max: function( $arg1, ...$args ) {
23269					var $array;
23270					if ( Array.isArray( $arg1 ) && 0 === $arg1.length ) {
23271						return THEMECOMPLETE_EPO_MATH_Error.trigger( 'Array must contain at least one element!', 'InvalidArgumentError', 0 );
23272					}
23273					$array = Array.isArray( $arg1 ) ? $arg1 : [ $arg1, ...$args ];
23274					$array = $array.map( floatval );
23275
23276					return Math.max( ...$array );
23277				},
23278				min: function( $arg1, ...$args ) {
23279					var $array;
23280					if ( Array.isArray( $arg1 ) && 0 === $arg1.length ) {
23281						return THEMECOMPLETE_EPO_MATH_Error.trigger( 'Array must contain at least one element!', 'InvalidArgumentError', 0 );
23282					}
23283					$array = Array.isArray( $arg1 ) ? $arg1 : [ $arg1, ...$args ];
23284					$array = $array.map( floatval );
23285
23286					return Math.min( ...$array );
23287				},
23288				octdec: octdec,
23289				pi: function() {
23290					return Math.PI;
23291				},
23292				pow: Math.pow,
23293				rad2deg: function( angle ) {
23294					return angle * 57.29577951308232; // angle / Math.PI * 180
23295				},
23296				round: roundWithPrecision,
23297				sin: Math.sin,
23298				sinh: Math.sinh,
23299				sec: function( $arg ) {
23300					return 1 / Math.cos( $arg );
23301				},
23302				sqrt: Math.sqrt,
23303				tan: Math.tan,
23304				tanh: Math.tanh,
23305				tn: Math.tan,
23306				tg: Math.tan,
23307				lookuptable: function( field, lookupTableId ) {
23308					var lookupTables;
23309					var x;
23310					var y;
23311					var table;
23312					var xColumn;
23313					var price = 0;
23314					var tableNum = 0;
23315					var TMEPOJS = window.TMEPOJS;
23316
23317					if ( TMEPOJS ) {
23318						lookupTables = window.jQuery.epoAPI.util.parseJSON( TMEPOJS.lookupTables );
23319						if ( lookupTables ) {
23320							if ( Array.isArray( lookupTableId ) ) {
23321								tableNum = lookupTableId[ 1 ];
23322								lookupTableId = lookupTableId[ 0 ];
23323							}
23324							if ( empty( lookupTableId ) ) {
23325								return 0;
23326							}
23327							if ( empty( tableNum ) ) {
23328								tableNum = 0;
23329							}
23330							if ( Array.isArray( field ) ) {
23331								x = String( field[ 0 ] );
23332								y = String( field[ 1 ] );
23333							} else {
23334								x = String( field );
23335								y = '';
23336							}
23337
23338							table = lookupTables[ lookupTableId ];
23339							if ( table ) {
23340								table = table[ tableNum ];
23341								if ( table ) {
23342									table = table.data;
23343									xColumn = table[ x ];
23344									if ( xColumn === undefined && x && x !== undefined ) {
23345										if ( x.isNumeric() && floatval( x ) === 0 ) {
23346											xColumn = table[ Object.keys( table )[ 0 ] ];
23347										} else if ( x ) {
23348											x = $this.find_lookup_table_index( x, table );
23349											xColumn = table[ x ];
23350										}
23351									}
23352									if ( xColumn !== undefined ) {
23353										if ( y && y !== undefined ) {
23354											y = $this.find_lookup_table_index( y, xColumn );
23355										} else {
23356											// fetch the first row since this means we want
23357											// a single row result
23358											y = Object.keys( xColumn )[ 0 ];
23359										}
23360										if ( y === 'max' ) {
23361											price = floatval( xColumn[ Object.keys( xColumn )[ Object.keys( xColumn ).length - 1 ] ] );
23362										} else {
23363											price = floatval( xColumn[ y ] );
23364										}
23365									}
23366								}
23367							}
23368						}
23369					}
23370					return price;
23371				},
23372				concat: function( $arg1, ...$args ) {
23373					var $array;
23374					if ( Array.isArray( $arg1 ) && 0 === $arg1.length ) {
23375						return THEMECOMPLETE_EPO_MATH_Error.trigger( 'Array must contain at least one element!', 'InvalidArgumentError', 0 );
23376					}
23377					$array = Array.isArray( $arg1 ) ? $arg1 : [ $arg1, ...$args ];
23378
23379					return $array.join( '' );
23380				}
23381			};
23382		},
23383		find_lookup_table_index: function( value, table ) {
23384			var r;
23385			var keys = Object.keys( table );
23386			var hasNumericValuesOrMaxLast = function( arr ) {
23387				var lastElement = arr[ arr.length - 1 ];
23388				var areAllNumericValues = arr.every( function( val ) {
23389					return String( val ).isNumeric();
23390				} );
23391				return areAllNumericValues || lastElement === 'max';
23392			};
23393
23394			if ( hasNumericValuesOrMaxLast( keys ) ) {
23395				value = isNumber( value ) ? floatval( value ) : value;
23396				r = keys.map( function( n ) {
23397					return isNumber( n ) ? floatval( n ) : n;
23398				} ).reduce( function( a, b ) {
23399					if ( b === 'max' && value > a ) {
23400						return b;
23401					}
23402					if ( a === 'max' && value > b ) {
23403						return a;
23404					}
23405					if ( a < b ) {
23406						if ( value > a && value <= b ) {
23407							return b;
23408						}
23409					} else {
23410						if ( ( value > b && value <= a ) || ( value > a || b === 'max' ) ) {
23411							return a;
23412						}
23413						return b;
23414					}
23415					if ( value > b ) {
23416						return b;
23417					}
23418					return a;
23419				} );
23420				keys = keys.map( function( n ) {
23421					return isNumber( n ) ? floatval( n ) : n;
23422				} );
23423
23424				if ( value > Math.max( ...keys ) || value < Math.min( ...keys ) ) {
23425					return false;
23426				}
23427			} else {
23428				r = value;
23429			}
23430
23431			return r;
23432		},
23433		default_vars: function() {
23434			return {
23435				pi: 3.141592653589793,
23436				e: 2.718281828459045
23437			};
23438		},
23439		default_var_validation: function( $variable, $value ) {
23440			if ( ! is_scalar( $value ) && ! Array.isArray( $value ) && undefined !== $value ) {
23441				return THEMECOMPLETE_EPO_MATH_Error.trigger( 'Variable (' + $variable + ') type (' + gettype( $value ) + ') is not scalar or array!', 0 );
23442			}
23443			return $value;
23444		}
23445	};
23446
23447	var THEMECOMPLETE_EPO_MATH_Tokenizer = function( $input, $operators ) {
23448		var node = {
23449			tokens: [],
23450			input: '',
23451			operators: {},
23452			number_buffer: '',
23453			string_buffer: '',
23454			allow_negative: true,
23455			in_single_quoted_string: false,
23456			in_double_quoted_string: false,
23457			is_number: function( ch ) {
23458				return ch >= '0' && ch <= '9';
23459			},
23460			is_alpha: function( ch ) {
23461				return ( ch >= 'a' && ch <= 'z' ) || ( ch >= 'A' && ch <= 'Z' ) || '_' === ch;
23462			},
23463			is_dot: function( ch ) {
23464				return '.' === ch;
23465			},
23466			is_lp: function( ch ) {
23467				return '(' === ch;
23468			},
23469			is_rp: function( ch ) {
23470				return ')' === ch;
23471			},
23472			is_comma: function( ch ) {
23473				return ',' === ch;
23474			},
23475			empty_number_buffer_as_literal: function() {
23476				if ( this.number_buffer.length ) {
23477					this.tokens.push( THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.LITERAL, this.number_buffer ) );
23478					this.number_buffer = '';
23479				}
23480			},
23481			empty_str_buffer_as_variable: function() {
23482				if ( '' !== this.string_buffer ) {
23483					this.tokens.push( THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.VARIABLE, this.string_buffer ) );
23484					this.string_buffer = '';
23485				}
23486			},
23487			tokenize: function() {
23488				var is_last_char_escape = false;
23489				var $this = this;
23490				var $token_test = [];
23491
23492				this.tokens = [];
23493
23494				this.input.toString().split( '' ).forEach( function( ch ) {
23495					switch ( true ) {
23496						case $this.in_single_quoted_string:
23497							if ( '\\' === ch ) {
23498								if ( is_last_char_escape ) {
23499									$this.string_buffer += '\\';
23500									is_last_char_escape = false;
23501								} else {
23502									is_last_char_escape = true;
23503								}
23504								break;
23505							} else if ( "'" === ch ) {
23506								if ( is_last_char_escape ) {
23507									$this.string_buffer += "'";
23508									is_last_char_escape = false;
23509								} else {
23510									$this.tokens.push( THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.STRING, $this.string_buffer ) );
23511									$this.in_single_quoted_string = false;
23512									$this.string_buffer = '';
23513								}
23514								break;
23515							}
23516							if ( is_last_char_escape ) {
23517								$this.string_buffer += '\\';
23518								is_last_char_escape = false;
23519							}
23520							$this.string_buffer += ch;
23521							break;
23522						case $this.in_double_quoted_string:
23523							if ( '\\' === ch ) {
23524								if ( is_last_char_escape ) {
23525									$this.string_buffer += '\\';
23526									is_last_char_escape = false;
23527								} else {
23528									is_last_char_escape = true;
23529								}
23530								break;
23531							} else if ( '"' === ch ) {
23532								if ( is_last_char_escape ) {
23533									$this.string_buffer += '"';
23534									is_last_char_escape = false;
23535								} else {
23536									$this.tokens.push( THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.STRING, $this.string_buffer ) );
23537									$this.in_double_quoted_string = false;
23538									$this.string_buffer = '';
23539								}
23540								break;
23541							}
23542							if ( is_last_char_escape ) {
23543								$this.string_buffer += '\\';
23544								is_last_char_escape = false;
23545							}
23546							$this.string_buffer += ch;
23547							break;
23548						case '[' === ch:
23549							$this.tokens.push( THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.FUNCTION, 'array' ) );
23550							$this.allow_negative = true;
23551							$this.tokens.push( THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.LEFTPARENTHESIS, '' ) );
23552							break;
23553						case ' ' === ch || '\n' === ch || '\r' === ch || '\t' === ch:
23554							/**
23555							 * In case those tokens must not be ingored use the following
23556							 *
23557							 * $this.empty_number_buffer_as_literal();
23558							 * $this.empty_str_buffer_as_variable();
23559							 * $this.tokens.push( THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.SPACE, '' ) );
23560							 */
23561							break;
23562						case $this.is_number( ch ):
23563							if ( '' !== $this.string_buffer ) {
23564								$this.string_buffer += ch;
23565								break;
23566							}
23567							$this.number_buffer += ch;
23568							$this.allow_negative = false;
23569							break;
23570						case 'e' === ch.toLowerCase():
23571							if ( $this.number_buffer.length && -1 !== $this.number_buffer.indexOf( '.' ) ) {
23572								$this.number_buffer += 'e';
23573								$this.allow_negative = false;
23574								break;
23575							}
23576							/* falls through */
23577						case $this.is_alpha( ch ):
23578							if ( $this.number_buffer.length ) {
23579								$this.empty_number_buffer_as_literal();
23580								$this.tokens.push( THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.OPERATOR, '*' ) );
23581							}
23582							$this.allow_negative = false;
23583							$this.string_buffer += ch;
23584							break;
23585						case '"' === ch:
23586							$this.in_double_quoted_string = true;
23587							break;
23588						case "'" === ch:
23589							$this.in_single_quoted_string = true;
23590							break;
23591						case $this.is_dot( ch ):
23592							$this.number_buffer += ch;
23593							$this.allow_negative = false;
23594							break;
23595						case $this.is_lp( ch ):
23596							if ( '' !== $this.string_buffer ) {
23597								$this.tokens.push( THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.FUNCTION, $this.string_buffer ) );
23598								$this.string_buffer = '';
23599							} else if ( $this.number_buffer.length ) {
23600								$this.empty_number_buffer_as_literal();
23601								$this.tokens.push( THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.OPERATOR, '*' ) );
23602							}
23603							$this.allow_negative = true;
23604							$this.tokens.push( THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.LEFTPARENTHESIS, ch ) );
23605							break;
23606						case $this.is_rp( ch ) || ']' === ch :
23607							$this.empty_number_buffer_as_literal();
23608							$this.empty_str_buffer_as_variable();
23609							$this.allow_negative = false;
23610							$this.tokens.push( THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.RIGHTPARENTHESIS, ch ) );
23611							break;
23612						case $this.is_comma( ch ):
23613							$this.empty_number_buffer_as_literal();
23614							$this.empty_str_buffer_as_variable();
23615							$this.allow_negative = true;
23616							$this.tokens.push( THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.PARAMSEPARATOR, ch ) );
23617							break;
23618						default:
23619							// special case for unary operations.
23620							if ( '-' === ch || '+' === ch ) {
23621								if ( $this.allow_negative ) {
23622									$this.allow_negative = false;
23623									$this.tokens.push( THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.OPERATOR, '-' === ch ? 'uNeg' : 'uPos' ) );
23624									break;
23625								}
23626								// could be in exponent, in which case negative should be added to the number_buffer.
23627								if ( $this.number_buffer && 'e' === $this.number_buffer[ $this.number_buffer.length - 1 ] ) {
23628									$this.number_buffer += ch;
23629									break;
23630								}
23631							}
23632							$this.empty_number_buffer_as_literal();
23633							$this.empty_str_buffer_as_variable();
23634
23635							if ( '$' !== ch ) {
23636								if ( $this.tokens.length > 0 ) {
23637									if ( THEMECOMPLETE_EPO_MATH_Constants.OPERATOR === $this.tokens[ $this.tokens.length - 1 ].type ) {
23638										$this.tokens[ $this.tokens.length - 1 ].value += ch;
23639									} else {
23640										$this.tokens.push( THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.OPERATOR, ch ) );
23641									}
23642								} else {
23643									$this.tokens.push( THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.OPERATOR, ch ) );
23644								}
23645							}
23646							$this.allow_negative = true;
23647					}
23648				} );
23649				this.empty_number_buffer_as_literal();
23650				this.empty_str_buffer_as_variable();
23651
23652				this.tokens.forEach( function( $token, $key ) {
23653					$token_test[ $key ] = $token.type;
23654				} );
23655				$token_test.forEach( function( $type, $key ) {
23656					if ( $key > 0 && 'space' === $type && 'variable' === $token_test[ $key + 1 ] && 'variable' === $token_test[ $key - 1 ] ) {
23657						$this.tokens[ $key ] = new THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.LITERAL, 0 );
23658						$this.tokens.splice( $key + 1, 1 );
23659						$this.tokens.splice( $key - 1, 1 );
23660					}
23661				} );
23662				return this;
23663			},
23664			build_reverse_polish_notation: function() {
23665				var $tokens = [];
23666				var $stack = [];
23667				var $param_counter = [];
23668				var $ctoken;
23669				var $f;
23670				var $op1;
23671				var $op2;
23672				var $this = this;
23673
23674				try {
23675					this.tokens.forEach( function( $token ) {
23676						switch ( $token.type ) {
23677							case THEMECOMPLETE_EPO_MATH_Constants.LITERAL:
23678							case THEMECOMPLETE_EPO_MATH_Constants.VARIABLE:
23679							case THEMECOMPLETE_EPO_MATH_Constants.STRING:
23680								$tokens.push( $token );
23681
23682								if ( $param_counter.length > 0 && 0 === $param_counter[ $param_counter.length - 1 ] ) {
23683									$param_counter.push( $param_counter.pop() + 1 );
23684								}
23685
23686								break;
23687
23688							case THEMECOMPLETE_EPO_MATH_Constants.FUNCTION:
23689								if ( $param_counter.length > 0 && 0 === $param_counter[ $param_counter.length - 1 ] ) {
23690									$param_counter.push( $param_counter.pop() + 1 );
23691								}
23692								$stack.push( $token );
23693								$param_counter.push( 0 );
23694
23695								break;
23696
23697							case THEMECOMPLETE_EPO_MATH_Constants.LEFTPARENTHESIS:
23698								$stack.push( $token );
23699
23700								break;
23701
23702							case THEMECOMPLETE_EPO_MATH_Constants.PARAMSEPARATOR:
23703								while ( THEMECOMPLETE_EPO_MATH_Constants.LEFTPARENTHESIS !== $stack[ $stack.length - 1 ].type ) {
23704									if ( 0 === $stack.length ) {
23705										return THEMECOMPLETE_EPO_MATH_Error.trigger( 'Incorrect Brackets', 'IncorrectBracketsError', $tokens );
23706									}
23707									$tokens.push( $stack.pop() );
23708								}
23709								$param_counter.push( $param_counter.pop() + 1 );
23710
23711								break;
23712
23713							case THEMECOMPLETE_EPO_MATH_Constants.OPERATOR:
23714								if ( ! array_key_exists( $token.value, $this.operators ) ) {
23715									return THEMECOMPLETE_EPO_MATH_Error.trigger( $token.value, 'UnknownOperatorError', $tokens );
23716								}
23717								$op1 = $this.operators[ $token.value ];
23718
23719								while ( $stack.length > 0 && THEMECOMPLETE_EPO_MATH_Constants.OPERATOR === $stack[ $stack.length - 1 ].type ) {
23720									if ( ! array_key_exists( $stack[ $stack.length - 1 ].value, $this.operators ) ) {
23721										return THEMECOMPLETE_EPO_MATH_Error.trigger( $stack[ $stack.length - 1 ].value, 'UnknownOperatorError', $tokens );
23722									}
23723									$op2 = $this.operators[ $stack[ $stack.length - 1 ].value ];
23724
23725									if ( $op2.priority >= $op1.priority ) {
23726										$tokens.push( $stack.pop() );
23727
23728										continue;
23729									}
23730
23731									break;
23732								}
23733								$stack.push( $token );
23734
23735								break;
23736
23737							case THEMECOMPLETE_EPO_MATH_Constants.RIGHTPARENTHESIS:
23738								while ( true ) {
23739									try {
23740										$ctoken = $stack.pop();
23741
23742										if ( THEMECOMPLETE_EPO_MATH_Constants.LEFTPARENTHESIS === $ctoken.type ) {
23743											break;
23744										}
23745										$tokens.push( $ctoken );
23746									} catch ( $e ) {
23747										return THEMECOMPLETE_EPO_MATH_Error.trigger( 'Incorrect Brackets', 'IncorrectBracketsError', $tokens );
23748									}
23749								}
23750
23751								if ( $stack.length > 0 && THEMECOMPLETE_EPO_MATH_Constants.FUNCTION === $stack[ $stack.length - 1 ].type ) {
23752									$f = $stack.pop();
23753									$f.param_count = $param_counter.pop();
23754									$tokens.push( $f );
23755								}
23756
23757								break;
23758
23759							case THEMECOMPLETE_EPO_MATH_Constants.SPACE:
23760								// do nothing.
23761						}
23762					} );
23763				} catch ( $e ) {
23764					$tokens = [];
23765					return THEMECOMPLETE_EPO_MATH_Error.trigger( $e, 'Error', $tokens );
23766				}
23767
23768				while ( 0 !== $stack.length ) {
23769					if ( THEMECOMPLETE_EPO_MATH_Constants.LEFTPARENTHESIS === $stack[ $stack.length - 1 ].type || THEMECOMPLETE_EPO_MATH_Constants.RIGHTPARENTHESIS === $stack[ $stack.length - 1 ].type ) {
23770						return THEMECOMPLETE_EPO_MATH_Error.trigger( 'Incorrect Brackets', 'IncorrectBracketsError', $tokens );
23771					}
23772
23773					if ( THEMECOMPLETE_EPO_MATH_Constants.SPACE === $stack[ $stack.length - 1 ].type ) {
23774						$stack.pop();
23775						continue;
23776					}
23777					$tokens.push( $stack.pop() );
23778				}
23779
23780				return $tokens;
23781			}
23782		};
23783		node.input = $input;
23784		node.operators = $operators;
23785		return node;
23786	};
23787
23788	var THEMECOMPLETE_EPO_MATH_Constants = {
23789		LITERAL: 'literal',
23790		VARIABLE: 'variable',
23791		OPERATOR: 'operator',
23792		LEFTPARENTHESIS: 'LP',
23793		RIGHTPARENTHESIS: 'RP',
23794		FUNCTION: 'function',
23795		PARAMSEPARATOR: 'separator',
23796		STRING: 'string',
23797		SPACE: 'space'
23798
23799	};
23800
23801	var THEMECOMPLETE_EPO_MATH_Token = function( $type, $value, $name ) {
23802		var node = {
23803			type: THEMECOMPLETE_EPO_MATH_Constants.LITERAL,
23804			value: undefined,
23805			name: undefined,
23806			param_count: null
23807		};
23808		node.type = $type;
23809		node.value = $value;
23810		node.name = $name;
23811		return node;
23812	};
23813
23814	var THEMECOMPLETE_EPO_MATH_Operator = function( $operator, $is_right_assoc, $priority, $function ) {
23815		var node = {
23816			operator: '',
23817			is_right_assoc: false,
23818			priority: 0,
23819			function: null,
23820			places: 0
23821		};
23822		node.operator = $operator;
23823		node.is_right_assoc = $is_right_assoc;
23824		node.priority = $priority;
23825		node.function = $function;
23826		node.places = getNumberOfParameters( $function );
23827
23828		node.execute = function( $stack ) {
23829			var $args = [];
23830			var $i;
23831			var $result;
23832
23833			if ( $stack.length < this.places ) {
23834				// Empty the $stack
23835				$stack.splice( 0, $stack.length );
23836				return THEMECOMPLETE_EPO_MATH_Error.trigger( 'Incorrect Expression', 'IncorrectExpressionError', new THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.LITERAL, 0 ) );
23837			}
23838
23839			for ( $i = 0; $i < this.places; $i++ ) {
23840				$args.unshift( $stack.pop().value );
23841			}
23842
23843			$result = this.function.apply( null, $args );
23844			if ( 'number' !== gettype( $result ) && 'string' !== gettype( $result ) ) {
23845				$result = 0;
23846			}
23847
23848			return new THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.LITERAL, $result );
23849		};
23850
23851		return node;
23852	};
23853
23854	var THEMECOMPLETE_EPO_MATH_CustomFunction = function( $name, $function ) {
23855		var node = {
23856			name: '',
23857			function: undefined,
23858			required_param_count: undefined
23859		};
23860
23861		node.name = $name;
23862		node.function = $function;
23863		node.required_param_count = getNumberOfRequiredParameters( $function );
23864
23865		node.execute = function( $stack, $param_count_in_stack ) {
23866			var $args = [];
23867			var $i;
23868			var $argument;
23869			var $result;
23870			if ( $param_count_in_stack < this.required_param_count ) {
23871				$param_count_in_stack = this.required_param_count;
23872			}
23873			// We skip this section.
23874			if ( $param_count_in_stack < this.required_param_count ) {
23875				// Empty the $stack
23876				$stack.splice( 0, $stack.length );
23877				return THEMECOMPLETE_EPO_MATH_Error.trigger( this.name, 'IncorrectNumberOfFunctionParametersError', new THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.LITERAL, 0 ) );
23878			}
23879
23880			if ( $param_count_in_stack > 0 ) {
23881				for ( $i = 0; $i < $param_count_in_stack; $i++ ) {
23882					$argument = $stack.length ? $stack.pop().value : 0;
23883					if ( null === $argument ) {
23884						$argument = '0';
23885						$args.push( $argument );
23886					} else {
23887						$args.unshift( $argument );
23888					}
23889				}
23890			}
23891
23892			$result = this.function.apply( null, $args );
23893
23894			if ( $result === Infinity || $result === -Infinity ) {
23895				$result = 0;
23896			}
23897
23898			return THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.LITERAL, $result );
23899		};
23900
23901		return node;
23902	};
23903
23904	var THEMECOMPLETE_EPO_MATH_Calculator = function( $functions, $operators ) {
23905		var node = {
23906			functions: {},
23907			operators: {}
23908		};
23909		node.functions = $functions;
23910		node.operators = $operators;
23911
23912		node.calculate = function( $tokens, $variables, $on_var_not_found = null, $math_object = false ) {
23913			var $this = this;
23914			/** Array of THEMECOMPLETE_EPO_MATH_Token */
23915			var $stack = [];
23916			var $result;
23917
23918			if ( empty( $tokens ) ) {
23919				return 0;
23920			}
23921			try {
23922				$tokens.forEach( function( $token ) {
23923					var $variable;
23924					var $value;
23925					if ( THEMECOMPLETE_EPO_MATH_Constants.LITERAL === $token.type || THEMECOMPLETE_EPO_MATH_Constants.STRING === $token.type ) {
23926						$stack.push( $token );
23927					} else if ( THEMECOMPLETE_EPO_MATH_Constants.VARIABLE === $token.type ) {
23928						$variable = $token.value;
23929
23930						$value = null;
23931
23932						if ( array_key_exists( $variable, $variables ) ) {
23933							$value = $variables[ $variable ];
23934						} else if ( $on_var_not_found && 'function' === typeof $on_var_not_found ) {
23935							$value = $on_var_not_found( $variable );
23936						} else {
23937							$value = $variable;
23938							$math_object.variables[ $variable ] = $value;
23939							$variables[ $variable ] = $value;
23940						}
23941						$stack.push( THEMECOMPLETE_EPO_MATH_Token( THEMECOMPLETE_EPO_MATH_Constants.LITERAL, $value, $variable ) );
23942					} else if ( THEMECOMPLETE_EPO_MATH_Constants.FUNCTION === $token.type ) {
23943						if ( ! array_key_exists( $token.value, $this.functions ) ) {
23944							$math_object.add_function(
23945								$token.value,
23946								function() {
23947									return 0;
23948								}
23949							);
23950							$this.functions = $math_object.functions;
23951							if ( ! array_key_exists( $token.value, $this.functions ) ) {
23952								return THEMECOMPLETE_EPO_MATH_Error.trigger( $token.value, 'UnknownFunctionError', 0 );
23953							}
23954							THEMECOMPLETE_EPO_MATH_Error.trigger( $token.value, 'UnknownFunctionError', 0 );
23955						}
23956						$stack.push( $this.functions[ $token.value ].execute( $stack, $token.param_count ) );
23957					} else if ( THEMECOMPLETE_EPO_MATH_Constants.OPERATOR === $token.type ) {
23958						if ( ! array_key_exists( $token.value, $this.operators ) ) {
23959							return THEMECOMPLETE_EPO_MATH_Error.trigger( $token.value, 'UnknownOperatorError', 0 );
23960						}
23961						$stack.push( $this.operators[ $token.value ].execute( $stack ) );
23962					}
23963				} );
23964			} catch ( $e ) {
23965				if ( window.TMEPOJS && window.TMEPOJS.WP_DEBUG ) {
23966					window.console.log( $e );
23967				}
23968			}
23969			$result = $stack.pop();
23970
23971			if ( undefined === $result || null === $result || ! empty( $stack ) ) {
23972				return THEMECOMPLETE_EPO_MATH_Error.trigger( 'Stack must be empty', 'IncorrectExpressionError', 0 );
23973			}
23974
23975			if ( false === $result.value ) {
23976				$result.value = 0;
23977			}
23978
23979			if ( true === $result.value ) {
23980				$result.value = 1;
23981			}
23982
23983			if ( 'string' === gettype( $result.value ) && $result.value.isNumeric() ) {
23984				$result.value = floatval( $result.value );
23985			}
23986
23987			return $result.value;
23988		};
23989		return node;
23990	};
23991
23992	var THEMECOMPLETE_EPO_MATH_Error = {
23993		trigger: function( $msg, $code = '', $return = false ) {
23994			if ( window.TMEPOJS && window.TMEPOJS.WP_DEBUG ) {
23995				window.console.log( $code + '\n' + $msg );
23996				window.console.trace();
23997			}
23998			if ( window.TMEPOGLOBALADMINJS && window.TMEPOGLOBALADMINJS.WP_DEBUG ) {
23999				return $msg;
24000			}
24001			return $return;
24002		}
24003	};
24004
24005	window.tcmexp = THEMECOMPLETE_EPO_MATH.construct();
24006}( window ) );
24007;
24008( function( window, document, $ ) {
24009	'use strict';
24010
24011	var tcAPI = {};
24012	var TMEPOJS = window.TMEPOJS;
24013	var wp = window.wp;
24014	var TMEPOQTRANSLATEXJS = window.TMEPOQTRANSLATEXJS;
24015	var noUiSlider = window.noUiSlider;
24016	var ClipboardEvent = window.ClipboardEvent;
24017	var DataTransfer = window.DataTransfer;
24018	var lateVariationEvent = [];
24019	var latecpflogicactions = [];
24020	var tmLazyloadContainer = false;
24021	var variationsFormIsLoaded = false;
24022	var jBody = $( 'body' );
24023	var jWindow = $( window );
24024	var jDocument = $( document );
24025	var errorObject;
24026	var FloatingTotalsBox;
24027	var currentAjaxButton;
24028	var currentAddToCartButton;
24029	var errorContainer = $( window );
24030	var tcmexp = window.tcmexp;
24031	var _ = window._;
24032	var priceCache = false;
24033
24034	var getLocalInputDecimalSeparator = function() {
24035		if ( TMEPOJS.tm_epo_global_input_decimal_separator === '' ) {
24036			return TMEPOJS.currency_format_decimal_sep;
24037		}
24038		return $.epoAPI.locale.getSystemDecimalSeparator();
24039	};
24040
24041	var getLocalDecimalSeparator = function() {
24042		if ( TMEPOJS.tm_epo_global_displayed_decimal_separator === '' ) {
24043			return TMEPOJS.currency_format_decimal_sep;
24044		}
24045		return $.epoAPI.locale.getSystemDecimalSeparator();
24046	};
24047
24048	var getLocalThousandSeparator = function() {
24049		if ( TMEPOJS.tm_epo_global_displayed_decimal_separator === '' ) {
24050			return TMEPOJS.currency_format_thousand_sep;
24051		}
24052		return $.epoAPI.locale.getSystemDecimalSeparator();
24053	};
24054
24055	var getEpoDelay = function() {
24056		if ( TMEPOJS.tm_epo_start_animation_delay ) {
24057			return TMEPOJS.tm_epo_start_animation_delay;
24058		} else if ( window.tc_epo_delay ) {
24059			return window.tc_epo_delay;
24060		}
24061		return 500;
24062	};
24063
24064	var getEpoAnimationDelay = function() {
24065		if ( TMEPOJS.tm_epo_animation_delay ) {
24066			return TMEPOJS.tm_epo_start_animation_delay;
24067		} else if ( window.tc_epo_animation_delay ) {
24068			return window.tc_epo_animation_delay;
24069		}
24070		return 500;
24071	};
24072
24073	var originalVal = $.fn.val;
24074
24075	if ( ! TMEPOJS || ! wp ) {
24076		return;
24077	}
24078
24079	// Trigger plugin events when other plugins use val.
24080	$.fn.val = function() {
24081		var result = originalVal.apply( this, arguments );
24082		if ( arguments.length > 0 ) {
24083			if ( $( this ).is( '.input-text.qty' ) ) {
24084				$( this ).trigger( 'change.cpf' );
24085			}
24086		}
24087		return result;
24088	};
24089
24090	// Set update event for Lazy Load XT
24091	if ( TMEPOJS.tm_epo_no_lazy_load === 'no' && $.lazyLoadXT ) {
24092		$.extend( $.lazyLoadXT, {
24093			autoInit: false,
24094			updateEvent: $.lazyLoadXT.updateEvent + ' tmlazy'
24095		} );
24096	}
24097
24098	tcAPI.localInputDecimalSeparator = getLocalInputDecimalSeparator();
24099	tcAPI.localDecimalSeparator = getLocalDecimalSeparator();
24100	tcAPI.localThousandSeparator = getLocalThousandSeparator();
24101	tcAPI.epoDelay = getEpoDelay();
24102	tcAPI.epoAnimationDelay = getEpoAnimationDelay();
24103	tcAPI.getElementFromFieldCache = [];
24104	tcAPI.epoSelector = '.tc-extra-product-options';
24105	tcAPI.associatedEpoSelector = '.tc-extra-product-options-inline';
24106	tcAPI.associatedEpoCart = '.tc-epo-element-product-container-cart';
24107	tcAPI.addToCartSelector = "input[name='add-to-cart']";
24108	tcAPI.tcAddToCartSelector = 'input.tc-add-to-cart';
24109	tcAPI.qtySelector = "input.qty,input[name='quantity'],select.qty,.drop-down-button #qty,.plus-minus-button #qty,.slider-input #amount";
24110	tcAPI.associateQtySelector = 'input.tm-qty-alt';
24111	tcAPI.addToCartButtonSelector = '.add_to_cart_button, .single_add_to_cart_button';
24112	tcAPI.compositeSelector = '.bto_item,.component';
24113	tcAPI.nativeProductPriceSelector = TMEPOJS.tm_epo_global_product_price_selector || '.woocommerce .product p.price, .wc-block-components-product-price';
24114	tcAPI.associatedNativeProductPriceSelector = '.product-price .associated-price';
24115	tcAPI.templateEngine = $.epoAPI.applyFilter( 'tc_adjust_templateEngine', {
24116		plain_price: wp.template( 'tc-plain-price' ),
24117		suffix: wp.template( 'tc-suffix' ),
24118		main_suffix: wp.template( 'tc-main-suffix' ),
24119		price: wp.template( 'tc-price' ),
24120		price_m: wp.template( 'tc-price-m' ),
24121		sale_price: wp.template( 'tc-sale-price' ),
24122		sale_price_m10: wp.template( 'tc-sale-price-m10' ),
24123		sale_price_m01: wp.template( 'tc-sale-price-m01' ),
24124		sale_price_m11: wp.template( 'tc-sale-price-m11' ),
24125		tc_chars_remanining: wp.template( 'tc-chars-remanining' ),
24126		tc_final_totals: wp.template( 'tc-final-totals' ),
24127		tc_floating_box: wp.template( 'tc-floating-box' ),
24128		tc_floating_box_nks: wp.template( 'tc-floating-box-nks' ),
24129		tc_formatted_price: wp.template( 'tc-formatted-price' ),
24130		tc_formatted_sale_price: wp.template( 'tc-formatted-sale-price' ),
24131		tc_lightbox: wp.template( 'tc-lightbox' ),
24132		tc_lightbox_zoom: wp.template( 'tc-lightbox-zoom' ),
24133		tc_section_pop_link: wp.template( 'tc-section-pop-link' ),
24134		tc_upload_messages: wp.template( 'tc-upload-messages' )
24135	} );
24136
24137	// make API available to 3rd party plugins
24138	$.tcAPI = function() {
24139		return tcAPI;
24140	};
24141	// method for accessing internal api variables
24142	$.tcAPIGet = function( name ) {
24143		return tcAPI[ name ];
24144	};
24145
24146	// method for setting  internal api variables
24147	$.tcAPISet = function( name, value ) {
24148		tcAPI[ name ] = value;
24149	};
24150
24151	if ( $.tc_validator ) {
24152		$.extend( $.tc_validator.messages, {
24153			required: TMEPOJS.tm_epo_global_validator_messages.required,
24154			email: TMEPOJS.tm_epo_global_validator_messages.email,
24155			url: TMEPOJS.tm_epo_global_validator_messages.url,
24156			number: TMEPOJS.tm_epo_global_validator_messages.number,
24157			digits: TMEPOJS.tm_epo_global_validator_messages.digits,
24158			maxlengthsingle: $.tc_validator.format( TMEPOJS.tm_epo_global_validator_messages.maxlengthsingle ),
24159			maxlength: $.tc_validator.format( TMEPOJS.tm_epo_global_validator_messages.maxlength ),
24160			minlengthsingle: $.tc_validator.format( TMEPOJS.tm_epo_global_validator_messages.minlengthsingle ),
24161			minlength: $.tc_validator.format( TMEPOJS.tm_epo_global_validator_messages.minlength ),
24162			max: $.tc_validator.format( TMEPOJS.tm_epo_global_validator_messages.max ),
24163			min: $.tc_validator.format( TMEPOJS.tm_epo_global_validator_messages.min ),
24164			step: $.tc_validator.format( TMEPOJS.tm_epo_global_validator_messages.step ),
24165			lettersonly: $.tc_validator.format( TMEPOJS.tm_epo_global_validator_messages.lettersonly ),
24166			lettersspaceonly: $.tc_validator.format( TMEPOJS.tm_epo_global_validator_messages.lettersspaceonly ),
24167			alphanumeric: $.tc_validator.format( TMEPOJS.tm_epo_global_validator_messages.alphanumeric ),
24168			alphanumericunicode: $.tc_validator.format( TMEPOJS.tm_epo_global_validator_messages.alphanumericunicode ),
24169			alphanumericunicodespace: $.tc_validator.format( TMEPOJS.tm_epo_global_validator_messages.alphanumericunicodespace ),
24170			repeaterminrows: $.tc_validator.format( TMEPOJS.tm_epo_global_validator_messages.repeaterminrows ),
24171			repeatermaxrows: $.tc_validator.format( TMEPOJS.tm_epo_global_validator_messages.repeatermaxrows )
24172		} );
24173
24174		/*
24175		ASCII Digits
24176		\u0030-\u0039
24177
24178		Latin Alphabet
24179		\u0041-\u005A\u0061-\u007A
24180
24181		Latin-1 Supplement
24182		\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF
24183
24184		Latin Extended-A
24185		\u0100-\u0148\u014A-\u017F
24186
24187		Latin Extended-B
24188		\u0180-\u01BF\u01C4-\u024F
24189
24190		Latin Extended Additional
24191		\u1E02-\u1EF3
24192
24193		Greek and Coptic
24194		\u0370-\u03FF
24195
24196		Cyrillic
24197		\u0400-\u04FF\u0500-\u052F
24198
24199		Japanese Hiragana
24200		\u3040-\u309f
24201		Japanese Katakana
24202		\u30a0-\u30ff
24203		Japanese Kanji (common & uncommon)
24204		\u4e00-\u9faf
24205		Japanese Kanji (rare)
24206		\u3400-\u4dbf
24207
24208		Arabic
24209		\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF\u10E60-\u10E7F\u1EC70-\u1ECBF\u1ED00-\u1ED4F\u1EE00-\u1EEFF
24210
24211		\u0600-\u06ff
24212		\u0600-\u06FF
24213
24214		Armenian
24215		\u0530-\u1058F
24216
24217		\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u0148\u014A-\u017F\u0180-\u01BF\u01C4-\u024F\u1E02-\u1EF3\u0370-\u03FF\u0400-\u04FF\u0500-\u052F\u3040-\u309f\u30a0-\u30ff\u4e00-\u9faf\u3400-\u4dbf\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF\u10E60-\u10E7F\u1EC70-\u1ECBF\u1ED00-\u1ED4F\u1EE00-\u1EEFF\u0530-\u1058F
24218
24219		*/
24220
24221		$.tc_validator.addMethod(
24222			'alphanumeric',
24223			function( value, element ) {
24224				return this.optional( element ) || /^[a-zA-Z0-9.-]+$/i.test( value );
24225			},
24226			$.tc_validator.messages.alphanumeric
24227		);
24228
24229		$.tc_validator.addMethod(
24230			'lettersonly',
24231			function( value, element ) {
24232				return this.optional( element ) || /^[a-z]+$/i.test( value );
24233			},
24234			$.tc_validator.messages.lettersonly
24235		);
24236
24237		$.tc_validator.addMethod(
24238			'lettersspaceonly',
24239			function( value, element ) {
24240				return this.optional( element ) || /^[a-z,\u0020]+$/i.test( value );
24241			},
24242			$.tc_validator.messages.lettersspaceonly
24243		);
24244
24245		$.tc_validator.addMethod(
24246			'alphanumericunicode',
24247			function( value, element ) {
24248				return (
24249					this.optional( element ) ||
24250					/^[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u0148\u014A-\u017F\u0180-\u01BF\u01C4-\u024F\u1E02-\u1EF3\u0370-\u03FF\u0400-\u04FF\u0500-\u052F\u3040-\u309f\u30a0-\u30ff\u4e00-\u9faf\u3400-\u4dbf\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF\u10E60-\u10E7F\u1EC70-\u1ECBF\u1ED00-\u1ED4F\u1EE00-\u1EEFF\u0530-\u1058F]+$/i.test(
24251						value
24252					)
24253				);
24254			},
24255			$.tc_validator.messages.alphanumericunicode
24256		);
24257
24258		$.tc_validator.addMethod(
24259			'alphanumericunicodespace',
24260			function( value, element ) {
24261				return (
24262					this.optional( element ) ||
24263					/^[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u0148\u014A-\u017F\u0180-\u01BF\u01C4-\u024F\u1E02-\u1EF3\u0370-\u03FF\u0400-\u04FF\u0500-\u052F\u3040-\u309f\u30a0-\u30ff\u4e00-\u9faf\u3400-\u4dbf\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF\u10E60-\u10E7F\u1EC70-\u1ECBF\u1ED00-\u1ED4F\u1EE00-\u1EEFF\u0530-\u1058F,\u0020]+$/i.test(
24264						value
24265					)
24266				);
24267			},
24268			$.tc_validator.messages.alphanumericunicodespace
24269		);
24270		$.tc_validator.addMethod(
24271			'repeaterminrows',
24272			function( value, element, param ) {
24273				return $.epoAPI.math.toInt( $( element ).closest( '.tc-element-inner-wrap' ).find( '.tc-repeater-element' ).length ) >= $.epoAPI.math.toInt( param );
24274			},
24275			$.tc_validator.messages.repeaterminrows
24276		);
24277		$.tc_validator.addMethod(
24278			'repeatermaxrows',
24279			function( value, element, param ) {
24280				return $.epoAPI.math.toInt( $( element ).closest( '.tc-element-inner-wrap' ).find( '.tc-repeater-element' ).length ) <= $.epoAPI.math.toInt( param );
24281			},
24282			$.tc_validator.messages.repeatermaxrows
24283		);
24284		$.tc_validator.addMethod(
24285			'product_element_radio_qty',
24286			function( value, element ) {
24287				var qty = $( element ).closest( '.tm-element-ul-product' ).find( 'input.tc-epo-field-product.tm
24287cp-radio:checked' ).closest( '.tc-epo-element-product-holder' ).find( '.tm-qty' ).first().val();
24288				return qty > 0;
24289			},
24290			$.tc_validator.messages.required
24291		);
24292		$.tc_validator.addMethod(
24293			'product_element_select_qty',
24294			function( value, element ) {
24295				var qty = $( element ).closest( '.tm-element-ul-product' ).find( 'select.tc-epo-field-product.tmcp-select' ).closest( '.tc-epo-element-product-holder' ).find( '.tm-qty' ).first().val();
24296				return qty > 0;
24297			},
24298			$.tc_validator.messages.required
24299		);
24300	}
24301
24302	$.epoAPI.util.unformat = function( o ) {
24303		var a = $.epoAPI.math.unformat( o, tcAPI.localInputDecimalSeparator );
24304		var n = parseFloat( a );
24305
24306		if ( ! Number.isFinite( n ) ) {
24307			return a;
24308		}
24309		return n;
24310	};
24311
24312	if ( ! $.tmType ) {
24313		$.tmType = function( obj ) {
24314			return Object.prototype.toString
24315				.call( obj )
24316				.match( /\s([a-zA-Z]+)/ )[ 1 ]
24317				.toLowerCase();
24318		};
24319	}
24320
24321	if ( ! $.is_on_screen ) {
24322		$.fn.is_on_screen = function() {
24323			// we don't use jWindow because we want the current window object
24324			var win = $( window );
24325			var scroll = $.epoAPI.dom.scroll();
24326			var bounds = this.offset();
24327			var viewport = {
24328				top: scroll.top,
24329				left: scroll.left
24330			};
24331
24332			viewport.right = viewport.left + win.width();
24333			viewport.bottom = viewport.top + win.height();
24334			bounds.right = bounds.left + this.outerWidth();
24335			bounds.bottom = bounds.top + this.outerHeight();
24336
24337			return ! ( viewport.right < bounds.left || viewport.left > bounds.right || viewport.bottom < bounds.top || viewport.top > bounds.bottom );
24338		};
24339	}
24340
24341	if ( ! $().tmtoggle ) {
24342		$.fn.tmtoggle = function() {
24343			var elements = this;
24344			var is_one_open_for_accordion = false;
24345			var init_done = 0;
24346
24347			if ( elements.length === 0 ) {
24348				return;
24349			}
24350
24351			if ( window.tc_accordion_offset === undefined ) {
24352				window.tc_accordion_offset = -100;
24353			}
24354
24355			elements.toArray().forEach( function( t ) {
24356				var headers;
24357				var wrap;
24358				var wraps;
24359
24360				t = $( t );
24361
24362				if ( ! t.data( 'tm-toggle-init' ) ) {
24363					t.data( 'tm-toggle-init', 1 );
24364					headers = t.find( '.tm-toggle' );
24365					wrap = t.find( '.tm-collapse-wrap' );
24366					wraps = $( '.tm-collapse.tmaccordion' );
24367					if ( wraps.closest( '.tc-epo-element-product-holder' ).length > 0 ) {
24368						wraps = wraps.closest( '.tc-epo-element-product-holder' ).find( '.tm-collapse.tmaccordion' ).find( '.tm-toggle' );
24369					} else {
24370						wraps = $( '.tm-collapse.tmaccordion' ).not( '.tc-epo-element-product-holder .tm-collapse.tmaccordion' ).find( '.tm-toggle' );
24371					}
24372					if ( headers.length === 0 || wrap.length === 0 ) {
24373						return;
24374					}
24375
24376					if ( wrap.is( '.closed' ) ) {
24377						$( wrap ).removeClass( 'closed open' ).addClass( 'closed' ).hide();
24378						$( headers ).find( '.tm-arrow' ).removeClass( 'tcfa-angle-down tcfa-angle-up' ).addClass( 'tcfa-angle-down' );
24379						$( headers ).removeClass( 'toggle-header-open toggle-header-closed' ).addClass( 'toggle-header-closed' );
24380						$( headers ).attr( 'aria-expanded', false );
24381					} else {
24382						$( wrap ).removeClass( 'closed open' ).addClass( 'open' ).show();
24383						$( headers ).find( '.tm-arrow' ).removeClass( 'tcfa-angle-down tcfa-angle-up' ).addClass( 'tcfa-angle-up' );
24384						$( headers ).removeClass( 'toggle-header-open toggle-header-closed' ).addClass( 'toggle-header-open' );
24385						$( headers ).attr( 'aria-expanded', true );
24386						is_one_open_for_accordion = true;
24387					}
24388
24389					headers.toArray().forEach( function( header ) {
24390						header = $( header );
24391						header.on( 'closewrap.tmtoggle', function() {
24392							if ( t.is( '.tmaccordion' ) && $( wrap ).is( '.closed' ) ) {
24393								return;
24394							}
24395							$( wrap ).removeClass( 'closed open' ).addClass( 'closed' );
24396							$( this ).find( '.tm-arrow' ).removeClass( 'tcfa-angle-down tcfa-angle-up' ).addClass( 'tcfa-angle-down' );
24397							$( this ).removeClass( 'toggle-header-open toggle-header-closed' ).addClass( 'toggle-header-closed' );
24398							$( this ).attr( 'aria-expanded', false );
24399							$( wrap ).removeClass( 'tm-animated fadein' );
24400							if ( t.is( '.tmaccordion' ) ) {
24401								$( wrap ).animate( { height: 'toggle' }, 100, function() {
24402									$( wrap ).hide();
24403								} );
24404							} else {
24405								$( wrap ).animate( { height: 'toggle' }, 100, function() {
24406									$( wrap ).hide();
24407								} );
24408							}
24409							jWindow.trigger( 'tmlazy' );
24410						} );
24411
24412						header.on( 'openwrap.tmtoggle', function( e, data ) {
24413							if ( t.is( '.tmaccordion' ) ) {
24414								$( wraps ).not( $( this ) ).trigger( 'closewrap.tmtoggle' );
24415							}
24416							$( wrap ).removeClass( 'closed open' ).addClass( 'open' );
24417							$( this ).find( '.tm-arrow' ).removeClass( 'tcfa-angle-down tcfa-angle-up' ).addClass( 'tcfa-angle-up' );
24418							$( this ).removeClass( 'toggle-header-open toggle-header-closed' ).addClass( 'toggle-header-open' );
24419							$( this ).attr( 'aria-expanded', true );
24420							$( wrap ).show().removeClass( 'tm-animated fadein' ).addClass( 'tm-animated fadein' );
24421							setTimeout( function() {
24422								jWindow.trigger( 'tmlazy' );
24423							}, 200 );
24424							setTimeout( function() {
24425								if ( data === undefined && init_done && t.is( '.tmaccordion' ) && ! t.is_on_screen() ) {
24426									jWindow.tcScrollTo( header, 10, window.tc_accordion_offset );
24427								}
24428							}, 110 );
24429						} );
24430
24431						header.on( 'click.tmtoggle keydown.tmtoggle', function( e ) {
24432							// If it's a keydown event, only trigger if the Enter key was pressed
24433							if ( e.type === 'keydown' && e.key !== 'Enter' ) {
24434								return; // Ignore all other keypresses
24435							}
24436							e.preventDefault();
24437							if ( $( wrap ).is( '.closed' ) ) {
24438								$( this ).trigger( 'openwrap.tmtoggle' );
24439							} else {
24440								$( this ).trigger( 'closewrap.tmtoggle' );
24441							}
24442						} );
24443
24444						header
24445							.find( '.tm-qty' )
24446							.closest( '.cpf-element' )
24447							.find( '.tm-epo-field' )
24448							.on( 'change.cpf', function() {
24449								header.trigger( 'openwrap.tmtoggle' );
24450							} );
24451					} );
24452				}
24453			} );
24454			if ( undefined === window.tc_accordion_closed_on_page_load && ! is_one_open_for_accordion && elements.filter( '.tmaccordion' ).length > 0 ) {
24455				elements.filter( '.tmaccordion' ).first().find( '.tm-toggle' ).trigger( 'openwrap.tmtoggle', { nomove: 1 } );
24456			}
24457			init_done = 1;
24458			return elements;
24459		};
24460	}
24461
24462	if ( ! $().tmpoplink ) {
24463		$.fn.tmpoplink = function( options ) {
24464			var elements = this;
24465			var floatbox_template;
24466			var settings = {
24467				classname: 'flasho tc-wrapper cart-popup'
24468			};
24469
24470			if ( elements.length === 0 ) {
24471				return;
24472			}
24473			if ( options ) {
24474				settings = $.extend( {}, settings, options );
24475			}
24476
24477			floatbox_template = function( data ) {
24478				return $.epoAPI.template.html( wp.template( 'tc-cart-options-popup' ), {
24479					title: data.title,
24480					id: data.id,
24481					html: data.html,
24482					close: TMEPOJS.i18n_close
24483				} );
24484			};
24485
24486			return elements.toArray().forEach( function( t ) {
24487				var id;
24488				var title;
24489				var html;
24490				var $_html;
24491
24492				t = $( t );
24493				if ( t.is( '.tc-poplink' ) ) {
24494					return;
24495				}
24496				t.addClass( 'tc-poplink' );
24497				id = t.attr( 'href' );
24498				title = t.attr( 'data-title' );
24499				html = $( id ).html();
24500				if ( ! title ) {
24501					title = TMEPOJS.i18n_addition_options;
24502				}
24503				$_html = floatbox_template( {
24504					id: 'tc-floatbox-content',
24505					html: html,
24506					title: title
24507				} );
24508
24509				t.on( 'click.tmpoplink', function( e ) {
24510					$.tcFloatBox( {
24511						fps: 1,
24512						ismodal: false,
24513						width: '80%',
24514						height: '80%',
24515						classname: settings.classname,
24516						data: $_html
24517					} );
24518
24519					e.preventDefault();
24520				} );
24521			} );
24522		};
24523	}
24524
24525	function readableFileSize( a, b, c, d, e ) {
24526		b = Math;
24527		c = b.log;
24528		d = 1024;
24529		e = c( a ) / c( d ) | 0; //eslint-disable-line no-bitwise
24530
24531		return ( ( a / b.pow( d, e ) ).toFixed( e ? 2 : 0 ) ) + ' ' + ( e ? 'KMGTPEZY'[ --e ] + 'B' : 'bytes' );
24532	}
24533
24534	function toggleState( thisProductContainerWrap, disabled ) {
24535		thisProductContainerWrap.toArray().forEach( function( setter ) {
24536			setter = $( setter );
24537			setter.find( ':input' ).prop( 'disabled', function( i, v ) {
24538				var state = $( this ).data( 'tc-state' );
24539				if ( state === undefined ) {
24540					$( this ).data( 'tc-state', v );
24541					return disabled;
24542				}
24543				if ( state === false ) {
24544					return disabled;
24545				}
24546				return state;
24547			} );
24548		} );
24549	}
24550	// Function to calculate tax amount
24551	function calculateTaxAmount( priceWithTax, _cart ) {
24552		var taxable;
24553		var tax_rate;
24554		var pricesIncludeTax;
24555		var tax_display_mode;
24556		var current_variation;
24557		var priceWithoutTax;
24558		var taxAmount = 0;
24559
24560		if ( _cart ) {
24561			taxable = _cart.attr( 'data-taxable' );
24562			tax_rate = _cart.attr( 'data-tax-rate' );
24563			pricesIncludeTax = _cart.attr( 'data-prices-include-tax' ) || TMEPOJS.prices_include_tax;
24564			tax_display_mode = _cart.attr( 'data-tax-display-mode' );
24565			if ( _cart.data( 'current_variation' ) !== undefined ) {
24566				current_variation = _cart.data( 'current_variation' );
24567			}
24568
24569			if ( current_variation !== undefined ) {
24570				current_variation = _cart.data( 'current_variation' );
24571				taxable = current_variation.tc_is_taxable;
24572				tax_rate = current_variation.tc_tax_rate;
24573			}
24574		}
24575
24576		if ( taxable ) {
24577			tax_rate = parseFloat( tax_rate / 100 );
24578
24579			if ( pricesIncludeTax === '1' ) {
24580				if ( tax_display_mode === 'incl' ) {
24581					priceWithTax = parseFloat( priceWithTax );
24582
24583					// Calculate price without tax
24584					priceWithoutTax = priceWithTax / ( 1 + tax_rate );
24585
24586					// Calculate tax amount
24587					taxAmount = priceWithTax - priceWithoutTax;
24588				} else {
24589					// Calculate price without tax
24590					priceWithoutTax = priceWithTax;
24591
24592					// Calculate tax amount
24593					taxAmount = priceWithTax * tax_rate;
24594				}
24595			} else if ( tax_display_mode === 'incl' ) {
24596				// Calculate price without tax
24597				priceWithoutTax = priceWithTax / ( 1 + tax_rate );
24598
24599				// Calculate tax amount
24600				taxAmount = priceWithTax - priceWithoutTax;
24601			}
24602
24603			taxAmount = $.epoAPI.math.toFloat( taxAmount );
24604		}
24605
24606		return taxAmount;
24607	}
24608	// Taxes setup
24609	function get_price_including_tax( price, _cart, element, force, variation, pricetype, doubleforce ) {
24610		var taxable;
24611		var tax_rate;
24612		var prices_include_tax;
24613		var is_vat_exempt;
24614		var non_base_location_prices;
24615		var taxes_of_one;
24616		var base_taxes_of_one;
24617		var modded_taxes_of_one;
24618		var current_variation;
24619
24620		if ( ! Number.isFinite( parseFloat( price ) ) ) {
24621			price = 0;
24622		}
24623		price = price * 10000;
24624		if ( _cart ) {
24625			taxable = _cart.attr( 'data-taxable' );
24626			tax_rate = _cart.attr( 'data-tax-rate' );
24627			prices_include_tax = _cart.attr( 'data-prices-include-tax' ) || TMEPOJS.prices_include_tax;
24628			is_vat_exempt = _cart.attr( 'data-is-vat-exempt' );
24629			non_base_location_prices = _cart.attr( 'data-non-base-location-prices' );
24630			taxes_of_one = _cart.attr( 'data-taxes-of-one' );
24631			base_taxes_of_one = _cart.attr( 'data-base-taxes-of-one' );
24632			modded_taxes_of_one = _cart.attr( 'data-modded-taxes-of-one' );
24633
24634			if ( _cart.data( 'current_variation' ) !== undefined ) {
24635				current_variation = _cart.data( 'current_variation' );
24636			} else if ( variation !== undefined ) {
24637				current_variation = variation;
24638			}
24639
24640			if ( current_variation !== undefined ) {
24641				current_variation = _cart.data( 'current_variation' );
24642				taxable = current_variation.tc_is_taxable;
24643				tax_rate = current_variation.tc_tax_rate;
24644				non_base_location_prices = current_variation.tc_non_base_location_prices;
24645				taxes_of_one = current_variation.tc_taxes_of_one;
24646				base_taxes_of_one = current_variation.tc_base_taxes_of_one;
24647				modded_taxes_of_one = current_variation.tc_modded_taxes_of_one;
24648			}
24649
24650			if ( element ) {
24651				if ( element.data( 'tax-obj' ) ) {
24652					tax_rate = element.data( 'tax-obj' );
24653					if ( tax_rate.has_fee === 'no' ) {
24654						taxable = false;
24655					} else if ( tax_rate.has_fee === 'yes' ) {
24656						taxable = true;
24657					}
24658					if ( base_taxes_of_one === '' || base_taxes_of_one === undefined ) {
24659						base_taxes_of_one = tax_rate / 100;
24660					}
24661					if ( tax_rate.base_taxes_of_one !== undefined ) {
24662						base_taxes_of_one = tax_rate.base_taxes_of_one;
24663					}
24664					tax_rate = tax_rate.tax_rate;
24665					taxes_of_one = tax_rate / 100;
24666				}
24667			}
24668			if ( taxable ) {
24669				if ( prices_include_tax === '1' && ! force ) {
24670					if ( is_vat_exempt === '1' ) {
24671						if ( non_base_location_prices === '1' ) {
24672							price = parseFloat( price ) - ( taxes_of_one * price );
24673						} else {
24674							price = parseFloat( price ) - ( base_taxes_of_one * price );
24675						}
24676					} else if ( non_base_location_prices === '1' ) {
24677						price = parseFloat( price ) - ( base_taxes_of_one * price ) + ( modded_taxes_of_one * price );
24678					}
24679				} else if ( element || current_variation === undefined || doubleforce ) {
24680					price = parseFloat( price ) + ( parseFloat( price ) * taxes_of_one );
24681				}
24682			}
24683		}
24684		price = price / 10000;
24685
24686		return price;
24687	}
24688
24689	function get_price_excluding_tax( price, _cart, element, force, variation, pricetype, doubleforce ) {
24690		var taxable;
24691		var tax_rate;
24692		var taxes_of_one;
24693		var base_taxes_of_one;
24694		var prices_include_tax;
24695		var current_variation;
24696		var tax_display_mode;
24697
24698		if ( ! Number.isFinite( parseFloat( price ) ) ) {
24699			price = 0;
24700		}
24701		price = price * 10000;
24702
24703		if ( _cart ) {
24704			tax_display_mode = _cart.attr( 'data-tax-display-mode' );
24705
24706			taxable = _cart.attr( 'data-taxable' );
24707			tax_rate = _cart.attr( 'data-tax-rate' );
24708			taxes_of_one = _cart.attr( 'data-taxes-of-one' );
24709			base_taxes_of_one = _cart.attr( 'data-base-taxes-of-one' );
24710			prices_include_tax = _cart.attr( 'data-prices-include-tax' );
24711
24712			if ( _cart.data( 'current_variation' ) !== undefined ) {
24713				current_variation = _cart.data( 'current_variation' );
24714			} else if ( variation !== undefined ) {
24715				current_variation = variation;
24716			}
24717			if ( current_variation !== undefined ) {
24718				taxable = current_variation.tc_is_taxable;
24719				tax_rate = current_variation.tc_tax_rate;
24720				taxes_of_one = current_variation.tc_taxes_of_one;
24721				base_taxes_of_one = current_variation.tc_base_taxes_of_one;
24722			}
24723
24724			if ( element ) {
24725				if ( element.data( 'tax-obj' ) ) {
24726					tax_rate = element.data( 'tax-obj' );
24727					if ( tax_rate.has_fee === 'no' ) {
24728						taxable = false;
24729					} else if ( tax_rate.has_fee === 'yes' ) {
24730						taxable = true;
24731					}
24732					if ( base_taxes_of_one === '' || base_taxes_of_one === undefined ) {
24733						base_taxes_of_one = tax_rate / 100;
24734					}
24735					if ( tax_rate.base_taxes_of_one !== undefined ) {
24736						base_taxes_of_one = tax_rate.base_taxes_of_one;
24737					}
24738					tax_rate = tax_rate.tax_rate;
24739				}
24740			}
24741
24742			if ( taxable ) {
24743				if ( prices_include_tax === '1' || force ) {
24744					if ( tax_display_mode === 'incl' ) {
24745						// This should never run on this function
24746					} else if ( element || current_variation === undefined || force || doubleforce ) {
24747						if ( base_taxes_of_one === taxes_of_one ) {
24748							price = parseFloat( price ) - parseFloat( taxes_of_one * price );
24749						} else {
24750							price = price * ( 1 - parseFloat( base_taxes_of_one ) );
24751						}
24752					}
24753				}
24754			}
24755		}
24756		price = price / 10000;
24757
24758		return price;
24759	}
24760
24761	function tm_set_tax_price( value, _cart, element, pricetype, force, variation, doubleforce ) {
24762		var tax_display_mode;
24763
24764		if ( ! Number.isFinite( parseFloat( value ) ) ) {
24765			value = 0;
24766		}
24767		if ( _cart ) {
24768			tax_display_mode = _cart.attr( 'data-tax-display-mode' ) || TMEPOJS.tax_display_mode;
24769			if ( tax_display_mode === 'incl' ) {
24770				value = get_price_including_tax( value, _cart, element, force, variation, pricetype, doubleforce );
24771			} else {
24772				value = get_price_excluding_tax( value, _cart, element, force, variation, pricetype, doubleforce );
24773			}
24774		}
24775		return value;
24776	}
24777
24778	// Return a formatted currency value
24779	function formatPrice( value, args ) {
24780		var data;
24781		if ( ! args ) {
24782			args = {};
24783		}
24784		data = $.extend( {
24785			symbol: '',
24786			format: '',
24787			decimal: tcAPI.localDecimalSeparator,
24788			thousand: tcAPI.localThousandSeparator,
24789			precision: TMEPOJS.currency_format_num_decimals
24790		}, args );
24791
24792		if ( TMEPOJS.tm_epo_trim_zeros === 'yes' ) {
24793			if ( ( ( value % 1 ).toString() !== '0' ) === false ) {
24794				data.precision = 0;
24795			}
24796		}
24797
24798		return $.epoAPI.applyFilter( 'tc_formatPrice', $.epoAPI.math.format( value, data ), data, value );
24799	}
24800
24801	// Return a formatted currency value
24802	function tm_set_price_( value, sign ) {
24803		return (
24804			sign +
24805			formatPrice( value, { symbol: TMEPOJS.currency_format_symbol, format: TMEPOJS.currency_format } )
24806		);
24807	}
24808
24809	// Return a formatted currency value
24810	function tm_set_price( value, _cart, notax, element, pricetype ) {
24811		var val;
24812		var sign = TMEPOJS.option_plus_sign + ' ';
24813
24814		if ( ! notax ) {
24815			value = tm_set_tax_price( value, _cart, element, pricetype );
24816		}
24817
24818		val = Math.abs( value );
24819
24820		if ( TMEPOJS.tm_epo_global_options_price_sign === 'minus' ) {
24821			sign = '';
24822		}
24823		if ( value < 0 ) {
24824			sign = TMEPOJS.option_minus_sign + ' ';
24825		}
24826
24827		return tm_set_price_( val, sign );
24828	}
24829
24830	// Return a price currency value without any formatting
24831	function tm_get_price( value, _cart, notax, element, pricetype, force ) {
24832		if ( ! notax ) {
24833			value = tm_set_tax_price( value, _cart, element, pricetype, force );
24834		}
24835
24836		return value;
24837	}
24838
24839	// FloatingTotalsBox plugin
24840	FloatingTotalsBox = function( this_epo_totals_container, is_quickview, main_cart ) {
24841		this.this_epo_totals_container = this_epo_totals_container;
24842		this.is_quickview = is_quickview;
24843		this.main_cart = main_cart;
24844
24845		if ( ! is_quickview && TMEPOJS.floating_totals_box && TMEPOJS.floating_totals_box !== 'disable' && main_cart && this_epo_totals_container.length ) {
24846			this.init();
24847			return this;
24848		}
24849
24850		return false;
24851	};
24852
24853	FloatingTotalsBox.prototype = {
24854		constructor: FloatingTotalsBox,
24855
24856		onUpdate: function() {
24857			var tm_epo_totals_html = this.this_epo_totals_container.data( 'tm-html' );
24858			var tm_floating_box_data = this.this_epo_totals_container.data( 'tm-floating-box-data' );
24859			var values_obj = [];
24860			var floatingBoxHtml;
24861			var floatingBoxaddToCartButton;
24862			var len;
24863			var i;
24864			var row;
24865			var tempDiv;
24866			var j;
24867			var spans;
24868			var container;
24869
24870			if ( tm_floating_box_data && tm_floating_box_data.length ) {
24871				len = tm_floating_box_data.length;
24872				for ( i = 0; i < len; i++ ) {
24873					row = tm_floating_box_data[ i ];
24874					// Ensure title and value are properly set
24875					row.title = row.title ? String( row.title ) : '&nbsp;';
24876					row.value = row.value ? String( row.value ) : '&nbsp;';
24877
24878					// Sanitize title by removing spans
24879					if ( row.title !== '&nbsp;' ) {
24880						tempDiv = document.createElement( 'div' );
24881						tempDiv.innerHTML = row.title;
24882						spans = tempDiv.querySelectorAll( 'span' );
24883						for ( j = 0; j < spans.length; j++ ) {
24884							spans[ j ].remove();
24885						}
24886						row.title = tempDiv.innerHTML;
24887					}
24888
24889					// Handle "is_nks" logic
24890					if ( this.is_nks ) {
24891						if ( row.label_show !== '' ) {
24892							row.title = '';
24893						}
24894						if ( row.value_show !== '' ) {
24895							row.value = '';
24896						}
24897					}
24898
24899					// Auto-hide price if zero
24900					if ( TMEPOJS.tm_epo_auto_hide_price_if_zero === 'yes' && $.epoAPI.util.isEmpty( row.price ) === true ) {
24901						row.price = '';
24902					} else {
24903						row.price = tm_set_price( row.price, this.this_epo_totals_container, true );
24904					}
24905
24906					// Push processed data into values_obj
24907					values_obj.push( {
24908						label_show: row.label_show,
24909						value_show: row.value_show,
24910						title: row.title,
24911						value: row.value,
24912						quantity: row.quantity,
24913						price: row.price
24914					} );
24915				}
24916			}
24917
24918			if ( ( ( tm_epo_totals_html && tm_epo_totals_html === '' ) && ( ! tm_floating_box_data || ! tm_floating_box_data.length ) ) || ! ( ( tm_epo_totals_html && tm_epo_totals_html !== '' ) || this.is_nks ) ) {
24919				tm_epo_totals_html = '';
24920				this.floatingBox.hide();
24921			}
24922
24923			floatingBoxHtml = $.epoAPI.template.html( this.engineTemplate, {
24924				html_before: TMEPOJS.floating_totals_box_html_before,
24925				html_after: TMEPOJS.floating_totals_box_html_after,
24926				option_label: TMEPOJS.i18n_option_label,
24927				option_value: TMEPOJS.i18n_option_value,
24928				option_qty: TMEPOJS.i18n_option_qty,
24929				option_price: TMEPOJS.i18n_option_price,
24930				values: values_obj,
24931				totals: tm_epo_totals_html
24932			} );
24933
24934			this.floatingBox.html( floatingBoxHtml );
24935			this.onUpdateScroll();
24936
24937			if ( TMEPOJS.tm_epo_no_lazy_load !== 'yes' ) {
24938				this.floatingBox.addClass( 'lazyxt' );
24939				container = this.floatingBox.find( 'img.tmlazy' );
24940				container.lazyLoadXT();
24941				container.on( 'lazyshow', function() {
24942					jWindow.trigger( 'lazyLoadXToncomplete' );
24943				} );
24944			}
24945
24946			if ( TMEPOJS.floating_totals_box_add_button === 'yes' ) {
24947				floatingBoxaddToCartButton = this.main_cart.find( tcAPI.addToCartButtonSelector ).first();
24948				floatingBoxaddToCartButton
24949					.tcClone()
24950					.addClass( 'tc-add-to-cart-button' )
24951					.on( 'click', function() {
24952						floatingBoxaddToCartButton.trigger( 'click' );
24953					} )
24954					.appendTo( this.floatingBox );
24955			}
24956		},
24957
24958		onUpdateScroll: function() {
24959			if ( TMEPOJS.floating_totals_box_visibility === 'always' ) {
24960				if ( this.floatingBox.is( ':empty' ) && ! this.is_nks_alt ) {
24961					this.floatingBox.hide();
24962				} else {
24963					this.floatingBox.show();
24964				}
24965				return;
24966			}
24967			if ( TMEPOJS.floating_totals_box_visibility === 'hideafterscroll' ) {
24968				if ( jWindow.scrollTop() > $.epoAPI.math.toFloat( TMEPOJS.floating_totals_box_pixels ) && ! this.is_nks_alt ) {
24969					if ( ! this.floatingBox.is( ':hidden' ) ) {
24970						if ( this.is_nks === false ) {
24971							this.floatingBox.fadeOut();
24972						} else {
24973							this.floatingBox.hide();
24974						}
24975					}
24976				} else if ( this.floatingBox.is( ':hidden' ) || this.is_nks_alt ) {
24977					if ( ! this.floatingBox.is( ':empty' ) || this.is_nks_alt ) {
24978						if ( this.is_nks === false ) {
24979							this.floatingBox.fadeIn();
24980						} else {
24981							this.floatingBox.show();
24982						}
24983					}
24984				}
24985			}
24986
24987			if ( TMEPOJS.floating_totals_box_visibility === 'afterscroll' ) {
24988				if ( jWindow.scrollTop() >
24988 $.epoAPI.math.toFloat( TMEPOJS.floating_totals_box_pixels ) || this.is_nks_alt ) {
24989					if ( ( this.floatingBox.is( ':hidden' ) && ! this.floatingBox.is( ':empty' ) ) || this.is_nks_alt ) {
24990						if ( this.is_nks === false ) {
24991							this.floatingBox.fadeIn();
24992						} else {
24993							this.floatingBox.show();
24994						}
24995					} else if ( ! this.floatingBox.is( ':hidden' ) && this.floatingBox.is( ':empty' ) ) {
24996						if ( this.is_nks === false ) {
24997							this.floatingBox.fadeOut();
24998						} else {
24999							this.floatingBox.hide();
25000						}
25001					}
25002				} else if ( ! this.floatingBox.is( ':hidden' ) ) {
25003					if ( this.is_nks === false ) {
25004						this.floatingBox.fadeOut();
25005					} else {
25006						this.floatingBox.hide();
25007					}
25008				}
25009			}
25010		},
25011
25012		addEvents: function() {
25013			this.onUpdate();
25014
25015			this.main_cart.on( 'tm-epo-after-update', this.onUpdate.bind( this ) );
25016			this.main_cart.on( 'tm-epo-short-update', this.onUpdate.bind( this ) );
25017
25018			if ( this.is_nks === false ) {
25019				jWindow.on( 'scroll', this.onUpdateScroll.bind( this ) );
25020			}
25021		},
25022
25023		init: function() {
25024			this.floatingBox = $( '<div class="tm-floating-box ' + TMEPOJS.floating_totals_box + '"></div>' );
25025			this.nks_selector = $( '.tm-floating-box-nks' ).first();
25026			this.alt_selector = $( '.tm-floating-box-alt' ).first();
25027			this.engineTemplate = tcAPI.templateEngine.tc_floating_box;
25028			this.is_nks = false;
25029			this.is_nks_alt = false;
25030
25031			if ( this.nks_selector.length > 0 ) {
25032				this.is_nks = true;
25033				this.floatingBox.removeClass( 'top left right bottom' ).appendTo( this.nks_selector ).show();
25034			} else if ( this.alt_selector.length > 0 ) {
25035				this.floatingBox.removeClass( 'top left right bottom' ).appendTo( this.alt_selector ).hide();
25036			} else {
25037				this.floatingBox.appendTo( 'body' ).hide();
25038			}
25039
25040			if ( this.nks_selector.length > 0 || this.alt_selector.length > 0 ) {
25041				this.is_nks_alt = true;
25042				this.engineTemplate = tcAPI.templateEngine.tc_floating_box_nks;
25043			}
25044
25045			this.addEvents();
25046		}
25047	};
25048
25049	$.tcFloatingTotalsBox = function( this_epo_totals_container, is_quickview, main_cart ) {
25050		var data = false;
25051
25052		if ( this_epo_totals_container && this_epo_totals_container.length && this_epo_totals_container.data( 'tcfloatingtotalsbox' ) === undefined ) {
25053			data = new FloatingTotalsBox( this_epo_totals_container, is_quickview, main_cart );
25054			this_epo_totals_container.data( 'tcfloatingtotalsbox', data );
25055		}
25056
25057		return data;
25058	};
25059
25060	$.tc_product_image = {};
25061	$.tc_product_image_store = {};
25062
25063	// replace obj1 values with obj2 values
25064	$.tc_replace_object_values = function( obj1, obj2 ) {
25065		Object.keys( obj1 ).forEach( function( x ) {
25066			Object.keys( obj1[ x ] ).forEach( function( attr ) {
25067				if ( undefined !== obj2[ x ] && undefined !== obj2[ x ][ attr ] && Object.prototype.hasOwnProperty.call( obj2[ x ], attr ) ) {
25068					obj1[ x ][ attr ] = obj2[ x ][ attr ];
25069				}
25070			} );
25071		} );
25072		return obj1;
25073	};
25074	// copy obj2 values to obj1
25075	$.tc_maybe_copy_object_values = function( obj1, obj2 ) {
25076		Object.keys( obj2 ).forEach( function( x ) {
25077			Object.keys( obj2[ x ] ).forEach( function( attr ) {
25078				if ( undefined !== obj2[ x ] && Object.prototype.hasOwnProperty.call( obj2[ x ], attr ) && undefined !== obj2[ x ][ attr ] && ( undefined === obj1[ x ] || undefined === obj1[ x ][ attr ] ) ) {
25079					if ( undefined === obj1[ x ] ) {
25080						obj1[ x ] = {};
25081					}
25082					obj1[ x ][ attr ] = obj2[ x ][ attr ];
25083				}
25084			} );
25085		} );
25086		return obj1;
25087	};
25088
25089	$.tc_pre_populate_store = function() {
25090		var obj = {};
25091
25092		obj[ 0 ] = {};
25093		obj[ 1 ] = {};
25094		obj[ 2 ] = {};
25095		obj[ 3 ] = {};
25096
25097		obj[ 0 ].src = '';
25098		obj[ 0 ].srcset = '';
25099		obj[ 0 ].sizes = '';
25100		obj[ 0 ].title = '';
25101		obj[ 0 ].alt = '';
25102		obj[ 0 ][ 'data-src' ] = '';
25103		obj[ 0 ][ 'data-large_image' ] = '';
25104		obj[ 0 ][ 'data-large_image_width' ] = '';
25105		obj[ 0 ][ 'data-large_image_height' ] = '';
25106		obj[ 1 ][ 'data-thumb' ] = '';
25107		obj[ 2 ].src = '';
25108		obj[ 3 ].href = '';
25109		obj[ 3 ].title = '';
25110
25111		return obj;
25112	};
25113
25114	$.tc_populate_store = function( img, product_element ) {
25115		var $gallery_img = product_element.find( '.flex-control-nav li:eq(0) img' );
25116		var $gallery_wrapper = product_element.find( '.woocommerce-product-gallery__wrapper ' );
25117		var $product_img_wrap = $gallery_wrapper.find( '.woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder' ).eq( 0 );
25118		var $product_img = img;
25119		var $product_link = img.closest( 'a' );
25120		var obj = {};
25121
25122		obj[ 0 ] = {};
25123		obj[ 1 ] = {};
25124		obj[ 2 ] = {};
25125		obj[ 3 ] = {};
25126
25127		obj[ 0 ].src = $product_img.attr( 'src' );
25128		obj[ 0 ].srcset = $product_img.attr( 'srcset' );
25129		obj[ 0 ].sizes = $product_img.attr( 'sizes' );
25130		obj[ 0 ].title = $product_img.attr( 'title' );
25131		obj[ 0 ].alt = $product_img.attr( 'alt' );
25132		obj[ 0 ][ 'data-src' ] = $product_img.attr( 'data-src' );
25133		obj[ 0 ][ 'data-large_image' ] = $product_img.attr( 'data-large_image' );
25134		obj[ 0 ][ 'data-large_image_width' ] = $product_img.attr( 'data-large_image_width' );
25135		obj[ 0 ][ 'data-large_image_height' ] = $product_img.attr( 'data-large_image_height' );
25136		obj[ 1 ][ 'data-thumb' ] = $product_img_wrap.attr( 'data-thumb' );
25137		obj[ 2 ].src = $gallery_img.attr( 'src' );
25138		obj[ 3 ].href = $product_link.attr( 'href' );
25139		obj[ 3 ].title = $product_link.attr( 'title' );
25140
25141		return obj;
25142	};
25143
25144	$.tc_maybe_copy_object_values_from_img = function( obj1, img, product_element ) {
25145		var $gallery_img = product_element.find( '.flex-control-nav li:eq(0) img' );
25146		var $gallery_wrapper = product_element.find( '.woocommerce-product-gallery__wrapper' );
25147		var $product_img_wrap = $gallery_wrapper.find( '.woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder' ).eq( 0 );
25148		var $product_img = img;
25149		var $product_link = img.closest( 'a' );
25150
25151		var all = [ $product_img, $product_img_wrap, $gallery_img, $product_link ];
25152		var attrs_all = [
25153			[ 'src', 'srcset', 'sizes', 'title', 'alt', 'data-src', 'data-large_image', 'data-large_image_width', 'data-large_image_height', 'large-image' ],
25154			[ 'data-thumb' ],
25155			[ 'src' ],
25156			[ 'href', 'title' ]
25157		];
25158		var i;
25159		var item;
25160		var attrs;
25161		var attr;
25162		var j;
25163
25164		for ( i = 0; i < all.length; i++ ) {
25165			item = all[ i ];
25166
25167			if ( item && item[ 0 ] ) {
25168				attrs = item[ 0 ].attributes;
25169
25170				for ( j = 0; j < attrs.length; j++ ) {
25171					attr = attrs[ j ];
25172
25173					if ( attr.specified && attrs_all[ i ].indexOf( attr.name ) !== -1 ) {
25174						if ( obj1[ i ] === undefined || ( obj1[ i ] !== undefined && obj1[ i ][ attr.name ] === undefined ) ) {
25175							if ( undefined === obj1[ i ] ) {
25176								obj1[ i ] = {};
25177							}
25178							obj1[ i ][ attr.name ] = attr.value;
25179						}
25180					}
25181				}
25182			}
25183		}
25184
25185		return obj1;
25186	};
25187
25188	// Stores a default attribute for an element so it can be reset later
25189	$.fn.tc_set_attr = function( attr, value, id ) {
25190		if ( undefined === id ) {
25191			id = 0;
25192		}
25193		if ( undefined === $.tc_product_image[ id ] || ( undefined !== $.tc_product_image[ id ] && undefined === $.tc_product_image[ id ][ attr ] ) ) {
25194			if ( undefined === $.tc_product_image[ id ] ) {
25195				$.tc_product_image[ id ] = {};
25196			}
25197			$.tc_product_image[ id ][ attr ] = '';
25198			if ( this.attr( attr ) ) {
25199				$.tc_product_image[ id ][ attr ] = this.attr( attr );
25200			}
25201		}
25202		if ( false === value ) {
25203			this.removeAttr( attr );
25204		} else {
25205			this.attr( attr, value );
25206		}
25207	};
25208
25209	// Reset a default attribute for an element so it can be reset later
25210	$.fn.tc_reset_attr = function( attr, id ) {
25211		if ( undefined === id ) {
25212			id = 0;
25213		}
25214		if ( undefined === $.tc_product_image[ id ] ) {
25215			return;
25216		}
25217		if ( undefined !== $.tc_product_image[ id ][ attr ] ) {
25218			this.attr( attr, $.tc_product_image[ id ][ attr ] );
25219		}
25220		delete $.tc_product_image[ id ][ attr ];
25221	};
25222
25223	$.fn.tc_update_attr = function( attr, id ) {
25224		if ( undefined === id ) {
25225			id = 0;
25226		}
25227		if ( undefined !== $.tc_product_image[ id ] ) {
25228			$.tc_product_image[ id ][ attr ] = this.attr( attr );
25229		}
25230	};
25231
25232	$.fn.tc_image_update = function( dom, image ) {
25233		var element = $( dom );
25234		var $form = this;
25235		var $image = $( image );
25236		var epo_object = $form.data( 'epo_object' );
25237		var image_info;
25238		var $product_img;
25239		var product_element = epo_object.main_product.closest( '#product-' + epo_object.product_id );
25240		var $product_element = product_element;
25241		var $product_img_thumb;
25242		var $product_link;
25243		var use_image_info;
25244
25245		if ( product_element.length <= 0 ) {
25246			$product_element = epo_object.main_product.closest( '.post-' + epo_object.product_id );
25247		}
25248
25249		$product_img_thumb = $product_element.find( '.flex-control-nav li:eq(0) img' );
25250
25251		if ( element.is( 'select' ) ) {
25252			element = element.children( 'option:selected' );
25253		}
25254		image_info = element.data( 'image-variations' );
25255
25256		if ( TMEPOJS.tm_epo_global_product_image_selector !== '' ) {
25257			$product_img = $( TMEPOJS.tm_epo_global_product_image_selector );
25258		} else {
25259			$product_img = $product_element.find( 'a.woocommerce-main-image img, img.woocommerce-main-image,a img' ).not( '.thumbnails img,.product_list_widget img,img.emoji,a.woocommerce-product-gallery__trigger img' ).first();
25260		}
25261		$product_link = $product_img.closest( 'a' );
25262
25263		if ( $product_img.length > 1 ) {
25264			$product_img = $product_img.first();
25265		}
25266
25267		if ( element && image_info && $image.length > 0 ) {
25268			$image.removeAttr( 'data-o_src' ).removeAttr( 'data-o_title' ).removeAttr( 'data-o_alt' ).removeAttr( 'data-o_srcset' ).removeAttr( 'data-o_sizes' ).removeAttr( 'srcset' ).removeAttr( 'sizes' );
25269
25270			use_image_info = image_info.imagep;
25271			if ( ! image_info.imagep.image_link ) {
25272				use_image_info = image_info.image;
25273			}
25274
25275			$image.attr( 'title', use_image_info.image_title );
25276			$image.attr( 'alt', use_image_info.image_alt );
25277			if ( use_image_info.image_srcset ) {
25278				$image.attr( 'srcset', use_image_info.image_srcset );
25279			}
25280			if ( use_image_info.image_sizes ) {
25281				$image.attr( 'sizes', use_image_info.image_sizes );
25282			}
25283
25284			$product_img.tc_set_attr( 'title', use_image_info.image_title );
25285			$product_img.tc_set_attr( 'alt', use_image_info.image_alt );
25286
25287			$product_img.tc_set_attr( 'data-large-image', use_image_info.image_link );
25288			if ( $product_img.data.wc27_zoom_target ) {
25289				$product_img.data.wc27_zoom_target.tc_set_attr( 'data-thumb', use_image_info.image_link );
25290				$product_img_thumb.tc_set_attr( 'src', use_image_info.image_link );
25291				if ( use_image_info.image_srcset ) {
25292					$product_img_thumb.tc_set_attr( 'srcset', use_image_info.image_srcset );
25293				}
25294				if ( use_image_info.image_sizes ) {
25295					$product_img_thumb.tc_set_attr( 'sizes', use_image_info.image_sizes );
25296				}
25297			}
25298
25299			$product_link.tc_set_attr( 'href', use_image_info.image_link );
25300			$product_link.tc_set_attr( 'title', use_image_info.image_caption );
25301		} else {
25302			$product_img.tc_reset_attr( 'title' );
25303			$product_img.tc_reset_attr( 'alt' );
25304
25305			$product_img.tc_reset_attr( 'data-large-image' );
25306			if ( $product_img.data.wc27_zoom_target ) {
25307				$product_img.data.wc27_zoom_target.tc_reset_attr( 'data-thumb' );
25308				$product_img_thumb.tc_reset_attr( 'src' );
25309				$product_img_thumb.tc_reset_attr( 'srcset' );
25310				$product_img_thumb.tc_reset_attr( 'sizes' );
25311			}
25312
25313			$product_link.tc_reset_attr( 'href' );
25314			$product_link.tc_reset_attr( 'title' );
25315		}
25316	};
25317
25318	// variations checker
25319	$.fn.tm_find_matching_variations = function( product_variations, settings ) {
25320		var matching = [];
25321		var i;
25322		var variation;
25323
25324		if ( product_variations ) {
25325			for ( i = 0; i < product_variations.length; i += 1 ) {
25326				variation = product_variations[ i ];
25327
25328				if ( $.fn.tm_variations_match( variation.attributes, settings ) ) {
25329					matching.push( variation );
25330				}
25331			}
25332		}
25333
25334		return matching;
25335	};
25336
25337	$.fn.tm_variations_match = function( attrs1, attrs2 ) {
25338		var match = true;
25339		var val1;
25340		var val2;
25341
25342		Object.keys( attrs1 ).forEach( function( x ) {
25343			if ( Object.prototype.hasOwnProperty.call( attrs1, x ) ) {
25344				val1 = attrs1[ x ];
25345				val2 = attrs2[ x ];
25346
25347				if ( val1 !== undefined && val2 !== undefined && val1.length !== 0 && val2.length !== 0 && val1 !== val2 ) {
25348					match = false;
25349				}
25350			}
25351		} );
25352
25353		return match;
25354	};
25355
25356	function get_element_from_field( element, ckey ) {
25357		var $element = $( element );
25358		var data_uniqid;
25359		var the_epo_id;
25360		var _class;
25361		var epoContainer;
25362
25363		if ( $element.length === 0 ) {
25364			return;
25365		}
25366
25367		if ( $element.is( '.cpf-section' ) ) {
25368			return element.find( '.tm-epo-field' );
25369		}
25370		data_uniqid = $element.attr( 'data-uniqid' );
25371		epoContainer = $element.closest( tcAPI.epoSelector );
25372		the_epo_id = epoContainer.attr( 'data-epo-id' );
25373
25374		if ( ! epoContainer.is( '.reactivate' ) && tcAPI.getElementFromFieldCache && tcAPI.getElementFromFieldCache[ the_epo_id ] && tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ] && tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ] ) {
25375			return tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ];
25376		}
25377		_class = $element
25378			.attr( 'class' )
25379			.split( ' ' )
25380			.map( function( cls ) {
25381				if ( cls.indexOf( 'cpf-type-', 0 ) !== -1 ) {
25382					return cls;
25383				}
25384				return null;
25385			} )
25386			.filter( function( v ) {
25387				if ( v !== null && v !== undefined ) {
25388					return v;
25389				}
25390				return null;
25391			} );
25392
25393		if ( _class.length > 0 ) {
25394			_class = _class[ 0 ];
25395			if ( _class === 'cpf-type-product' ) {
25396				if ( $element.is( '.cpf-type-product-mode-product' ) || $element.is( '.cpf-type-product-checkbox' ) || $element.is( '.cpf-type-product-thumbnailmultiple' ) || $element.is( '.cpf-type-product-custombundle' ) ) {
25397					_class = 'cpf-type-checkbox';
25398				} else if ( $element.is( '.cpf-type-product-dropdown' ) ) {
25399					_class = 'cpf-type-select';
25400				} else {
25401					_class = 'cpf-type-radio';
25402				}
25403			}
25404			if ( tcAPI.getElementFromFieldCache[ the_epo_id ] === undefined ) {
25405				tcAPI.getElementFromFieldCache[ the_epo_id ] = [];
25406			}
25407			if ( tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ] === undefined ) {
25408				tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ] = [];
25409			}
25410			switch ( _class ) {
25411				case 'cpf-type-radio':
25412					tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ] = $element.find( '.tm-epo-field.tmcp-radio' );
25413					return tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ];
25414
25415				case 'cpf-type-checkbox':
25416					tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ] = $element.find( '.tm-epo-field.tmcp-checkbox' );
25417					return tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ];
25418
25419				case 'cpf-type-select':
25420					tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ] = $element.find( '.tm-epo-field.tmcp-select' );
25421					return tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ];
25422
25423				case 'cpf-type-selectmultiple':
25424					tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ] = $element.find( '.tm-epo-field.tmcp-selectmultiple' );
25425					return tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ];
25426
25427				case 'cpf-type-textarea':
25428					tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ] = $element.find( '.tm-epo-field.tmcp-textarea' );
25429					return tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ];
25430
25431				case 'cpf-type-textfield':
25432					tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ] = $element.find( '.tm-epo-field.tmcp-textfield' );
25433					return tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ];
25434
25435				case 'cpf-type-color':
25436					tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ] = $element.find( '.tm-epo-field.tm-color-picker' );
25437					return tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ];
25438
25439				case 'cpf-type-range':
25440					tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ] = $element.find( '.tm-epo-field.tmcp-range' );
25441					return tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ];
25442
25443				case 'cpf-type-date':
25444					tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ] = $element.find( '.tm-epo-field.tmcp-date' );
25445					return tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ];
25446
25447				case 'cpf-type-time':
25448					tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ] = $element.find( '.tm-epo-field.tmcp-time' );
25449					return tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ];
25450
25451				case 'cpf-type-variations':
25452					tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ] = $element.closest( '.cpf-section' ).find( '.tm-epo-field.tm-epo-variation-element' );
25453					return tcAPI.getElementFromFieldCache[ the_epo_id ][ data_uniqid ][ ckey ];
25454			}
25455		}
25456	}
25457
25458	// tc-lightbox
25459	if ( ! $().tclightbox ) {
25460		$.fn.tclightbox = function() {
25461			var elements = this;
25462
25463			if ( elements.length === 0 ) {
25464				return;
25465			}
25466
25467			return elements.toArray().forEach( function( lb ) {
25468				var $this = $( lb );
25469				var _imgsrc;
25470				var _label;
25471				var _input;
25472				var tclightboxwrap;
25473				var _img_button;
25474				var preload_img;
25475				var addButtonEvent;
25476
25477				if ( $this.is( '.tcinit' ) ) {
25478					return;
25479				}
25480				_imgsrc = $this.attr( 'src' ) || $this.attr( 'data-original' );
25481				_label = $this.closest( 'label' );
25482				_input = _label.closest( '.tmcp-field-wrap' ).find( ".tm-epo-field[id='" + _label.attr( 'for' ) + "']" );
25483				_imgsrc = $this.attr( 'data-large_image' ) || _input.attr( 'data-imagel' ) || _input.attr( 'data-imagep' ) || _input.attr( 'data-image' ) || _imgsrc;
25484
25485				if ( ! _imgsrc ) {
25486					return;
25487				}
25488
25489				$this.addClass( 'tcinit' ).before( $.epoAPI.template.html( tcAPI.templateEngine.tc_lightbox, {} ) );
25490				tclightboxwrap = $this.prev();
25491
25492				$this.wrap( "<div class='tc-lightbox-image-wrap'/>" );
25493				$this.after( tclightboxwrap );
25494
25495				_img_button = tclightboxwrap.find( '.tc-lightbox-button' );
25496
25497				addButtonEvent = function() {
25498					_img_button.addClass( 'tcinit' ).on( 'click.tclightbox', function( buttonevent ) {
25499						var size;
25500						var _img;
25501
25502						if ( $( '.tc-closing.tc-lightbox' ).length > 0 ) {
25503							return;
25504						}
25505
25506						size = $.epoAPI.dom.size();
25507						_img = $( '<img>' )
25508							.addClass( 'tc-lightbox-img' )
25509							.attr( 'src', _imgsrc )
25510							.css( 'maxHeight', size.visibleHeight + 'px' )
25511							.css( 'maxWidth', size.visibleWidth + 'px' );
25512
25513						$.tcFloatBox( {
25514							fps: 1,
25515							ismodal: false,
25516							width: 'auto',
25517							height: 'auto',
25518							classname: 'flasho tc-lightbox',
25519							animateIn: 'tc-lightbox-zoomin',
25520							animateOut: 'tc-lightbox-zoomout',
25521							data: $.epoAPI.template.html( tcAPI.templateEngine.tc_lightbox_zoom, { img: _img[ 0 ].outerHTML } ),
25522							zIndex: 102001,
25523							cancelClass: '.tc-lightbox-img, .tc-lightbox-button-close',
25524							unique: true
25525						} );
25526
25527						buttonevent.preventDefault();
25528					} );
25529				};
25530				if ( TMEPOJS.tm_epo_preload_lightbox_image === 'yes' ) {
25531					preload_img = new Image();
25532					preload_img.src = _imgsrc;
25533					preload_img.onload = function() {
25534						addButtonEvent();
25535					};
25536				} else {
25537					addButtonEvent();
25538				}
25539			} );
25540		};
25541	}
25542
25543	// Start Section popup
25544	if ( ! $().tmsectionpoplink ) {
25545		$.fn.tmsectionpoplink = function() {
25546			var elements = this;
25547
25548			if ( elements.length === 0 ) {
25549				return;
25550			}
25551
25552			return elements.toArray().forEach( function( pl ) {
25553				var $this = $( pl );
25554				var id;
25555				var title;
25556				var section;
25557				var clicked;
25558				var cancelfunc;
25559				var cancelpop;
25560
25561				if ( $this.data( 'tmsectionpoplink' ) ) {
25562					return;
25563				}
25564
25565				$this.data( 'tmsectionpoplink', true );
25566				id = $this.attr( 'data-sectionid' );
25567				title = TMEPOJS.i18n_addition_options;
25568				section = $this.closest( ".cpf-section[data-uniqid='" + id + "']" );
25569				clicked = false;
25570				cancelfunc = function() {
25571					var pop = $( '#tm-section-pop-up' );
25572					pop.parents().removeClass( 'noanimated' );
25573
25574					pop.after( section );
25575					pop.remove();
25576
25577					section.find( '.tm-section-link' ).show();
25578					section.find( '.tm-section-pop' ).hide();
25579				};
25580				cancelpop = function( e ) {
25581					if ( e.target === $( '.flasho' )[ 0 ] || $( e.target ).closest( '.flasho' ).length ) {
25582						return;
25583					}
25584
25585					cancelfunc();
25586				};
25587
25588				if ( $this.attr( 'data-title' ) ) {
25589					title = $this.attr( 'data-title' );
25590				}
25591
25592				$this.on( 'click.tmsectionpoplink', function( e ) {
25593					var pop;
25594
25595					e.preventDefault();
25596					clicked = false;
25597
25598					section.before(
25599						$.epoAPI.template.html( tcAPI.templateEngine.tc_section_pop_link, {
25600							title: title,
25601							close: TMEPOJS.i18n_close
25602						} )
25603					);
25604
25605					pop = $( '#tm-section-pop-up' );
25606
25607					pop.on( 'click', cancelpop );
25608
25609					pop.find( '.float-editbox' ).prepend( section );
25610
25611					section.find( '.tm-section-link' ).hide();
25612					section.find( '.tm-section-pop' ).show();
25613
25614					pop.parents().addClass( 'noanimated' );
25615
25616					pop.find( '.floatbox-cancel' ).on( 'click', function() {
25617						if ( clicked ) {
25618							return;
25619						}
25620						clicked = true;
25621						cancelfunc();
25622					} );
25623					jWindow.trigger( 'tmlazy' );
25624					jWindow.trigger( 'tmsectionpoplink' );
25625				} );
25626			} );
25627		};
25628	} // End Section popup
25629
25630	function getVariationIdSelector( currentCart ) {
25631		var variationIdSelector = "input[name^='variation_id']";
25632
25633		if ( currentCart.find( 'input.variation_id' ).length > 0 ) {
25634			variationIdSelector = 'input.variation_id';
25635		} else {
25636			variationIdSelector = 'input.product-variation-id';
25637		}
25638
25639		return variationIdSelector;
25640	}
25641
25642	function getVariationIdElement( currentCart, not ) {
25643		var variationIdSelector = getVariationIdSelector( currentCart );
25644		var variationIdElement = currentCart.find( variationIdSelector );
25645
25646		if ( currentCart.is( '.tc-epo-element-product-container-cart' ) ) {
25647			variationIdElement = currentCart.closest( '.tc-epo-element-product-li-container' ).find( variationIdSelector );
25648		} else {
25649			variationIdElement = currentCart.find( variationIdSelector ).not( '.tc-epo-element-product-container-variation-id ' + variationIdSelector );
25650		}
25651		if ( not ) {
25652			variationIdElement = variationIdElement.not( not );
25653		}
25654
25655		return variationIdElement;
25656	}
25657
25658	function getCurrentVariation( currentCart ) {
25659		var field_div;
25660		var newCart;
25661
25662		if ( currentCart.is( '.tc-epo-element-product-container-cart' ) ) {
25663			field_div = currentCart.closest( '.cpf-element' );
25664			if ( field_div.is( '.cpf-type-product-thumbnailmultiple' ) || field_div.is( '.cpf-type-product-checkbox' ) || field_div.is( '.cpf-type-product-custombundle' ) ) {
25665				newCart = currentCart.closest( '.tc-epo-element-product-holder' ).find( '.tc-epo-element-product-container-variation-id' );
25666			} else if ( field_div.is( '.cpf-type-product-thumbnail' ) || field_div.is( '.cpf-type-product-radio' ) || field_div.is( '.cpf-type-product-dropdown' ) || field_div.is( '.cpf-type-product-single' ) ) {
25667				newCart = field_div.find( '.tc-epo-element-product-container-variation-id' );
25668			}
25669			if ( newCart && newCart.length ) {
25670				currentCart = newCart;
25671			}
25672		}
25673		return currentCart.find( getVariationIdSelector( currentCart ) ).val() || 0;
25674	}
25675
25676	function getQtyElement( currentCart ) {
25677		var qty = currentCart.find( tcAPI.qtySelector ).last();
25678		if ( qty.length === 0 ) {
25679			qty = currentCart.find( tcAPI.associateQtySelector ).last();
25680		}
25681		qty = $.epoAPI.applyFilter( 'tc_getQtyElement', qty, currentCart );
25682		return qty;
25683	}
25684
25685	function getCurrentQty( currentCart ) {
25686		return $.epoAPI.applyFilter( 'tc_getCurrentQty', $.epoAPI.math.toFloat( getQtyElement( currentCart ).val() ), currentCart );
25687	}
25688
25689	function add_variation_event( name, selector, func ) {
25690		lateVariationEvent[ lateVariationEvent.length ] = {
25691			name: name,
25692			selector: selector,
25693			func: func
25694		};
25695	}
25696
25697	function field_is_active( field, nochecks, single, simpleCheck ) {
25698		var ret = true;
25699		var insideProductCheck = false;
25700		var productElement;
25701		field = $( field );
25702		productElement = field.filter( '.tc-epo-field-product' );
25703		if ( productElement.length && productElement.data( 'islogicinit' ) === undefined ) {
25704			insideProductCheck = true;
25705		}
25706		field.toArray().forEach( function( element ) {
25707			if ( ! ret && field.has( '.tc-epo-field-product' ).length ) {
25708				if ( insideProductCheck || productElement.is( '.tcdisabled' ) ) {
25709					if ( simpleCheck ) {
25710						field_check_active_simple( element );
25711					} else {
25712						field_check_active( element, nochecks, single, simpleCheck );
25713					}
25714				}
25715			} else if ( simpleCheck ) {
25716				ret = field_check_active_simple( element );
25717			} else {
25718				ret = field_check_active( element, nochecks, single, simpleCheck ) && ret;
25719			}
25720		} );
25721
25722		return ret;
25723	}
25724
25725	function field_check_active_simple( field ) {
25726		var hideElement;
25727		var isInsideProductElement;
25728		var productElementActive;
25729		var productElementSectionActive;
25730		var section;
25731
25732		field = $( field );
25733		if ( field.is( '.cpf-element' ) ) {
25734			hideElement = field;
25735			field = field.find( '.tmcp-field, .tmcp-fee-field' );
25736		} else {
25737			hideElement = field.closest( '.cpf-element' );
25738		}
25739
25740		section = hideElement.closest( '.cpf-section' );
25741
25742		isInsideProductElement = ! field.is( '.tc-epo-field-product' ) && hideElement.closest( '.cpf-type-product' );
25743
25744		if ( isInsideProductElement.length ) {
25745			productElementActive = isInsideProductElement.data( 'isactive' );
25746			isInsideProductElement = isInsideProductElement.closest( '.cpf-section' );
25747			productElementSectionActive = isInsideProductElement.data( 'isactive' );
25748		}
25749		if ( productElementActive !== false && productElementSectionActive !== false && hideElement.data( 'isactive' ) !== false && section.data( 'isactive' ) !== false ) {
25750			return true;
25751		}
25752
25753		return false;
25754	}
25755
25756	function field_check_active( field, nochecks, single ) {
25757		var hideElement;
25758		var singleField;
25759		var ul;
25760		var isInsideProductElement;
25761		var productElementActive;
25762		var productElementSectionActive;
25763		var productElementQty;
25764		var section;
25765		var disableElements;
25766		var sectionLi;
25767		var allSectionsDisabled;
25768		var tabSlide;
25769		var epoFields;
25770
25771		field = $( field );
25772		if ( field.is( '.cpf-element' ) ) {
25773			hideElement = field;
25774			field = field.find( '.tmcp-field, .tmcp-fee-field' );
25775		} else {
25776			hideElement = field.closest( '.cpf-element' );
25777		}
25778
25779		section = hideElement.closest( '.cpf-section' );
25780		sectionLi = section.closest( '.tm-extra-product-options-field' );
25781
25782		if ( single ) {
25783			ul = field.closest( '.tmcp-ul-wrap' );
25784		} else {
25785			ul = hideElement;
25786		}
25787
25788		if ( field.is( '.tc-epo-field-product' ) && field.data( 'islogicinit' ) === undefined ) {
25789			field.data( 'islogicinit', 1 );
25790		}
25791
25792		if ( ! field.is( '.tc-force-disabled' ) ) {
25793			isInsideProductElement = ! field.is( '.tc-epo-field-product' ) && hideElement.closest( '.cpf-type-product' );
25794
25795			if ( isInsideProductElement.length ) {
25796				productElementQty = isInsideProductElement.find( tcAPI.associateQtySelector );
25797				if ( productElementQty.length ) {
25798					if ( $.epoAPI.math.toFloat( productElementQty.val() ) > 0 ) {
25799						field.removeClass( 'ignore' );
25800						productElementQty.removeClass( 'ignore' );
25801					} else {
25802						field.addClass( 'ignore' );
25803						productElementQty.addClass( 'ignore' );
25804					}
25805				}
25806				productElementActive = isInsideProductElement.data( 'isactive' );
25807				isInsideProductElement = isInsideProductElement.closest( '.cpf-section' );
25808				productElementSectionActive = isInsideProductElement.data( 'isactive' );
25809			}
25810			if ( productElementActive !== false && productElementSectionActive !== false && hideElement.data( 'isactive' ) !== false && section.data( 'isactive' ) !== false ) {
25811				singleField = field.first();
25812				field.prop( 'disabled', false );
25813				if ( field.data( 'tc-state' ) !== undefined ) {
25814					field.data( 'tc-state', false );
25815				}
25816
25817				if ( TMEPOJS.tm_epo_show_only_active_quantities !== 'yes' ) {
25818					if ( singleField.is( ':radio, .cpf-type-radio' ) || singleField.is( ':checkbox, .cpf-type-checkbox' ) ) {
25819						field.filter( ':checked' ).closest( '.tmcp-field-wrap' ).find( '.tm-qty, .tc-qty-button' ).prop( 'disabled', false );
25820						field.not( ':checked' ).closest( '.tmcp-field-wrap' ).find( '.tm-qty, .tc-qty-button' ).prop( 'disabled', true );
25821					} else if ( singleField.is( 'select, .cpf-type-select, .cpf-type-selectmultiple' ) ) {
25822						if ( singleField.val() ) {
25823							ul.find( '.tm-qty, .tc-qty-button' ).prop( 'disabled', false );
25824						} else {
25825							ul.find( '.tm-qty, .tc-qty-button' ).prop( 'disabled', true );
25826						}
25827					} else if ( singleField.val() ) {
25828						ul.find( '.tm-qty, .tc-qty-button' ).prop( 'disabled', false );
25829					} else {
25830						ul.find( '.tm-qty, .tc-qty-button' ).prop( 'disabled', true );
25831					}
25832				} else if ( ! nochecks ) {
25833					hideElement.find( '.tm-quantity' ).trigger( 'showhide.cpfcustom' );
25834				}
25835
25836				if ( ! singleField.is( '.cpf-element' ) ) {
25837					field.removeClass( 'tcdisabled' ).addClass( 'tcenabled' );
25838
25839					if ( field.is( '.tmcp-upload' ) ) {
25840						if ( field.next( '.tmcp-upload-hidden' ).length ) {
25841							field.next( '.tmcp-upload-hidden' ).removeClass( 'tcdisabled' ).addClass( 'tcenabled' ).prop( 'disabled', false );
25842						}
25843					}
25844				}
25845
25846				hideElement.removeClass( 'tc-container-disabled' ).addClass( 'tc-container-enabled' );
25847				hideElement.find( '.product-variation-id, .tc-epo-field-product-counter' ).prop( 'disabled', false );
25848
25849				field.trigger( {
25850					type: 'tm-field-is-active',
25851					field: field,
25852					value: true
25853				} );
25854				section.removeClass( 'section-disabled' );
25855				sectionLi.removeClass( 'tc-hidden' );
25856
25857				hideElement.closest( '.owl-item' ).removeClass( 'tc-hidden-visibility' );
25858
25859				return true;
25860			}
25861		}
25862
25863		if ( ! field.is( '.cpf-element' ) ) {
25864			field.prop( 'disabled', true ).removeClass( 'tcenabled' ).addClass( 'tcdisabled' );
25865			if ( field.data( 'tc-state' ) !== undefined ) {
25866				field.data( 'tc-state', true );
25867			}
25868			hideElement.find( '.tm-qty, .tc-qty-button' ).prop( 'disabled', true );
25869			if ( field.is( '.tmcp-upload' ) ) {
25870				if ( field.next( '.tmcp-upload-hidden' ).length ) {
25871					field.next( '.tmcp-upload-hidden' ).removeClass( 'tcenabled' ).addClass( 'tcdisabled' ).prop( 'disabled', true );
25872				}
25873			}
25874		}
25875
25876		if ( field.is( '.tc-force-disabled' ) ) {
25877			epoFields = hideElement.find( '.tmcp-field, .tmcp-fee-field' );
25878			if ( epoFields.length !== epoFields.filter( '.tc-force-disabled' ).length ) {
25879				return false;
25880			}
25881		}
25882
25883		hideElement.removeClass( 'tc-container-enabled' ).addClass( 'tc-container-disabled' );
25884		hideElement.find( '.product-variation-id, .tc-epo-field-product-counter' ).prop( 'disabled', true );
25885
25886		field.trigger( {
25887			type: 'tm-field-is-active',
25888			field: field,
25889			value: false
25890		} );
25891
25892		disableElements = section.find( '.cpf-element.tc-container-enabled' ).length === 0;
25893		if ( disableElements ) {
25894			section.addClass( 'section-disabled' );
25895			allSectionsDisabled = sectionLi.children( '.cpf-section' ).filter( function() {
25896				return ! $( this ).hasClass( 'section-disabled' );
25897			} ).length === 0;
25898			if ( allSectionsDisabled ) {
25899				sectionLi.addClass( 'tc-hidden' );
25900			}
25901		}
25902
25903		tabSlide = hideElement.closest( '.tc-tab-slide' ).find( '.cpf-element.tc-container-enabled' ).length === 0;
25904		if ( tabSlide ) {
25905			hideElement.closest( '.owl-item' ).addClass( 'tc-hidden-visibility' );
25906		}
25907
25908		return false;
25909	}
25910
25911	function tm_variation_check_match( element, val2, operator ) {
25912		var $element = $( element );
25913		var epoId = $element.attr( 'data-epo_id' ) || $element.attr( 'data-epo-id' );
25914		var productId = $element.attr( 'data-product_id' ) || $element.attr( 'data-product-id' );
25915
25916		var variationsForm = $( '.variations_form[data-epo_id="' + epoId + '"][data-product_id="' + productId + '"]' );
25917		var val1;
25918		var variationIdSelector = 'input[name^="variation_id"]';
25919		var $variationId;
25920
25921		variationsForm = $.epoAPI.applyFilter( 'tm_variation_check_match_variationsForm', variationsForm, epoId, productId, $element );
25922		if ( $element.attr( 'data-product_type' ) === 'variation' ) {
25923			variationsForm = $element;
25924			$variationId = productId;
25925		}
25926		if ( variationsForm.length === 0 && $element.is( '.cpf-type-variations' ) && ! epoId && ! productId ) {
25927			variationsForm = $element.closest( '.tc-epo-element-product-container' );
25928			if ( variationsForm.length ) {
25929				productId = variationsForm.attr( 'data-product_id' );
25930				$variationId = productId;
25931			}
25932		}
25933
25934		if ( variationsForm.length === 0 ) {
25935			return false;
25936		}
25937
25938		if ( ! $variationId ) {
25939			$variationId = variationsForm.find( variationIdSelector );
25940
25941			if ( $variationId.length === 0 ) {
25942				variationIdSelector = 'input.variation_id';
25943
25944				$variationId = variationsForm.find( variationIdSelector );
25945
25946				if ( $variationId.length === 0 ) {
25947					$variationId = variationsForm.closest( '.tc-epo-element-product-li-container' ).find( '.product-variation-id' );
25948				}
25949			}
25950			if ( $variationId.length ) {
25951				$variationId = $variationId.val();
25952			} else {
25953				$variationId = 0;
25954			}
25955		}
25956
25957		if ( element !== null && val2 !== null && element !== undefined && val2 !== undefined && element !== false && val2 !== false ) {
25958			if ( val2 ) {
25959				val2 = parseInt( val2, 10 );
25960			} else {
25961				val2 = -1;
25962			}
25963		}
25964
25965		val1 = parseInt( $variationId, 10 );
25966
25967		if ( ! Number.isFinite( val1 ) ) {
25968			val1 = 0;
25969		}
25970		if ( ! Number.isFinite( val2 ) ) {
25971			val2 = 0;
25972		}
25973
25974		switch ( operator ) {
25975			case 'is':
25976				return val1 !== '' && val1 === val2;
25977
25978			case 'isnot':
25979				return val1 !== '' && val1 !== val2;
25980
25981			case 'isempty':
25982				return val1 === '' || val1 === 0;
25983
25984			case 'isnotempty':
25985				return val1 !== '' && val1 !== 0;
25986
25987			case 'startswith':
25988				return val1.toString().startsWith( val2 );
25989
25990			case 'endswith':
25991				return val1.toString().endsWith( val2 );
25992
25993			case 'greaterthan':
25994				return parseFloat( val1 ) > parseFloat( val2 );
25995
25996			case 'lessthan':
25997				return parseFloat( val1 ) < parseFloat( val2 );
25998
25999			case 'greaterthanequal':
26000				return parseFloat( val1 ) >= parseFloat( val2 );
26001
26002			case 'lessthanequal':
26003				return parseFloat( val1 ) <= parseFloat( val2 );
26004		}
26005		return false;
26006	}
26007
26008	function tm_variationattributes_check_match( element, rule ) {
26009		var $element = $( element );
26010		var val2 = rule.value;
26011		var operator = rule.operator;
26012		var epoId = $element.attr( 'data-epo_id' ) || $element.attr( 'data-epo-id' );
26013		var productId = $element.attr( 'data-product_id' ) || $element.attr( 'data-product-id' );
26014
26015		var variationsForm = $( '.variations_form[data-epo_id="' + epoId + '"][data-product_id="' + productId + '"]' );
26016		var val1;
26017		var $att;
26018
26019		variationsForm = $.epoAPI.applyFilter( 'tm_variation_check_match_variationsForm', variationsForm, epoId, productId, $element );
26020		if ( $element.attr( 'data-product_type' ) === 'variation' ) {
26021			variationsForm = $element;
26022		}
26023		if ( variationsForm.length === 0 && $element.is( '.cpf-type-variations' ) && ! epoId && ! productId ) {
26024			variationsForm = $element.closest( '.tc-epo-element-product-container' );
26025			if ( variationsForm.length ) {
26026				productId = variationsForm.attr( 'data-product_id' );
26027			}
26028		}
26029
26030		if ( variationsForm.length === 0 ) {
26031			return false;
26032		}
26033
26034		$att = variationsForm.find( '[name="attribute_' + rule.taxonomy + '"]' );
26035
26036		if ( ! $att.length ) {
26037			return false;
26038		}
26039
26040		val1 = $att.find( 'option:selected' ).attr( 'value' );
26041
26042		switch ( operator ) {
26043			case 'is':
26044				return val1 !== '' && val1 === val2;
26045
26046			case 'isnot':
26047				return val1 !== '' && val1 !== val2;
26048
26049			case 'isempty':
26050				return val1 === '' || val1 === 0;
26051
26052			case 'isnotempty':
26053				return val1 !== '' && val1 !== 0;
26054
26055			case 'startswith':
26056				return val1.toString().startsWith( val2 );
26057
26058			case 'endswith':
26059				return val1.toString().endsWith( val2 );
26060		}
26061		return false;
26062	}
26063
26064	function tm_check_match( val1, val2, operator ) {
26065		if ( val1 !== null && val2 !== null ) {
26066			val1 = encodeURIComponent( val1 );
26067			if ( $.qtranxj_split ) {
26068				//backwards compatible
26069				val2 = encodeURIComponent( $.qtranxj_split( decodeURIComponent( val2 ) )[ TMEPOQTRANSLATEXJS.language ] );
26070			} else {
26071				//backwards compatible
26072				val2 = encodeURIComponent( decodeURIComponent( val2 ) );
26073			}
26074
26075			if ( val1 ) {
26076				val1 = val1.toLowerCase();
26077			} else {
26078				val1 = '';
26079			}
26080			if ( val2 ) {
26081				val2 = val2.toLowerCase();
26082			} else {
26083				val2 = '';
26084			}
26085		} else {
26086			return false;
26087		}
26088
26089		val1 = val1.toString();
26090		val2 = val2.toString();
26091
26092		switch ( operator ) {
26093			case 'is':
26094				return val1 !== null && val1 === val2;
26095
26096			case 'isnot':
26097				return val1 !== null && val1 !== val2;
26098
26099			case 'isempty':
26100				return ! ( val1 !== 'undefined' && val1 !== undefined && val1 !== '' );
26101
26102			case 'isnotempty':
26103				return val1 !== 'undefined' && val1 !== undefined && val1 !== '';
26104
26105			case 'startswith':
26106				return val1.startsWith( val2 );
26107
26108			case 'endswith':
26109				return val1.endsWith( val2 );
26110
26111			case 'greaterthan':
26112				return parseFloat( val1 ) > parseFloat( val2 );
26113
26114			case 'lessthan':
26115				return parseFloat( val1 ) < parseFloat( val2 );
26116
26117			case 'greaterthanequal':
26118				return parseFloat( val1 ) >= parseFloat( val2 );
26119
26120			case 'lessthanequal':
26121				return parseFloat( val1 ) <= parseFloat( val2 );
26122		}
26123
26124		return false;
26125	}
26126
26127	function tm_check_section_match( elements, operator ) {
26128		var any_checked;
26129		var val;
26130		var all_elements = elements.find( '.cpf-element' );
26131		var radio_checked;
26132		var checkbox_checked;
26133		var noSplit = false;
26134
26135		if ( elements.is( '.tc-hidden' ) ) {
26136			if ( operator === 'isnotempty' ) {
26137				return false;
26138			} else if ( operator === 'isempty' ) {
26139				return true;
26140			}
26141		}
26142
26143		if ( operator === 'isnotempty' ) {
26144			any_checked = false;
26145		} else if ( operator === 'isempty' ) {
26146			any_checked = true;
26147		}
26148
26149		$( all_elements ).toArray().forEach( function( element ) {
26150			var _class;
26151			var elementToCheck = '';
26152
26153			element = $( element );
26154			if ( element.is( '.cpf-type-product' ) ) {
26155				elementToCheck = ':not(' + tcAPI.associatedEpoSelector + ' .tm-epo-field)';
26156			}
26157			if ( field_is_active( element, undefined, undefined, true ) ) {
26158				_class = element
26159					.attr( 'class' )
26160					.split( ' ' )
26161					.map( function( cls ) {
26162						if ( cls.indexOf( 'cpf-type-', 0 ) !== -1 ) {
26163							return cls;
26164						}
26165						return null;
26166					} )
26167					.filter( function( v ) {
26168						if ( v !== null && v !== undefined ) {
26169							return v;
26170						}
26171						return null;
26172					} );
26173
26174				if ( _class.length > 0 ) {
26175					_class = _class[ 0 ];
26176					if ( _class === 'cpf-type-product' ) {
26177						noSplit = true;
26178						if ( element.is( '.cpf-type-product-mode-product' ) || element.is( '.cpf-type-product-checkbox' ) || element.is( '.cpf-type-product-thumbnailmultiple' ) || element.is( '.cpf-type-product-custombundle' ) ) {
26179							_class = 'cpf-type-checkbox';
26180						} else if ( element.is( '.cpf-type-product-dropdown' ) ) {
26181							_class = 'cpf-type-select';
26182						} else {
26183							_class = 'cpf-type-radio';
26184						}
26185					}
26186					switch ( _class ) {
26187						case 'cpf-type-radio':
26188							radio_checked = element.find( 'input.tm-epo-field.tmcp-radio:checked' + elementToCheck );
26189							if ( operator === 'isnotempty' ) {
26190								any_checked = any_checked || radio_checked.length > 0;
26191								if ( radio_checked.length > 0 ) {
26192									val = radio_checked.length;
26193								}
26194							} else if ( operator === 'isempty' ) {
26195								any_checked = any_checked && radio_checked.length === 0;
26196							}
26197							break;
26198
26199						case 'cpf-type-checkbox':
26200							checkbox_checked = element.find( 'input.tm-epo-field.tmcp-checkbox:checked' + elementToCheck );
26201							if ( operator === 'isnotempty' ) {
26202								any_checked = any_checked || checkbox_checked.length > 0;
26203								if ( checkbox_checked.length > 0 ) {
26204									val = checkbox_checked.length;
26205								}
26206							} else if ( operator === 'isempty' ) {
26207								any_checked = any_checked && checkbox_checked.length === 0;
26208							}
26209							break;
26210
26211						case 'cpf-type-select':
26212							val = element.find( 'select.tm-epo-field.tmcp-select' + elementToCheck ).val();
26213							if ( val && ! noSplit ) {
26214								val = val.slice( 0, val.lastIndexOf( '_' ) );
26215							}
26216							break;
26217
26218						case 'cpf-type-selectmultiple':
26219							val = element.find( 'select.tm-epo-field.tmcp-selectmultiple' + elementToCheck ).val();
26220							if ( val && ! noSplit ) {
26221								val.forEach( function( option, i ) {
26222									val[ i ] = option.slice( 0, option.lastIndexOf( '_' ) );
26223								} );
26224							}
26225							break;
26226
26227						default:
26228							val = element.find( '.tm-epo-field' + elementToCheck ).val();
26229							break;
26230					}
26231					if ( Array.isArray( val ) ) {
26232						if ( operator === 'isnotempty' ) {
26233							any_checked = any_checked || val.some( function( item ) {
26234								return tm_check_match( item, '', operator );
26235							} );
26236						} else if ( operator === 'isempty' ) {
26237							any_checked = any_checked && val.every( function( item ) {
26238								return tm_check_match( item, '', operator );
26239							} );
26240						}
26241					} else if ( operator === 'isnotempty' ) {
26242						any_checked = any_checked || tm_check_match( val, '', operator );
26243					} else if ( operator === 'isempty' ) {
26244						any_checked = any_checked && tm_check_match( val, '', operator );
26245					}
26246				} else {
26247					any_checked = false;
26248				}
26249			}
26250		} );
26251
26252		return any_checked;
26253	}
26254
26255	function tm_check_field_match( f ) {
26256		var element = f.element;
26257		var operator = f.rule.operator;
26258		var value = f.rule.value;
26259		var type = f.type;
26260		var val;
26261		var radio_checked;
26262		var checkbox_checked;
26263		var ret;
26264		var _class;
26265		var noSplit = false;
26266		var elementToCheck = '';
26267
26268		if ( type === 'quantity' ) {
26269			return tm_check_match( element.val(), value, operator );
26270		}
26271		if ( type === 'productid' ) {
26272			return tm_check_match( element, value, operator );
26273		}
26274		if ( type === 'variation' ) {
26275			return tm_variation_check_match( element, value, operator );
26276		}
26277		if ( type === 'variationattributes' ) {
26278			return tm_variationattributes_check_match( element, f.rule );
26279		}
26280		element = $( f.element );
26281
26282		if ( ! element.length ) {
26283			return false;
26284		}
26285		if ( element.is( '.cpf-section' ) ) {
26286			return tm_check_section_match( element, operator );
26287		}
26288		if ( element.is( '.cpf-type-product' ) ) {
26289			elementToCheck = ':not(' + tcAPI.associatedEpoSelector + ' .tm-epo-field)';
26290		}
26291		_class = element
26292			.attr( 'class' )
26293			.split( ' ' )
26294			.map( function( cls ) {
26295				if ( cls.indexOf( 'cpf-type-', 0 ) !== -1 ) {
26296					return cls;
26297				}
26298				return null;
26299			} )
26300			.filter( function( v ) {
26301				if ( v !== null && v !== undefined ) {
26302					return v;
26303				}
26304				return null;
26305			} );
26306
26307		if ( _class.length > 0 ) {
26308			_class = _class[ 0 ];
26309			if ( _class === 'cpf-type-product' ) {
26310				noSplit = true;
26311				if ( element.is( '.cpf-type-product-mode-product' ) || element.is( '.cpf-type-product-checkbox' ) || element.is( '.cpf-type-product-thumbnailmultiple' ) || element.is( '.cpf-type-product-custombundle' ) ) {
26312					_class = 'cpf-type-checkbox';
26313				} else if ( element.is( '.cpf-type-product-dropdown' ) ) {
26314					_class = 'cpf-type-select';
26315				} else {
26316					_class = 'cpf-type-radio';
26317				}
26318			}
26319			switch ( _class ) {
26320				case 'cpf-type-radio':
26321					radio_checked = element.find( 'input.tm-epo-field.tmcp-radio:checked' + elementToCheck );
26322
26323					if ( operator === 'is' || operator === 'isnot' ) {
26324						if ( radio_checked.length === 0 && operator === 'is' ) {
26325							return false;
26326						}
26327						if ( radio_checked.length === 0 && operator === 'isnot' ) {
26328							return true;
26329						}
26330					}
26331					if ( operator === 'isnotempty' ) {
26332						return radio_checked.length > 0;
26333					}
26334					if ( operator === 'isempty' ) {
26335						return radio_checked.length === 0;
26336					}
26337					val = element.find( 'input.tm-epo-field.tmcp-radio:checked' + elementToCheck ).val();
26338					if ( val && ! noSplit ) {
26339						val = val.slice( 0, val.lastIndexOf( '_' ) );
26340					}
26341					break;
26342				case 'cpf-type-checkbox':
26343					checkbox_checked = element.find( 'input.tm-epo-field.tmcp-checkbox:checked' + elementToCheck );
26344
26345					if ( operator === 'is' || operator === 'isnot' ) {
26346						if ( checkbox_checked.length === 0 && operator === 'is' ) {
26347							return false;
26348						}
26349						if ( checkbox_checked.length === 0 && operator === 'isnot' ) {
26350							return true;
26351						}
26352						ret = false;
26353						checkbox_checked.toArray().forEach( function( el ) {
26354							val = $( el ).val();
26355							if ( val && ! noSplit ) {
26356								val = val.slice( 0, val.lastIndexOf( '_' ) );
26357							}
26358							if ( tm_check_match( val, value, operator ) ) {
26359								ret = true;
26360							} else if ( operator === 'isnot' ) {
26361								ret = false;
26362								return false;
26363							}
26364						} );
26365						return ret;
26366					}
26367					if ( operator === 'isnotempty' ) {
26368						return checkbox_checked.length > 0;
26369					}
26370					if ( operator === 'isempty' ) {
26371						return checkbox_checked.length === 0;
26372					}
26373					break;
26374				case 'cpf-type-select':
26375					val = element.find( 'select.tm-epo-field.tmcp-select' + elementToCheck ).val();
26376					if ( val && ! noSplit ) {
26377						val = val.slice( 0, val.lastIndexOf( '_' ) );
26378					}
26379					break;
26380				case 'cpf-type-selectmultiple':
26381					val = element.find( 'select.tm-epo-field.tmcp-selectmultiple' + elementToCheck ).val();
26382					if ( val && ! noSplit ) {
26383						val.forEach( function( option, i ) {
26384							val[ i ] = option.slice( 0, option.lastIndexOf( '_' ) );
26385						} );
26386					}
26387					break;
26388				case 'cpf-type-variations':
26389					return tm_variation_check_match( element, value, operator );
26390
26391				default:
26392					val = element.find( '.tm-epo-field' + elementToCheck ).val();
26393					break;
26394			}
26395			if ( Array.isArray( val ) ) {
26396				return val.some( function( item ) {
26397					return tm_check_match( item, value, operator );
26398				} );
26399			}
26400			return tm_check_match( val, value, operator );
26401		}
26402		return false;
26403	}
26404
26405	function tm_check_rules( o, theevent ) {
26406		o.toArray().forEach( function( oel ) {
26407			var $this = $( oel );
26408			var matches = $this.data( 'matches' );
26409			var toggle = $this.data( 'toggle' );
26410			var fields = $this.data( 'fields' );
26411			var checked = [];
26412			var show = toggle !== 'show';
26413			var ifield;
26414			var i;
26415			var field;
26416			var ii;
26417			var im;
26418			var sectionFields;
26419			var isInSectionRepeater;
26420
26421			if ( ! fields ) {
26422				return;
26423			}
26424
26425			sectionFields = $this.closest( '.tc-section-fields' );
26426			isInSectionRepeater = sectionFields.is( '.tc-repeater-section' );
26427
26428			for ( i = 0; i < fields.length; i++ ) {
26429				ifield = fields[ i ];
26430				if ( Array.isArray( ifield ) ) {
26431					checked[ i ] = 0;
26432					for ( ii = 0; ii < ifield.length; ii++ ) {
26433						field = ifield[ ii ];
26434						if ( isInSectionRepeater ) {
26435							field.element = sectionFields.find( '[data-uniqid="' + field.element.attr( 'data-uniqid' ) + '"' );
26436						}
26437						if ( theevent !== 'cpflogic' || field_is_active( $( field.element ), undefined, undefined, true ) ) {
26438							if ( tm_check_field_match( field ) ) {
26439								checked[ i ] = ( checked[ i ] || 0 ) + 1;
26440							}
26441						}
26442					}
26443				}
26444			}
26445
26446			for ( im = 0; im < matches.length; im++ ) {
26447				if ( parseInt( checked[ im ], 10 ) === parseInt( matches[ im ], 10 ) ) {
26448					show = ! show;
26449					break;
26450				}
26451			}
26452
26453			if ( show ) {
26454				if ( theevent === 'cpflogic' && ! $this.data( 'did_initial_activation' ) ) {
26455					latecpflogicactions.push( function() {
26456						var enabledEpos = $this.find( '.tm-epo-field.tcenabled' );
26457						enabledEpos.toArray().forEach( function( el ) {
26458							el = $( el );
26459							if ( ! el.data( 'initial_activation' ) && ! $this.closest( '.cpf-section' ).is( '.tc-hidden' ) && field_is_active( el ) ) {
26460								el.trigger( 'tc_element_epo_rules' );
26461								el.data( 'initial_activation', 1 );
26462							}
26463						} );
26464						if ( ( $this.is( '.cpf-element' ) && enabledEpos.length ) || $this.is( '.cpf-section' ) ) {
26465							$this.data( 'did_initial_activation', 1 );
26466						}
26467					} );
26468				}
26469				$this.removeClass( 'tc-hidden' );
26470			} else {
26471				$this.addClass( 'tc-hidden' );
26472			}
26473
26474			$this.data( 'isactive', show ).trigger( 'tc-logic' );
26475		} );
26476	}
26477
26478	function run_cpfdependson( obj ) {
26479		var iscpfdependson;
26480		var last_activate_field = [];
26481		var epo_object;
26482
26483		if ( ! $( obj ).length ) {
26484			obj = 'body';
26485		}
26486		obj = $( obj );
26487		iscpfdependson = obj.find( '.iscpfdependson' );
26488		iscpfdependson.toArray().forEach( function( elements ) {
26489			$( elements ).toArray().forEach( function( el ) {
26490				tm_check_rules( $( el ) );
26491			} );
26492		} );
26493		iscpfdependson.toArray().forEach( function( elements ) {
26494			$( elements ).toArray().forEach( function( el ) {
26495				tm_check_rules( $( el ), 'cpflogic' );
26496			} );
26497		} );
26498		iscpfdependson.toArray().forEach( function( elements ) {
26499			$( elements ).toArray().forEach( function( o ) {
26500				o = $( o );
26501				if ( o.is( '.cpf-section' ) ) {
26502					o = o.find( '.cpf-element' );
26503				}
26504				o.toArray().forEach( function( theelement ) {
26505					var $theelement = $( theelement );
26506					var $theelementepos = $theelement.find( '.tm-epo-field' );
26507					var len = $theelementepos.length;
26508					if ( len > 0 ) {
26509						field_is_active( $theelementepos );
26510					} else {
26511						field_is_active( $theelement );
26512					}
26513				} );
26514			} );
26515		} );
26516		latecpflogicactions.forEach( function( func ) {
26517			func();
26518		} );
26519		latecpflogicactions = [];
26520		if ( $().selectric ) {
26521			$( '.tm-extra-product-options select' ).selectric( 'refresh' );
26522		}
26523		setTimeout( function() {
26524			$( '.tm-owl-slider' ).toArray().forEach( function( slider ) {
26525				$( slider ).trigger( 'refresh.owl.carousel' );
26526			} );
26527		}, 200 );
26528
26529		obj.find( '.tm-product-image:checked,select.tm-product-image' ).toArray().forEach( function( t ) {
26530			t = $( t );
26531			if ( field_is_active( t ) && t.val() !== '' ) {
26532				last_activate_field.push( t );
26533			}
26534		} );
26535		if ( last_activate_field.length ) {
26536			last_activate_field[ last_activate_field.length - 1 ].trigger( 'tm_trigger_product_image' );
26537		} else {
26538			epo_object = obj.data( 'epo_object' );
26539			if ( epo_object ) {
26540				epo_object.main_product.trigger( 'tm_restore_product_image' );
26541			}
26542		}
26543
26544		jWindow.trigger( 'cpflogicrun' );
26545		jWindow.trigger( 'tmlazy' );
26546		jWindow.trigger( 'cpflogicdone' );
26547	}
26548
26549	// Start Conditional logic
26550	if ( ! $().cpfdependson ) {
26551		$.fn.cpfdependson = function( fields, toggle, refresh, epoObject ) {
26552			var elements = this;
26553			var matches = [];
26554			var $this_epo_container = null;
26555			var get_element;
26556			var $element;
26557			var $pid1;
26558			var $epo_id1;
26559			var _events;
26560			var $container;
26561			var i;
26562			var ii;
26563			var iii;
26564			var e;
26565			var ifield;
26566			var field;
26567			var $this;
26568			var show;
26569			var sectionFields;
26570			var isInSectionRepeater;
26571
26572			if ( ! elements.length || typeof fields !== 'object' ) {
26573				return;
26574			}
26575
26576			if ( ! toggle ) {
26577				toggle = 'show';
26578			}
26579
26580			sectionFields = elements.first().closest( '.tc-section-fields' );
26581			isInSectionRepeater = sectionFields.is( '.tc-repeater-section' );
26582
26583			// Initialize the matches array
26584			for ( i = 0; i < fields.length; i++ ) {
26585				matches[ i ] = 0;
26586				ifield = fields[ i ];
26587
26588				if ( Array.isArray( ifield ) ) {
26589					for ( ii = 0; ii < ifield.length; ii++ ) {
26590						field = ifield[ ii ];
26591						if ( typeof field !== 'object' ) {
26592							continue;
26593						}
26594						if ( isInSectionRepeater ) {
26595							field.element = sectionFields.find( '[data-uniqid="' + field.element.attr( 'data-uniqid' ) + '"' );
26596						}
26597
26598						if ( field.type === 'quantity' ) {
26599							if ( ! field.element.data( 'didcpflogic' ) ) {
26600								_events = 'change.cpflogic input.cpflogic';
26601								field.element.off( _events ).on( _events, function() {
26602									run_cpfdependson( epoObject.this_epo_container );
26603								} ).data( 'didcpflogic', 1 );
26604							}
26605						} else if ( field.type === 'variation' || field.type === 'variationattributes' ) {
26606							add_variation_event( 'found_variation.tmlogic', false, function() {
26607								run_cpfdependson( field.element );
26608								jWindow.trigger( 'tm-do-epo-update' );
26609							} );
26610
26611							add_variation_event( 'hide_variation.tmlogic', false, function() {
26612								run_cpfdependson( field.element );
26613								jWindow.trigger( 'tm-do-epo-update' );
26614							} );
26615						} else {
26616							get_element = get_element_from_field( field.element, isInSectionRepeater ? sectionFields.index() : 0 );
26617							if ( get_element && get_element.length > 0 ) {
26618								for ( iii = 0; iii < get_element.length; iii++ ) {
26619									if ( ! get_element[ iii ] ) {
26620										continue;
26621									}
26622									$element = $( get_element[ iii ] );
26623
26624									if ( $element.length === 0 ) {
26625										continue;
26626									}
26627
26628									_events = 'change.cpflogic';
26629
26630									// this essentially only works for the plugin so we use
26631									// cache and not recalcualte each time
26632									if ( ! $this_epo_container || $this_epo_container.closest( tcAPI.associatedEpoSelector ).length ) {
26633										$container = $element.closest( tcAPI.epoSelector );
26634										$pid1 = '.tm-product-id-' + $container.attr( 'data-product-id' );
26635										$epo_id1 = "[data-epo-id='" + $container.attr( 'data-epo-id' ) + "']";
26636										$this_epo_container = $( tcAPI.epoSelector + $pid1 + $epo_id1 );
26637									}
26638
26639									if ( ! $element.data( 'tmhaslogicevents' ) || refresh ) {
26640										/* For variation elements we must only add the event once,
26641										* otherwise if the variable product is on a product element
26642										* it will generate multiple events and slow down the site
26643										*/
26644										if ( ! $element.data( 'tmhaslogicevents' ) && $element.is( '.tm-epo-variation-element' ) ) {
26645											// associated product event prefixes are added later
26646											add_variation_event( 'found_variation.tmlogic', false, function() {
26647												run_cpfdependson( $this_epo_container );
26648												jWindow.trigger( 'tm-do-epo-update' );
26649											} );
26650
26651											add_variation_event( 'hide_variation.tmlogic', false, function() {
26652												run_cpfdependson( $this_epo_container );
26653												jWindow.trigger( 'tm-do-epo-update' );
26654											} );
26655										} else {
26656											if ( $element.is( ':text, textarea' ) ) {
26657												_events = 'change.cpflogic input.cpflogic';
26658											}
26659											if ( $element.is( ':input[type="number"]' ) ) {
26660												_events = 'change.cpflogic input.cpflogic keypress.cpflogic';
26661											}
26662
26663											$element.off( _events ).on( _events, function() {
26664												run_cpfdependson( $this_epo_container );
26665											} );
26666										}
26667
26668										$element.data( 'tmhaslogicevents', 1 );
26669									}
26670								}
26671							}
26672						}
26673
26674						matches[ i ]++;
26675					}
26676				}
26677			}
26678
26679			// Loop through all elements and apply visibility toggling
26680			for ( e = 0; e < elements.length; e++ ) {
26681				$this = $( elements[ e ] );
26682				show = toggle === 'hide';
26683
26684				$this.data( {
26685					matches: matches,
26686					toggle: toggle,
26687					fields: fields,
26688					isactive: show
26689				} );
26690
26691				if ( show ) {
26692					$this.removeClass( 'tc-hidden' );
26693				} else {
26694					$this.addClass( 'tc-hidden' );
26695				}
26696			}
26697
26698			elements.addClass( 'iscpfdependson is-epo-depend' ).data( 'iscpfdependson', 1 );
26699			return elements;
26700		};
26701
26702		$.fn.run_cpfdependson = function() {
26703			run_cpfdependson();
26704		};
26705	}
26706
26707	$.tcepo = {
26708		formSubmitEvents: {},
26709
26710		oneOptionIsSelected: {},
26711
26712		initialActivation: {},
26713
26714		// Holds the active precentage of total current price type fields
26715		lateFieldsPrices: {},
26716
26717		errorObject: {},
26718
26719		showHideTotal: {}
26720	};
26721
26722	function validate_logic( l ) {
26723		return typeof l === 'object' && 'toggle' in l && 'rules' in l && l.rules.length > 0;
26724	}
26725
26726	function convert_rules( rules ) {
26727		if ( rules.what ) {
26728			if ( rules.what === 'all' ) {
26729				rules.rules = [ rules.rules ];
26730			} else if ( rules.what === 'any' ) {
26731				rules.rules = rules.rules.reduce( function( accumulator, elrule ) {
26732					accumulator.push( [ elrule ] );
26733					return accumulator;
26734				}, [] );
26735			}
26736			delete rules.what;
26737		}
26738		return rules;
26739	}
26740
26741	function getLogicType( section, element ) {
26742		var type = 'element';
26743		if ( section === 'product-properties' ) {
26744			switch ( String( element ) ) {
26745				case '0':
26746					type = 'quantity';
26747					break;
26748				case '1':
26749					type = 'productid';
26750					break;
26751				case '2':
26752					type = 'variation';
26753					break;
26754				case '3':
26755					type = 'variationattributes';
26756					break;
26757			}
26758		}
26759
26760		return type;
26761	}
26762
26763	// The following loops are required for the logic to work on composite products that have custom variations
26764	function cpf_section_logic( epoObject ) {
26765		var obj = epoObject.this_epo_container;
26766		var root_element = $( obj );
26767		var all_sections = root_element.find( '.cpf-section' ).not( '.cpf-type-product .cpf-section' );
26768		var search_obj;
26769		var cpf_section;
26770		var sect;
26771		var logic;
26772		var haslogic;
26773		var fields;
26774		var section;
26775		var element;
26776		var type;
26777		var obj_section;
26778		var obj_element;
26779		var closestProductElement;
26780		var selector = '[data-haslogic="1"]';
26781
26782		if ( root_element.is( '.cpf-section' ) ) {
26783			search_obj = false;
26784		} else {
26785			search_obj = all_sections;
26786		}
26787
26788		if ( ! epoObject.is_associated ) {
26789			selector = '[data-haslogic="1"]:not(".cpf-type-product .cpf-section")';
26790		}
26791
26792		root_element.toArray().forEach( function( obj_el ) {
26793			if ( $( obj_el ).is( '.cpf-section' ) ) {
26794				cpf_section = $( obj_el );
26795			} else {
26796				cpf_section = $( obj_el ).find( '.cpf-section' );
26797			}
26798
26799			cpf_section.filter( selector ).toArray().forEach( function( el ) {
26800				var irule;
26801				var rule;
26802				var i;
26803				var ii;
26804				sect = $( el );
26805				logic = sect.data( 'logic' );
26806				// backwards compatibility conversion.
26807				logic = convert_rules( logic );
26808				haslogic = parseInt( sect.data( 'haslogic' ), 10 );
26809				fields = [];
26810				closestProductElement = sect.parent().closest( '.cpf-type-product' );
26811
26812				if ( haslogic === 1 && validate_logic( logic ) ) {
26813					for ( i = 0; i < logic.rules.length; i++ ) {
26814						irule = logic.rules[ i ];
26815						if ( ! irule || ! Array.isArray( irule ) ) {
26816							continue;
26817						}
26818						fields[ i ] = [];
26819						for ( ii = 0; ii < irule.length; ii++ ) {
26820							rule = irule[ ii ];
26821							section = rule.section;
26822							element = rule.element;
26823							type = getLogicType( section, element );
26824
26825							if ( type === 'quantity' ) {
26826								if ( closestProductElement.length ) {
26827									obj_element = epoObject.main_cart.find( tcAPI.associateQtySelector );
26828								} else {
26829									obj_element = epoObject.main_cart.find( epoObject.qtySelector );
26830								}
26831							} else if ( type === 'productid' ) {
26832								obj_element = epoObject.product_id;
26833							} else if ( type === 'variation' || type === 'variationattributes' ) {
26834								if ( closestProductElement.length ) {
26835									obj_element = closestProductElement.find( '.tc-epo-element-product-container' );
26836									if ( ! obj_element.length ) {
26837										obj_element = closestProductElement.closest( '.tc-epo-element-product-container' );
26838									}
26839								} else {
26840									obj_element = root_element;
26841								}
26842							} else if ( search_obj ) {
26843								if ( closestProductElement.length ) {
26844									obj_section = root_element.find( '.cpf-section' ).filter( "[data-uniqid='" + section + "']" );
26845									if ( element !== section ) {
26846										obj_element = obj_section.find( '.cpf-element' ).eq( element );
26847									} else {
26848										obj_element = obj_section;
26849									}
26850								} else {
26851									obj_section = search_obj.filter( "[data-uniqid='" + section + "']" );
26852									if ( element !== section ) {
26853										obj_element = obj_section.find( '.cpf-element:not(.cpf-element .cpf-element)' ).eq( element );
26854									} else {
26855										obj_element = obj_section;
26856									}
26857								}
26858							} else if ( element !== section ) {
26859								obj_element = root_element.find( '.cpf-element' ).eq( element );
26860							} else {
26861								obj_section = root_element.find( '.cpf-section' ).filter( "[data-uniqid='" + section + "']" );
26862								obj_element = obj_section;
26863							}
26864
26865							fields[ i ].push( {
26866								element: obj_element,
26867								rule: rule,
26868								type: type
26869							} );
26870						}
26871					}
26872					if ( ! sect.data( 'iscpfdependson' ) ) {
26873						sect.data( 'cpfdependson-fields', fields );
26874						sect.cpfdependson( fields, logic.toggle, false, epoObject );
26875					} else {
26876						sect.cpfdependson( sect.data( 'cpfdependson-fields' ), logic.toggle, true, epoObject );
26877					}
26878				}
26879			} );
26880		} );
26881	}
26882
26883	function cpf_element_logic( epoObject ) {
26884		var obj = epoObject.this_epo_container;
26885		var root_element = $( obj );
26886		var all_sections = root_element.find( '.cpf-section' ).not( '.cpf-type-product .cpf-section' );
26887		var search_obj;
26888		var current_element;
26889		var logic;
26890		var haslogic;
26891		var section;
26892		var element;
26893		var type;
26894		var obj_section;
26895		var obj_element;
26896		var closestProductElement;
26897		var isInProductElement = false;
26898		var selector = '.cpf-element[data-haslogic="1"]';
26899
26900		if ( root_element.parent().is( tcAPI.associatedEpoSelector ) ) {
26901			all_sections = root_element.find( '.cpf-section' );
26902			isInProductElement = true;
26903		}
26904		if ( root_element.is( '.cpf-section' ) ) {
26905			search_obj = false;
26906		} else {
26907			search_obj = all_sections;
26908		}
26909
26910		if ( ! epoObject.is_associated ) {
26911			selector = '.cpf-element[data-haslogic="1"]:not(".cpf-type-product .cpf-element")';
26912		}
26913
26914		root_element.find( selector ).toArray().forEach( function( el ) {
26915			var fields = [];
26916			var irule;
26917			var rule;
26918			var i;
26919			var ii;
26920
26921			current_element = $( el );
26922			logic = current_element.data( 'logic' );
26923			// backwards compatibility conversion.
26924			logic = convert_rules( logic );
26925
26926			haslogic = parseInt( current_element.data( 'haslogic' ), 10 );
26927
26928			if ( isInProductElement ) {
26929				closestProductElement = root_element;
26930			} else {
26931				closestProductElement = current_element.parent().closest( '.cpf-type-product' );
26932			}
26933
26934			if ( haslogic === 1 && validate_logic( logic ) ) {
26935				for ( i = 0; i < logic.rules.length; i++ ) {
26936					irule = logic.rules[ i ];
26937					if ( ! irule || ! Array.isArray( irule ) ) {
26938						continue;
26939					}
26940					fields[ i ] = [];
26941					for ( ii = 0; ii < irule.length; ii++ ) {
26942						rule = irule[ ii ];
26943						section = rule.section;
26944						element = rule.element;
26945						type = getLogicType( section, element );
26946
26947						if ( type === 'quantity' ) {
26948							if ( closestProductElement.length ) {
26949								obj_element = epoObject.main_cart.find( tcAPI.associateQtySelector );
26950							} else {
26951								obj_element = epoObject.main_cart.find( epoObject.qtySelector );
26952							}
26953						} else if ( type === 'productid' ) {
26954							obj_element = epoObject.product_id;
26955						} else if ( type === 'variation' || type === 'variationattributes' ) {
26956							if ( closestProductElement.length ) {
26957								obj_element = closestProductElement.find( '.tc-epo-element-product-container' );
26958								if ( ! obj_element.length ) {
26959									obj_element = closestProductElement.closest( '.tc-epo-element-product-container' );
26960								}
26961							} else {
26962								obj_element = root_element;
26963							}
26964						} else if ( search_obj ) {
26965							if ( closestProductElement.length ) {
26966								obj_section = root_element.find( '.cpf-section' ).filter( "[data-uniqid='" + section + "']" );
26967								if ( element !== section ) {
26968									obj_element = obj_section.find( '.cpf-element' ).eq( element );
26969								} else {
26970									obj_element = obj_section;
26971								}
26972							} else {
26973								obj_section = search_obj.filter( "[data-uniqid='" + section + "']" );
26974								if ( element !== section ) {
26975									obj_element = obj_section.find( '.cpf-element:not(.cpf-element .cpf-element)' ).eq( element );
26976								} else {
26977									obj_element = obj_section;
26978								}
26979							}
26980						} else if ( element !== section ) {
26981							obj_element = root_element.find( '.cpf-element' ).eq( element );
26982						} else {
26983							obj_section = root_element.find( '.cpf-section' ).filter( "[data-uniqid='" + section + "']" );
26984							obj_element = obj_section;
26985						}
26986
26987						fields[ i ].push( {
26988							element: obj_element,
26989							rule: rule,
26990							type: type
26991						} );
26992					}
26993				}
26994
26995				if ( ! current_element.data( 'iscpfdependson' ) ) {
26996					current_element.data( 'cpfdependson-fields', fields );
26997					current_element.cpfdependson( fields, logic.toggle, false, epoObject );
26998				} else {
26999					current_element.cpfdependson( current_element.data( 'cpfdependson-fields' ), logic.toggle, true, epoObject );
27000				}
27001			}
27002		} );
27003	} // End Conditional logic
27004
27005	// Return price value without tax
27006	function tm_set_price_without_tax( value, _cart, force ) {
27007		var taxable;
27008		var tax_rate;
27009		var tax_display_mode;
27010		var prices_include_tax;
27011
27012		if ( _cart ) {
27013			taxable = _cart.attr( 'data-taxable' );
27014			tax_rate = _cart.attr( 'data-tax-rate' );
27015			tax_display_mode = _cart.attr( 'data-tax-display-mode' );
27016			prices_include_tax = _cart.attr( 'data-prices-include-tax' );
27017
27018			if ( force || ( taxable && tax_display_mode === 'incl' && prices_include_tax !== '1' ) ) {
27019				value = parseFloat( value ) / ( 1 + ( tax_rate / 100 ) );
27020			}
27021		}
27022
27023		return value;
27024	}
27025
27026	// Return price with tax
27027	function tm_set_price_with_tax( value, _cart, force ) {
27028		var taxable;
27029		var tax_rate;
27030		var tax_display_mode;
27031		var prices_include_tax;
27032
27033		if ( _cart ) {
27034			taxable = _cart.attr( 'data-taxable' );
27035			tax_rate = _cart.attr( 'data-tax-rate' );
27036			tax_display_mode = _cart.attr( 'data-tax-display-mode' );
27037			prices_include_tax = _cart.attr( 'data-prices-include-tax' );
27038			if ( force || ( taxable && tax_display_mode !== 'incl' && prices_include_tax === '1' ) ) {
27039				value = parseFloat( value ) * ( 1 + ( tax_rate / 100 ) );
27040			}
27041		}
27042
27043		return value;
27044	}
27045
27046	// Return  price
27047	function tm_set_backend_price( value, _cart, variation ) {
27048		var taxable;
27049		var tax_display_mode;
27050		var prices_include_tax;
27051
27052		if ( _cart ) {
27053			taxable = _cart.attr( 'data-taxable' );
27054			tax_display_mode = _cart.attr( 'data-tax-display-mode' );
27055			prices_include_tax = _cart.attr( 'data-prices-include-tax' );
27056			if ( taxable ) {
27057				if ( variation && variation.tc_tax_rate && String( variation.tc_tax_rate ) === '0' ) {
27058					return value;
27059				}
27060				if ( prices_include_tax === '1' ) {
27061					if ( tax_display_mode === 'incl' ) {
27062						// if we are not in the base tax
27063						if ( variation.tc_base_taxes_of_one !== variation.tc_modded_taxes_of_one ) {
27064							value = parseFloat( ( ( 1 - variation.tc_taxes_of_one ) * value ) * ( 1 / ( 1 - variation.tc_base_taxes_of_one ) ) );
27065						}
27066					} else {
27067						value = tm_set_price_with_tax( value, _cart );
27068					}
27069				} else if ( prices_include_tax !== '1' && tax_display_mode === 'incl' ) {
27070					value = tm_set_price_without_tax( value, _cart );
27071				}
27072			}
27073		}
27074
27075		return value;
27076	}
27077
27078	// Return a formatted currency value
27079	function tm_set_price_totals( value, _cart, notax, element ) {
27080		var sign = '';
27081		var val;
27082
27083		if ( ! notax ) {
27084			value = tm_set_tax_price( value, _cart, element, undefined, true );
27085		}
27086		val = Math.abs( value );
27087
27088		if ( value < 0 ) {
27089			sign = TMEPOJS.minus_sign + ' ';
27090		}
27091
27092		return tm_set_price_( val, sign );
27093	}
27094
27095	function replace_suffixes( value, rawValue, tc_totals_ob, totalsHolder ) {
27096		if ( totalsHolder.attr( 'data-tax-display-mode' ) === 'excl' ) {
27097			tc_totals_ob[ value ] = tc_totals_ob[ value ].replaceAll( '{price_excluding_tax}', tm_set_price_totals( tc_totals_ob[ rawValue ], totalsHolder, true ) );
27098			tc_totals_ob[ value ] = tc_totals_ob[ value ].replaceAll( '{price_including_tax}', tm_set_price_totals( tm_set_price_with_tax( tc_totals_ob[ rawValue ], totalsHolder, true ), totalsHolder, true ) );
27099		} else {
27100			tc_totals_ob[ value ] = tc_totals_ob[ value ].replaceAll( '{price_including_tax}', tm_set_price_totals( tc_totals_ob[ rawValue ], totalsHolder, true ) );
27101			tc_totals_ob[ value ] = tc_totals_ob[ value ].replaceAll( '{price_excluding_tax}', tm_set_price_totals( tm_set_price_without_tax( tc_totals_ob[ rawValue ], totalsHolder, true ), totalsHolder, true ) );
27102		}
27103		return tc_totals_ob;
27104	}
27105
27106	function replace_suffixes_in_html( html, price, totalsHolder ) {
27107		if ( totalsHolder.attr( 'data-tax-display-mode' ) === 'excl' ) {
27108			html = html.replaceAll( '{price_excluding_tax}', tm_set_price_totals( price, totalsHolder, true ) );
27109			html = html.replaceAll( '{price_including_tax}', tm_set_price_totals( tm_set_price_with_tax( price, totalsHolder, true ), totalsHolder, true ) );
27110		} else {
27111			html = html.replaceAll( '{price_including_tax}', tm_set_price_totals( price, totalsHolder, true ) );
27112			html = html.replaceAll( '{price_excluding_tax}', tm_set_price_totals( tm_set_price_without_tax( price, totalsHolder, true ), totalsHolder, true ) );
27113		}
27114		return html;
27115	}
27116
27117	function tm_force_update_price( epoObject, obj, price, formatted_price, original_price, useFormattedPrice ) {
27118		tm_update_price( {
27119			epoObject: epoObject,
27120			obj: obj,
27121			price: price,
27122			formatted_price: formatted_price,
27123			original_price: original_price,
27124			force: true,
27125			useFormattedPrice: useFormattedPrice
27126		} );
27127	}
27128
27129	function tm_update_price( priceobj ) {
27130		var $obj;
27131		var w;
27132		var $ba_amount;
27133		var f;
27134		var pw;
27135		var price;
27136		var formatted_price;
27137		var original_price;
27138		var force;
27139		var useFormattedPrice;
27140		var template;
27141		var templatePrice;
27142		var html;
27143		var totalsHolder;
27144		var sign;
27145
27146		$obj = $( priceobj.obj );
27147
27148		if ( $obj.length === 0 ) {
27149			return;
27150		}
27151
27152		w = $obj.closest( '.tmcp-field-wrap' );
27153		pw = $obj.closest( '.tc-price-wrap' );
27154		f = w.find( '.tm-epo-field' );
27155
27156		price = priceobj.price || 0;
27157		formatted_price = priceobj.formatted_price || '';
27158		original_price = priceobj.original_price || '';
27159		force = priceobj.force || false;
27160		useFormattedPrice = priceobj.useFormattedPrice || false;
27161		totalsHolder = priceobj.epoObject.this_epo_totals_container;
27162
27163		if ( ! force && f.attr( 'data-no-price-change' ) === '1' && f.data( 'price-changed' ) ) {
27164			return;
27165		}
27166
27167		price = $.epoAPI.applyFilter( 'tc_adjust_update_price_price', price ); //number
27168		formatted_price = $.epoAPI.applyFilter( 'tc_adjust_update_price_formatted_price', formatted_price, price ); //formatted
27169		original_price = $.epoAPI.applyFilter( 'tc_adjust_update_price_original_price', original_price ); //number
27170
27171		if ( ! Number.isFinite( parseFloat( original_price ) ) ) {
27172			original_price = 0;
27173		}
27174		if ( ! Number.isFinite( parseFloat( price ) ) ) {
27175			price = 0;
27176		}
27177
27178		$ba_amount = w.find( '.before-amount,.after-amount' );
27179
27180		if ( ( TMEPOJS.tm_epo_auto_hide_price_if_zero === 'yes' && ( $.epoAPI.util.isEmpty( price ) === false || ( TMEPOJS.tm_epo_no_hide_price_if_original_not_zero === 'yes' && $.epoAPI.util.isEmpty( price ) === true && $.epoAPI.util.isEmpty( original_price ) === false ) ) ) || TMEPOJS.tm_epo_auto_hide_price_if_zero !== 'yes' ) {
27181			if ( ( $.epoAPI.util.isEmpty( price ) === true && f.attr( 'data-no-price' ) === '1' ) || ( ! force && f.length > 0 && ( f.attr( 'data-no-price' ) === '1' || ( f.attr( 'data-type' ) === 'variable' && ! f.data( 'price' ) ) ) ) ) {
27182				pw.addClass( 'tm-hidden' );
27183				$obj.addClass( 'tm-hidden' );
27184				$obj.empty();
27185				$ba_amount.addClass( 'tm-hidden' );
27186			} else {
27187				if ( original_price && original_price !== undefined && parseFloat( original_price ) !== parseFloat( price ) ) {
27188					if ( useFormattedPrice ) {
27189						template = tcAPI.templateEngine.plain_price;
27190						templatePrice = { price: formatted_price };
27191					} else {
27192						if ( price < 0 && ! original_price < 0 ) {
27193							template = tcAPI.templateEngine.sale_price_m10;
27194							price = Math.abs( price );
27195						} else if ( ! price < 0 && original_price < 0 ) {
27196							template = tcAPI.templateEngine.sale_price_m01;
27197							original_price = Math.abs( original_price );
27198						} else if ( price < 0 && original_price < 0 ) {
27199							template = tcAPI.templateEngine.sale_price_m11;
27200							price = Math.abs( price );
27201							original_price = Math.abs( original_price );
27202						} else {
27203							template = tcAPI.templateEngine.sale_price;
27204						}
27205						templatePrice = { price: formatPrice( original_price ), sale_price: formatPrice( price ) };
27206					}
27207				} else if ( useFormattedPrice ) {
27208					template = tcAPI.templateEngine.plain_price;
27209					templatePrice = { price: formatted_price };
27210				} else {
27211					template = tcAPI.templateEngine.price;
27212					if ( price < 0 ) {
27213						template = tcAPI.templateEngine.price_m;
27214						price = Math.abs( price );
27215					}
27216					templatePrice = { price: formatPrice( price ) };
27217				}
27218				html = $.epoAPI.util.decodeHTML( $.epoAPI.template.html( template, templatePrice ) );
27219				html = replace_suffixes_in_html( html, price, totalsHolder );
27220				html = $.epoAPI.applyFilter( 'tc_adjust_update_price_formatted_price_html', html );
27221				sign = '';
27222				if ( ! useFormattedPrice && price > 0 && TMEPOJS.tm_epo_global_options_price_sign !== 'minus' ) {
27223					sign = TMEPOJS.option_plus_sign + ' ';
27224				}
27225				$obj.html( sign + html );
27226				pw.removeClass( 'tm-hidden' );
27227				$obj.removeClass( 'tm-hidden' );
27228				$ba_amount.removeClass( 'tm-hidden' );
27229			}
27230		} else {
27231			pw.addClass( 'tm-hidden' );
27232			$obj.addClass( 'tm-hidden' );
27233			$obj.empty();
27234			$ba_amount.addClass( 'tm-hidden' );
27235		}
27236	}
27237
27238	function get_variation_current_settings( form, epoObject ) {
27239		var current_settings = {};
27240
27241		if ( epoObject.thisForm ) {
27242			form = epoObject.thisForm;
27243		}
27244
27245		form.find( '.variations select, .tc-epo-variable-product-selector' ).toArray().forEac
27245h( function( el ) {
27246			var attribute_name;
27247			var value;
27248			el = $( el );
27249
27250			// Get attribute name from data-attribute_name, or from input name
27251			// if it doesn't exist
27252			if ( typeof el.data( 'attribute_name' ) !== 'undefined' ) {
27253				attribute_name = el.data( 'attribute_name' );
27254			} else {
27255				attribute_name = el.attr( 'name' );
27256			}
27257
27258			// Encode entities
27259			value = el.val();
27260
27261			// Add to settings array
27262			current_settings[ attribute_name ] = value;
27263		} );
27264
27265		return current_settings;
27266	}
27267
27268	function do_tm_custom_variations_update( form, all_variations, epoObject ) {
27269		var check_if_all_are_not_set = [];
27270		var formSettings = get_variation_current_settings( form, epoObject );
27271		var redo_check = true;
27272		var variationId = form.find( getVariationIdSelector( form ) ).val();
27273		var ii;
27274
27275		variationId = ( variationId !== '0' && variationId !== '' ) || form.find( '.cpf-type-variations' ).find( '.tm-epo-variation-element:checked' ).length > 0 || form.find( '.cpf-type-variations' ).find( '.tm-epo-variation-element option[value!=""]:selected' );
27276
27277		if ( ! variationId ) {
27278			return;
27279		}
27280		form.find( '.cpf-type-variations' ).toArray().forEach( function( el, i ) {
27281			var t = $( el ).find( '.tm-epo-variation-element' );
27282			var id;
27283			var v;
27284			var exists = false;
27285
27286			check_if_all_are_not_set[ i ] = true;
27287
27288			if ( t.is( 'select' ) ) {
27289				id = $.epoAPI.dom.id( t.attr( 'data-tm-for-variation' ) );
27290				v = t.val();
27291				if ( v ) {
27292					check_if_all_are_not_set[ i ] = false;
27293				}
27294				t.children( 'option' ).toArray().forEach( function( o ) {
27295					exists = false;
27296					form.find( "[data-attribute_name='attribute_" + id + "']" )
27297						.children( 'option' )
27298						.toArray().forEach( function( att ) {
27299							if ( $( att ).attr( 'value' ) === $( o ).attr( 'value' ) ) {
27300								exists = true;
27301								return false;
27302							}
27303						} );
27304					if ( ! exists ) {
27305						$( o ).prop( 'disabled', true ).hide();
27306					} else {
27307						$( o ).prop( 'disabled', false ).show();
27308					}
27309				} );
27310			} else {
27311				t.toArray().forEach( function( oe ) {
27312					var o = $( oe );
27313					var li = o.closest( 'li' );
27314					var input = li.find( '.tm-epo-variation-element' );
27315					var this_settings = $.extend( true, {}, formSettings );
27316					var matching_variations;
27317					var variation;
27318					var is_in_stock;
27319
27320					id = o.attr( 'data-tm-for-variation' );
27321					v = o.val();
27322					if ( o.is( ':checked' ) ) {
27323						check_if_all_are_not_set[ i ] = false;
27324					}
27325
27326					this_settings[ 'attribute_' + id ] = v;
27327
27328					matching_variations = $.fn.tm_find_matching_variations( all_variations, this_settings );
27329					variation = matching_variations.shift();
27330
27331					is_in_stock = variation && 'is_in_stock' in variation && variation.is_in_stock;
27332
27333					if ( ! is_in_stock ) {
27334						if ( ! input.is( ':checked' ) && ! is_in_stock ) {
27335							li.addClass( 'pointereventsoff' );
27336						} else {
27337							li.removeClass( 'pointereventsoff' );
27338						}
27339
27340						o.attr( 'disabled', 'disabled' ).addClass( 'tm-disabled' );
27341
27342						input.attr( 'disabled', 'disabled' );
27343						input.attr( 'data-tm-disabled', 'disabled' );
27344
27345						li.addClass( 'tm-attribute-disabled' ).fadeTo( 'fast', 0.5 );
27346					} else {
27347						o.prop( 'disabled', false ).removeClass( 'tm-disabled' );
27348						li.removeClass( 'pointereventsoff tm-attribute-disabled' ).fadeTo( 'fast', 1, function() {
27349							$( this ).css( 'opacity', '' );
27350						} );
27351						input.prop( 'disabled', false );
27352						input.removeAttr( 'data-tm-disabled' );
27353					}
27354				} );
27355			}
27356		} );
27357
27358		if ( check_if_all_are_not_set ) {
27359			check_if_all_are_not_set.shift();
27360
27361			for ( ii = 0; ii < check_if_all_are_not_set.length; ii++ ) {
27362				if ( check_if_all_are_not_set[ ii ] === false ) {
27363					redo_check = false;
27364					break;
27365				}
27366			}
27367
27368			if ( redo_check ) {
27369				form.find( '.cpf-type-variations' )
27370					.first()
27371					.toArray().forEach( function( el ) {
27372						var t;
27373						var li;
27374						var input;
27375
27376						t = $( el ).find( '.tm-epo-variation-element' );
27377
27378						if ( ! t.is( 'select' ) ) {
27379							t.toArray().forEach( function( o ) {
27380								o = $( o );
27381								li = o.closest( 'li' );
27382								input = li.find( '.tm-epo-variation-element' );
27383								o.prop( 'disabled', false ).removeClass( 'tm-disabled' );
27384								li.removeClass( 'tm-attribute-disabled' ).stop().css( 'opacity', '' );
27385								input.prop( 'disabled', false );
27386								input.removeAttr( 'data-tm-disabled' );
27387							} );
27388						}
27389					} );
27390			}
27391		}
27392	}
27393
27394	function tm_custom_variations_update( form, epoObject ) {
27395		var data;
27396		var all_variations = form.data( 'product_variations' );
27397		var product_id = parseInt( form.data( 'product_id' ), 10 );
27398		var globalVariationObject = form.data( 'globalVariationObject' ) || false;
27399
27400		if ( TMEPOJS.skipCustomVariationsUpdate ) {
27401			return;
27402		}
27403
27404		if ( ! product_id ) {
27405			product_id = form.data( 'tc_product_id' );
27406		}
27407
27408		if ( ! product_id && form.is( tcAPI.compositeSelector ) ) {
27409			data = form.find( '.component_options' ).data( 'options_data' );
27410			product_id = data[ 0 ].option_id;
27411			if ( ! all_variations ) {
27412				all_variations = form.find( '.details.component_data' ).data( 'product_variations' );
27413			}
27414		}
27415
27416		if ( ! epoObject.is_associated ) {
27417			// Fallback to window property if not set - backwards compat
27418			if ( ! all_variations && window.product_variations && window.product_variations.product_id ) {
27419				all_variations = window.product_variations.product_id;
27420			}
27421			if ( ! all_variations && window.product_variations ) {
27422				all_variations = window.product_variations;
27423			}
27424			if ( ! all_variations && window[ 'product_variations_' + product_id ] ) {
27425				all_variations = window[ 'product_variations_' + product_id ];
27426			}
27427		}
27428
27429		if ( ! all_variations ) {
27430			if ( ! globalVariationObject ) {
27431				data = {
27432					action: 'woocommerce_tm_get_variations_array',
27433					post_id: product_id
27434				};
27435				$.post(
27436					TMEPOJS.ajax_url,
27437					data,
27438					function( response ) {
27439						globalVariationObject = response;
27440						form.data( 'globalVariationObject', response );
27441						do_tm_custom_variations_update( form, globalVariationObject.variations, epoObject );
27442					},
27443					'json'
27444				);
27445			} else {
27446				do_tm_custom_variations_update( form, globalVariationObject.variations, epoObject );
27447			}
27448
27449			return;
27450		}
27451		// may need 2.4 check for woocommerce_ajax_variation_threshold
27452		do_tm_custom_variations_update( form, all_variations, epoObject );
27453	}
27454
27455	function tm_fix_stock( cart, html ) {
27456		var custom_variations;
27457		var section;
27458
27459		if ( html === undefined ) {
27460			return false;
27461		}
27462		cart = $( cart );
27463		custom_variations = cart.find( '.tm-epo-variation-element' ).first();
27464		section = custom_variations.closest( '.tm-epo-variation-section' );
27465
27466		if ( custom_variations.length ) {
27467			section.find( '.tm-stock' ).remove();
27468			section.append( '<div class="tm-stock">' + html + '</div>' );
27469			return true;
27470		}
27471		cart.find( '.tm-stock' ).remove();
27472		cart.find( '.variations' ).after( '<div class="tm-stock">' + html + '</div>' );
27473		return true;
27474	}
27475
27476	function fix_stock( $this, form ) {
27477		var stock;
27478
27479		if ( TMEPOJS.tm_epo_global_move_out_of_stock === 'no' ) {
27480			return;
27481		}
27482		stock = $this.find( '.woocommerce-variation-availability' ).last();
27483		if ( ! stock.length ) {
27484			stock = $this.find( '.stock' ).last();
27485		}
27486
27487		if ( stock.length ) {
27488			form.find( '.tm-stock' ).remove();
27489			if ( tm_fix_stock( form, stock.prop( 'outerHTML' ) ) ) {
27490				stock.remove();
27491			}
27492		} else {
27493			form.find( '.tm-stock' ).remove();
27494		}
27495	}
27496
27497	function get_main_input_id( main_product, product, id ) {
27498		var selector = '';
27499		var inputid;
27500
27501		if ( id ) {
27502			selector = selector + "[value='" + id + "']";
27503		}
27504		if ( ! product ) {
27505			product = main_product;
27506		}
27507		inputid = product.find( tcAPI.addToCartSelector + selector );
27508		if ( inputid.length === 0 ) {
27509			inputid = product.find( tcAPI.tcAddToCartSelector + selector );
27510		}
27511		return inputid.last();
27512	}
27513
27514	function get_main_form( main_product, product, selector, id ) {
27515		if ( ! selector ) {
27516			selector = 'form';
27517		}
27518		return get_main_input_id( main_product, product, id ).closest( selector );
27519	}
27520
27521	function get_main_cart( main_product, product, selector, id ) {
27522		return get_main_form( main_product, product, selector, id );
27523	}
27524
27525	function tm_get_native_prices_block( obj ) {
27526		var selector = $.epoAPI.applyFilter( 'tcGetNativePricesBlockSelector', '.single_variation .price', obj );
27527
27528		return $( obj ).find( selector ).not( '.tc-price' );
27529	}
27530
27531	// URL replacement setup
27532	function tm_set_url_fields() {
27533		jDocument.on( 'click.cpfurl change.cpfurl tmredirect', '.tc-url-container .tmcp-radio, .tc-url-container .tmcp-radio+label', function( e ) {
27534			var data_url = $( this ).attr( 'data-url' );
27535			if ( data_url ) {
27536				if ( window.location !== data_url ) {
27537					e.preventDefault();
27538					window.location = data_url;
27539				}
27540			}
27541		} );
27542		jDocument.on( 'change.cpfurl tmredirect', '.tc-url-container .tmcp-select', function( e ) {
27543			var data_url = $( this ).children( 'option:selected' ).attr( 'data-url' );
27544			if ( data_url ) {
27545				if ( window.location !== data_url ) {
27546					e.preventDefault();
27547					window.location = data_url;
27548				}
27549			}
27550		} );
27551	}
27552
27553	function tm_floating_totals( this_epo_totals_container, is_quickview, main_cart ) {
27554		$.tcFloatingTotalsBox( this_epo_totals_container, is_quickview, main_cart );
27555	}
27556
27557	function tm_show_hide_add_to_cart_button( main_product, currentEpoObject ) {
27558		var button;
27559		var qty;
27560		var showHideCart = currentEpoObject.showHideCart;
27561
27562		if ( ! currentEpoObject.is_associated && showHideCart !== undefined ) {
27563			if ( TMEPOJS.tm_epo_hide_add_cart_button === 'yes' || TMEPOJS.tm_epo_hide_all_add_cart_button === 'yes' || TMEPOJS.tm_epo_hide_required_add_cart_button === 'yes' ) {
27564				button = main_product.find( tcAPI.addToCartButtonSelector ).first();
27565				qty = main_product.find( tcAPI.qtySelector ).first();
27566
27567				if ( showHideCart ) {
27568					button.removeClass( 'tc-hide-add-to-cart-button' );
27569					qty.removeClass( 'tc-hide-add-to-cart-button' );
27570				} else {
27571					button.addClass( 'tc-hide-add-to-cart-button' );
27572					qty.addClass( 'tc-hide-add-to-cart-button' );
27573				}
27574
27575				jWindow.trigger( 'epoShowHideCart', {
27576					main_product: main_product,
27577					currentEpoObject: currentEpoObject,
27578					showHideCart: showHideCart,
27579					button: button,
27580					qty: qty
27581				} );
27582			}
27583		}
27584	}
27585
27586	function addShowHidetoEpoObject( currentEpoObject, epoEventId, epoObject ) {
27587		var has_epo;
27588		var this_epo_container;
27589		var epos;
27590		var allElementsAreSelected;
27591		var showHideCart = true;
27592		var showHideTotal = true;
27593		var one_option_is_selected = $.tcepo.oneOptionIsSelected[ epoEventId ];
27594
27595		if ( currentEpoObject.is_associated ) {
27596			currentEpoObject = $.epoAPI.applyFilter( 'tc_currentEpoObject_associated', currentEpoObject, epoEventId, epoObject );
27597			return currentEpoObject;
27598		}
27599
27600		if ( typeof epoObject === 'object' ) {
27601			has_epo = epoObject.has_epo;
27602			this_epo_container = epoObject.this_epo_container;
27603			has_epo = has_epo && ( this_epo_container.find( '.tmcp-fee-field' ).length || this_epo_container.find( '.tmcp-field' ).not( '.cpf-type-variations .tmcp-field' ).length );
27604		} else {
27605			has_epo = epoObject;
27606		}
27607
27608		currentEpoObject.showHideCart = undefined;
27609		currentEpoObject.showHideTotal = undefined;
27610
27611		if ( has_epo && ( TMEPOJS.tm_epo_hide_add_cart_button === 'yes' || TMEPOJS.tm_epo_hide_all_add_cart_button === 'yes' || TMEPOJS.tm_epo_hide_required_add_cart_button === 'yes' || TMEPOJS.tm_epo_hide_totals_until_any === 'yes' || TMEPOJS.tm_epo_hide_totals_until_all_required === 'yes' || TMEPOJS.tm_epo_hide_totals_until_all === 'yes' ) ) {
27612			// Hide cart button until an element is chosen
27613			if ( TMEPOJS.tm_epo_hide_add_cart_button === 'yes' ) {
27614				showHideCart = one_option_is_selected;
27615			}
27616
27617			// Hide Final total box until an element is chosen
27618			if ( TMEPOJS.tm_epo_hide_totals_until_any === 'yes' ) {
27619				showHideTotal = one_option_is_selected;
27620			}
27621
27622			// Hide until all required elements are chosen
27623			if ( TMEPOJS.tm_epo_hide_required_add_cart_button === 'yes' || TMEPOJS.tm_epo_hide_totals_until_all_required === 'yes' ) {
27624				epos = currentEpoObject.this_epo_container.find( '.cpf-element' ).not( '.cpf-type-variations' ).filter( '.tc-container-enabled.tc-is-required' );
27625				allElementsAreSelected = checkFullSelection( epos );
27626				if ( TMEPOJS.tm_epo_hide_required_add_cart_button === 'yes' ) {
27627					showHideCart = allElementsAreSelected;
27628				}
27629				if ( TMEPOJS.tm_epo_hide_totals_until_all_required === 'yes' ) {
27630					showHideTotal = allElementsAreSelected;
27631				}
27632			}
27633
27634			// Hide until all elements are chosen
27635			if ( TMEPOJS.tm_epo_hide_all_add_cart_button === 'yes' || TMEPOJS.tm_epo_hide_totals_until_all === 'yes' ) {
27636				epos = currentEpoObject.this_epo_container.find( '.cpf-element' ).not( '.cpf-type-variations' ).filter( '.tc-container-enabled' );
27637				allElementsAreSelected = checkFullSelection( epos );
27638				if ( TMEPOJS.tm_epo_hide_all_add_cart_button === 'yes' ) {
27639					showHideCart = allElementsAreSelected;
27640				}
27641				if ( TMEPOJS.tm_epo_hide_totals_until_all === 'yes' ) {
27642					showHideTotal = allElementsAreSelected;
27643				}
27644			}
27645
27646			currentEpoObject.showHideCart = showHideCart;
27647			currentEpoObject.showHideTotal = showHideTotal;
27648			$.tcepo.showHideTotal[ epoEventId ] = showHideTotal;
27649		}
27650
27651		currentEpoObject = $.epoAPI.applyFilter( 'tc_currentEpoObject', currentEpoObject, epoEventId, epoObject );
27652
27653		return currentEpoObject;
27654	}
27655
27656	function checkFullSelection( epos, mode ) {
27657		var funcCallback = function( element ) {
27658			var elementToCheck = '';
27659			var _class;
27660			var noSplit = false;
27661			var radio_checked;
27662			var checkbox_checked;
27663			var val;
27664
27665			element = $( element );
27666			if ( element.is( '.cpf-type-product' ) ) {
27667				elementToCheck = ':not(' + tcAPI.associatedEpoSelector + ' .tm-epo-field)';
27668			}
27669			_class = element
27670				.attr( 'class' )
27671				.split( ' ' )
27672				.map( function( cls ) {
27673					if ( cls.indexOf( 'cpf-type-', 0 ) !== -1 ) {
27674						return cls;
27675					}
27676					return null;
27677				} )
27678				.filter( function( v ) {
27679					if ( v !== null && v !== undefined ) {
27680						return v;
27681					}
27682					return null;
27683				} );
27684
27685			if ( _class.length > 0 ) {
27686				_class = _class[ 0 ];
27687				if ( _class === 'cpf-type-product' ) {
27688					noSplit = true;
27689					if ( element.is( '.cpf-type-product-mode-product' ) || element.is( '.cpf-type-product-checkbox' ) || element.is( '.cpf-type-product-thumbnailmultiple' ) || element.is( '.cpf-type-product-custombundle' ) ) {
27690						_class = 'cpf-type-checkbox';
27691					} else if ( element.is( '.cpf-type-product-dropdown' ) ) {
27692						_class = 'cpf-type-select';
27693					} else {
27694						_class = 'cpf-type-radio';
27695					}
27696				}
27697				switch ( _class ) {
27698					case 'cpf-type-radio':
27699						radio_checked = element.find( 'input.tm-epo-field.tmcp-radio:checked' + elementToCheck );
27700						return radio_checked.length > 0;
27701
27702					case 'cpf-type-checkbox':
27703						checkbox_checked = element.find( 'input.tm-epo-field.tmcp-checkbox:checked' + elementToCheck );
27704						return checkbox_checked.length > 0;
27705
27706					case 'cpf-type-select':
27707						val = element.find( 'select.tm-epo-field.tmcp-select' + elementToCheck ).val();
27708						if ( val && ! noSplit ) {
27709							val = val.slice( 0, val.lastIndexOf( '_' ) );
27710						}
27711						break;
27712
27713					case 'cpf-type-selectmultiple':
27714						val = element.find( 'select.tm-epo-field.tmcp-selectmultiple' + elementToCheck ).val();
27715						if ( val && ! noSplit ) {
27716							val.forEach( function( option, i ) {
27717								val[ i ] = option.slice( 0, option.lastIndexOf( '_' ) );
27718							} );
27719						}
27720						break;
27721
27722					default:
27723						val = element.find( '.tm-epo-field' + elementToCheck ).val();
27724						break;
27725				}
27726				if ( Array.isArray( val ) ) {
27727					return val.some( function( item ) {
27728						return item !== '';
27729					} );
27730				}
27731				return val !== '';
27732			}
27733			return false;
27734		};
27735		if ( mode === 'some' ) {
27736			return epos.toArray().some( funcCallback );
27737		}
27738		return epos.toArray().every( funcCallback );
27739	}
27740
27741	function goto_error_item( item, epoEventId ) {
27742		var el = $.tcepo.errorObject[ epoEventId ] || item;
27743		var elsection;
27744		var elsectionlink;
27745		var cpfElement;
27746		var productSection;
27747		var pos;
27748
27749		if ( el ) {
27750			if ( TMEPOJS.tm_epo_disable_error_scroll !== 'yes' ) {
27751				elsection = el.closest( '.cpf-section' );
27752				elsectionlink = elsection.find( '.tm-section-link' );
27753				cpfElement = el.closest( '.cpf-element' );
27754				productSection = el.closest( '.cpf-type-product' ).closest( '.cpf-section' );
27755
27756				if ( productSection.length && productSection.find( '.tm-toggle' ).length ) {
27757					productSection.find( '.tm-toggle' ).trigger( 'openwrap.tmtoggle' );
27758				}
27759
27760				if ( elsection.find( '.tm-toggle' ).length ) {
27761					elsection.find( '.tm-toggle' ).trigger( 'openwrap.tmtoggle' );
27762				}
27763				if ( window.tc_validation_offset === undefined ) {
27764					window.tc_validation_offset = -100;
27765				}
27766				if ( elsection.is( '.section_popup' ) ) {
27767					errorContainer.tcScrollTo( elsectionlink, 300, window.tc_validation_offset );
27768					elsectionlink.trigger( 'click.tmsectionpoplink' );
27769				} else if ( elsection.is( '.tm-owl-slider-section' ) ) {
27770					pos = el.closest( '.owl-item' ).index();
27771					elsection.find( '.tcowl-carousel' ).trigger( 'to.owl.carousel', [ pos, 100 ] );
27772					setTimeout( function() {
27773						elsection.find( '.tcowl-carousel' ).trigger( 'refresh.owl.carousel' );
27774
27775						if ( cpfElement.length > 0 ) {
27776							errorContainer.tcScrollTo( cpfElement, 300, window.tc_validation_offset );
27777						}
27778					}, 200 );
27779				} else if ( elsection.is( '.tc-tabs-section' ) ) {
27780					pos = el.closest( '.tc-tab-slide' ).index();
27781					el.closest( '.tc-tabs' ).find( '.tc-tab-headers .tc-tab-header .tab-header[data-id="tc-tab-slide' + pos + '"]' ).trigger( 'click.tmtabs' );
27782					setTimeout( function() {
27783						if ( cpfElement.length > 0 ) {
27784							errorContainer.tcScrollTo( cpfElement, 300, window.tc_validation_offset );
27785						}
27786					}, 200 );
27787				} else if ( cpfElement.length > 0 ) {
27788					errorContainer.tcScrollTo( cpfElement, 300, window.tc_validation_offset );
27789				}
27790			}
27791
27792			if ( ! item ) {
27793				$.tcepo.errorObject[ epoEventId ] = false;
27794			}
27795		}
27796	}
27797
27798	function tm_limit_c_selection( field, prevent ) {
27799		var allowed = parseInt( field.attr( 'data-limit' ), 10 );
27800		var checked = false;
27801		var val;
27802		var q;
27803
27804		if ( allowed > 0 ) {
27805			checked = 0;
27806			field
27807				.closest( '.tm-extra-product-options-checkbox' )
27808				.find( "input.tm-epo-field[type='checkbox']:checked" )
27809				.toArray().forEach( function( cb ) {
27810					cb = $( cb );
27811					q = cb.closest( 'li.tmcp-field-wrap' ).find( 'input.tm-qty' );
27812					if ( q.length > 0 ) {
27813						val = parseInt( q.val(), 10 );
27814						if ( val <= 0 ) {
27815							val = 1;
27816						}
27817						checked = parseInt( checked, 10 ) + val;
27818					} else {
27819						checked = parseInt( checked, 10 ) + 1;
27820					}
27821				} );
27822			if ( checked > allowed ) {
27823				if ( prevent ) {
27824					field.prop( 'checked', '' ).trigger( 'change' );
27825				}
27826				return false;
27827			}
27828		}
27829		return true;
27830	}
27831
27832	function tm_exact_c_selection( field, prevent ) {
27833		var allowed = parseInt( field.attr( 'data-exactlimit' ), 10 );
27834		var checked = false;
27835		var val;
27836		var q;
27837
27838		if ( allowed > 0 ) {
27839			checked = 0;
27840			field
27841				.closest( '.tm-extra-product-options-checkbox' )
27842				.find( "input.tm-epo-field[type='checkbox']:checked" )
27843				.toArray().forEach( function( cb ) {
27844					cb = $( cb );
27845					q = cb.closest( 'li.tmcp-field-wrap' ).find( 'input.tm-qty' );
27846					if ( q.length > 0 ) {
27847						val = parseInt( q.val(), 10 );
27848						if ( val <= 0 ) {
27849							val = 1;
27850						}
27851						checked = parseInt( checked, 10 ) + val;
27852					} else {
27853						checked = parseInt( checked, 10 ) + 1;
27854					}
27855				} );
27856			if ( checked > allowed ) {
27857				if ( prevent ) {
27858					field.prop( 'checked', '' ).trigger( 'change' );
27859				}
27860				return false;
27861			}
27862		}
27863		return true;
27864	}
27865
27866	function tm_limit_cont( fields, main_product, epoEventId ) {
27867		var checkall = true;
27868		var first_error_obj = false;
27869		var limit;
27870		var eln;
27871		var checked;
27872		var val;
27873		var q;
27874		var ew;
27875		var em;
27876		var message;
27877
27878		fields.toArray().forEach( function( field ) {
27879			field = $( field );
27880			limit = field.find( "[type='checkbox'][data-limit]" );
27881			if ( limit.length && field_is_active( limit ) ) {
27882				eln = parseInt( limit.attr( 'data-limit' ), 10 );
27883				checked = 0;
27884				field.find( "input.tm-epo-field[type='checkbox']:checked" ).toArray().forEach( function( cb ) {
27885					cb = $( cb );
27886					q = cb.closest( 'li.tmcp-field-wrap' ).find( 'input.tm-qty' );
27887					if ( q.length > 0 ) {
27888						val = parseInt( q.val(), 10 );
27889						if ( val <= 0 ) {
27890							val = 1;
27891						}
27892						checked = parseInt( checked, 10 ) + val;
27893					} else {
27894						checked = parseInt( checked, 10 ) + 1;
27895					}
27896				} );
27897				ew = field.closest( '.cpf-element' );
27898				em = ew.find( 'div.tm-error-min' );
27899
27900				if ( eln < checked ) {
27901					checkall = false;
27902					first_error_obj = field;
27903					if ( eln === 1 ) {
27904						message = TMEPOJS.tm_epo_global_validator_messages.epolimitsingle.replaceAll( '{0}', eln );
27905					} else {
27906						message = TMEPOJS.tm_epo_global_validator_messages.epolimit.replaceAll( '{0}', eln );
27907					}
27908					if ( em.length ) {
27909						em.remove();
27910					}
27911					if ( TMEPOJS.tm_epo_global_error_label_placement === 'before' ) {
27912						field.closest( '.tc-element-container' ).before( '<div class="tm-error-min tm-error tc-cell tcwidth tcwidth-100">' + message + '</div>' );
27913					} else {
27914						field.closest( '.tc-element-container' ).after( '<div class="tm-error-min tm-error tc-cell tcwidth tcwidth-100">' + message + '</div>' );
27915					}
27916					main_product.find( tcAPI.addToCartButtonSelector ).first().removeClass( 'disabled loading fpd-disabled' ).prop( 'disabled', false );
27917				} else {
27918					em.remove();
27919				}
27920			}
27921		} );
27922		if ( first_error_obj ) {
27923			$.tcepo.errorObject[ epoEventId ] = first_error_obj;
27924		}
27925		return checkall;
27926	}
27927
27928	function tm_check_limit_cont( limit_cont, main_product, epoEventId ) {
27929		$.tcepo.formSubmitEvents[ epoEventId ][ $.tcepo.formSubmitEvents[ epoEventId ].length ] = {
27930			trigger: function() {
27931				return tm_limit_cont( limit_cont, main_product, epoEventId );
27932			},
27933			on_true: function() {
27934				return true;
27935			},
27936			on_false: function() {
27937				goto_error_item( $( limit_cont ).find( '.tm-epo-field' ).first(), epoEventId );
27938				return true;
27939			}
27940		};
27941	}
27942
27943	function tm_exactlimit_cont( fields, main_product, epoEventId ) {
27944		var checkall = true;
27945		var first_error_obj = false;
27946		var exactlimit;
27947		var eln;
27948		var checked;
27949		var val;
27950		var q;
27951		var ew;
27952		var em;
27953		var message;
27954
27955		fields.toArray().forEach( function( field ) {
27956			field = $( field );
27957			exactlimit = field.find( "[type='checkbox'][data-exactlimit]" );
27958			if ( exactlimit.length && field_is_active( exactlimit ) ) {
27959				eln = parseInt( exactlimit.attr( 'data-exactlimit' ), 10 );
27960				checked = 0;
27961				field.find( "input.tm-epo-field[type='checkbox']:checked" ).toArray().forEach( function( cb ) {
27962					cb = $( cb );
27963					q = cb.closest( 'li.tmcp-field-wrap' ).find( 'input.tm-qty' );
27964					if ( q.length > 0 ) {
27965						val = parseInt( q.val(), 10 );
27966						if ( val <= 0 ) {
27967							val = 1;
27968						}
27969						checked = parseInt( checked, 10 ) + val;
27970					} else {
27971						checked = parseInt( checked, 10 ) + 1;
27972					}
27973				} );
27974				ew = field.closest( '.cpf-element' );
27975				em = ew.find( 'div.tm-error-min' );
27976
27977				if ( eln !== checked ) {
27978					checkall = false;
27979					first_error_obj = field;
27980					if ( eln === 1 ) {
27981						message = TMEPOJS.tm_epo_global_validator_messages.epoexactsingle.replaceAll( '{0}', eln );
27982					} else {
27983						message = TMEPOJS.tm_epo_global_validator_messages.epoexact.replaceAll( '{0}', eln );
27984					}
27985					if ( em.length ) {
27986						em.remove();
27987					}
27988					if ( TMEPOJS.tm_epo_global_error_label_placement === 'before' ) {
27989						field.closest( '.tc-element-container' ).before( '<div class="tm-error-min tm-error tc-cell tcwidth tcwidth-100">' + message + '</div>' );
27990					} else {
27991						field.closest( '.tc-element-container' ).after( '<div class="tm-error-min tm-error tc-cell tcwidth tcwidth-100">' + message + '</div>' );
27992					}
27993					main_product.find( tcAPI.addToCartButtonSelector ).first().removeClass( 'disabled loading fpd-disabled' ).prop( 'disabled', false );
27994				} else {
27995					em.remove();
27996				}
27997			}
27998		} );
27999		if ( first_error_obj ) {
28000			$.tcepo.errorObject[ epoEventId ] = first_error_obj;
28001		}
28002		return checkall;
28003	}
28004
28005	function tm_check_exactlimit_cont( exactlimit_cont, main_product, epoEventId ) {
28006		$.tcepo.formSubmitEvents[ epoEventId ][ $.tcepo.formSubmitEvents[ epoEventId ].length ] = {
28007			trigger: function() {
28008				return tm_exactlimit_cont( exactlimit_cont, main_product, epoEventId );
28009			},
28010			on_true: function() {
28011				return true;
28012			},
28013			on_false: function() {
28014				goto_error_item( $( exactlimit_cont ).find( '.tm-epo-field' ).first(), epoEventId );
28015				return true;
28016			}
28017		};
28018	}
28019
28020	function tm_minimumlimit_cont( fields, epoEventId ) {
28021		var checkall = true;
28022		var first_error_obj = false;
28023		var eln;
28024		var checked;
28025		var val;
28026		var q;
28027		var ew;
28028		var em;
28029		var message;
28030
28031		fields.toArray().forEach( function( field ) {
28032			var minimumlimit;
28033
28034			field = $( field );
28035			minimumlimit = field.find( "[type='checkbox'][data-minimumlimit]" );
28036
28037			if ( minimumlimit.length && field_is_active( minimumlimit ) ) {
28038				eln = parseInt( minimumlimit.attr( 'data-minimumlimit' ), 10 );
28039				checked = 0;
28040				field.find( "input.tm-epo-field[type='checkbox']:checked" ).toArray().forEach( function( cb ) {
28041					cb = $( cb );
28042					q = cb.closest( 'li.tmcp-field-wrap' ).find( 'input.tm-qty' );
28043					if ( q.length > 0 ) {
28044						val = parseInt( q.val(), 10 );
28045						if ( val <= 0 ) {
28046							val = 1;
28047						}
28048						checked = parseInt( checked, 10 ) + val;
28049					} else {
28050						checked = parseInt( checked, 10 ) + 1;
28051					}
28052				} );
28053				ew = field.closest( '.cpf-element' );
28054				em = ew.find( 'div.tm-error-min' );
28055				if ( eln > checked ) {
28056					checkall = false;
28057					first_error_obj = field;
28058					if ( eln === 1 ) {
28059						message = TMEPOJS.tm_epo_global_validator_messages.epominsingle.replaceAll( '{0}', eln );
28060					} else {
28061						message = TMEPOJS.tm_epo_global_validator_messages.epomin.replaceAll( '{0}', eln );
28062					}
28063					if ( em.length ) {
28064						em.remove();
28065					}
28066					if ( TMEPOJS.tm_epo_global_error_label_placement === 'before' ) {
28067						field.closest( '.tc-element-container' ).before( '<div class="tm-error-min tm-error tc-cell tcwidth tcwidth-100">' + message + '</div>' );
28068					} else {
28069						field.closest( '.tc-element-container' ).after( '<div class="tm-error-min tm-error tc-cell tcwidth tcwidth-100">' + message + '</div>' );
28070					}
28071				} else {
28072					em.remove();
28073				}
28074			}
28075		} );
28076
28077		if ( first_error_obj ) {
28078			$.tcepo.errorObject[ epoEventId ] = first_error_obj;
28079		}
28080
28081		return checkall;
28082	}
28083
28084	function tm_check_minimumlimit_cont( minimumlimit_cont, epoEventId ) {
28085		$.tcepo.formSubmitEvents[ epoEventId ][ $.tcepo.formSubmitEvents[ epoEventId ].length ] = {
28086			trigger: function() {
28087				return tm_minimumlimit_cont( minimumlimit_cont, epoEventId );
28088			},
28089			on_true: function() {
28090				return true;
28091			},
28092			on_false: function() {
28093				goto_error_item( false, epoEventId );
28094				return true;
28095			}
28096		};
28097	}
28098
28099	function cleanPrice( price ) {
28100		if ( price === null ) {
28101			return 0;
28102		}
28103
28104		if ( typeof price === 'object' ) {
28105			price = price[ 0 ];
28106		}
28107
28108		if ( ! Number.isFinite( parseFloat( price ) ) ) {
28109			price = 0;
28110		}
28111
28112		return price;
28113	}
28114
28115	function tm_apply_dpd( price, totals, apply, force ) {
28116		price = cleanPrice( price );
28117
28118		if ( apply ) {
28119			price = $.epoAPI.applyFilter( 'tc_apply_dpd', price, totals, apply, force );
28120		}
28121
28122		return price;
28123	}
28124
28125	function tm_calculate_product_regular_price( totals, allowfalse ) {
28126		var price = 0;
28127
28128		if ( totals.length > 0 ) {
28129			price = totals.data( 'regular-price' );
28130		}
28131
28132		price = $.epoAPI.applyFilter( 'tc_calculate_product_regular_price', price, totals );
28133
28134		if ( allowfalse && price === false ) {
28135			return false;
28136		}
28137		price = parseFloat( price );
28138
28139		if ( ! Number.isFinite( price ) ) {
28140			price = 0;
28141		}
28142
28143		return price;
28144	}
28145
28146	function tm_calculate_product_price( totals, allowfalse ) {
28147		var price = 0;
28148
28149		if ( totals.length > 0 ) {
28150			price = totals.data( 'price' );
28151		}
28152
28153		price = $.epoAPI.applyFilter( 'tc_calculate_product_price', price, totals );
28154
28155		if ( allowfalse && price === false ) {
28156			return false;
28157		}
28158		price = parseFloat( price );
28159
28160		if ( ! Number.isFinite( price ) ) {
28161			price = 0;
28162		}
28163
28164		return price;
28165	}
28166
28167	function calculateMathPrice( price, thisElement, epoObject, noevents, useOriginalPrice, funcTotal, cumulativeTotal, mathskip ) {
28168		var formula = price.toString();
28169		var val = 0;
28170		var matches;
28171		var match;
28172		var elementWrap;
28173		var allElements;
28174		var element;
28175		var reg;
28176		var elementPrice = 0;
28177		var pos;
28178		var type;
28179		var id;
28180		var thisVal;
28181		var thisValForced;
28182		var thisElementId = thisElement.closest( '.cpf-element' ).attr( 'data-uniqid' );
28183		var thisElementWrap = thisElement.closest( '.tmcp-ul-wrap' );
28184		var totalsHolder_tc_totals_ob = epoObject.this_epo_totals_container.data( 'totalsHolder_tc_totals_ob' );
28185		var thisElementIndex = thisElementWrap.find( '.tmcp-field, .tmcp-fee-field, .tmcp-sub-fee-field' ).filter( ':checked' ).index( thisElement );
28186		var thisElementIndexForced = thisElementWrap.find( '.tmcp-field, .tmcp-fee-field, .tmcp-sub-fee-field' ).index( thisElement );
28187		var this_epo_container = epoObject.is_associated ? epoObject.this_epo_container : epoObject.this_epo_container.not( tcAPI.epoSelector + ' ' + tcAPI.associatedEpoSelector );
28188		var constants = $.epoAPI.util.parseJSON( TMEPOJS.tm_epo_math ) || {};
28189		var productPrice;
28190		var dynamicProductPrice;
28191		var __price;
28192		var __price_type;
28193
28194		if ( thisElementIndex === -1 && ! thisElement.is( '.tmcp-checkbox, .tmcp-radio' ) ) {
28195			thisElementIndex = 0;
28196		}
28197		if ( thisElementIndexForced === -1 && ! thisElement.is( '.tmcp-checkbox, .tmcp-radio' ) ) {
28198			thisElementIndexForced = 0;
28199		}
28200
28201		if ( ! thisElement.is( '.tcdisabled' ) && ! noevents && ! thisElement.data( 'addedtcEpoBeforeOptionPriceCalculation' ) ) {
28202			jWindow.on( 'tcEpoBeforeOptionPriceCalculation.math' + thisElementId, function() {
28203				tm_element_epo_rules( epoObject, thisElement, undefined, undefined, undefined, true );
28204			} );
28205			jWindow.on( 'tcEpoAfterNormalOptionPriceCalculation.math' + thisElementId, function() {
28206				thisElement.data( 'fetchOptionPrices', false );
28207				thisElement.data( 'fetchOptionPrices-forced', false );
28208				thisElement.data( 'fetchOptionPrices-fee', false );
28209				thisElement.data( 'fetchOptionPrices-fee-forced', false );
28210				thisElement.data( 'fetchOptionPrices-sub-fee', false );
28211				thisElement.data( 'fetchOptionPrices-sub-fee-forced', false );
28212			} );
28213			thisElement.data( 'addedtcEpoBeforeOptionPriceCalculation', 1 );
28214		}
28215
28216		if ( thisElement.is( '.tmcp-field' ) ) {
28217			if ( thisElement.data( 'fetchOptionPrices' ) ) {
28218				thisVal = thisElement.data( 'fetchOptionPrices' );
28219				thisValForced = thisElement.data( 'fetchOptionPrices-forced' );
28220			} else {
28221				thisVal = fetchOptionPrices( epoObject, thisElementWrap, '.tmcp-field', 0, 0, [], true, undefined, undefined, undefined, undefined, undefined, true );
28222				thisValForced = fetchOptionPrices( epoObject, thisElementWrap, '.tmcp-field', 0, 0, [], true, true, undefined, undefined, undefined, undefined, true );
28223				thisElement.data( 'fetchOptionPrices', thisVal );
28224				thisElement.data( 'fetchOptionPrices-forced', thisValForced );
28225			}
28226		} else if ( thisElement.is( '.tmcp-fee-field' ) ) {
28227			if ( thisElement.data( 'fetchOptionPrices-fee' ) ) {
28228				thisVal = thisElement.data( 'fetchOptionPrices-fee' );
28229				thisValForced = thisElement.data( 'fetchOptionPrices-fee-forced' );
28230			} else {
28231				thisVal = fetchOptionPrices( epoObject, thisElementWrap, '.tmcp-fee-field', 0, 0, [], true, undefined, undefined, undefined, undefined, undefined, true );
28232				thisValForced = fetchOptionPrices( epoObject, thisElementWrap, '.tmcp-fee-field', 0, 0, [], true, true, undefined, undefined, undefined, undefined, true );
28233				thisElement.data( 'fetchOptionPrices-fee', thisVal );
28234				thisElement.data( 'fetchOptionPrices-fee-forced', thisValForced );
28235			}
28236		} else if ( thisElement.is( '.tmcp-sub-fee-field' ) ) {
28237			if ( thisElement.data( 'fetchOptionPrices-sub-fee' ) ) {
28238				thisVal = thisElement.data( 'fetchOptionPrices-sub-fee' );
28239				thisValForced = thisElement.data( 'fetchOptionPrices-sub-fee-forced' );
28240			} else {
28241				thisVal = fetchOptionPrices( epoObject, thisElementWrap, '.tmcp-sub-fee-field', 0, 0, [], true, undefined, undefined, undefined, undefined, undefined, true );
28242				thisValForced = fetchOptionPrices( epoObject, thisElementWrap, '.tmcp-sub-fee-field', 0, 0, [], true, true, undefined, undefined, undefined, undefined, true );
28243				thisElement.data( 'fetchOptionPrices-sub-fee', thisVal );
28244				thisElement.data( 'fetchOptionPrices-sub-fee-forced', thisValForced );
28245			}
28246		}
28247
28248		Object.keys( constants ).forEach( function( key ) {
28249			var name;
28250			var value;
28251
28252			if ( constants[ key ].name !== '' && constants[ key ].value !== '' ) {
28253				name = constants[ key ].name;
28254				value = constants[ key ].value;
28255				if ( name.startsWith( '{' ) ) {
28256					formula = formula.replaceAll( name, value );
28257				} else if ( constants[ key ].name.isNumeric() ) {
28258					formula = formula.replaceAll( '{' + name + '}', value );
28259				} else {
28260					name = '{' + name + '}';
28261					if ( value.startsWith( '{' ) ) {
28262						formula = formula.replaceAll( name, value );
28263					} else {
28264						formula = formula.replaceAll( name, $.epoAPI.math.unformat( value.replace( ',', '.' ) ) );
28265					}
28266				}
28267			}
28268		} );
28269
28270		if ( totalsHolder_tc_totals_ob !== undefined ) {
28271			// product quantity
28272			formula = formula.replaceAll( '{quantity}', $.epoAPI.math.unformat( totalsHolder_tc_totals_ob.qty ) );
28273			// original product price
28274			if ( useOriginalPrice && totalsHolder_tc_totals_ob.original_product_price !== undefined ) {
28275				productPrice = $.epoAPI.math.unformat( totalsHolder_tc_totals_ob.original_product_price );
28276			} else {
28277				productPrice = $.epoAPI.math.unformat( totalsHolder_tc_totals_ob.product_price );
28278			}
28279			if ( totalsHolder_tc_totals_ob.dynamic_product_price !== undefined ) {
28280				dynamicProductPrice = $.epoAPI.math.unformat( totalsHolder_tc_totals_ob.dynamic_product_price );
28281			} else {
28282				dynamicProductPrice = 0;
28283			}
28284		} else {
28285			// product quantity
28286			formula = formula.replaceAll( '{quantity}', 0 );
28287			// original product price
28288			productPrice = 0;
28289			dynamicProductPrice = 0;
28290		}
28291
28292		productPrice = $.epoAPI.math.toFloat( productPrice );
28293
28294		formula = formula.replaceAll( '{product_price}', productPrice );
28295		formula = formula.replaceAll( '{dynamic_product_price}', dynamicProductPrice );
28296
28297		if ( ! funcTotal ) {
28298			funcTotal = 0;
28299		}
28300		funcTotal = $.epoAPI.math.toFloat( funcTotal );
28301
28302		if ( ! cumulativeTotal ) {
28303			cumulativeTotal = 0;
28304		}
28305		cumulativeTotal = $.epoAPI.math.toFloat( cumulativeTotal );
28306
28307		formula = formula.replaceAll( '{options_total}', funcTotal );
28308		formula = formula.replaceAll( '{product_price_plus_options_total}', productPrice + funcTotal );
28309		formula = formula.replaceAll( '{cumulative_total}', cumulativeTotal );
28310		formula = formula.replaceAll( '{product_price_plus_cumulative_total}', productPrice + cumulativeTotal );
28311
28312		if ( thisValForced.floatingBoxData !== undefined && thisValForced.floatingBoxData[ thisElementIndexForced ] !== undefined ) {
28313			// the option/element value to float
28314			if ( thisValForced.floatingBoxData[ thisElementIndexForced ].input_type === 'number' || thisValForced.floatingBoxData[ thisElementIndexForced ].input_type === 'hidden' ) {
28315				formula = formula
28316					.replaceAll( '{this.value}', $.epoAPI.math.toFloat( $.epoAPI.math.unformat( thisValForced.floatingBoxData[ thisElementIndexForced ].valueText ) ) );
28317			} else {
28318				formula = formula
28319					.replaceAll( '{this.value}', $.epoAPI.math.toFloat( $.epoAPI.math.unformat( thisValForced.floatingBoxData[ thisElementIndexForced ].valueText, tcAPI.localDecimalSeparator ) ) );
28320			}
28321			// the option/element raw value
28322			formula = formula.replaceAll( '{this.rawvalue}', thisValForced.floatingBoxData[ thisElementIndexForced ].valueText );
28323			// the option/element raw value
28324			formula = formula.replaceAll( '{this.text}', thisValForced.floatingBoxData[ thisElementIndexForced ].valueText );
28325			// the option/element value length
28326			formula = formula
28327				.replaceAll( '{this.value.length}', thisValForced.floatingBoxData[ thisElementIndexForced ].valueText.length );
28328		} else {
28329			formula = formula
28330				// the option/element value to float
28331				.replaceAll( '{this.value}', 0 )
28332				// the option/element raw value
28333				.replaceAll( '{this.rawvalue}', '' )
28334				// the option/element raw value
28335				.replaceAll( '{this.text}', '' )
28336				// the option/element value length
28337				.replaceAll( '{this.value.length}', 0 );
28338		}
28339
28340		formula = formula
28341			// the number of options the user has selected
28342			.replaceAll( '{this.count}', thisVal.floatingBoxData.length )
28343			// the total option quantity of this element
28344			.replaceAll(
28345				'{this.count.quantity}',
28346				thisVal.floatingBoxData
28347					.map( function( x ) {
28348						return x.quantity;
28349					} )
28350					.reduce( function( acc, thisval ) {
28351						return $.epoAPI.math.toFloat( acc ) + $.epoAPI.math.toFloat
28351( thisval );
28352					}, 0 )
28353			)
28354			// the option quantity of this element
28355			.replaceAll( '{this.quantity}', thisElement.data( 'tm-quantity' ) );
28356
28357		if ( formula.match( /\{(\s)*?field\.([^}]*)}/ ) ) {
28358			matches = formula.match( /\{(\s)*?field\.([^}]*)}/g );
28359			matches.forEach( function( field ) {
28360				match = field.match( /\{(\s)*?field\.([^}]*)}/ );
28361				if ( undefined !== match[ 2 ] && 'string' === typeof match[ 2 ] ) {
28362					pos = match[ 2 ].lastIndexOf( '.' );
28363					if ( pos !== -1 ) {
28364						id = match[ 2 ].substring( 0, pos );
28365						type = match[ 2 ].substring( pos + 1 );
28366
28367						if ( $.inArray( type, [ 'price', 'value', 'value.length', 'rawvalue', 'text', 'text.length', 'quantity', 'count', 'count.quantity' ] ) !== -1 ) {
28368							elementWrap = this_epo_container.find( "[data-uniqid='" + $.epoAPI.util.escapeSelector( id ) + "']" );
28369							if ( elementWrap.length ) {
28370								allElements = elementWrap.find( '.tmcp-field, .tmcp-fee-field, .tmcp-sub-fee-field' );
28371								element = allElements.first();
28372
28373								if ( ! thisElement.is( '.tcdisabled' ) && ! noevents && ! thisElement.data( 'addedfieldtcEpoBeforeOptionPriceCalculation' ) ) {
28374									jWindow.on( 'tcEpoBeforeOptionPriceCalculation.math' + thisElementId, function() {
28375										tm_element_epo_rules( epoObject, thisElement, undefined, undefined, undefined, true );
28376										thisElement.trigger( 'tm-math-select-change-html-all' );
28377									} );
28378									thisElement.data( 'addedfieldtcEpoBeforeOptionPriceCalculation', 1 );
28379								}
28380								val = 0;
28381								switch ( type ) {
28382									case 'text':
28383									case 'rawvalue':
28384										val = '';
28385										break;
28386								}
28387
28388								if ( elementWrap.is( '.tc-container-enabled' ) ) {
28389									if ( ! mathskip ) {
28390										allElements.toArray().forEach( function( singleElement ) {
28391											singleElement = $( singleElement );
28392											// For now we only recalculate dynamic elements
28393											if ( thisElement.attr( 'id' ) !== singleElement.attr( 'id' ) && ( thisElement.is( '.tmcp-dynamic' ) || singleElement.is( '.tmcp-dynamic' ) ) ) {
28394												tm_element_epo_rules( epoObject, singleElement, undefined, undefined, undefined, true, undefined, undefined, true );
28395											}
28396											if ( ! noevents && ! singleElement.data( 'addedmathevent' ) ) {
28397												singleElement.on( 'input.mathevent', function() {
28398													if ( ! thisElement.is( '.tcdisabled' ) ) {
28399														thisElement.trigger( 'change.cpf', { calculateMathPrice: 1 } );
28400													}
28401												} );
28402												singleElement.data( 'addedmathevent', 1 );
28403											}
28404										} );
28405									}
28406									if ( element.is( '.tmcp-field' ) ) {
28407										val = fetchOptionPrices( epoObject, elementWrap, '.tmcp-field', 0, 0, [], true, undefined, undefined, true, undefined, undefined, true );
28408									} else if ( element.is( '.tmcp-fee-field' ) ) {
28409										val = fetchOptionPrices( epoObject, elementWrap, '.tmcp-fee-field', 0, 0, [], true, undefined, undefined, true, undefined, undefined, true );
28410									} else if ( element.is( '.tmcp-sub-fee-field' ) ) {
28411										val = fetchOptionPrices( epoObject, elementWrap, '.tmcp-sub-fee-field', 0, 0, [], true, undefined, undefined, true, undefined, undefined, true );
28412									}
28413
28414									switch ( type ) {
28415										// element price
28416										case 'price':
28417											val = val.total;
28418											if ( thisElement.is( '.tmcp-dynamic' ) ) {
28419												__price = get_type( epoObject, element, 'price' );
28420												__price_type = get_type( epoObject, element, 'price_type' );
28421												if ( 'math' === __price_type && __price.includes( '{dynamic_product_price}' ) ) {
28422													val = 0;
28423												}
28424											}
28425											break;
28426
28427										// element value
28428										case 'value':
28429										case 'text':
28430										case 'rawvalue':
28431											if ( val.floatingBoxData ) {
28432												val = val.floatingBoxData
28433													.map( function( x ) {
28434														if ( x.input_type === 'number' || x.input_type === 'hidden' ) {
28435															if ( type === 'text' || type === 'rawvalue' ) {
28436																return x.valueText;
28437															}
28438															return $.epoAPI.math.unformat( x.valueText );
28439														}
28440														if ( type === 'text' || type === 'rawvalue' ) {
28441															return x.valueText;
28442														}
28443														return $.epoAPI.math.unformat( x.valueText, tcAPI.localIn
28443putDecimalSeparator );
28444													} )
28445													.reduce( function( acc, thisval ) {
28446														if ( type === 'text' || type === 'rawvalue' ) {
28447															return acc + thisval;
28448														}
28449														return $.epoAPI.math.toFloat( acc ) + $.epoAPI.math.toFloat( thisval );
28450													}, ( type === 'text' || type === 'rawvalue' ) ? '' : 0 );
28451
28452												if ( type === 'text' || type === 'rawvalue' ) {
28453													if ( val === '' ) {
28454														val = "''";
28455													} else if ( ! val.toString().isNumeric() ) {
28456														val = "'" + val.replaceAll( "'", "\\'" ) + "'";
28457													}
28458												}
28459											}
28460											break;
28461
28462										// element value length
28463										case 'value.length':
28464										case 'text.length':
28465											if ( val.floatingBoxData ) {
28466												val = val.floatingBoxData
28467													.map( function( x ) {
28468														return x.valueText;
28469													} )
28470													.reduce( function( acc, thisval ) {
28471														return $.epoAPI.math.toFloat( acc ) + thisval.length;
28472													}, 0 );
28473											}
28474											break;
28475
28476										// element quantity
28477										// the total option quantity of this element
28478										case 'quantity':
28479										case 'count.quantity':
28480											if ( val.floatingBoxData ) {
28481												val = val.floatingBoxData
28482													.map( function( x ) {
28483														return x.quantity;
28484													} )
28485													.reduce( function( acc, thisval ) {
28486														return $.epoAPI.math.toFloat( acc ) + $.epoAPI.math.toFloat( thisval );
28487													}, 0 );
28488											}
28489											break;
28490
28491										// number of element options the user has selected
28492										case 'count':
28493											if ( val.floatingBoxData ) {
28494												val = val.floatingBoxData.length;
28495											}
28496											break;
28497									}
28498									if ( type !== 'text' && type !== 'rawvalue' ) {
28499										val = $.epoAPI.math.toFloat( val );
28500										if ( ! Number.isFinite( val ) ) {
28501											val = 0;
28502										}
28503									}
28504								}
28505							} else {
28506								val = 0;
28507								switch ( type ) {
28508									case 'text':
28509									case 'rawvalue':
28510										val = '';
28511										break;
28512								}
28513							}
28514							reg = new RegExp( match[ 0 ] );
28515							if ( type === 'text' || type === 'rawvalue' ) {
28516								formula = val === '' ? formula.replace( reg, "''" ) : formula.replace( reg, val );
28517							} else {
28518								formula = ! Number.isFinite( val ) ? formula.replace( reg, "'" + val.replace( "'", "\\'" ) + "'" ) : formula.replace( reg, val );
28519							}
28520						}
28521					}
28522				}
28523			} );
28524		}
28525
28526		try {
28527			elementPrice = tcmexp.evaluate( formula );
28528		} catch ( e ) {
28529			elementPrice = 0;
28530		}
28531
28532		return elementPrice;
28533	}
28534
28535	function get_type( epoObject, obj, type ) {
28536		var element = $( obj );
28537		var setter = element;
28538		var cart;
28539		var current_variation;
28540		var rules;
28541		var rulestype;
28542		var _rulestype;
28543		var pricetype;
28544		var variation_id_selector;
28545		var _tmcpulwrap;
28546		var price;
28547		var _rules;
28548
28549		cart = epoObject.main_cart;
28550		variation_id_selector = "input[name^='variation_id']";
28551		if ( cart.find( 'input.variation_id' ).length > 0 ) {
28552			variation_id_selector = 'input.variation_id';
28553		}
28554		current_variation = cart.find( variation_id_selector ).val();
28555		// Get current woocommerce variation
28556		if ( ! current_variation ) {
28557			current_variation = 0;
28558		}
28559
28560		if ( element.is( 'select' ) ) {
28561			setter = element.find( 'option:selected' );
28562		}
28563
28564		rules = $.epoAPI.util.parseJSON( setter.attr( 'data-rules' ) );
28565		rulestype = $.epoAPI.util.parseJSON( setter.attr( 'data-rulestype' ) );
28566
28567		pricetype = '';
28568		if ( typeof rules === 'object' ) {
28569			if ( current_variation in rules ) {
28570				price = rules[ current_variation ];
28571			} else {
28572				_rules = $.epoAPI.util.parseJSON( element.closest( '.tmcp-ul-wrap' ).attr( 'data-rules' ) );
28573
28574				if ( typeof _rules === 'object' ) {
28575					if ( current_variation in _rules ) {
28576						price = _rules[ current_variation ];
28577					} else {
28578						price = rules[ 0 ];
28579					}
28580				} else {
28581					price = rules[ 0 ];
28582				}
28583			}
28584			if ( typeof rulestype === 'object' ) {
28585				if ( current_variation in rulestype ) {
28586					pricetype = rulestype[ current_variation ];
28587				} else {
28588					_rulestype = $.epoAPI.util.parseJSON( element.closest( '.tmcp-ul-wrap' ).attr( 'data-rulestype' ) );
28589					if ( typeof _rulestype === 'object' ) {
28590						if ( current_variation in _rulestype ) {
28591							pricetype = _rulestype[ current_variation ];
28592						} else {
28593							pricetype = rulestype[ 0 ];
28594						}
28595					} else {
28596						pricetype = rulestype[ 0 ];
28597					}
28598				}
28599			} else {
28600				rulestype = $.epoAPI.util.parseJSON( element.closest( '.tmcp-ul-wrap' ).attr( 'data-ulestype' ) );
28601				if ( typeof rulestype === 'object' ) {
28602					if ( current_variation in rulestype ) {
28603						pricetype = rulestype[ current_variation ];
28604					} else {
28605						pricetype = rulestype[ 0 ];
28606					}
28607				}
28608			}
28609		} else {
28610			_tmcpulwrap = element.closest( '.tmcp-ul-wrap' );
28611			rules = $.epoAPI.util.parseJSON( _tmcpulwrap.attr( 'data-rules' ) );
28612
28613			if ( typeof rules === 'object' ) {
28614				if ( current_variation in rules ) {
28615					price = rules[ current_variation ];
28616				} else {
28617					price = rules[ 0 ];
28618				}
28619				if ( typeof rulestype === 'object' ) {
28620					if ( current_variation in rulestype ) {
28621						pricetype = rulestype[ current_variation ];
28622					} else {
28623						_rulestype = $.epoAPI.util.parseJSON( _tmcpulwrap.attr( 'data-rulestype' ) );
28624						if ( typeof _rulestype === 'object' ) {
28625							if ( current_variation in _rulestype ) {
28626								pricetype = _rulestype[ current_variation ];
28627							} else {
28628								pricetype = rulestype[ 0 ];
28629							}
28630						} else {
28631							pricetype = rulestype[ 0 ];
28632						}
28633					}
28634				} else {
28635					rulestype = $.epoAPI.util.parseJSON( _tmcpulwrap.attr( 'data-rulestype' ) );
28636					if ( typeof rulestype === 'object' ) {
28637						if ( current_variation in rulestype ) {
28638							pricetype = rulestype[ current_variation ];
28639						} else {
28640							pricetype = rulestype[ 0 ];
28641						}
28642					}
28643				}
28644			}
28645		}
28646
28647		if ( typeof price === 'object' ) {
28648			price = price[ 0 ];
28649		}
28650
28651		if ( typeof pricetype === 'object' ) {
28652			pricetype = pricetype[ 0 ];
28653		}
28654		if ( element.is( '.tmcp-fee-field' ) ) {
28655			if ( $.inArray( pricetype, [ 'fee', 'stepfee', 'currentstepfee' ] ) !== -1 ) {
28656				pricetype = '';
28657			}
28658		}
28659
28660		if ( type === 'price' ) {
28661			return price;
28662		}
28663
28664		return pricetype;
28665	}
28666
28667	function wordLength( str ) {
28668		var regex = new RegExp( /[\p{L}\d!@#$%^&*()_+=\{[\}\]|\\"':;?/>.<,-]+/, 'gu' );
28669		var m;
28670		var len = 0;
28671
28672		while ( ( m = regex.exec( str ) ) !== null ) {
28673			// This is necessary to avoid infinite loops with zero-width matches
28674			if ( m.index === regex.lastIndex ) {
28675				regex.lastIndex++;
28676			}
28677			len = len + m.length;
28678		}
28679		return len;
28680	}
28681
28682	// Set field price rules
28683	function tm_element_epo_rules( epoObject, obj, args, setter_override, noremove, manthnoevent, funcTotal, cumulativeTotal, mathskip ) {
28684		var element = obj instanceof $ ? obj : $( obj );
28685		var setterall = element;
28686		var cart;
28687		var current_variation;
28688		var bundleid;
28689		var epoTotalsContainer;
28690		var apply_dpd;
28691		var product_price;
28692		var product_original_price;
28693		var is_range_field = element.is( '.tmcp-range' );
28694		var rules;
28695		var rulestype;
28696		var original_rules;
28697		var _rules;
28698		var _rulestype;
28699		var _original_rules;
28700		var pricetype;
28701		var price;
28702		var original_price;
28703		var raw_price;
28704		var raw_original_price;
28705		var formatted_price = '';
28706		var textlength;
28707		var freechars;
28708		var min_value;
28709		var main_product = epoObject.main_product;
28710		var epoEventId = epoObject.epoEventId;
28711		var variation_id_selector;
28712		var _tmcpulwrap;
28713		var cart_total;
28714		var per_product_pricing = true;
28715		var addedPrice = 0;
28716		var originalAddedPrice = 0;
28717		var undiscounted_raw_price;
28718		var undiscounted_raw_original_price;
28719		var undiscounted_price;
28720		var undiscounted_original_price;
28721		var isFilled;
28722		var useFormattedPrice = false;
28723
28724		if ( element.data( 'associated_price_set' ) ) {
28725			return;
28726		}
28727
28728		if ( ! args ) {
28729			cart = epoObject.main_cart;
28730			if ( cart.data( 'per_product_pricing' ) !== undefined ) {
28731				per_product_pricing = cart.data( 'per_product_pricing' );
28732			}
28733			variation_id_selector = "input[name^='variation_id']";
28734			if ( cart.find( 'input.variation_id' ).length > 0 ) {
28735				variation_id_selector = 'input.variation_id';
28736			}
28737			current_variation = cart.find( variation_id_selector ).val();
28738
28739			bundleid = $.epoAPI.applyFilter( 'tc_get_bundleid', cart.attr( 'data-product_id' ), cart, epoObject );
28740
28741			// Get current woocommerce variation
28742			if ( ! current_variation ) {
28743				current_variation = 0;
28744			}
28745
28746			epoTotalsContainer = $.epoAPI.applyFilter( 'tc_get_totals_container', epoObject.this_epo_totals_container, element, main_product, bundleid );
28747			product_price = tm_calculate_product_price( epoTotalsContainer );
28748			product_original_price = tm_calculate_product_regular_price( epoTotalsContainer );
28749			apply_dpd = epoTotalsContainer.data( 'fields-price-rules' );
28750		} else {
28751			cart = args.cart;
28752			current_variation = args.current_variation;
28753
28754			bundleid = args.bundleid;
28755			epoTotalsContainer = args.epoTotalsContainer;
28756			product_price = args.product_price;
28757			product_original_price = args.product_original_price;
28758			apply_dpd = args.apply_dpd;
28759			per_product_pricing = args.per_product_pricing;
28760		}
28761		product_price = $.epoAPI.applyFilter( 'tc_alter_product_price', product_price, element, cart, epoTotalsContainer, bundleid );
28762		product_original_price = $.epoAPI.applyFilter( 'tc_alter_product_original_price', product_original_price, element, cart, epoTotalsContainer );
28763
28764		if ( product_price === false || ! per_product_pricing ) {
28765			return;
28766		}
28767
28768		if ( element.is( 'select' ) ) {
28769			setterall = element.find( 'option:selected' );
28770		}
28771		if ( setter_override ) {
28772			setterall = setter_override;
28773		}
28774
28775		setterall.toArray().forEach( function( setter ) {
28776			var mathopc = false;
28777			setter = $( setter );
28778
28779			rules = $.epoAPI.util.parseJSON( setter.attr( 'data-rules' ) );
28780			rulestype = $.epoAPI.util.parseJSON( setter.attr( 'data-rulestype' ) );
28781			original_rules = $.epoAPI.util.parseJSON( setter.attr( 'data-original-rules' ) );
28782			if ( element.is( '.tmcp-dynamic' ) ) {
28783				rules = $.epoAPI.util.parseJSON( setter.attr( 'data-formula' ) );
28784				original_rules = $.epoAPI.util.parseJSON( setter.attr( 'data-formula' ) );
28785				rulestype = $.epoAPI.util.parseJSON( setter.attr( 'data-calculation-type' ) );
28786			}
28787			if ( original_rules === undefined ) {
28788				original_rules = rules;
28789			}
28790
28791			pricetype = '';
28792			if ( typeof rules === 'object' ) {
28793				if ( current_variation in rules ) {
28794					price = rules[ current_variation ];
28795					original_price = original_rules[ current_variation ];
28796				} else {
28797					_rules = $.epoAPI.util.parseJSON( element.closest( '.tmcp-ul-wrap' ).attr( 'data-rules' ) );
28798					_original_rules = element.closest( '.tmcp-ul-wrap' ).data( 'original-rules' );
28799
28800					if ( typeof _rules === 'object' ) {
28801						if ( current_variation in _rules ) {
28802							price = _rules[ current_variation ];
28803						} else {
28804							price = rules[ 0 ];
28805						}
28806					} else {
28807						price = rules[ 0 ];
28808					}
28809
28810					if ( typeof _original_rules === 'object' ) {
28811						if ( current_variation in _original_rules ) {
28812							original_price = _original_rules[ current_variation ];
28813						} else {
28814							original_price = original_rules[ 0 ];
28815						}
28816					} else {
28817						original_price = original_rules[ 0 ];
28818					}
28819				}
28820
28821				if ( typeof rulestype === 'object' ) {
28822					if ( current_variation in rulestype ) {
28823						pricetype = rulestype[ current_variation ];
28824					} else {
28825						_rulestype = $.epoAPI.util.parseJSON( element.closest( '.tmcp-ul-wrap' ).attr( 'data-rulestype' ) );
28826						if ( typeof _rulestype === 'object' ) {
28827							if ( current_variation in _rulestype ) {
28828								pricetype = _rulestype[ current_variation ];
28829							} else {
28830								pricetype = rulestype[ 0 ];
28831							}
28832						} else {
28833							pricetype = rulestype[ 0 ];
28834						}
28835					}
28836				} else {
28837					rulestype = $.epoAPI.util.parseJSON( element.closest( '.tmcp-ul-wrap' ).attr( 'data-ulestype' ) );
28838					if ( typeof rulestype === 'object' ) {
28839						if ( current_variation in rulestype ) {
28840							pricetype = rulestype[ current_variation ];
28841						} else {
28842							pricetype = rulestype[ 0 ];
28843						}
28844					}
28845				}
28846			} else {
28847				_tmcpulwrap = element.closest( '.tmcp-ul-wrap' );
28848				rules = $.epoAPI.util.parseJSON( _tmcpulwrap.attr( 'data-rules' ) );
28849				original_rules = $.epoAPI.util.parseJSON( _tmcpulwrap.attr( 'data-original-rules' ) );
28850
28851				if ( typeof rules === 'object' ) {
28852					if ( current_variation in rules ) {
28853						price = rules[ current_variation ];
28854					} else {
28855						price = rules[ 0 ];
28856					}
28857					if ( typeof original_rules === 'object' ) {
28858						if ( current_variation in original_rules ) {
28859							original_price = original_rules[ current_variation ];
28860						} else {
28861							original_price = original_rules[ 0 ];
28862						}
28863					} else {
28864						original_price = price;
28865					}
28866
28867					if ( typeof rulestype === 'object' ) {
28868						if ( current_variation in rulestype ) {
28869							pricetype = rulestype[ current_variation ];
28870						} else {
28871							_rulestype = $.epoAPI.util.parseJSON( _tmcpulwrap.attr( 'data-rulestype' ) );
28872							if ( typeof _rulestype === 'object' ) {
28873								if ( current_variation in _rulestype ) {
28874									pricetype = _rulestype[ current_variation ];
28875								} else {
28876									pricetype = rulestype[ 0 ];
28877								}
28878							} else {
28879								pricetype = rulestype[ 0 ];
28880							}
28881						}
28882					} else {
28883						rulestype = $.epoAPI.util.parseJSON( _tmcpulwrap.attr( 'data-rulestype' ) );
28884						if ( typeof rulestype === 'object' ) {
28885							if ( current_variation in rulestype ) {
28886								pricetype = rulestype[ current_variation ];
28887							} else {
28888								pricetype = rulestype[ 0 ];
28889							}
28890						}
28891					}
28892				}
28893			}
28894
28895			if ( typeof pricetype === 'object' ) {
28896				pricetype = pricetype[ 0 ];
28897			}
28898			if ( element.is( '.tmcp-fee-field' ) ) {
28899				if ( $.inArray( pricetype, [ 'fee', 'stepfee', 'currentstepfee' ] ) !== -1 ) {
28900					pricetype = '';
28901				}
28902				apply_dpd = 0;
28903			}
28904
28905			if ( noremove === undefined ) {
28906				if ( element.is( 'select' ) ) {
28907					element.find( 'option' ).removeClass( 'tm-epo-late-field' ).removeData( 'tm-price-for-late islate' );
28908				} else {
28909					setter.removeClass( 'tm-epo-late-field' ).removeData( 'tm-price-for-late islate' );
28910				}
28911			}
28912			if ( pricetype !== 'math' ) {
28913				price = cleanPrice( price );
28914				original_price = cleanPrice( original_price );
28915			} else if ( typeof price === 'object' ) {
28916				price = price[ 0 ];
28917			}
28918
28919			undiscounted_raw_price = price;
28920			undiscounted_raw_original_price = original_price;
28921
28922			switch ( pricetype ) {
28923				case '':
28924					undiscounted_price = price;
28925					price = tm_apply_dpd( price, epoTotalsContainer, apply_dpd );
28926					// original_price No change
28927					break;
28928				case 'fixednon':
28929					freechars = parseInt( setter.attr( 'data-freechars' ), 10 );
28930					if ( ! Number.isFinite( freechars ) ) {
28931						freechars = 0;
28932					}
28933					textlength = setter.val().length - freechars;
28934					if ( textlength <= 0 ) {
28935						price = 0;
28936					}
28937					undiscounted_price = price;
28938					price = tm_apply_dpd( price, epoTotalsContainer, apply_dpd );
28939					break;
28940				case 'math':
28941					if ( price !== original_price ) {
28942						mathopc = true;
28943					} else {
28944						mathopc = false;
28945					}
28946					if ( mathopc ) {
28947						original_price = calculateMathPrice( original_price, element, epoObject, true, true, funcTotal, cumulativeTotal, mathskip );
28948					}
28949					price = calculateMathPrice( price, element, epoObject, manthnoevent, $.epoAPI.applyFilter( 'tc_use_undiscounted_price', undefined, element, cart, epoTotalsContainer ), funcTotal, cumulativeTotal, mathskip );
28950					if ( ! mathopc ) {
28951						original_price = price;
28952					}
28953					undiscounted_price = price;
28954					price = tm_apply_dpd( price, epoTotalsContainer, apply_dpd );
28955					// original_price No other change
28956					break;
28957				case 'percent_cart_total':
28958					cart_total = parseFloat( TMEPOJS.cart_total );
28959
28960					if ( ! Number.isFinite( cart_total ) ) {
28961						cart_total = 0;
28962					}
28963					undiscounted_price = ( price / 100 ) * cart_total;
28964					price = ( tm_apply_dpd( price, epoTotalsContainer, apply_dpd ) / 100 ) * cart_total;
28965					original_price = ( tm_apply_dpd( original_price, epoTotalsContainer, apply_dpd ) / 100 ) * cart_total;
28966					break;
28967
28968				case 'percent':
28969					undiscounted_price = ( price / 100 ) * product_price;
28970					price = ( tm_apply_dpd( price, epoTotalsContainer, apply_dpd ) / 100 ) * product_price;
28971					original_price = ( original_price / 100 ) * product_original_price;
28972					break;
28973				case 'percentcurrenttotal':
28974					$.tcepo.lateFieldsPrices[ epoEventId ].push( {
28975						setter: setter,
28976						price: price,
28977						original_price: original_price,
28978						bundleid: bundleid,
28979						pricetype: pricetype
28980					} );
28981					setter.data( 'tm-price-for-late', price ).data( 'tm-original-price-for-late', original_price ).data( 'islate', 1 ).addClass( 'tm-epo-late-field' );
28982					undiscounted_price = 0;
28983					price = 0;
28984					original_price = 0;
28985					break;
28986				case 'fixedcurrenttotal':
28987					price = tm_apply_dpd( price, epoTotalsContainer, apply_dpd );
28988					$.tcepo.lateFieldsPrices[ epoEventId ].push( {
28989						setter: setter,
28990						price: price,
28991						original_price: original_price,
28992						bundleid: bundleid,
28993						pricetype: pricetype
28994					} );
28995					setter.data( 'tm-price-for-late', price ).data( 'tm-original-price-for-late', original_price ).data( 'islate', 1 ).addClass( 'tm-epo-late-field' );
28996					undiscounted_price = 0;
28997					price = 0;
28998					original_price = 0;
28999					break;
29000				case 'word':
29001					undiscounted_price = price * wordLength( setter.val() );
29002					price = tm_apply_dpd( price, epoTotalsContainer, apply_dpd ) * wordLength( setter.val() );
29003					original_price = original_price * wordLength( setter.val() );
29004					break;
29005				case 'wordpercent':
29006					undiscounted_price = ( price / 100 ) * product_price * wordLength( setter.val() );
29007					price = ( tm_apply_dpd( price, epoTotalsContainer, apply_dpd ) / 100 ) * product_price * wordLength( setter.val() );
29008					original_price = ( original_price / 100 ) * product_original_price * wordLength( setter.val() );
29009					break;
29010				case 'wordnon':
29011					freechars = parseInt( setter.attr( 'data-freechars' ), 10 );
29012					if ( ! Number.isFinite( freechars ) ) {
29013						freechars = 0;
29014					}
29015					textlength = wordLength( setter.val() ) - freechars;
29016					if ( textlength < 0 ) {
29017						textlength = 0;
29018					}
29019					undiscounted_price = price * textlength;
29020					price = tm_apply_dpd( price, epoTotalsContainer, apply_dpd ) * textlength;
29021					original_price = original_price * textlength;
29022					break;
29023				case 'wordpercentnon':
29024					freechars = parseInt( setter.attr( 'data-freechars' ), 10 );
29025					if ( ! Number.isFinite( freechars ) ) {
29026						freechars = 0;
29027					}
29028					textlength = wordLength( setter.val() ) - freechars;
29029					if ( textlength < 0 ) {
29030						textlength = 0;
29031					}
29032					undiscounted_price = ( price / 100 ) * product_price * textlength;
29033					price = ( tm_apply_dpd( price, epoTotalsContainer, apply_dpd ) / 100 ) * product_price * textlength;
29034					original_price = ( original_price / 100 ) * product_original_price * textlength;
29035					break;
29036
29037				case 'char':
29038					undiscounted_price = price * setter.val().length;
29039					price = tm_apply_dpd( price, epoTotalsContainer, apply_dpd ) * setter.val().length;
29040					original_price = original_price * setter.val().length;
29041					break;
29042				case 'charpercent':
29043					undiscounted_price = ( price / 100 ) * product_price * setter.val().length;
29044					price = ( tm_apply_dpd( price, epoTotalsContainer, apply_dpd ) / 100 ) * product_price * setter.val().length;
29045					original_price = ( original_price / 100 ) * product_original_price * setter.val().length;
29046					break;
29047				case 'charnospaces':
29048					undiscounted_price = price * setter.val().replace( /\s/g, '' ).length;
29049					price = tm_apply_dpd( price, epoTotalsContainer, apply_dpd ) * setter.val().replace( /\s/g, '' ).length;
29050					original_price = original_price * setter.val().replace( /\s/g, '' ).length;
29051					break;
29052				case 'charnofirst':
29053					textlength = setter.val().length - 1;
29054					if ( textlength < 0 ) {
29055						textlength = 0;
29056					}
29057					undiscounted_price = price * textlength;
29058					price = tm_apply_dpd( price, epoTotalsContainer, apply_dpd ) * textlength;
29059					original_price = original_price * textlength;
29060					break;
29061
29062				case 'charnon':
29063					freechars = parseInt( setter.attr( 'data-freechars' ), 10 );
29064					if ( ! Number.isFinite( freechars ) ) {
29065						freechars = 0;
29066					}
29067					textlength = setter.val().length - freechars;
29068					if ( textlength < 0 ) {
29069						textlength = 0;
29070					}
29071					undiscounted_price = price * textlength;
29072					price = tm_apply_dpd( price, epoTotalsContainer, apply_dpd ) * textlength;
29073					original_price = original_price * textlength;
29074					break;
29075				case 'charpercentnon':
29076					freechars = parseInt( setter.attr( 'data-freechars' ), 10 );
29077					if ( ! Number.isFinite( freechars ) ) {
29078						freechars = 0;
29079					}
29080					textlength = setter.val().length - freechars;
29081					if ( textlength < 0 ) {
29082						textlength = 0;
29083					}
29084					undiscounted_price = ( price / 100 ) * product_price * textlength;
29085					price = ( tm_apply_dpd( price, epoTotalsContainer, apply_dpd ) / 100 ) * product_price * textlength;
29086					original_price = ( original_price / 100 ) * product_original_price * textlength;
29087					break;
29088				case 'charnonnospaces':
29089					freechars = parseInt( setter.attr( 'data-freechars' ), 10 );
29090					if ( ! Number.isFinite( freechars ) ) {
29091						freechars = 0;
29092					}
29093					textlength = setter.val().replace( /\s/g, '' ).length - freechars;
29094					if ( textlength < 0 ) {
29095						textlength = 0;
29096					}
29097					undiscounted_price = price * textlength;
29098					price = tm_apply_dpd( price, epoTotalsContainer, apply_dpd ) * textlength;
29099					original_price = original_price * textlength;
29100					break;
29101				case 'charpercentnonnospaces':
29102					freechars = parseInt( setter.attr( 'data-freechars' ), 10 );
29103					if ( ! Number.isFinite( freechars ) ) {
29104						freechars = 0;
29105					}
29106					textlength = setter.val().replace( /\s/g, '' ).length - freechars;
29107					if ( textlength < 0 ) {
29108						textlength = 0;
29109					}
29110					undiscounted_price = ( price / 100 ) * product_price * textlength;
29111					price = ( tm_apply_dpd( price, epoTotalsContainer, apply_dpd ) / 100 ) * product_price * textlength;
29112					original_price = ( original_price / 100 ) * product_original_price * textlength;
29113					break;
29114
29115				case 'charpercentnofirst':
29116					textlength = setter.val().length - 1;
29117					if ( textlength < 0 ) {
29118						textlength = 0;
29119					}
29120					undiscounted_price = ( price / 100 ) * product_price * textlength;
29121					price = ( tm_apply_dpd( price, epoTotalsContainer, apply_dpd ) / 100 ) * product_price * textlength;
29122					original_price = ( original_price / 100 ) * product_original_price * textlength;
29123					break;
29124				case 'step':
29125					if ( is_range_field ) {
29126						undiscounted_price = price * setter.val();
29127						price = tm_apply_dpd( price, epoTotalsContainer, apply_dpd ) * setter.val();
29128						original_price = original_price * $.epoAPI.math.toFloat( setter.val() );
29129					} else {
29130						undiscounted_price = price * $.epoAPI.math.toFloat( setter.val() );
29131						price = tm_apply_dpd( price, epoTotalsContainer, apply_dpd ) * $.epoAPI.math.toFloat( setter.val() );
29132						original_price = original_price * $.epoAPI.math.toFloat( setter.val() );
29133					}
29134					break;
29135				case 'currentstep':
29136					if ( is_range_field ) {
29137						undiscounted_price = setter.val();
29138						price = tm_apply_dpd( setter.val(), epoTotalsContainer, apply_dpd );
29139						original_price = tm_apply_dpd( setter.val(), epoTotalsContainer, apply_dpd );
29140					} else {
29141						undiscounted_price = $.epoAPI.math.toFloat( setter.val() );
29142						price = tm_apply_dpd( $.epoAPI.math.toFloat( setter.val() ), epoTotalsContainer, apply_dpd );
29143						original_price = tm_apply_dpd( $.epoAPI.math.toFloat( setter.val() ), epoTotalsContainer, apply_dpd );
29144					}
29145					break;
29146				case 'intervalstep':
29147					if ( is_range_field ) {
29148						min_value = parseFloat( $( '.tm-range-picker[data-field-id="' + setter.attr( 'id' ) + '"]' ).attr( 'data-min' ) );
29149						undiscounted_price = price * ( setter.val() - min_value );
29150						price = tm_apply_dpd( price, epoTotalsContainer, apply_dpd ) * ( setter.val() - min_value );
29151						original_price = original_price * ( setter.val() - min_value );
29152					}
29153					break;
29154				case 'row':
29155					undiscounted_price = price * ( ( setter.val().match( /(\r\n|\n|\r)/gm ) || [] ).length + 1 );
29156					price = tm_apply_dpd( price, epoTotalsContainer, apply_dpd ) * ( ( setter.val().match( /(\r\n|\n|\r)/gm ) || [] ).length + 1 );
29157					original_price = original_price * ( ( setter.val().match( /(\r\n|\n|\r)/gm ) || [] ).length + 1 );
29158					break;
29159			}
29160
29161			raw_price = price;
29162			raw_original_price = original_price;
29163
29164			undiscounted_original_price = original_price;
29165			undiscounted_raw_price = undiscounted_price;
29166			undiscounted_raw_original_price = undiscounted_original_price;
29167
29168			if ( element.data( 'tm-quantity' ) ) {
29169				undiscounted_price = undiscounted_price * parseFloat( element.data( 'tm-quantity' ) );
29170				undiscounted_original_price = undiscounted_original_price * parseFloat( element.data( 'tm-quantity' ) );
29171				undiscounted_raw_price = undiscounted_raw_price * parseFloat( element.data( 'tm-quantity' ) );
29172				undiscounted_raw_original_price = undiscounted_raw_original_price * parseFloat( element.data( 'tm-quantity' ) );
29173
29174				price = price * parseFloat( element.data( 'tm-quantity' ) );
29175				original_price = original_price * parseFloat( element.data( 'tm-quantity' ) );
29176				raw_price = raw_price * parseFloat( element.data( 'tm-quantity' ) );
29177				raw_original_price = raw_original_price * parseFloat( element.data( 'tm-quantity' ) );
29178			}
29179
29180			addedPrice = addedPrice + parseFloat( price );
29181			originalAddedPrice = originalAddedPrice + parseFloat( original_price );
29182
29183			element.data( 'price_set', 1 );
29184			setter.data( 'price_set', 1 );
29185
29186			setter.data( 'undiscounted_raw_price', undiscounted_raw_price );
29187			setter.data( 'undiscounted_raw_original_price', undiscounted_raw_original_price );
29188			setter.data( 'undiscounted_price', tm_set_tax_price( undiscounted_price, epoTotalsContainer, setter, pricetype ) );
29189			setter.data( 'undiscounted_original_price', tm_set_tax_price( undiscounted_original_price, epoTotalsContainer, setter, pricetype ) );
29190
29191			setter.data( 'raw_price', raw_price );
29192			setter.data( 'raw_original_price', raw_original_price );
29193			setter.data( 'price', tm_set_tax_price( price, epoTotalsContainer, setter, pricetype ) );
29194			setter.data( 'original_price', tm_set_tax_price( original_price, epoTotalsContainer, setter, pricetype ) );
29195
29196			if ( ! setter_override ) {
29197				if ( element.is( '.tc-epo-field-product' ) ) {
29198					isFilled = false;
29199					if ( element.is( 'select' ) ) {
29200						if ( element.val() !== '' ) {
29201							isFilled = true;
29202						}
29203					} else if ( ( element.is( ':checkbox' ) || element.is( ':radio' ) ) ) {
29204						if ( element.is( ':checked' ) ) {
29205							isFilled = true;
29206						}
29207					} else if ( element.val() !== '' ) {
29208						isFilled = true;
29209					}
29210
29211					if ( ! isFilled ) {
29212						formatted_price = setter.data( 'price-html' );
29213						useFormattedPrice = true;
29214					}
29215				}
29216				tm_update_price( {
29217					epoObject: epoObject,
29218					obj: setter.closest( '.tmcp-field-wrap' ).find( '.tc-price' ),
29219					price: tm_get_price( addedPrice, epoTotalsContainer, false, setter, pricetype ),
29220					formatted_price: formatted_price,
29221					original_price: tm_get_price( originalAddedPrice, epoTotalsContainer, false, setter, pricetype ),
29222					force: false,
29223					useFormattedPrice: useFormattedPrice
29224				} );
29225				element.data( 'price-changed', 1 );
29226			}
29227		} );
29228	}
29229
29230	function tm_epo_rules( epoObject, theCart ) {
29231		var all_carts;
29232		var variation_id_selector;
29233		var per_product_pricing = true;
29234		var current_variation;
29235		var bundleid;
29236		var epoContainer;
29237		var epoTotalsContainer;
29238		var apply_dpd;
29239		var rules;
29240		var original_rules;
29241		var price;
29242		var original_price;
29243		var product_price;
29244		var product_original_price;
29245		var all_fields;
29246		var active_fields;
29247		var args;
29248		var main_product = epoObject.main_product;
29249		var epoEventId = epoObject.epoEventId;
29250		var this_epo_container = epoObject.this_epo_container;
29251		var this_epo_totals_container = epoObject.this_epo_totals_container;
29252
29253		if ( ! theCart ) {
29254			all_carts = main_product.find( '.cart' );
29255		} else {
29256			all_carts = theCart;
29257		}
29258		if ( all_carts.length <= 0 ) {
29259			return;
29260		}
29261
29262		$.tcepo.lateFieldsPrices[ epoEventId ] = [];
29263
29264		all_carts.toArray().forEach( function( cart ) {
29265			cart = $( cart );
29266			variation_id_selector = "input[name^='variation_id']";
29267			if ( cart.find( 'input.variation_id' ).length > 0 ) {
29268				variation_id_selector = 'input.variation_id';
29269			}
29270
29271			if ( cart.data( 'per_product_pricing' ) !== undefined ) {
29272				per_product_pricing = cart.data( 'per_product_pricing' );
29273			}
29274
29275			current_variation = cart.find( variation_id_selector ).val();
29276			bundleid = $.epoAPI.applyFilter( 'tc_get_bundleid', cart.attr( 'data-product_id' ), cart, epoObject );
29277
29278			// get current woocommerce variation
29279			if ( ! current_variation ) {
29280				current_variation = 0;
29281			}
29282
29283			epoContainer = $.epoAPI.applyFilter( 'tc_get_epo_container', this_epo_container, cart, main_product, bundleid );
29284			epoTotalsContainer = $.epoAPI.applyFilter( 'tc_get_totals_container', this_epo_totals_container, cart, main_product, bundleid );
29285
29286			// WooCommerce Dynamic Pricing & Discounts
29287			apply_dpd = epoTotalsContainer.data( 'fields-price-rules' );
29288
29289			// set initial prices for all fields
29290			if ( ! epoContainer.data( 'tm_rules_init_done' ) ) {
29291				if ( epoTotalsContainer.data( 'force-quantity' ) ) {
29292					cart.find( tcAPI.qtySelector ).val( epoTotalsContainer.data( 'force-quantity' ) );
29293				}
29294				epoContainer.toArray().forEach( function( el ) {
29295					$( el ).closest( '.tmcp-field-wrap' ).find( '.tm-epo-field' ).data( 'tm-quantity', $( el ).val() );
29296				} );
29297
29298				epoContainer
29299					.find( '.tmcp-attributes, .tmcp-elements' )
29300					.toArray()
29301					.forEach( function( element ) {
29302						element = $( element );
29303						rules = $.epoAPI.util.parseJSON( element.attr( 'data-rules' ) );
29304						original_rules = $.epoAPI.util.parseJSON( element.attr( 'data-original-rules' ) );
29305
29306						// if rule doesn't exit then init an empty rule
29307						if ( typeof rules !== 'object' ) {
29308							rules = {
29309								0: '0'
29310							};
29311						}
29312						if ( typeof original_rules !== 'object' ) {
29313							original_rules = {
29314								0: '0'
29315							};
29316						}
29317						if ( typeof rules === 'object' ) {
29318							// we skip price validation test so that every field has at least a price of 0
29319							price = tm_apply_dpd( rules[ $.epoAPI.math.toFloat( current_variation ) ], epoTotalsContainer, apply_dpd );
29320							original_price = tm_apply_dpd( original_rules[ $.epoAPI.math.toFloat( current_variation ) ], epoTotalsContainer, apply_dpd );
29321
29322							element
29323								.find( '.tmcp-field, .tmcp-fee-field ' )
29324								.toArray()
29325								.forEach( function( el ) {
29326									el = $( el );
29327									if ( per_product_pricing ) {
29328										if ( el.attr( 'data-no-price' ) === '1' ) {
29329											price = 0;
29330											original_price = 0;
29331										}
29332										el.data( 'raw_price', price );
29333										el.data( 'raw_original_price', original_price );
29334
29335										el.data( 'price', tm_set_tax_price( price, epoTotalsContainer, el ) );
29336										el.data( 'original_price', tm_set_tax_price( original_price, epoTotalsContainer, el ) );
29337
29338										tm_update_price( {
29339											epoObject: epoObject,
29340											obj: el.closest( '.tmcp-field-wrap' ).find( '.tc-price' ),
29341											price: tm_get_price( price, epoTotalsContainer ),
29342											original_price: tm_get_price( original_price, epoTotalsContainer ),
29343											force: false,
29344											useFormattedPrice: false
29345										} );
29346									} else {
29347										el.data( 'price', 0 );
29348										el.data( 'original_price', 0 );
29349										el.closest( '.tmcp-field-wrap' ).find( '.amount' ).empty();
29350									}
29351								} );
29352						}
29353					} );
29354				epoContainer.data( 'tm_rules_init_done', 1 );
29355			}
29356
29357			// skip specific field rules if per_product_pricing is false
29358			if ( ! per_product_pricing ) {
29359				return true;
29360			}
29361
29362			product_price = tm_calculate_product_price( epoTotalsContainer );
29363			product_original_price = tm_calculate_product_regular_price( epoTotalsContainer );
29364
29365			args = {
29366				cart: cart,
29367				current_variation: current_variation,
29368				bundleid: bundleid,
29369				epoTotalsContainer: epoTotalsContainer,
29370				product_price: product_price,
29371				product_original_price: product_original_price,
29372				apply_dpd: apply_dpd,
29373				per_product_pricing: per_product_pricing
29374			};
29375
29376			all_fields = epoContainer.find( '.tmcp-field,.tmcp-sub-fee-field,.tmcp-fee-field' );
29377			if ( ! epoObject.is_associated ) {
29378				all_fields = all_fields.not( tcAPI.associatedEpoSelector + ' ' + '.tmcp-field,' + tcAPI.associatedEpoSelector + ' ' + '.tmcp-sub-fee-field,' + tcAPI.associatedEpoSelector + ' ' + '.tmcp-fee-field' );
29379			}
29380			active_fields = all_fields.filter( '.tcenabled' );
29381
29382			// note: find a better way if any
29383			if ( ! $.tcepo.initialActivation[ epoEventId ] || ( active_fields.length === 0 && all_fields.length > 0 ) ) {
29384				all_fields.toArray().forEach( function( el ) {
29385					field_is_active( $( el ) );
29386				} );
29387				epoContainer.find( '.cpf-type-divider,.cpf-type-header' ).toArray().forEach( function( el ) {
29388					field_is_active( $( el ) );
29389				} );
29390
29391				$.tcepo.initialActivation[ epoEventId ] = true;
29392			}
29393
29394			//  apply specific field rules
29395			all_fields.filter( '.tcenabled' ).toArray().forEach( function( element ) {
29396				tm_element_epo_rules( epoObject, element, args );
29397			} );
29398
29399			all_fields.toArray().forEach( function( element ) {
29400				$( element ).on( 'tc_element_epo_rules', function() {
29401					tm_element_epo_rules( epoObject, element, args );
29402				} );
29403			} );
29404		} );
29405	}
29406
29407	function add_late_fields_prices( epoObject, currentProductPrice, originalProductPrice, options_total, original_options_total, bid, _cart, applydpd ) {
29408		var total = 0;
29409		var originalTotal = 0;
29410		var totalFees = 0;
29411		var originalTotalFees = 0;
29412		var price;
29413		var originalPrice;
29414		var priceType;
29415		var setter;
29416		var id;
29417		var hidden;
29418		var bundleid;
29419		var realSetter;
29420		var productId;
29421		var epoId;
29422		var epoEventId = epoObject.epoEventId;
29423		var taxPrice;
29424		var taxOriginalPrice;
29425		var hiddenName;
29426		var productPrice;
29427		var oProductPrice;
29428		var apply_dpd;
29429
29430		if ( applydpd !== undefined ) {
29431			apply_dpd = applydpd;
29432		} else {
29433			apply_dpd = epoObject.this_epo_totals_container.data( 'fields-price-rules' );
29434		}
29435		productPrice = currentProductPrice;
29436		oProductPrice = originalProductPrice;
29437
29438		$.tcepo.lateFieldsPrices[ epoEventId ].forEach( function( field ) {
29439			price = field.price;
29440			originalPrice = field.original_price;
29441			priceType = field.pricetype;
29442			setter = field.setter;
29443			bundleid = field.bundleid;
29444			realSetter = setter;
29445
29446			if ( priceType === 'percentcurrenttotal' ) {
29447				hiddenName = '_hidden';
29448			} else {
29449				hiddenName = '_hiddenfixed';
29450			}
29451
29452			if ( setter.is( 'option' ) ) {
29453				realSetter = setter.closest( 'select' );
29454			}
29455
29456			productPrice = parseFloat( $.epoAPI.applyFilter( 'tc_alter_product_price', productPrice, realSetter, _cart, epoObject.this_epo_totals_container, bid ) );
29457			oProductPrice = parseFloat( $.epoAPI.applyFilter( 'tc_alter_original_product_price', oProductPrice, realSetter, _cart, epoObject.this_epo_totals_container, bid ) );
29458
29459			id = $.epoAPI.dom.id( realSetter.attr( 'name' ) );
29460			productId = $( '.tc-totals-form.tm-totals-form-' + _cart.attr( 'data-cart-id' ) ).attr( 'data-product-id' );
29461			epoId = $( '.tc-totals-form.tm-totals-form-' + _cart.attr( 'data-cart-id' ) ).attr( 'data-epo-id' );
29462			//workaround to support composite products
29463			hidden = $( tcAPI.epoSelector + '.tm-product-id-' + productId + "[data-epo-id='" + epoId + "']" ).find( '#' + id + hiddenName );
29464
29465			if ( bundleid === bid ) {
29466				if ( priceType === 'percentcurrenttotal' ) {
29467					price = ( parseFloat( price ) / 100 ) * ( tm_apply_dpd( parseFloat( productPrice ), epoObject.this_epo_totals_container, apply_dpd ) + parseFloat( options_total ) );
29468					originalPrice = ( parseFloat( originalPrice ) / 100 ) * ( parseFloat( oProductPrice ) + parseFloat( original_options_total ) );
29469				} else if ( priceType === 'fixedcurrenttotal' ) {
29470					price = parseFloat( price ) + parseFloat( options_total );
29471					originalPrice = parseFloat( originalPrice ) + parseFloat( original_options_total );
29472				}
29473				if ( realSetter.data( 'tm-quantity' ) ) {
29474					price = price * parseFloat( realSetter.data( 'tm-quantity' ) );
29475					originalPrice = originalPrice * parseFloat( realSetter.data( 'tm-quantity' ) );
29476				}
29477
29478				if ( setter.data( 'isset' ) === 1 && field_is_active( setter ) ) {
29479					if ( realSetter.is( '.tmcp-field' ) ) {
29480						total = total + price;
29481						originalTotal = originalTotal + originalPrice;
29482					} else if ( realSetter.is( '.tmcp-fee-field' ) ) {
29483						totalFees = totalFees + price;
29484						originalTotalFees = originalTotalFees + originalPrice;
29485					}
29486				}
29487
29488				taxPrice = tm_set_tax_price( price, _cart, setter );
29489				taxOriginalPrice = tm_set_tax_price( originalPrice, _cart, setter );
29490
29491				setter.data( 'price', taxPrice );
29492				setter.data( 'pricew', taxPrice );
29493				setter.data( 'original_price', taxOriginalPrice );
29494				setter.data( 'original_pricew', taxOriginalPrice );
29495
29496				tm_update_price( {
29497					epoObject: epoObject,
29498					obj: setter.closest( '.tmcp-field-wrap' ).find( '.tc-price' ),
29499					price: tm_get_price( taxPrice, _cart, false, setter ),
29500					original_price: tm_get_price( taxOriginalPrice, _cart, false, setter ),
29501					force: false,
29502					useFormattedPrice: false
29503				} );
29504
29505				if ( hidden.length === 0 ) {
29506					realSetter.before( '<input type="hidden" id="' + id + hiddenName + '" name="' + id + hiddenName + '" value="' + tm_set_price_without_tax( price, _cart ) + '">' );
29507				}
29508				if ( setter.is( '.tm-epo-field.tmcp-radio' ) || setter.is( '.tm-epo-field.tmcp-checkbox' ) ) {
29509					if ( setter.is( ':checked' ) ) {
29510						hidden.val( tm_set_price_without_tax( price, _cart ) );
29511					}
29512				} else {
29513					hidden.val( tm_set_price_without_tax( price, _cart ) );
29514				}
29515			} else if ( setter.data( 'pricew' ) !== undefined ) {
29516				// Prices are already taxed
29517				tm_update_price( {
29518					epoObject: epoObject,
29519					obj: setter.closest( '.tmcp-field-wrap' ).find( '.tc-price' ),
29520					price: setter.data( 'pricew' ),
29521					original_price: ( setter.data( 'original_pricew' ) !== undefined ? setter.data( 'original_pricew' ) : setter.data( 'pricew' ) ),
29522					force: false,
29523					useFormattedPrice: false
29524				} );
29525			}
29526		} );
29527
29528		return {
29529			normal: [ total, originalTotal ],
29530			fees: [ totalFees, originalTotalFees ]
29531		};
29532	}
29533
29534	function tm_lazyload() {
29535		var container;
29536
29537		if ( TMEPOJS.tm_epo_no_lazy_load === 'yes' ) {
29538			return;
29539		}
29540
29541		if ( tmLazyloadContainer ) {
29542			container = $( tmLazyloadContainer ).find( 'img.tmlazy' );
29543		} else {
29544			container = $( tcAPI.epoSelector + ' img.tmlazy, .tc-styled-variations img.tmlazy' );
29545		}
29546
29547		container.lazyLoadXT();
29548		container.on( 'lazyshow', function() {
29549			jWindow.trigger( 'lazyLoadXToncomplete' );
29550		} );
29551	}
29552
29553	function tm_css_styles( obj ) {
29554		var $cart;
29555		var sliderSection;
29556
29557		if ( ! obj ) {
29558			return;
29559		}
29560
29561		$cart = $( '.cart' );
29562
29563		sliderSection = obj.find( '.tm-owl-slider-section' );
29564		if ( ! sliderSection.length ) {
29565			sliderSection = obj;
29566			if ( ! sliderSection.is( '.tc-repeater-section' ) ) {
29567				sliderSection = false;
29568			}
29569		}
29570
29571		if ( sliderSection ) {
29572			sliderSection.toArray().forEach( function( slider ) {
29573				slider = $( slider );
29574
29575				slider.find( '.tc-slider-content' ).addClass( 'tm-owl-slider' );
29576
29577				slider = slider.find( '.tm-owl-slider' );
29578				if ( slider.length ) {
29579					slider
29580						.addClass( 'tcowl-carousel-show' )
29581						.addClass( 'tcowl-carousel' )
29582						.on( 'changed.owl.carousel', function() {
29583							$cart.css( 'display', 'block' ).addClass( 'slider-setup' );
29584						} )
29585						.tmowlCarousel( Object.assign( {
29586							rtl: TMEPOJS.isRTL === '1',
29587							dots: false,
29588							nav: true,
29589							items: 1,
29590							autoHeight: true,
29591							mouseDrag: false,
29592							touchDrag: true,
29593							navText: [ TMEPOJS.i18n_prev_text, TMEPOJS.i18n_next_text ],
29594							navClass: [ 'owl-prev button', 'owl-next button' ],
29595							navElement: 'a',
29596							loop: false,
29597							navRewind: false
29598						}, window.tmowlCarouselSettings || {} ) );
29599
29600					slider.removeClass( 'tcowl-carousel-show' );
29601				}
29602			} );
29603		}
29604
29605		if ( $.fn.tcTabs ) {
29606			obj.find( '.tc-tabs' ).tcTabs( {
29607				headers: 'tc-tab-headers',
29608				header: 'tab-header',
29609				dataopenattribute: 'data-tab',
29610				sortabletabs: false
29611			} );
29612		}
29613	}
29614
29615	function repeaterAdd( el, elementInnerWrap ) {
29616		var cpf = elementInnerWrap.closest( '.cpf-element' );
29617		var maxRows = cpf.data( 'repeater-max-rows' );
29618		var minRows = cpf.data( 'repeater-min-rows' ) || 1;
29619		var repeater = elementInnerWrap.find( '.tc-repeater-element' );
29620		var rows = repeater.length;
29621		var repeaterLast = repeater.last();
29622		var repeaterLastUL = repeaterLast.find( '.tmcp-ul-wrap' );
29623		var clone;
29624		var cloneUl;
29625		var index = el.data( 'tc-repeater-init' );
29626
29627		if ( maxRows ) {
29628			if ( rows >= maxRows ) {
29629				return false;
29630			}
29631		}
29632
29633		if ( rows + 1 >= maxRows && el.is( '.tc-repeater-add' ) ) {
29634			el.closest( '.tc-repeater-wrap' ).addClass( 'tc-hidden' );
29635		}
29636
29637		if ( repeaterLastUL.is( '.tm-element-ul-date, .tm-element-ul-time' ) ) {
29638			repeaterLastUL.find( '.tm-epo-field' ).tm_datepicker( 'destroy' );
29639			repeaterLastUL.find( '.tmcp-date-select' ).off();
29640		}
29641		if ( repeaterLastUL.is( '.tm-element-ul-color' ) ) {
29642			repeaterLastUL.find( '.tm-epo-field' ).spectrum( 'destroy' );
29643		}
29644
29645		clone = repeaterLast.tcClone( true );
29646
29647		clone.find( '.tm-epo-field' ).attr( 'id', function( i, value ) {
29648			return value.replace( /\_\d+$/g, '' ) + '_' + index;
29649		} );
29650		clone.find( '[data-for]' ).attr( 'data-for', function( i, value ) {
29651			return value.replace( /\_\d+$/g, '' ) + '_' + index;
29652		} );
29653		clone.find( '[for]:not(:has(.tmcp-date-select))' ).attr( 'for', function( i, value ) {
29654			return value.replace( /\_\d+$/g, '' ) + '_' + index;
29655		} );
29656		clone.find( '[data-field-id]' ).attr( 'data-field-id', function( i, value ) {
29657			return value.replace( /\_\d+$/g, '' ) + '_' + index;
29658		} );
29659		clone.find( '[data-tm-date]' ).attr( 'data-tm-date', function( i, value ) {
29660			return value.replace( /\_\d+$/g, '' ) + '_' + index;
29661		} );
29662		clone.find( '.tmcp-date-select' ).attr( 'id', function( i, value ) {
29663			return $( this ).attr( 'data-tm-date' ) + '_' + value.substring( value.lastIndexOf( '_' ) + 1 );
29664		} );
29665		clone.find( '[for]:has(.tmcp-date-select)' ).attr( 'for', function( i, value ) {
29666			return $( this ).find( '.tmcp-date-select' ).attr( 'data-tm-date' ) + '_' + value.substring( value.lastIndexOf( '_' ) + 1 );
29667		} );
29668		clone.find( '.tm-epo-field' ).attr( 'name', function( i, value ) {
29669			return value.replace( /\[\d+\]/g, '[' + index + ']' );
29670		} );
29671		clone.find( '.tm-qty' ).attr( 'name', function( i, value ) {
29672			return value.replace( /\[\d+\]/g, '[' + index + ']' );
29673		} );
29674		clone.find( '.tmcp-date-select' ).attr( 'name', function( i, value ) {
29675			return value.replace( /\[\d+\]/g, '[' + index + ']' );
29676		} );
29677
29678		cloneUl = clone.find( '.tmcp-ul-wrap' );
29679
29680		if ( rows >= minRows && elementInnerWrap.find( '.tc-repeater-element' ).length >= 1 ) {
29681			elementInnerWrap.find( '.tc-repeater-delete' ).removeClass( 'tc-hidden' );
29682			clone.find( '.tc-repeater-delete' ).removeClass( 'tc-hidden' );
29683		}
29684
29685		if ( cloneUl.is( '.tm-element-ul-checkbox' ) ) {
29686			clone.find( 'li.tmcp-field-wrap' ).removeClass( 'tc-active' );
29687			clone
29688				.find( '.tm-epo-field.tmcp-checkbox' )
29689				.prop( 'checked', false );
29690			clone
29691				.find( '.tm-epo-field.tmcp-checkbox' )
29692				.filter( function( i, x ) {
29693					return $.inArray( $( x ).val(), el.data( 'repeater-init-value' ) ) !== -1;
29694				} )
29695				.prop( 'checked', true )
29696				.closest( 'li.tmcp-field-wrap' ).addClass( 'tc-active' );
29697		} else if ( cloneUl.is( '.tm-element-ul-radio' ) ) {
29698			clone.find( 'li.tmcp-field-wrap' ).removeClass( 'tc-active' );
29699			clone
29700				.find( '.tm-epo-field.tmcp-radio' )
29701				.filter( function( i, x ) {
29702					return $( x ).val() === el.data( 'repeater-init-value' );
29703				} )
29704				.prop( 'checked', true )
29705				.closest( 'li.tmcp-field-wrap' ).addClass( 'tc-active' );
29706		} else {
29707			clone.find( '.tm-epo-field' ).val( el.data( 'repeater-init-value' ) );
29708		}
29709
29710		if ( ! cloneUl.is( '.tm-element-ul-checkbox' ) && ! cloneUl.is( '.tm-element-ul-radio' ) ) {
29711			clone.find( '.tm-epo-field' ).val( el.data( 'repeater-init-value' ) );
29712		}
29713
29714		if ( cloneUl.is( '.tm-element-ul-upload' ) ) {
29715			clone.find( '.tc-upload-preview' ).remove();
29716			clone.find( '.tm-filename' ).remove();
29717		}
29718
29719		clone.find( '.tc-element-qty' ).toArray().forEach( function( elc ) {
29720			var qelc = $( elc );
29721			var defaultValue = qelc.attr( 'data-default-value' );
29722			qelc.val( defaultValue ).trigger( 'change' );
29723		} );
29724
29725		el.data( 'tc-repeater-init', el.data( 'tc-repeater-init' ) + 1 );
29726
29727		return [ clone, repeaterLast, repeaterLastUL ];
29728	}
29729
29730	function repeaterAddAfter( epoObject, clone, repeaterLastUL, doevents ) {
29731		var cloneUl = clone.find( '.tmcp-ul-wrap' );
29732
29733		if ( cloneUl.is( '.tm-element-ul-range' ) ) {
29734			clone.find( '.tm-range-picker' ).removeData( 'tc-picker-init' ).attr( 'class', 'tm-range-picker' ).html( '' );
29735			setRangePickers( clone );
29736		}
29737		if ( cloneUl.is( '.tm-element-ul-date, .tm-element-ul-time' ) ) {
29738			tm_set_datepicker( repeaterLastUL );
29739			tm_set_datepicker( cloneUl );
29740		}
29741		if ( cloneUl.is( '.tm-element-ul-color' ) ) {
29742			tm_set_color_pickers( repeaterLastUL );
29743			tm_set_color_pickers( cloneUl );
29744		}
29745		clone.find( '.tm-tooltip' ).removeData( 'tctooltip' ).removeData( 'tm-has-tm-tip' );
29746		$.tcToolTip( clone.find( '.tm-tooltip' ) );
29747
29748		//  apply specific field rules
29749		clone.find( '.tm-epo-field' )
29750			.removeData( 'addedtcEpoBeforeOptionPriceCalculation' )
29751			.toArray().forEach( function( elc ) {
29752				elc = $( elc );
29753				field_is_active( elc, false, true );
29754				if ( elc.is( '.tcenabled' ) ) {
29755					tm_element_epo_rules( epoObject, elc );
29756				}
29757				elc.on( 'tc_element_epo_rules', function() {
29758					tm_element_epo_rules( epoObject, elc );
29759				} );
29760			} );
29761		if ( ! doevents ) {
29762			return;
29763		}
29764		if ( cloneUl.is( '.tm-element-ul-checkbox' ) ) {
29765			clone
29766				.find( '.tm-epo-field.tmcp-checkbox' )
29767				.filter( ':checked' )
29768				.trigger( 'change.cpf' );
29769		} else if ( cloneUl.is( '.tm-element-ul-radio' ) ) {
29770			clone
29771				.find( '.tm-epo-field.tmcp-radio' )
29772				.filter( ':checked' )
29773				.trigger( 'change.cpf' );
29774		} else {
29775			clone.find( '.tm-epo-field' ).trigger( 'change.cpf' );
29776		}
29777	}
29778
29779	function repeaterDelete( elementInnerWrap, repeaterElement, repeaterObj, $this ) {
29780		var cpf = elementInnerWrap.closest( '.cpf-element' );
29781		var minRows = cpf.data( 'repeater-min-rows' );
29782		var rows = elementInnerWrap.find( '.tc-repeater-element' ).length;
29783		var repeaterElementIndex = repeaterElement.index();
29784		var message;
29785
29786		if ( minRows ) {
29787			if ( rows <= minRows ) {
29788				if ( $.tc_validator && $this ) {
29789					message = '<div class="tm-error-repeater tm-error tc-cell tcwidth tcwidth-100">' + $.tc_validator.messages.repeaterminrows( minRows ) + '</div>';
29790					$this.tcToolTip( { tip: message, onetime: true, trigger: true, tipclass: 'tc-error' } );
29791				}
29792				return false;
29793			}
29794		}
29795		if ( rows === 1 ) {
29796			return;
29797		}
29798		if ( rows === 2 || ( rows - 1 ) <= minRows ) {
29799			elementInnerWrap.find( '.tc-repeater-delete' ).addClass( 'tc-hidden' );
29800		}
29801		if ( repeaterObj.is( '.tc-repeater-add' ) ) {
29802			repeaterObj.closest( '.tc-repeater-wrap' ).removeClass( 'tc-hidden' );
29803		}
29804		repeaterElement.remove();
29805		repeaterObj.data( 'tc-repeater-init', repeaterObj.data( 'tc-repeater-init' ) - 1 );
29806		if ( repeaterElementIndex + 1 === rows ) {
29807			return;
29808		}
29809		elementInnerWrap.find( '.tc-repeater-element' )
29810			.toArray()
29811			.forEach( function( clone, index ) {
29812				clone = $( clone );
29813				clone.find( '.tm-epo-field' ).attr( 'id', function( i, value ) {
29814					return value.replace( /\_\d+$/g, '' ) + ( ( index !== 0 ) ? '_' + index : '' );
29815				} );
29816				clone.find( '[data-for]' ).attr( 'data-for', function( i, value ) {
29817					return value.replace( /\_\d+$/g, '' ) + ( ( index !== 0 ) ? '_' + index : '' );
29818				} );
29819				clone.find( '[for]:not(:has(.tmcp-date-select))' ).attr( 'for', function( i, value ) {
29820					return value.replace( /\_\d+$/g, '' ) + ( ( index !== 0 ) ? '_' + index : '' );
29821				} );
29822				clone.find( '[data-field-id]' ).attr( 'data-field-id', function( i, value ) {
29823					return value.replace( /\_\d+$/g, '' ) + ( ( index !== 0 ) ? '_' + index : '' );
29824				} );
29825				clone.find( '[data-tm-date]' ).attr( 'data-tm-date', function( i, value ) {
29826					return value.replace( /\_\d+$/g, '' ) + ( ( index !== 0 ) ? '_' + index : '' );
29827				} );
29828				clone.find( '.tmcp-date-select' ).attr( 'id', function( i, value ) {
29829					return $( this ).attr( 'data-tm-date' ) + '_' + value.substring( value.lastIndexOf( '_' ) + 1 );
29830				} );
29831				clone.find( '[for]:has(.tmcp-date-select)' ).attr( 'for', function( i, value ) {
29832					return $( this ).find( '.tmcp-date-select' ).attr( 'data-tm-date' ) + '_' + value.substring( value.lastIndexOf( '_' ) + 1 );
29833				} );
29834				clone.find( '.tm-epo-field' ).attr( 'name', function( i, value ) {
29835					return value.replace( /\[\d+\]/g, '[' + index + ']' );
29836				} );
29837				clone.find( '.tm-qty' ).attr( 'name', function( i, value ) {
29838					return value.replace( /\[\d+\]/g, '[' + index + ']' );
29839				} );
29840				clone.find( '.tmcp-date-select' ).attr( 'name', function( i, value ) {
29841					return value.replace( /\[\d+\]/g, '[' + index + ']' );
29842				} );
29843			} );
29844	}
29845
29846	function repeaterDeleteSection( elementInnerWrap, repeaterElement, repeaterObj, $this ) {
29847		var cpf = elementInnerWrap.closest( '.cpf-section' );
29848		var minRows = cpf.data( 'repeater-min-rows' );
29849		var rows = elementInnerWrap.find( '.tc-repeater-section' ).length;
29850		var repeaterElementIndex = repeaterElement.index();
29851		var message;
29852
29853		if ( minRows ) {
29854			if ( rows <= minRows ) {
29855				if ( $.tc_validator && $this ) {
29856					message = '<div class="tm-error-repeater tm-error tc-cell tcwidth tcwidth-100">' + $.tc_validator.messages.repeaterminrows( minRows ) + '</div>';
29857					$this.tcToolTip( { tip: message, onetime: true, trigger: true, tipclass: 'tc-error' } );
29858				}
29859				return false;
29860			}
29861		}
29862		if ( rows === 1 ) {
29863			return;
29864		}
29865		if ( rows === 2 || ( rows - 1 ) <= minRows ) {
29866			elementInnerWrap.find( '.tc-repeater-section-delete' ).addClass( 'tc-hidden' );
29867			elementInnerWrap.find( '.tc-repeater-section' ).removeClass( 'has-delete' );
29868			cpf.find( '.tcowl-carousel' ).trigger( 'refresh.owl.carousel' );
29869		}
29870		if ( repeaterObj.is( '.tc-section-repeater-add' ) ) {
29871			repeaterObj.closest( '.tc-repeater-wrap' ).removeClass( 'tc-hidden' );
29872		}
29873		repeaterElement.remove();
29874		repeaterObj.data( 'tc-repeater-init', repeaterObj.data( 'tc-repeater-init' ) - 1 );
29875		if ( repeaterElementIndex + 1 === rows ) {
29876			return;
29877		}
29878		elementInnerWrap.find( '.tc-repeater-section' )
29879			.toArray()
29880			.forEach( function( clone, index ) {
29881				clone = $( clone );
29882
29883				clone.find( '.tm-epo-field' ).attr( 'id', function( i, value ) {
29884					return value.replace( /\_\d+$/g, '' ) + ( ( index !== 0 ) ? '_' + index : '' );
29885				} );
29886				clone.find( '[data-for]' ).attr( 'data-for', function( i, value ) {
29887					return value.replace( /\_\d+$/g, '' ) + ( ( index !== 0 ) ? '_' + index : '' );
29888				} );
29889				clone.find( '[for]:not(:has(.tmcp-date-select))' ).attr( 'for', function( i, value ) {
29890					return value.replace( /\_\d+$/g, '' ) + ( ( index !== 0 ) ? '_' + index : '' );
29891				} );
29892				clone.find( '[data-field-id]' ).attr( 'data-field-id', function( i, value ) {
29893					return value.replace( /\_\d+$/g, '' ) + ( ( index !== 0 ) ? '_' + index : '' );
29894				} );
29895				clone.find( '[data-tm-date]' ).attr( 'data-tm-date', function( i, value ) {
29896					return value.replace( /\_\d+$/g, '' ) + ( ( index !== 0 ) ? '_' + index : '' );
29897				} );
29898				clone.find( '.tmcp-date-select' ).attr( 'id', function( i, value ) {
29899					return $( this ).attr( 'data-tm-date' ) + '_' + value.substring( value.lastIndexOf( '_' ) + 1 );
29900				} );
29901				clone.find( '[for]:has(.tmcp-date-select)' ).attr( 'for', function( i, value ) {
29902					return $( this ).find( '.tmcp-date-select' ).attr( 'data-tm-date' ) + '_' + value.substring( value.lastIndexOf( '_' ) + 1 );
29903				} );
29904				clone.find( '.tm-epo-field' ).attr( 'name', function( i, value ) {
29905					return value.replace( /\[\d+\]/g, '[' + index + ']' );
29906				} );
29907				clone.find( '.tm-qty' ).attr( 'name', function( i, value ) {
29908					return value.replace( /\[\d+\]/g, '[' + index + ']' );
29909				} );
29910				clone.find( '.tmcp-date-select' ).attr( 'name', function( i, value ) {
29911					return value.replace( /\[\d+\]/g, '[' + index + ']' );
29912				} );
29913			} );
29914	}
29915
29916	function repeaterAddSection( el, elementInnerWrap ) {
29917		var cpf = elementInnerWrap.closest( '.cpf-section' );
29918		var maxRows = cpf.data( 'repeater-max-rows' );
29919		var minRows = cpf.data( 'repeater-min-rows' ) || 1;
29920		var repeater = elementInnerWrap.find( '.tc-repeater-section' );
29921		var rows = repeater.length;
29922		var repeaterLast = repeater.last();
29923		var repeaterLastUL = repeaterLast.find( '.tmcp-ul-wrap' );
29924		var clone;
29925		var cloneUl;
29926		var index = el.data( 'tc-repeater-init' );
29927		var repeaterInitValue = el.data( 'repeater-init-value' );
29928
29929		if ( maxRows ) {
29930			if ( rows >= maxRows ) {
29931				return false;
29932			}
29933		}
29934
29935		if ( rows + 1 >= maxRows && el.is( '.tc-repeater-add' ) ) {
29936			el.closest( '.tc-repeater-wrap' ).addClass( 'tc-hidden' );
29937		}
29938
29939		repeaterLastUL.toArray().forEach( function( field ) {
29940			var rlul = $( field );
29941			if ( rlul.is( '.tm-element-ul-date, .tm-element-ul-time' ) ) {
29942				rlul.find( '.tm-epo-field' ).tm_datepicker( 'destroy' );
29943				rlul.find( '.tmcp-date-select' ).off();
29944			}
29945			if ( rlul.is( '.tm-element-ul-color' ) ) {
29946				rlul.find( '.tm-epo-field' ).spectrum( 'destroy' );
29947			}
29948		} );
29949
29950		clone = repeaterLast.tcClone( true );
29951
29952		clone.find( '.tm-epo-field' ).attr( 'id', function( i, value ) {
29953			return value.replace( /\_\d+$/g, '' ) + '_' + index;
29954		} );
29955		clone.find( '[data-for]' ).attr( 'data-for', function( i, value ) {
29956			return value.replace( /\_\d+$/g, '' ) + '_' + index;
29957		} );
29958		clone.find( '[for]:not(:has(.tmcp-date-select))' ).attr( 'for', function( i, value ) {
29959			return value.replace( /\_\d+$/g, '' ) + '_' + index;
29960		} );
29961		clone.find( '[data-field-id]' ).attr( 'data-field-id', function( i, value ) {
29962			return value.replace( /\_\d+$/g, '' ) + '_' + index;
29963		} );
29964		clone.find( '[data-tm-date]' ).attr( 'data-tm-date', function( i, value ) {
29965			return value.replace( /\_\d+$/g, '' ) + '_' + index;
29966		} );
29967		clone.find( '.tmcp-date-select' ).attr( 'id', function( i, value ) {
29968			return $( this ).attr( 'data-tm-date' ) + '_' + value.substring( value.lastIndexOf( '_' ) + 1 );
29969		} );
29970		clone.find( '[for]:has(.tmcp-date-select)' ).attr( 'for', function( i, value ) {
29971			return $( this ).find( '.tmcp-date-select' ).attr( 'data-tm-date' ) + '_' + value.substring( value.lastIndexOf( '_' ) + 1 );
29972		} );
29973		clone.find( '.tm-epo-field' ).attr( 'name', function( i, value ) {
29974			return value.replace( /\[\d+\]/g, '[' + index + ']' );
29975		} );
29976		clone.find( '.tm-qty' ).attr( 'name', function( i, value ) {
29977			return value.replace( /\[\d+\]/g, '[' + index + ']' );
29978		} );
29979		clone.find( '.tmcp-date-select' ).attr( 'name', function( i, value ) {
29980			return value.replace( /\[\d+\]/g, '[' + index + ']' );
29981		} );
29982
29983		cloneUl = clone.find( '.tmcp-ul-wrap' );
29984
29985		if ( rows >= minRows && elementInnerWrap.find( '.tc-repeater-section' ).length >= 1 ) {
29986			elementInnerWrap.find( '.tc-repeater-section-delete' ).removeClass( 'tc-hidden' );
29987			clone.find( '.tc-repeater-section-delete' ).removeClass( 'tc-hidden' );
29988			repeater.addClass( 'has-delete' );
29989			clone.addClass( 'has-delete' );
29990			cpf.find( '.tcowl-carousel' ).trigger( 'refresh.owl.carousel' );
29991		}
29992
29993		cloneUl.toArray().forEach( function( field ) {
29994			var cUl = $( field );
29995			if ( cUl.is( '.tm-element-ul-checkbox' ) ) {
29996				cUl.find( 'li.tmcp-field-wrap' ).removeClass( 'tc-active' );
29997				cUl
29998					.find( '.tm-epo-field.tmcp-checkbox' )
29999					.prop( 'checked', false );
30000				cUl
30001					.find( '.tm-epo-field.tmcp-checkbox' )
30002					.filter( function( i, x ) {
30003						var $field = $( x );
30004						var fieldId = $field.attr( 'id' ).replace( /_(\d+)$/, '' );
30005						return $.inArray( $field.val(), repeaterInitValue[ fieldId ] ) !== -1;
30006					} )
30007					.prop( 'checked', true )
30008					.closest( 'li.tmcp-field-wrap' ).addClass( 'tc-active' );
30009			} else if ( cUl.is( '.tm-element-ul-radio' ) ) {
30010				cUl.find( 'li.tmcp-field-wrap' ).removeClass( 'tc-active' );
30011				cUl
30012					.find( '.tm-epo-field.tmcp-radio' )
30013					.filter( function( i, x ) {
30014						var $field = $( x );
30015						var fieldId = $field.attr( 'id' ).replace( /_(\d+)$/, '' );
30016						return $field.val() === repeaterInitValue[ fieldId ];
30017					} )
30018					.prop( 'checked', true )
30019					.closest( 'li.tmcp-field-wrap' ).addClass( 'tc-active' );
30020			} else {
30021				cUl.find( '.tm-epo-field' ).each( function() {
30022					var $field = $( this );
30023					var fieldId = $field.attr( 'id' ).replace( /_(\d+)$/, '' );
30024					if ( repeaterInitValue.hasOwnProperty( fieldId ) ) {
30025						$field.val( repeaterInitValue[ fieldId ] );
30026					}
30027				} );
30028			}
30029
30030			if ( ! cUl.is( '.tm-element-ul-checkbox' ) && ! cUl.is( '.tm-element-ul-radio' ) ) {
30031				cUl.find( '.tm-epo-field' ).each( function() {
30032					var $field = $( this );
30033					var fieldId = $field.attr( 'id' ).replace( /_(\d+)$/, '' );
30034					if ( repeaterInitValue.hasOwnProperty( fieldId ) ) {
30035						$field.val( repeaterInitValue[ fieldId ] );
30036					}
30037				} );
30038			}
30039
30040			if ( cUl.is( '.tm-element-ul-upload' ) ) {
30041				cUl.find( '.tc-upload-preview' ).remove();
30042				cUl.find( '.tm-filename' ).remove();
30043			}
30044		} );
30045
30046		clone.find( '.tc-element-qty' ).toArray().forEach( function( elc ) {
30047			var qelc = $( elc );
30048			var defaultValue = qelc.attr( 'data-default-value' );
30049			qelc.val( defaultValue ).trigger( 'change' );
30050		} );
30051
30052		el.data( 'tc-repeater-init', el.data( 'tc-repeater-init' ) + 1 );
30053
30054		return [ clone, repeaterLast, repeaterLastUL ];
30055	}
30056
30057	function repeaterAddSectionAfter( epoObject, clone, repeaterLastUL, doevents ) {
30058		var cloneUl = clone.find( '.tmcp-ul-wrap' );
30059		var doDate;
30060		var doColor;
30061		cloneUl.toArray().forEach( function( field ) {
30062			var cUL = $( field );
30063			if ( cUL.is( '.tm-element-ul-range' ) ) {
30064				cUL.find( '.tm-range-picker' ).removeData( 'tc-picker-init' ).attr( 'class', 'tm-range-picker' ).html( '' );
30065				setRangePickers( cUL );
30066			}
30067			if ( cUL.is( '.tm-element-ul-date, .tm-element-ul-time' ) ) {
30068				doDate = true;
30069				tm_set_datepicker( cUL );
30070			}
30071			if ( cUL.is( '.tm-element-ul-color' ) ) {
30072				doColor = true;
30073				tm_set_color_pickers( cUL );
30074			}
30075			if ( doevents ) {
30076				if ( cUL.is( '.tm-element-ul-checkbox' ) ) {
30077					cUL
30078						.find( '.tm-epo-field.tmcp-checkbox' )
30079						.filter( ':checked' )
30080						.trigger( 'change.cpf' );
30081				} else if ( cUL.is( '.tm-element-ul-radio' ) ) {
30082					cUL
30083						.find( '.tm-epo-field.tmcp-radio' )
30084						.filter( ':checked' )
30085						.trigger( 'change.cpf' );
30086				} else {
30087					cUL.find( '.tm-epo-field' ).trigger( 'change.cpf' );
30088				}
30089			}
30090		} );
30091		if ( doDate ) {
30092			tm_set_datepicker( repeaterLastUL );
30093		}
30094		if ( doColor ) {
30095			tm_set_color_pickers( repeaterLastUL );
30096		}
30097
30098		clone.find( '.tm-tooltip' ).removeData( 'tctooltip' ).removeData( 'tm-has-tm-tip' );
30099		$.tcToolTip( clone.find( '.tm-tooltip' ) );
30100
30101		//  apply specific field rules
30102		clone.find( '.tm-epo-field' )
30103			.removeData( 'addedtcEpoBeforeOptionPriceCalculation' )
30104			.toArray().forEach( function( elc ) {
30105				elc = $( elc );
30106				field_is_active( elc, false, true );
30107				if ( elc.is( '.tcenabled' ) ) {
30108					tm_element_epo_rules( epoObject, elc );
30109				}
30110				elc.on( 'tc_element_epo_rules', function() {
30111					tm_element_epo_rules( epoObject, elc );
30112				} );
30113			} );
30114		clone.find( '.tc-tabs' ).removeData( 'tctabs' );
30115		clone.find( '.tcowl-carousel' ).removeData( 'owl.carousel' );
30116		clone.find( '.owl-nav, .owl-dots' ).remove();
30117		tm_css_styles( clone );
30118	}
30119
30120	function tm_set_repeaters( obj, epoObject ) {
30121		var currentCart;
30122		var qtyElement;
30123		var quantityRepeaters;
30124		var sectionQuantityRepeaters;
30125		if ( ! obj ) {
30126			return;
30127		}
30128		obj = $( obj );
30129
30130		if ( obj.length ) {
30131			currentCart = epoObject.main_cart;
30132			qtyElement = getQtyElement( currentCart );
30133			qtyElement = $.epoAPI.applyFilter( 'qtyElementForRepeaterQuantity', qtyElement, {
30134				epo: epoObject,
30135				currentCart: currentCart,
30136				obj: obj,
30137				qtyElement: qtyElement
30138			} );
30139			quantityRepeaters = obj.find( '.tc-repeater-quantity' );
30140			quantityRepeaters
30141				.toArray()
30142				.forEach( function( repeater, qx ) {
30143					var el = $( repeater );
30144					var tmEpoField;
30145					var repeaterElement;
30146					var length;
30147					var elementInnerWrap;
30148					var maxRows = el.data( 'repeater-max-rows' );
30149
30150					if ( el.data( 'tc-repeater-init' ) ) {
30151						return;
30152					}
30153					repeaterElement = el.find( '.tc-repeater-element' );
30154					length = repeaterElement.length;
30155					tmEpoField = repeaterElement.first().find( '.tm-epo-field' );
30156					if ( tmEpoField.is( ':checkbox' ) ) {
30157						el.data( 'repeater-init-value',
30158							tmEpoField.filter( ':checked' ).toArray().map( function( x ) {
30159								return $( x ).val();
30160							} ) );
30161					} else if ( tmEpoField.is( ':radio' ) ) {
30162						el.data( 'repeater-init-value', tmEpoField.filter( ':checked' ).val() );
30163					} else {
30164						el.data( 'repeater-init-value', tmEpoField.val() );
30165					}
30166					el.data( 'tc-repeater-init', length );
30167					el.data( 'tc-repeater-name', tmEpoField.attr( 'name' ) );
30168
30169					if ( qtyElement.length ) {
30170						elementInnerWrap = el.find( '.tc-element-inner-wrap' );
30171						qtyElement
30172							.off( 'change.r' + qx + 'cpf input.r' + qx + 'cpf' )
30173							.on( 'change.r' + qx + 'cpf input.r' + qx + 'cpf', function( event ) {
30174								var field = $( this );
30175								var prevValue = field.data( 'epopreviousvalue' );
30176								var value = $.epoAPI.applyFilter( 'qtyElementForRepeaterQuantityValue', field.val(), {
30177									epo: epoObject,
30178									currentCart: currentCart,
30179									obj: obj,
30180									qtyElement: qtyElement,
30181									field: field
30182								} );
30183								var difference = value - prevValue;
30184								var thisRepeaterElement;
30185								var i;
30186								var ele;
30187								var cloned;
30188								var repeaterLastUL;
30189								var fieldprevval;
30190								if ( prevValue === undefined ) {
30191									prevValue = value - 1;
30192									difference = value - prevValue;
30193								}
30194								if ( event.isTrigger !== undefined ) {
30195									prevValue = el.find( '.tc-element-inner-wrap' ).find( '.tc-repeater-element' ).length;
30196									difference = value - prevValue;
30197								}
30198								if ( maxRows && difference > maxRows ) {
30199									difference = maxRows - 1;
30200								}
30201								if ( difference === 0 ) {
30202									return;
30203								}
30204								fieldprevval = $.epoAPI.applyFilter( 'qtyElementForRepeaterQuantity_epopreviousvalue', field.val(), {
30205									epo: epoObject,
30206									currentCart: currentCart,
30207									obj: obj,
30208									qtyElement: qtyElement,
30209									field: field
30210								} );
30211								if ( fieldprevval <= 0 ) {
30212									fieldprevval = 1;
30213								}
30214								if ( qx === quantityRepeaters.length ) {
30215									field.data( 'epopreviousvalue', fieldprevval );
30216								}
30217								if ( difference > 0 ) {
30218									thisRepeaterElement = elementInnerWrap.find( '.tc-repeater-element' ).eq( value - 1 );
30219									if ( thisRepeaterElement.length ) {
30220										return;
30221									}
30222									for ( i = 0; i < difference; i++ ) {
30223										cloned = repeaterAdd( el, elementInnerWrap );
30224										if ( cloned ) {
30225											if ( ele ) {
30226												ele = ele.add( cloned[ 0 ] );
30227											} else {
30228												ele = cloned[ 0 ];
30229											}
30230											if ( i === 0 ) {
30231												repeaterLastUL = cloned[ 2 ];
30232											}
30233										}
30234									}
30235									if ( ele ) {
30236										ele.appendTo( elementInnerWrap.find( '.tc-element-container' ) );
30237										repeaterAddAfter( epoObject, ele, repeaterLastUL );
30238									}
30239								} else {
30240									for ( i = value - difference; i > value; i -= 1 ) {
30241										thisRepeaterElement = elementInnerWrap.find( '.tc-repeater-element' ).eq( i - 1 );
30242										repeaterDelete( elementInnerWrap, thisRepeaterElement, el );
30243									}
30244								}
30245								currentCart.trigger( {
30246									type: 'tm-epo-update',
30247									norules: 2
30248								} );
30249							} );
30250						qtyElement.data( 'epopreviousvalue', $.epoAPI.applyFilter( 'qtyElementForRepeaterQuantityValue', qtyElement.val(), {
30251							epo: epoObject,
30252							currentCart: currentCart,
30253							obj: obj,
30254							qtyElement: qtyElement,
30255							field: qtyElement
30256						} ) ).trigger( 'change' );
30257					}
30258				} );
30259
30260			obj.find( '.tc-repeater-add' )
30261				.toArray()
30262				.forEach( function( repeater ) {
30263					var el = $( repeater );
30264					var tmEpoField;
30265					var elementInnerWrap;
30266					var repeaterElement;
30267					var length;
30268					if ( el.data( 'tc-repeater-init' ) ) {
30269						return;
30270					}
30271					elementInnerWrap = el.closest( '.tc-element-inner-wrap' );
30272					repeaterElement = elementInnerWrap.find( '.tc-repeater-element' );
30273					length = repeaterElement.length;
30274					tmEpoField = repeaterElement.first().find( '.tm-epo-field' );
30275					if ( elementInnerWrap.find( '.tc-repeater-element' ).length > 1 ) {
30276						elementInnerWrap.find( '.tc-repeater-delete' ).removeClass( 'tc-hidden' );
30277					}
30278					if ( tmEpoField.is( ':checkbox' ) ) {
30279						el.data( 'repeater-init-value',
30280							tmEpoField.filter( ':checked' ).toArray().map( function( x ) {
30281								return $( x ).val();
30282							} ) );
30283					} else if ( tmEpoField.is( ':radio' ) ) {
30284						el.data( 'repeater-init-value', tmEpoField.filter( ':checked' ).val() );
30285					} else {
30286						el.data( 'repeater-init-value', tmEpoField.val() );
30287					}
30288					el.data( 'tc-repeater-init', length );
30289					el.data( 'tc-repeater-name', tmEpoField.attr( 'name' ) );
30290
30291					el.on( 'click.repeater', function() {
30292						var ele = repeaterAdd( el, elementInnerWrap );
30293						if ( ele ) {
30294							ele[ 0 ].appendTo( elementInnerWrap.find( '.tc-element-container' ) );
30295							repeaterAddAfter( epoObject, ele[ 0 ], ele[ 2 ], true );
30296						}
30297					} );
30298				} );
30299
30300			obj.find( '.tc-repeater-delete .delete' ).on( 'click', function() {
30301				var $this = $( this );
30302				var elementInnerWrap = $this.closest( '.tc-element-inner-wrap' );
30303				var repeaterElement = $this.closest( '.tc-repeater-element' );
30304				repeaterDelete( elementInnerWrap, repeaterElement, elementInnerWrap.find( '.tc-repeater-add' ), $this );
30305			} );
30306
30307			obj.find( '.tc-section-repeater-add' )
30308				.toArray()
30309				.forEach( function( repeater ) {
30310					var el = $( repeater );
30311					var tmEpoField;
30312					var elementInnerWrap;
30313					var repeaterElement;
30314					var length;
30315					var fieldData = [];
30316					if ( el.data( 'tc-repeater-init' ) ) {
30317						return;
30318					}
30319					elementInnerWrap = el.closest( '.tc-section-inner-wrap' );
30320					repeaterElement = elementInnerWrap.find( '.tc-repeater-section' );
30321					length = repeaterElement.length;
30322					tmEpoField = elementInnerWrap.find( '.sections_repeater' );
30323					if ( elementInnerWrap.find( '.tc-repeater-section' ).length > 1 ) {
30324						elementInnerWrap.find( '.tc-section-repeater-delete' ).removeClass( 'tc-hidden' );
30325					}
30326					repeaterElement.first().find( '.tm-epo-field' )
30327						.toArray()
30328						.forEach( function( field ) {
30329							var epofield = $( field );
30330							var id = epofield.attr( 'id' );
30331							if ( epofield.is( ':checkbox' ) ) {
30332								fieldData[ id ] = epofield.filter( ':checked' ).toArray().map( function( x ) {
30333									return $( x ).val();
30334								} );
30335							} else if ( epofield.is( ':radio' ) ) {
30336								fieldData[ id ] = epofield.filter( ':checked' ).val();
30337							} else {
30338								fieldData[ id ] = epofield.val();
30339							}
30340						} );
30341					el.data( 'repeater-init-value', fieldData );
30342
30343					el.data( 'tc-repeater-init', length );
30344					el.data( 'tc-repeater-name', tmEpoField.attr( 'name' ) );
30345
30346					el.on( 'click.repeater', function() {
30347						var ele = repeaterAddSection( el, elementInnerWrap );
30348						if ( ele ) {
30349							ele[ 1 ].after( ele[ 0 ] );
30350							repeaterAddSectionAfter( epoObject, ele[ 0 ], ele[ 2 ], true );
30351							tmEpoField.val( function( i, currentValue ) {
30352								return currentValue ? parseInt( currentValue, 10 ) + 1 : 2;
30353							} );
30354						}
30355					} );
30356				} );
30357
30358			sectionQuantityRepeaters = obj.find( '.tc-section-repeater-quantity' );
30359			sectionQuantityRepeaters
30360				.toArray()
30361				.forEach( function( repeater, qx ) {
30362					var el = $( repeater );
30363					var tmEpoField;
30364					var elementInnerWrap;
30365					var repeaterElement;
30366					var length;
30367					var fieldData = [];
30368					var maxRows = el.closest( '.cpf-section' ).data( 'repeater-max-rows' );
30369					if ( el.data( 'tc-repeater-init' ) ) {
30370						return;
30371					}
30372					elementInnerWrap = el.find( '.tc-section-inner-wrap' );
30373					repeaterElement = elementInnerWrap.find( '.tc-repeater-section' );
30374					length = repeaterElement.length;
30375					tmEpoField = elementInnerWrap.find( '.sections_repeater' );
30376					repeaterElement.first().find( '.tm-epo-field' )
30377						.toArray()
30378						.forEach( function( field ) {
30379							var epofield = $( field );
30380							var id = epofield.attr( 'id' );
30381							if ( epofield.is( ':checkbox' ) ) {
30382								fieldData[ id ] = epofield.filter( ':checked' ).toArray().map( function( x ) {
30383									return $( x ).val();
30384								} );
30385							} else if ( epofield.is( ':radio' ) ) {
30386								fieldData[ id ] = epofield.filter( ':checked' ).val();
30387							} else {
30388								fieldData[ id ] = epofield.val();
30389							}
30390						} );
30391					el.data( 'repeater-init-value', fieldData );
30392
30393					el.data( 'tc-repeater-init', length );
30394					el.data( 'tc-repeater-name', tmEpoField.attr( 'name' ) );
30395
30396					if ( qtyElement.length ) {
30397						qtyElement
30398							.off( 'change.rs' + qx + 'cpf input.rs' + qx + 'cpf' )
30399							.on( 'change.rs' + qx + 'cpf input.rs' + qx + 'cpf', function( event ) {
30400								var field = $( this );
30401								var prevValue = field.data( 'epopreviousvalue' + qx );
30402								var value = $.epoAPI.applyFilter( 'qtyElementForRepeaterQuantityValue', field.val(), {
30403									epo: epoObject,
30404									currentCart: currentCart,
30405									obj: obj,
30406									qtyElement: qtyElement,
30407									field: field
30408								} );
30409								var difference = value - prevValue;
30410								var thisRepeaterElement;
30411								var i;
30412								var ele;
30413								var cloned;
30414								var repeaterLastUL;
30415								var fieldprevval;
30416								var firstcloned;
30417								if ( prevValue === undefined ) {
30418									prevValue = value - 1;
30419									difference = value - prevValue;
30420								}
30421								if ( event.isTrigger !== undefined ) {
30422									prevValue = el.find( '.tc-section-inner-wrap' ).find( '.tc-repeater-section' ).length;
30423									difference = value - prevValue;
30424								}
30425								if ( maxRows && difference > maxRows ) {
30426									difference = maxRows - 1;
30427								}
30428								if ( difference === 0 ) {
30429									return;
30430								}
30431								fieldprevval = $.epoAPI.applyFilter( 'qtyElementForRepeaterQuantity_epopreviousvalue', field.val(), {
30432									epo: epoObject,
30433									currentCart: currentCart,
30434									obj: obj,
30435									qtyElement: qtyElement,
30436									field: field
30437								} );
30438								if ( fieldprevval <= 0 ) {
30439									fieldprevval = 1;
30440								}
30441								if ( qx === quantityRepeaters.length ) {
30442									field.data( 'epopreviousvalue' + qx, fieldprevval );
30443								}
30444								if ( difference > 0 ) {
30445									thisRepeaterElement = elementInnerWrap.find( '.tc-repeater-section' ).eq( value - 1 );
30446									if ( thisRepeaterElement.length ) {
30447										return;
30448									}
30449									for ( i = 0; i < difference; i++ ) {
30450										cloned = repeaterAddSection( el, elementInnerWrap );
30451										if ( cloned ) {
30452											if ( ! firstcloned ) {
30453												firstcloned = cloned;
30454											}
30455											if ( ele ) {
30456												ele = ele.add( cloned[ 0 ] );
30457											} else {
30458												ele = cloned[ 0 ];
30459											}
30460											if ( i === 0 ) {
30461												repeaterLastUL = cloned[ 2 ];
30462											}
30463										}
30464									}
30465									if ( ele ) {
30466										tmEpoField.val( function( ii, currentValue ) {
30467											return currentValue ? parseInt( currentValue, 10 ) + parseInt( difference, 10 ) : 2;
30468										} );
30469										firstcloned[ 1 ].after( ele );
30470										repeaterAddSectionAfter( epoObject, ele, r
30470epeaterLastUL );
30471									}
30472								} else {
30473									for ( i = value - difference; i > value; i -= 1 ) {
30474										thisRepeaterElement = elementInnerWrap.find( '.tc-repeater-section' ).eq( i - 1 );
30475										repeaterDeleteSection( elementInnerWrap, thisRepeaterElement, el );
30476									}
30477									tmEpoField.val( elementInnerWrap.find( '.tc-repeater-section' ).length );
30478								}
30479								currentCart.trigger( {
30480									type: 'tm-epo-update',
30481									norules: 2
30482								} );
30483							} );
30484						qtyElement.data( 'epopreviousvalue' + qx, $.epoAPI.applyFilter( 'qtyElementForRepeaterQuantityValue', qtyElement.val(), {
30485							epo: epoObject,
30486							currentCart: currentCart,
30487							obj: obj,
30488							qtyElement: qtyElement,
30489							field: qtyElement
30490						} ) ).trigger( 'change' );
30491					}
30492				} );
30493
30494			obj.find( '.tc-repeater-section-delete .delete' ).on( 'click', function() {
30495				var $this = $( this );
30496				var elementInnerWrap = $this.closest( '.tc-section-inner-wrap' );
30497				var repeaterElement = $this.closest( '.tc-repeater-section' );
30498				var tmEpoField;
30499				tmEpoField = elementInnerWrap.find( '.sections_repeater' );
30500				repeaterDeleteSection( elementInnerWrap, repeaterElement, elementInnerWrap.find( '.tc-section-repeater-add' ), $this );
30501				tmEpoField.val( function( ii, currentValue ) {
30502					return currentValue ? parseInt( currentValue, 10 ) - 1 : 2;
30503				} );
30504			} );
30505		}
30506	}
30507
30508	function tm_set_color_pickers( obj ) {
30509		if ( ! obj ) {
30510			return;
30511		}
30512		obj = $( obj ).find( '.tm-color-picker' );
30513		if ( obj.length ) {
30514			obj.spectrum( {
30515				type: 'color',
30516				theme: 'epo',
30517				showButtons: true,
30518				allowEmpty: true,
30519				showInitial: true,
30520				showInput: true,
30521				clickoutFiresChange: false,
30522				chooseText: TMEPOJS.closeText,
30523				cancelText: TMEPOJS.i18n_cancel,
30524				clearText: ''
30525			} );
30526			obj.spectrum( 'enable' );
30527		}
30528	}
30529
30530	function tm_set_lightbox( obj ) {
30531		if ( ! obj ) {
30532			return;
30533		}
30534		if ( $( obj ).length ) {
30535			// document ready
30536			$( function() {
30537				$( obj ).tclightbox();
30538			} );
30539		}
30540	}
30541
30542	function has_active_changes_product_image( field ) {
30543		var uic = field.closest( '.tmcp-field-wrap' ).find( 'label img' );
30544		var src = $( uic ).first().attr( 'data-original' );
30545
30546		if ( field.is( 'select.tm-product-image' ) ) {
30547			field = field.children( 'option:selected' );
30548		}
30549
30550		if ( ! src ) {
30551			src = $( uic ).first().attr( 'src' );
30552		}
30553		if ( ! src ) {
30554			src = field.attr( 'data-image' );
30555		}
30556		if ( field.attr( 'data-imagep' ) ) {
30557			src = field.attr( 'data-imagep' );
30558		}
30559		if ( src ) {
30560			return true;
30561		}
30562
30563		return false;
30564	}
30565
30566	function tm_set_upload_fields( epoObject ) {
30567		var field;
30568		var dT;
30569		var name;
30570		var file;
30571		var selector = epoObject.is_associated
30572			? epoObject.this_epo_container.find( '.tm-epo-field.tmcp-upload' )
30573			: epoObject.this_epo_container.find( '.tm-epo-field.tmcp-upload' ).not( tcAPI.epoSelector + ' ' + tcAPI.associatedEpoSelector + ' .tm-epo-field.tmcp-upload' );
30574
30575		try {
30576			selector
30577				.not( tcAPI.epoSelector + ' ' + tcAPI.associatedEpoSelector + ' .tm-epo-field.tmcp-upload' )
30578				.toArray()
30579				.forEach( function( el ) {
30580					var dataFiles;
30581					var dataFilename;
30582					file = [];
30583					field = $( el );
30584					if ( field.attr( 'data-file' ) === '' ) {
30585						return false;
30586					}
30587					dataFiles = field.attr( 'data-file' ).split( '|' );
30588					if ( dataFiles.length === 0 ) {
30589						return false;
30590					}
30591					if ( ClipboardEvent || DataTransfer ) {
30592						dT = new ClipboardEvent( '' ).clipboardData || new DataTransfer();
30593					}
30594					dataFiles.forEach( function( dataFile ) {
30595						dataFilename = $.epoAPI.util.basename( dataFile );
30596						if ( ClipboardEvent || DataTransfer ) {
30597							dT.items.add( new File( [ dataFile ], dataFilename ) );
30598						}
30599						file.push( dataFile );
30600					} );
30601					if ( dT ) {
30602						el.files = dT.files;
30603					}
30604					file = file.join( '|' );
30605					name = field.attr( 'name' );
30606					field.trigger( 'tcupload' );
30607					field.after( '<input type="hidden" class="tmcp-upload-hidden" name="' + name + '" value="' + file + '">' );
30608					field.removeAttr( 'data-file' );
30609				} );
30610		} catch ( err ) {
30611			window.console.log( err );
30612			$( '.tm-epo-field.tmcp-upload' ).not( '.tm-multiple-file-upload' ).addClass( 'tc-nodt' );
30613			errorObject = err;
30614		}
30615	}
30616
30617	function tm_set_upload_rules( epoObject ) {
30618		var epoEventId = epoObject.epoEventId;
30619		var this_epo_container = epoObject.this_epo_container;
30620
30621		if ( TMEPOJS.tm_epo_upload_popup === 'yes' ) {
30622			$.tcepo.formSubmitEvents[ epoEventId ][ $.tcepo.formSubmitEvents[ epoEventId ].length ] = {
30623				trigger: function() {
30624					return true;
30625				},
30626				on_true: function() {
30627					var upload_fields = this_epo_container.data( 'num_uploads' );
30628					var thisPopup;
30629					var ajaxSuccessFunc;
30630					if ( upload_fields && Object.keys( upload_fields ).length ) {
30631						thisPopup = $.tcFloatBox( {
30632							fps: 1,
30633							ismodal: true,
30634							width: '50%',
30635							height: '300px',
30636							classname: 'flasho tc-wrapper',
30637							data: $.epoAPI.template.html( tcAPI.templateEngine.tc_upload_messages, {
30638								files: upload_fields,
30639								title: TMEPOJS.i18n_uploading_files,
30640								message: TMEPOJS.i18n_uploading_message
30641							} )
30642						} );
30643						ajaxSuccessFunc = function() {
30644							thisPopup.destroy();
30645							jDocument.off( 'ajaxSuccess', ajaxSuccessFunc );
30646						};
30647						jDocument.on( 'ajaxSuccess', ajaxSuccessFunc );
30648					}
30649
30650					return true;
30651				},
30652				on_false: function() {
30653					return true;
30654				}
30655			};
30656		}
30657	}
30658
30659	function tm_set_expanables() {
30660		var obj = $( '.tc-expand' );
30661		obj.toArray().forEach( function( o ) {
30662			var el = $( o );
30663			var max = parseInt( el.attr( 'data-max-items' ), 10 );
30664			var ul = el.find( '.tmcp-ul-wrap' );
30665			var gap = ul.css( 'gap' ) || ul.css( 'row-gap' );
30666			var lis = ul.find( '> li' );
30667			var lisMax = lis.slice( 0, max );
30668			var height;
30669			var totalHeight;
30670			if ( lisMax.length ) {
30671				totalHeight = lis.toArray()
30672					.reduce( function( sum, element ) {
30673						return sum + $( element ).outerHeight( true );
30674					}, 0 );
30675				totalHeight = 'calc(100px + ' + totalHeight + 'px + (' + gap + ' * (' + max + ' - 1) ) )';
30676				height = lisMax.toArray()
30677					.reduce( function( sum, element ) {
30678						return sum + $( element ).outerHeight( true );
30679					}, 0 );
30680				height = 'calc(' + height + 'px + (' + gap + ' * (' + max + ' - 1) ) )';
30681				ul.css( 'max-height', height );
30682				ul.after( $( '<button type="button" data-height="' + height + '" data-total-height="' + totalHeight + '" class="button load-more-button">Load more</button>' ) );
30683			}
30684		} );
30685		jDocument.on( 'click.txexpand', '.load-more-button', function() {
30686			var b = $( this );
30687			var h;
30688			if ( b.is( '.expanded' ) ) {
30689				b.removeClass( 'expanded' );
30690			} else {
30691				b.addClass( 'expanded' );
30692			}
30693			h = b.is( '.expanded' ) ? $( this ).attr( 'data-total-height' ) : $( this ).attr( 'data-height' );
30694			b.prev().css( 'max-height', h );
30695		} );
30696	}
30697
30698	function tm_set_checkboxes_rules( epoObject ) {
30699		var this_epo_container = epoObject.this_epo_container;
30700		var main_product = epoObject.main_product;
30701		var epoEventId = epoObject.epoEventId;
30702		var limit_cont = this_epo_container.find( '.tm-limit' );
30703		var exactlimit_cont = this_epo_container.find( '.tm-exactlimit' );
30704		var minimumlimit_cont = this_epo_container.find( '.tm-minimumlimit' );
30705
30706		// Limit checkbox selection
30707		this_epo_container.on( 'change.cpflimit', 'input.tm-epo-field.tmcp-checkbox', function() {
30708			var $this = $( this );
30709			tm_limit_c_selection( $this, true );
30710			tm_exact_c_selection( $this, true );
30711		} );
30712		if ( limit_cont.length ) {
30713			tm_check_limit_cont( limit_cont, main_product, epoEventId );
30714		}
30715
30716		// Exact value checkbox check (Todo:check for isvisible)
30717		if ( exactlimit_cont.length ) {
30718			tm_check_exactlimit_cont( exactlimit_cont, main_product, epoEventId );
30719		}
30720
30721		// Minimum number checkbox check (Todo:check for isvisible)
30722		if ( minimumlimit_cont.length ) {
30723			tm_check_minimumlimit_cont( minimumlimit_cont, epoEventId );
30724		}
30725	}
30726
30727	function tm_theme_specific_actions( epoObject ) {
30728		var this_epo_container = epoObject.this_epo_container;
30729		var theme_name = TMEPOJS.theme_name;
30730		var all_epo_selects;
30731		var blaszok_selects;
30732
30733		if ( theme_name ) {
30734			theme_name = theme_name.toLowerCase();
30735			all_epo_selects = this_epo_container.find( 'select' );
30736
30737			switch ( theme_name ) {
30738				case 'flatsome':
30739				case 'flatsome-child':
30740				case 'flatsome child':
30741					all_epo_selects.wrap( '<div class="custom select-wrapper"/>' );
30742					break;
30743
30744				case 'avada':
30745				case 'avada-child':
30746				case 'avada child':
30747					all_epo_selects.wrap( '<div class="avada-select-parent tm-select-parent"></div>' );
30748					$( '<div class="select-arrow">&#xe61f;</div>' ).appendTo( this_epo_container.find( '.tm-select-parent' ) );
30749					if ( window.calc_select_arrow_dimensions ) {
30750						window.calc_select_arrow_dimensions();
30751						jWindow.on( 'tmsectionpoplink cpflogicdone', function() {
30752							window.calc_select_arrow_dimensions();
30753						} );
30754					} else if ( window.calcSelectArrowDimensions ) {
30755						window.calcSelectArrowDimensions();
30756						jWindow.on( 'tmsectionpoplink cpflogicdone', function() {
30757							window.calcSelectArrowDimensions();
30758						} );
30759					}
30760					break;
30761
30762				case 'bazar':
30763				case 'bazar-child':
30764				case 'bazar child':
30765					all_epo_selects.wrap( '<div class="tm-select-wrapper select-wrapper"/>' );
30766					break;
30767
30768				case 'blaszok':
30769				case 'blaszok-child':
30770				case 'blaszok child':
30771					blaszok_selects = function() {
30772						setTimeout( function() {
30773							$( '.tm-extra-product-options select' )
30774								.not( '.hasCustomSelect' )
30775								.filter( ':visible' )
30776								.toArray().forEach( function( el ) {
30777									el = $( el );
30778									if ( ! el.is( '.mpcthSelect' ) ) {
30779										el.width( el.outerWidth() );
30780										el.customSelect( { customClass: 'mpcthSelect' } );
30781									}
30782								} );
30783						}, 100 );
30784					};
30785					jWindow.on( 'cpflogicrun', function() {
30786						blaszok_selects();
30787					} );
30788					jWindow.on( 'epo_options_visible', function() {
30789						blaszok_selects();
30790					} );
30791
30792					break;
30793
30794				case 'handmade':
30795				case 'handmade child theme':
30796					$( '.tm-owl-slider.tcowl-carousel' ).addClass( 'manual' );
30797					break;
30798			}
30799
30800			jWindow.trigger( 'tm-theme-specific-actions', {
30801				epo: {
30802					theme_name: theme_name,
30803					all_epo_selects: all_epo_selects
30804				}
30805			} );
30806		}
30807
30808		// Fix added +/- quantity button on most themes.
30809		jDocument.off( 'click.cpf', '.quantity:not(.buttons_added) .minus, .quantity:not(.buttons_added) .plus' ).on( 'click.cpf', '.quantity:not(.buttons_added) .minus, .quantity:not(.buttons_added) .plus', function() {
30810			$( this ).closest( '.quantity' ).find( tcAPI.qtySelector ).trigger( 'change.cpf' );
30811		} );
30812	}
30813
30814	function tm_custom_variations( epoObject, form, item_id, $main_product, $epo_holder ) {
30815		var epoEventId = epoObject.epoEventId;
30816		var variation_id_selector = "input[name^='variation_id']";
30817		var tm_epo_variation_section;
30818		var li_variations;
30819		var composite_load_test;
30820		var form_event;
30821		var type;
30822		var name;
30823		var selector;
30824		var func;
30825		var i;
30826		var eventName = epoObject.is_associated ? 'tc_variation_form.tmlogic' : 'wc_variation_form.tmlogic';
30827		var eventNamePrefix = epoObject.is_associated ? 'tc_' : '';
30828		var variationsForm = epoObject.variations_form;
30829		var variationsTable = epoObject.is_associated ? variationsForm.find( '.tc-epo-element-variations' ) : variationsForm.find( '.variations' );
30830		var resetSelector = epoObject.is_associated ? '.tc-epo-element-variable-reset-variations' : '.reset_variations';
30831
30832		variationsForm.attr( 'data-epo_id', epoObject.epo_id );
30833
30834		if ( form.find( 'input.variation_id' ).length > 0 ) {
30835			variation_id_selector = 'input.variation_id';
30836		}
30837		if ( $epo_holder.find( '.tm-epo-variation-element' ).length || $epo_holder.data( 'tm-epo-variation-element' ) ) {
30838			tm_epo_variation_section = $epo_holder.find( '.tm-epo-variation-section' ).first();
30839			tm_epo_variation_section.find( '.cpf-type-variations' ).attr( 'data-epo_id', epoObject.epo_id ).attr( 'data-product_id', variationsForm.attr( 'data-product_id' ) );
30840
30841			$epo_holder.data( 'tm-epo-variation-element', tm_epo_variation_section.find( '.tm-epo-variation-element' ) );
30842
30843			if ( item_id && item_id !== 'main' && ! epoObject.is_associated ) {
30844				// on composite
30845
30846				variationsForm = epoObject.form;
30847				variationsTable = variationsForm.find( '.composite_component[data-item_id="' + item_id + '"]' ).find( '.variations' );
30848				variationsForm.attr( 'data-epo_id', epoObject.epo_id );
30849
30850				if ( variationsTable.length === 0 ) {
30851					return;
30852				}
30853
30854				li_variations = tm_epo_variation_section.closest( 'li.tm-extra-product-options-field' );
30855				if ( ! tm_epo_variation_section.is( '.tm-hidden' ) ) {
30856					variationsTable.hide();
30857				}
30858
30859				variationsTable.after( tm_epo_variation_section.addClass( 'tm-extra-product-options nopadding' ) );
30860				if ( li_variations.is( ':empty' ) ) {
30861					li_variations.hide();
30862				}
30863
30864				if ( ! tm_epo_variation_section.is( '.section_popup' ) ) {
30865					tm_epo_variation_section.removeClass( 'tc-cell' );
30866					tm_epo_variation_section.wrap( "<div class='tc-styled-variations'></div>" );
30867				} else {
30868					tm_epo_variation_section.wrap( "<div class='tc-styled-variations tc-row'></div>" );
30869				}
30870
30871				composite_load_test = false;
30872				form.off( eventName ).on( eventName, function() {
30873					composite_load_test = true;
30874					variationsForm.on( 'click.tmlogic', '.reset_variations', function() {
30875						tm_epo_variation_section.find( 'select.tm-epo-variation-element' ).val( '' ).children( 'option' ).prop( 'disabled', false ).show();
30876						tm_epo_variation_section.find( '.tm-epo-variation-element' ).prop( 'disabled', false ).removeClass( 'tm-disabled' ).prop( 'checked', false ).closest( 'li' ).show();
30877						jWindow.trigger( 'tmlazy' );
30878						tm_epo_variation_section.find( '.tm-epo-variation-element' ).trigger( 'tm_trigger_product_image' );
30879						tm_epo_variation_section.find( 'li' ).removeClass( 'tc-active tm-attribute-disabled' ).css( 'opacity', '' );
30880					} );
30881
30882					// Disable option fields that are unavaiable for current set of attributes
30883					form.off( 'woocommerce_update_variation_values_tmlogic' ).on( 'woocommerce_update_variation_values_tmlogic', function() {
30884						tm_custom_variations_update( form, epoObject );
30885					} );
30886					for ( i = 0; i < lateVariationEvent.length; i += 1 ) {
30887						form_event = lateVariationEvent[ i ];
30888						type = typeof form_event;
30889						if ( type === 'object' ) {
30890							name = typeof form_event.name === 'string' || false;
30891							selector = typeof form_event.selector === 'string' || false;
30892							func = typeof form_event.func === 'function' || false;
30893							if ( name && func ) {
30894								if ( selector === "input[name='variation_id']" ) {
30895									selector = variation_id_selector;
30896								}
30897								if ( form_event.selector ) {
30898									form.data( 'tm-styled-variations', 1 )
30899										.off( eventNamePrefix + form_event.name + i, selector )
30900										.on( eventNamePrefix + form_event.name + i, selector, form_event.func );
30901								} else {
30902									form.data( 'tm-styled-variations', 1 )
30903										.off( eventNamePrefix + form_event.name + i )
30904										.on( eventNamePrefix + form_event.name + i, form_event.func );
30905								}
30906							}
30907						}
30908					}
30909					lateVariationEvent = [];
30910					tm_epo_variation_section.find( '.tm-epo-variation-element:not(.tm-hidden .tm-epo-variation-element)' ).last().trigger( 'tm_epo_variation_element_change' );
30911				} );
30912				// document ready
30913				$( function() {
30914					if ( composite_load_test === false ) {
30915						form.trigger( eventName );
30916					}
30917				} );
30918			} else {
30919				if ( tm_epo_variation_section.length ) {
30920					if ( ! tm_epo_variation_section.is( '.tm-hidden' ) ) {
30921						variationsTable.hide();
30922					}
30923
30924					li_variations = tm_epo_variation_section.closest( 'li.tm-extra-product-options-field' );
30925
30926					variationsTable.after( tm_epo_variation_section.addClass( 'tm-extra-product-options nopadding' ) );
30927					if ( li_variations.is( ':empty' ) ) {
30928						li_variations.hide();
30929					}
30930
30931					if ( ! tm_epo_variation_section.is( '.section_popup' ) ) {
30932						tm_epo_variation_section.removeClass( 'tc-cell' );
30933						tm_epo_variation_section.wrap( "<div class='tc-styled-variations'></div>" );
30934					} else {
30935						tm_epo_variation_section.wrap( "<div class='tc-styled-variations tc-row'></div>" );
30936					}
30937
30938					variationsForm.off( 'click.tmlogic', resetSelector ).on( 'click.tmlogic', resetSelector, function() {
30939						tm_epo_variation_section.find( 'select.tm-epo-variation-element' ).val( '' ).children( 'option' ).prop( 'disabled', false ).show();
30940						tm_epo_variation_section.find( '.tm-epo-variation-element' ).prop( 'disabled', false ).removeClass( 'tm-disabled' ).prop( 'checked', false ).closest( 'li' ).show();
30941						jWindow.trigger( 'tmlazy' );
30942						tm_epo_variation_section.find( '.tm-epo-variation-element' ).trigger( 'tm_trigger_product_image' );
30943						tm_epo_variation_section.find( 'li' ).removeClass( 'tc-active tm-attribute-disabled' ).css( 'opacity', '' );
30944					} );
30945				}
30946
30947				// Disable option fields that are unavaiable for current set of attributes
30948				variationsForm.off( 'woocommerce_update_variation_values_tmlogic' ).on( 'woocommerce_update_variation_values_tmlogic', function() {
30949					tm_custom_variations_update( variationsForm, epoObject );
30950				} );
30951
30952				for ( i = 0; i < lateVariationEvent.length; i += 1 ) {
30953					form_event = lateVariationEvent[ i ];
30954					type = typeof form_event;
30955					if ( type === 'object' ) {
30956						name = typeof form_event.name === 'string' || false;
30957						selector = typeof form_event.selector === 'string' || false;
30958						func = typeof form_event.func === 'function' || false;
30959						if ( name && func ) {
30960							if ( selector === "input[name='variation_id']" ) {
30961								selector = variation_id_selector;
30962							}
30963							if ( form_event.selector ) {
30964								variationsForm
30965									.data( 'tm-styled-variations', 1 )
30966									.off( eventNamePrefix + form_event.name + i, selector )
30967									.on( eventNamePrefix + form_event.name + i, selector, form_event.func );
30968							} else {
30969								variationsForm
30970									.data( 'tm-styled-variations', 1 )
30971									.off( eventNamePrefix + form_event.name + i )
30972									.on( eventNamePrefix + form_event.name + i, form_event.func );
30973							}
30974						}
30975					}
30976				}
30977				lateVariationEvent = [];
30978				tm_epo_variation_section.find( '.tm-epo-variation-element:not(.tm-hidden .tm-epo-variation-element)' ).last().trigger( 'tm_epo_variation_element_change' );
30979			}
30980
30981			// global event for custom variations
30982			$.tcepo.formSubmitEvents[ epoEventId ][ $.tcepo.formSubmitEvents[ epoEventId ].length ] = {
30983				trigger: function() {
30984					return true;
30985				},
30986				on_true: function() {
30987					tm_epo_variation_section.find( '.tm-epo-variation-element' ).attr( 'disabled', 'disabled' );
30988					return true;
30989				},
30990				on_false: function() {
30991					tm_epo_variation_section.find( '.tm-epo-variation-element' ).prop( 'disabled', false );
30992				}
30993			};
30994
30995			$( document.body ).on( 'added_to_cart', function() {
30996				tm_epo_variation_section.find( '.tm-epo-variation-element' ).prop( 'disabled', false );
30997			} );
30998		}
30999	}
31000
31001	function repopulate_backup_image_atts( img, product_element ) {
31002		var $gallery_img = product_element.find( '.flex-control-nav li:eq(0) img' );
31003		var $gallery_wrapper = product_element.find( '.woocommerce-product-gallery__wrapper ' );
31004		var $product_img_wrap = $gallery_wrapper.find( '.woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder' ).eq( 0 );
31005		var $product_img = img;
31006		var $product_link = img.closest( 'a' );
31007
31008		$product_img.attr( 'data-o_' + 'src', $.tc_product_image_store[ 0 ].src );
31009		$product_img.attr( 'data-o_' + 'srcset', $.tc_product_image_store[ 0 ].srcset );
31010		$product_img.attr( 'data-o_' + 'sizes', $.tc_product_image_store[ 0 ].sizes );
31011		$product_img.attr( 'data-o_' + 'title', $.tc_product_image_store[ 0 ].title );
31012		$product_img.attr( 'data-o_' + 'alt', $.tc_product_image_store[ 0 ].alt );
31013		$product_img.attr( 'data-o_' + 'data-src', $.tc_product_image_store[ 0 ][ 'data-src' ] );
31014		$product_img.attr( 'data-o_' + 'data-large_image', $.tc_product_image_store[ 0 ][ 'data-large_image' ] );
31015		$product_img.attr( 'data-o_' + 'data-large_image_width', $.tc_product_image_store[ 0 ][ 'data-large_image_width' ] );
31016		$product_img.attr( 'data-o_' + 'data-large_image_height', $.tc_product_image_store[ 0 ][ 'data-large_image_height' ] );
31017		$product_img_wrap.attr( 'data-o_' + 'data-thumb', $.tc_product_image_store[ 1 ][ 'data-thumb' ] );
31018		if ( $.tc_product_image_store[ 2 ] ) {
31019			$gallery_img.attr( 'data-o_' + 'src', $.tc_product_image_store[ 2 ].src );
31020		}
31021
31022		$product_link.attr( 'data-o_' + 'href', $.tc_product_image_store[ 3 ].href );
31023		$product_link.attr( 'data-o_' + 'title', $.tc_product_image_store[ 3 ].title );
31024	}
31025
31026	function reset_saved_image( img, product_element ) {
31027		var $gallery_img = product_element.find( '.flex-control-nav li:eq(0) img' );
31028		var $gallery_wrapper = product_element.find( '.woocommerce-product-gallery__wrapper ' );
31029		var $product_img_wrap = $gallery_wrapper.find( '.woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder' ).eq( 0 );
31030		var $product_img = img;
31031		var $product_link = img.closest( 'a' );
31032
31033		// backup current product image attributes
31034		if ( ! $.isEmptyObject( $.tc_product_image ) ) {
31035			$.tc_product_image_store = $.tc_pre_populate_store();
31036			$.tc_product_image_store = $.tc_maybe_copy_object_values( $.tc_product_image_store, $.tc_product_image );
31037		} else {
31038			$.tc_product_image_store = $.tc_populate_store( img, product_element );
31039		}
31040
31041		$product_img.tc_update_attr( 'src', 0 );
31042		$product_img.tc_update_attr( 'srcset', 0 );
31043		$product_img.tc_update_attr( 'sizes', 0 );
31044		$product_img.tc_update_attr( 'title', 0 );
31045		$product_img.tc_update_attr( 'alt', 0 );
31046		$product_img.tc_update_attr( 'data-src', 0 );
31047		$product_img.tc_update_attr( 'data-large_image', 0 );
31048		$product_img.tc_update_attr( 'data-large_image_width', 0 );
31049		$product_img.tc_update_attr( 'data-large_image_height', 0 );
31050		$product_img_wrap.tc_update_attr( 'data-thumb', 1 );
31051		$gallery_img.tc_update_attr( 'src', 2 );
31052
31053		$product_link.tc_update_attr( 'href', 3 );
31054		$product_link.tc_update_attr( 'title', 3 );
31055	}
31056
31057	function image_update( data, img, product_element ) {
31058		var $gallery_img = product_element.find( '.flex-control-nav li:eq(0) img' );
31059		var $gallery_wrapper = product_element.find( '.woocommerce-product-gallery__wrapper ' );
31060		var $product_img_wrap = $gallery_wrapper.find( '.woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder' ).eq( 0 );
31061		var $product_img = img;
31062		var $product_link = img.closest( 'a' );
31063		var $img_zoom = $product_link.next( '.zoomImg' );
31064
31065		if ( data && data.image_link && data.image_link && data.image_link.length > 1 ) {
31066			if ( data.full_src === null || data.full_src === '' ) {
31067				data.full_src = data.image_link;
31068			}
31069			if ( data.full_src_w === null || data.full_src_w === '' ) {
31070				data.full_src_w = $product_img.attr( 'data-large_image_width' );
31071			}
31072			if ( data.full_src_h === null || data.full_src_h === '' ) {
31073				data.full_src_h = $product_img.attr( 'data-large_image_height' );
31074			}
31075			if ( $product_img.length ) {
31076				if ( ! data.image_srcset ) {
31077					data.image_sizes = false;
31078				}
31079				if ( ! data.image_sizes ) {
31080					data.image_srcset = false;
31081				}
31082				$product_img.tc_set_attr( 'src', data.image_link, 0 );
31083				$product_img.tc_set_attr( 'srcset', data.image_srcset, 0 );
31084				$product_img.tc_set_attr( 'sizes', data.image_sizes, 0 );
31085				$product_img.tc_set_attr( 'title', data.image_title, 0 );
31086				$product_img.tc_set_attr( 'alt', data.image_alt, 0 );
31087				$product_img.tc_set_attr( 'data-src', data.full_src, 0 );
31088				$product_img.tc_set_attr( 'data-large_image', data.full_src, 0 );
31089				$product_img.tc_set_attr( 'data-large_image_width', data.full_src_w, 0 );
31090				$product_img.tc_set_attr( 'data-large_image_height', data.full_src_h, 0 );
31091				$product_img_wrap.tc_set_attr( 'data-thumb', data.image_link, 1 );
31092			}
31093			if ( $gallery_img.length ) {
31094				$gallery_img.tc_set_attr( 'src', data.image_link, 2 );
31095				$gallery_img.tc_set_attr( 'srcset', data.image_srcset, 2 );
31096				$gallery_img.tc_set_attr( 'sizes', data.image_sizes, 2 );
31097			}
31098			if ( $product_link.length ) {
31099				$product_link.tc_set_attr( 'href', data.full_src, 3 );
31100				$product_link.tc_set_attr( 'title', data.image_caption, 3 );
31101			}
31102			if ( $img_zoom.length ) {
31103				$img_zoom.tc_set_attr( 'src', data.full_src, 4 );
31104			}
31105		} else {
31106			if ( $product_img.length ) {
31107				$product_img.tc_reset_attr( 'src', 0 );
31108				$product_img.tc_reset_attr( 'srcset', 0 );
31109				$product_img.tc_reset_attr( 'sizes', 0 );
31110				$product_img.tc_reset_attr( 'title', 0 );
31111				$product_img.tc_reset_attr( 'alt', 0 );
31112				$product_img.tc_reset_attr( 'data-src', 0 );
31113				$product_img.tc_reset_attr( 'data-large_image', 0 );
31114				$product_img.tc_reset_attr( 'data-large_image_width', 0 );
31115				$product_img.tc_reset_attr( 'data-large_image_height', 0 );
31116				$product_img_wrap.tc_reset_attr( 'data-thumb', 1 );
31117			}
31118			if ( $gallery_img.length ) {
31119				$gallery_img.tc_reset_attr( 'src', 2 );
31120				$gallery_img.tc_reset_attr( 'srcset', 2 );
31121				$gallery_img.tc_reset_attr( 'sizes', 2 );
31122			}
31123			if ( $product_link.length ) {
31124				$product_link.tc_reset_attr( 'href', 3 );
31125				$product_link.tc_reset_attr( 'title', 3 );
31126			}
31127			if ( $img_zoom.length ) {
31128				$img_zoom.tc_reset_attr( 'src', 4 );
31129			}
31130		}
31131	}
31132
31133	function get_main_product_image( epoObject, product_element ) {
31134		var img;
31135
31136		if ( epoObject.is_associated ) {
31137			img = product_element.find( '.tc-product-image .wp-post-image' ).first();
31138		} else if ( TMEPOJS.tm_epo_global_product_image_selector !== '' ) {
31139			img = $( TMEPOJS.tm_epo_global_product_image_selector );
31140		} else {
31141			img = product_element.find( '.woocommerce-product-gallery__image:not(.clone), .woocommerce-product-gallery__image--placeholder:not(.clone)' ).eq( 0 ).find( '.wp-post-image' ).first();
31142			if ( img.length === 0 ) {
31143				img = product_element.find( 'a.woocommerce-main-image img, img.woocommerce-main-image,a img' ).not( '.thumbnails img,.product_list_widget img' ).first();
31144			}
31145			if ( img.length === 0 ) {
31146				img = product_element.find( 'img.wp-post-image' ).not( '.thumbnails img,.product_list_widget img' ).first();
31147			}
31148			if ( img.length === 0 ) {
31149				img = product_element.find( '.elementor-widget-wd_single_product_gallery img.wp-post-image' ).first();
31150			}
31151			if ( img.length === 0 ) {
31152				img = product_element.find( '.elementor-widget-ae-post-image .wp-post-image' ).first();
31153			}
31154			if ( img.length === 0 ) {
31155				img = $( '.woocommerce div.product div.images' ).not( '.thumbnails img,.product_list_widget img' ).first();
31156			}
31157		}
31158
31159		if ( $( img ).length > 1 ) {
31160			img = $( img ).first();
31161		}
31162
31163		return img;
31164	}
31165
31166	function get_product_element( epoObject ) {
31167		var main_product;
31168		var product_id;
31169		var product_element;
31170
31171		if ( epoObject.is_associated ) {
31172			return epoObject.main_product.closest( '.tc-epo-element-product-container' );
31173		}
31174
31175		main_product = epoObject.main_product;
31176		product_id = epoObject.product_id;
31177		product_element = main_product.closest( '#product-' + product_id );
31178
31179		if ( product_element.length <= 0 ) {
31180			product_element = main_product.closest( '.post-' + product_id );
31181		}
31182
31183		return product_element;
31184	}
31185
31186	function gallery_compatibility_actions( gallery_type, clone_image, preload_img, visible, event_data, $form, product_element ) {
31187		var gallery;
31188		var _elements;
31189		var ge;
31190		var galleryWidth;
31191		var zoomEnabled;
31192		var image;
31193		var zoom_options;
31194
31195		for ( gallery in gallery_type ) {
31196			if ( Object.prototype.hasOwnProperty.call( gallery_type, gallery ) ) {
31197				gallery = gallery_type[ gallery ];
31198
31199				if ( gallery.enabled ) {
31200					switch ( gallery.type ) {
31201						case 'yith':
31202							if ( ! clone_image ) {
31203								if ( ! visible ) {
31204									gallery.yith_wcmg_zoom.attr( 'href', gallery._yith_wcmg_default_zoom );
31205								} else {
31206									gallery.yith_wcmg_zoom.attr( 'href', gallery.yith_wcmg_default_zoom );
31207								}
31208								if ( gallery.element.data( 'yith_magnifier' ) ) {
31209									gallery.element.yith_magnifier( 'destroy' );
31210								}
31211
31212								gallery.element.yith_magnifier( window.yith_magnifier_options );
31213							} else {
31214								clone_image.attr( 'srcset', preload_img ).attr( 'src-orig', preload_img );
31215
31216								if ( gallery.element.data( 'yith_magnifier' ) ) {
31217									gallery.element.yith_magnifier( 'destroy' );
31218								}
31219								_elements = {
31220									elements: {
31221										zoom: $( '.yith_magnifier_zoom' ),
31222										zoomImage: clone_image,
31223										gallery: $( '.yith_magnifier_gallery li a' )
31224									}
31225								};
31226
31227								gallery.element.yith_magnifier( $.extend( true, {}, window.yith_magnifier_options, _elements ) );
31228							}
31229							break;
31230						case 'iosslider':
31231							setTimeout(
31232								function( g ) {
31233									g.element.iosSlider( 'update' );
31234								}.bind( null, gallery ),
31235								150
31236							);
31237							break;
31238						case 'flexslider':
31239							jWindow.trigger( 'resize' );
31240							break;
31241						case 'elevatezoom':
31242							gallery.element.each(
31243								function( g, j ) {
31244									var elevateZoom = j( this ).data( 'elevateZoom' );
31245									if ( typeof elevateZoom !== 'undefined' ) {
31246										elevateZoom.swaptheimage( g, g );
31247									}
31248								}.bind( null, preload_img, $ )
31249							);
31250							break;
31251						case 'easyzoom':
31252							gallery.element.swap( null, preload_img );
31253							break;
31254						case 'easyzoom-flatsome':
31255							gallery.element.swap( preload_img, preload_img );
31256							break;
31257						case 'woocommerce':
31258							if ( clone_image ) {
31259								gallery.element.flexslider( 0 );
31260								gallery.element.trigger( 'woocommerce_gallery_reset_slide_position' );
31261								ge = gallery.element;
31262								window.setTimeout(
31263									function( g, w ) {
31264										g.trigger( 'woocommerce_gallery_init_zoom' );
31265										w.trigger( 'resize' );
31266									}.bind( null, ge, jWindow ),
31267									10
31268								);
31269							}
31270							break;
31271						case 'zoom':
31272							if ( product_element ) {
31273								galleryWidth = product_element.find( '.woocommerce-product-gallery--with-images' ).width();
31274								zoomEnabled = false;
31275
31276								image = gallery.element.find( 'img.wp-post-image' );
31277								if ( image.attr( 'data-large_image_width' ) > galleryWidth ) {
31278									zoomEnabled = true;
31279								}
31280
31281								if ( zoomEnabled ) {
31282									zoom_options = {
31283										touch: false
31284									};
31285									if ( 'ontouchstart' in window ) {
31286										zoom_options.on = 'click';
31287									}
31288
31289									gallery.element.trigger( 'zoom.destroy' );
31290									gallery.element.zoom( zoom_options );
31291								} else {
31292									gallery.element.trigger( 'zoom.destroy' );
31293								}
31294							}
31295							break;
31296					}
31297				}
31298			}
31299		}
31300
31301		jWindow.trigger( 'tm_gallery_compatibility_actions', {
31302			event_data: event_data,
31303			product_element: product_element,
31304			form: $form
31305		} );
31306	}
31307
31308	function get_gallery_type( epoObject, img, product_element ) {
31309		// YITH WooCommerce Zoom Magnifier
31310		var is_yith_wcmg;
31311		var yith_wcmg;
31312		var yith_wcmg_zoom;
31313		var yith_wcmg_default_zoom;
31314		var _yith_wcmg_default_zoom;
31315		var yith_wcmg_default_image;
31316
31317		// iosslider - Touch Enabled, Responsive jQuery Horizontal Content
31318		// Slider/Carousel/Image Gallery Plugin
31319		var is_iosSlider;
31320		var is_iosSlider_element;
31321
31322		// ThemeFusion flexslider
31323		var is_flexslider;
31324		var is_flexslider_element;
31325
31326		// elevateZoom A Jquery Image Zoom Plugin
31327		var is_elevateZoom;
31328		var is_elevateZoom_obj;
31329
31330		// EasyZoom jQuery image zoom plugin
31331		var is_easyzoom;
31332		var is_easyzoom_element;
31333
31334		// new flatsome easyzoom
31335		var is_easyzoom_flatsome;
31336		var is_easyzoom_flatsome_element;
31337
31338		// WooCommerce 2.7x gallery
31339		var is_wc27_gallery;
31340		var is_wc27_gallery_element;
31341		var wc27_zoom_target;
31342		var wc_single_product_params;
31343		var zoom_target_temp;
31344
31345		// fn.zoom
31346		var is_zoom_enabled;
31347		var zoom_images;
31348		var gallery;
31349
31350		if ( epoObject.is_associated ) {
31351			return {};
31352		}
31353
31354		// YITH WooCommerce Zoom Magnifier
31355		is_yith_wcmg = false;
31356		yith_wcmg = $( '.images' );
31357		yith_wcmg_zoom = $( '.yith_magnifier_zoom' );
31358		yith_wcmg_default_zoom = yith_wcmg.find( '.yith_magnifier_zoom' ).first().attr( 'href' );
31359		_yith_wcmg_default_zoom = yith_wcmg_default_zoom;
31360		yith_wcmg_default_image = yith_wcmg.find( '.yith_magnifier_zoom img' ).first().attr( 'src' );
31361
31362		// iosslider - Touch Enabled, Responsive jQuery Horizontal Content
31363		// Slider/Carousel/Image Gallery Plugin
31364		is_iosSlider = false;
31365		is_iosSlider_element = $( '.iosSlider.product-gallery-slider,.iosSlider.product-slider' );
31366
31367		// ThemeFusion flexslider
31368		is_flexslider = false;
31369		is_flexslider_element = product_element.find( '.images .fusion-flexslider' );
31370
31371		// elevateZoom A Jquery Image Zoom Plugin
31372		is_elevateZoom = img.data( 'elevateZoom' ) || false;
31373		is_elevateZoom_obj = product_element.find( 'div.product-images .woocommerce-main-image' );
31374
31375		// EasyZoom jQuery image zoom plugin
31376		is_easyzoom = false;
31377		is_easyzoom_element = product_element.find( '.images .easyzoom' );
31378
31379		// new flatsome easyzoom
31380		is_easyzoom_flatsome = false;
31381		is_easyzoom_flatsome_element = product_element.find( '.images .easyzoom' );
31382
31383		// WooCommerce 2.7x gallery
31384		is_wc27_gallery = false;
31385		is_wc27_gallery_element = product_element.find( '.woocommerce-product-gallery' );
31386		wc27_zoom_target = false;
31387		wc_single_product_params = window.wc_single_product_params;
31388
31389		// fn.zoom
31390		is_zoom_enabled = typeof $.fn.zoom === 'function' && wc_single_product_params && wc_single_product_params.zoom_enabled;
31391		zoom_images = false;
31392
31393		if ( window.yith_magnifier_options && yith_wcmg.data( 'yith_magnifier' ) ) {
31394			is_yith_wcmg = true;
31395		}
31396
31397		if ( is_iosSlider_element.length && is_iosSlider_element.iosSlider ) {
31398			is_iosSlider = true;
31399		}
31400
31401		if ( is_flexslider_element.length && is_flexslider_element.flexslider ) {
31402			is_flexslider = true;
31403		}
31404
31405		if ( is_easyzoom_element.length && is_easyzoom_element.filter( '.images .easyzoom.first' ).data( 'easyZoom' ) ) {
31406			is_easyzoom_element = is_easyzoom_element.filter( '.images .easyzoom.first' ).data( 'easyZoom' );
31407			is_easyzoom = true;
31408		}
31409
31410		if ( ! is_easyzoom ) {
31411			is_easyzoom_flatsome_element = product_element.find( '.images .has-image-zoom .slide' );
31412			if ( is_easyzoom_flatsome_element.length && is_easyzoom_flatsome_element.filter( '.images .has-image-zoom .slide.first' ).data( 'easyZoom' ) ) {
31413				is_easyzoom_flatsome_element = is_easyzoom_flatsome_element.filter( '.images .has-image-zoom .slide.first' ).data( 'easyZoom' );
31414				is_easyzoom_flatsome = true;
31415			}
31416		}
31417
31418		if ( document.readyState === 'complete' ) {
31419			setTimeout( function() {
31420				if ( is_easyzoom_element.length && is_easyzoom_element.data( 'easyZoom' ) ) {
31421					is_easyzoom_element = is_easyzoom_element.data( 'easyZoom' );
31422					is_easyzoom = true;
31423				}
31424				if ( is_easyzoom_flatsome_element.length && is_easyzoom_flatsome_element.data( 'easyZoom' ) ) {
31425					is_easyzoom_flatsome_element = is_easyzoom_flatsome_element.data( 'easyZoom' );
31426					is_easyzoom_flatsome = true;
31427				}
31428			}, 150 );
31429		} else {
31430			jWindow.on( 'load', function() {
31431				setTimeout( function() {
31432					if ( is_easyzoom_element.length && is_easyzoom_element.data( 'easyZoom' ) ) {
31433						is_easyzoom_element = is_easyzoom_element.data( 'easyZoom' );
31434						is_easyzoom = true;
31435					}
31436					if ( is_easyzoom_flatsome_element.length && is_easyzoom_flatsome_element.data( 'easyZoom' ) ) {
31437						is_easyzoom_flatsome_element = is_easyzoom_flatsome_element.data( 'easyZoom' );
31438						is_easyzoom_flatsome = true;
31439					}
31440				}, 150 );
31441			} );
31442		}
31443
31444		if ( is_wc27_gallery_element.length && is_wc27_gallery_element.data( 'flexslider' ) ) {
31445			is_wc27_gallery = true;
31446
31447			if ( typeof $.fn.zoom === 'function' && wc_single_product_params && wc_single_product_params.zoom_enabled ) {
31448				zoom_target_temp = img.closest( '.woocommerce-product-gallery__image' );
31449				// Ommiting the following condition:
31450				// && img.width() > $( '.woocommerce-product-gallery' ).width()
31451				if ( zoom_target_temp.length > 0 ) {
31452					wc27_zoom_target = zoom_target_temp;
31453					img.data.wc27_zoom_target = wc27_zoom_target;
31454				}
31455			}
31456		}
31457
31458		if ( ! is_wc27_gallery && is_zoom_enabled ) {
31459			zoom_images = product_element.find( '.woocommerce-product-gallery .woocommerce-product-gallery__image' );
31460		}
31461
31462		gallery = {
31463			is_yith_wcmg: {
31464				type: 'yith',
31465				enabled: is_yith_wcmg,
31466				element: yith_wcmg,
31467				yith_wcmg_zoom: yith_wcmg_zoom,
31468				_yith_wcmg_default_zoom: _yith_wcmg_default_zoom,
31469				yith_wcmg_default_image: yith_wcmg_default_image
31470			},
31471			is_iosSlider: {
31472				type: 'iosslider',
31473				enabled: is_iosSlider,
31474				element: is_iosSlider_element
31475			},
31476			is_flexslider: {
31477				type: 'flexslider',
31478				enabled: is_flexslider,
31479				element: is_flexslider_element
31480			},
31481			is_elevateZoom: {
31482				type: 'elevatezoom',
31483				enabled: is_elevateZoom,
31484				element: is_elevateZoom_obj
31485			},
31486			is_easyzoom: {
31487				type: 'easyzoom',
31488				enabled: is_easyzoom,
31489				element: is_easyzoom_element
31490			},
31491			is_easyzoom_flatsome: {
31492				type: 'easyzoom-flatsome',
31493				enabled: is_easyzoom_flatsome,
31494				element: is_easyzoom_flatsome_element
31495			},
31496			is_wc27_gallery: {
31497				type: 'woocommerce',
31498				enabled: is_wc27_gallery,
31499				element: is_wc27_gallery_element
31500			},
31501			is_zoom_enabled: {
31502				type: 'zoom',
31503				enabled: ! is_wc27_gallery && is_zoom_enabled,
31504				element: zoom_images
31505			}
31506		};
31507
31508		return gallery;
31509	}
31510
31511	function tm_product_image_self( epoObject ) {
31512		var this_epo_container = epoObject.is_associated ? epoObject.this_epo_container : epoObject.this_epo_container.not( tcAPI.epoSelector + ' ' + tcAPI.associatedEpoSelector );
31513		var variationsForm = epoObject.variations_form;
31514		var main_product = epoObject.main_product;
31515		var $form = epoObject.form;
31516		var img;
31517		var gallery_type;
31518		var product_element = get_product_element( epoObject );
31519		var last_active_field = [];
31520		var el;
31521		var el_current;
31522		var imp;
31523		var selector;
31524		var eventNamePrefix = epoObject.is_associated ? 'tc_' : '';
31525		// This currently has a limitation that it doesn't work if there is variation active onload.
31526		var main_tc_product_image_store;
31527
31528		img = get_main_product_image( epoObject, product_element );
31529		gallery_type = get_gallery_type( epoObject, img, product_element );
31530
31531		if ( $( img ).length > 0 ) {
31532			$form.on( eventNamePrefix + 'reset_image.tcpi', function() {
31533				setTimeout( function() {
31534					if ( TMEPOJS.tm_epo_global_image_recalculate === 'yes' ) {
31535						img = get_main_product_image( epoObject, product_element );
31536						$.tc_product_image_store = main_tc_product_image_store;
31537					}
31538					// restore product image atts from backup
31539					$.tc_product_image = $.tc_replace_object_values( $.tc_product_image, $.tc_product_image_store );
31540
31541					last_active_field = [];
31542
31543					$( this_epo_container.find( '.tm-product-image:checked,select.tm-product-image' ) )
31544						.add( variationsForm.find( '.tm-epo-variation-section' ).first().find( '.tm-product-image:checked,select.tm-product-image' ) )
31545						.toArray().forEach( function( pi ) {
31546							pi = $( pi );
31547							if ( field_is_active( pi ) && pi.val() !== '' ) {
31548								last_active_field.push( pi );
31549							}
31550						} );
31551					if ( last_active_field.length ) {
31552						last_active_field[ last_active_field.length - 1 ].trigger( 'tm_trigger_product_image' );
31553					} else {
31554						$.tc_product_image_store = $.tc_populate_store( img, product_element );
31555					}
31556				}, window.tc_epo_product_image_change_delay || 0 );
31557			} );
31558
31559			$form.on( eventNamePrefix + 'found_variation.tcpi', function() {
31560				setTimeout( function() {
31561					if ( TMEPOJS.tm_epo_global_image_recalculate === 'yes' ) {
31562						img = get_main_product_image( epoObject, product_element );
31563					}
31564					reset_saved_image( img, product_element );
31565
31566					last_active_field = [];
31567					$( this_epo_container.find( '.tm-product-image:checked,select.tm-product-image' ) )
31568						.add( variationsForm.find( '.tm-epo-variation-section' ).first().find( '.tm-product-image:checked,select.tm-product-image' ) )
31569						.toArray().forEach( function( pi ) {
31570							pi = $( pi );
31571							if ( field_is_active( pi ) && pi.val() !== '' ) {
31572								last_active_field.push( pi );
31573							}
31574						} );
31575					if ( last_active_field.length ) {
31576						repopulate_backup_image_atts( img, product_element );
31577						last_active_field[ last_active_field.length - 1 ].trigger( 'tm_trigger_product_image' );
31578					}
31579				}, window.tc_epo_product_image_change_delay || 0 );
31580			} );
31581
31582			$.tc_product_image_store = $.tc_maybe_copy_object_values_from_img( $.tc_product_image_store, img, product_element );
31583			main_tc_product_image_store = $.epoAPI.util.deepCopyArray( $.tc_product_image_store );
31584
31585			main_product.off( 'tm_change_product_image' ).on( 'tm_change_product_image', function( evt, event_data ) {
31586				var data;
31587				evt.stopImmediatePropagation();
31588
31589				el = event_data.element;
31590				el_current = event_data.element_current;
31591				if ( el && el_current ) {
31592					imp = el.data( 'imagep' );
31593					selector = '';
31594					if ( imp !== '' ) {
31595						selector = 'imagep';
31596					} else if ( el.data( 'changes-product-image' ) === 'images' ) {
31597						selector = 'image';
31598					}
31599					data = event_data.element_current.data( 'image-variations' );
31600
31601					if ( data ) {
31602						data = data[ selector ];
31603					}
31604
31605					if ( data === undefined ) {
31606						// Enter the following if you want to restore the original product image
31607						// main_product.trigger( 'tm_restore_product_image', event_data );
31608						return;
31609					}
31610
31611					last_active_field = [];
31612					$( this_epo_container.find( '.tm-product-image:checked,select.tm-product-image' ) )
31613						.add( variationsForm.find( '.tm-epo-variation-section' ).first().find( '.tm-product-image:checked,select.tm-product-image' ) )
31614						.toArray().forEach( function( pi ) {
31615							pi = $( pi );
31616							if ( pi.is( 'option' ) ) {
31617								pi = pi.closest( 'select' );
31618							}
31619							if ( pi.val() !== '' && field_is_active( pi ) ) {
31620								last_active_field.push( pi );
31621							}
31622						} );
31623
31624					if ( last_active_field.length ) {
31625						if ( ! last_active_field[ last_active_field.length - 1 ].is( el ) ) {
31626							return;
31627						}
31628					}
31629
31630					setTimeout( function() {
31631						if ( TMEPOJS.tm_epo_global_image_recalculate === 'yes' ) {
31632							img = get_main_product_image( epoObject, product_element );
31633						}
31634						image_update( data, img, product_element );
31635						gallery_compatibility_actions( gallery_type, img, data.image_link, false, event_data, $form, product_element );
31636					}, window.tc_epo_product_image_change_delay || 0 );
31637				}
31638			} );
31639
31640			main_product.off( 'tm_restore_product_image' ).on( 'tm_restore_product_image', function( evt, event_data ) {
31641				evt.stopImmediatePropagation();
31642
31643				el = event_data ? event_data.element : false;
31644				last_active_field = [];
31645
31646				if ( el ) {
31647					$( this_epo_container.find( '.tm-product-image:checked,select.tm-product-image' ) )
31648						.add( variationsForm.find( '.tm-epo-variation-section' ).first().find( '.tm-product-image:checked,select.tm-product-image' ) )
31649						.toArray().forEach( function( pi ) {
31650							pi = $( pi );
31651							if ( field_is_active( pi ) && pi.val() !== '' ) {
31652								last_active_field.push( pi );
31653							}
31654						} );
31655					if ( last_active_field.length ) {
31656						if ( ! last_active_field[ last_active_field.length - 1 ].is( el ) ) {
31657							last_active_field[ last_active_field.length - 1 ].trigger( 'tm_trigger_product_image' );
31658							return;
31659						}
31660					}
31661				}
31662
31663				setTimeout( function() {
31664					if ( TMEPOJS.tm_epo_global_image_recalculate === 'yes' ) {
31665						img = get_main_product_image( epoObject, product_element );
31666					}
31667					image_update( false, img, product_element );
31668					gallery_compatibility_actions( gallery_type, false, img.attr( 'src' ), false, event_data, $form, product_element );
31669				}, window.tc_epo_product_image_change_delay || 0 );
31670			} );
31671
31672			last_active_field = [];
31673			$( this_epo_container.find( '.tm-product-image:checked,select.tm-product-image' ) )
31674				.add( variationsForm.find( '.tm-epo-variation-section' ).first().find( '.tm-product-image:checked,select.tm-product-image' ) )
31675				.toArray().forEach( function( pi ) {
31676					pi = $( pi );
31677					if ( field_is_active( pi ) && pi.val() !== '' ) {
31678						last_active_field.push( pi );
31679					}
31680				} );
31681			if ( last_active_field.length ) {
31682				last_active_field[ last_active_field.length - 1 ].trigger( 'tm_trigger_product_image' );
31683			}
31684		}
31685
31686		jWindow.trigger( 'tm_product_image_loaded' );
31687	}
31688
31689	function tm_product_image_inline( epoObject ) {
31690		var this_epo_container = epoObject.is_associated ? epoObject.this_epo_container : epoObject.this_epo_container.not( tcAPI.epoSelector + ' ' + tcAPI.associatedEpoSelector );
31691		var main_product = epoObject.main_product;
31692		var product_id = epoObject.product_id;
31693		var $form = epoObject.form;
31694		var img;
31695		var product_element = get_product_element( epoObject );
31696		var gallery_type;
31697		var a;
31698		var img_src_original;
31699		var img_width;
31700		var img_height;
31701		var last_active_field = [];
31702
31703		img = get_main_product_image( epoObject, product_element );
31704
31705		gallery_type = get_gallery_type( epoObject, img, product_element );
31706
31707		if ( $( img ).length > 0 ) {
31708			img.data( 'tm-current-image', false );
31709			a = img.closest( 'a' );
31710			img_src_original = img.attr( 'src' );
31711			img_width = img.width();
31712			img_height = img.height();
31713
31714			main_product.off( 'tm_change_product_image' ).on( 'tm_change_product_image', function( evt, e ) {
31715				setTimeout( function() {
31716					var variation_element_section;
31717					var is_variation_element;
31718					var $this_epo_container;
31719					var tm_last_visible_image_element;
31720					var last_activate_field = [];
31721					var tm_current_image_element_id;
31722					var can_show_image;
31723					var $main_product;
31724					var $current_product_element;
31725					var preload_width;
31726					var preload_height;
31727					var current_cloned_image;
31728					var preloader;
31729					var clone_image;
31730					var preload_img;
31731					var preload_img_onerror;
31732
31733					if ( TMEPOJS.tm_epo_global_image_recalculate === 'yes' ) {
31734						img = get_main_product_image( epoObject, product_element );
31735					}
31736
31737					variation_element_section = e.element.closest( '.cpf-section' );
31738					is_variation_element = variation_element_section.is( '.tm-epo-variation-section' );
31739					$this_epo_container = e.epo_holder;
31740					if ( is_variation_element ) {
31741						$this_epo_container = variation_element_section;
31742					}
31743					tm_last_visible_image_element = $this_epo_container.find( '.tm-product-image:checked,select.tm-product-image' );
31744					last_activate_field = [];
31745					tm_current_image_element_id = e.element.attr( 'id' );
31746					can_show_image = true;
31747					$main_product = e.main_product;
31748					$current_product_element = $main_product.closest( '#product-' + product_id );
31749					preload_width = img_width;
31750					preload_height = img_height;
31751					preloader = $( "<div class='blockUI blockOverlay tm-preloader-img'></div>" );
31752
31753					if ( $current_product_element.length <= 0 ) {
31754						$current_product_element = $main_product.closest( '.post-' + product_id );
31755					}
31756
31757					current_cloned_image = $current_product_element.find( '#' + tm_current_image_element_id + '_tmimage' );
31758					if ( current_cloned_image.length === 0 ) {
31759						current_cloned_image = img;
31760					}
31761
31762					preload_img_onerror = function() {
31763						preloader.remove();
31764						$form.tc_image_update( false );
31765						img.data( 'tm-current-image', false );
31766						$current_product_element.find( '.tm-clone-product-image' ).hide();
31767						img.show();
31768					};
31769
31770					if ( e.src === current_cloned_image.attr( 'src' ) && current_cloned_image.is( ':visible' ) ) {
31771						return;
31772					}
31773
31774					if ( e.src === false ) {
31775						preload_img_onerror();
31776						return;
31777					}
31778
31779					preloader.css( {
31780						width: preload_width,
31781						height: preload_height
31782					} );
31783
31784					// Get last active field
31785					tm_last_visible_image_element.toArray().forEach( function( ie ) {
31786						ie = $( ie );
31787						if (
31788							field_is_active( ie ) &&
31789							has_active_changes_product_image( ie ) &&
31790							tm_check_field_match( {
31791								element: ie.closest( '.cpf-element' ),
31792								rule: {
31793									operator: 'isnotempty',
31794									value: ''
31795								},
31796								type: 'element'
31797							} )
31798						) {
31799							last_activate_field.push( ie );
31800						}
31801					} );
31802					// Get last active image
31803					if ( last_activate_field.length ) {
31804						tm_last_visible_image_element = last_activate_field[ last_activate_field.length - 1 ];
31805					}
31806
31807					if ( tm_last_visible_image_element.attr( 'id' ) !== e.element.attr( 'id' ) ) {
31808						can_show_image = false;
31809					}
31810
31811					clone_image = img.tcClone();
31812					preload_img = new Image();
31813					clone_image.removeAttr( 'data-o_src' ).removeAttr( 'data-o_title' ).removeAttr( 'data-o_alt' ).removeAttr( 'data-o_srcset' ).removeAttr( 'data-o_sizes' ).removeAttr( 'srcset' ).removeAttr( 'sizes' );
31814
31815					if ( can_show_image ) {
31816						img.before( preloader );
31817					}
31818
31819					gallery_type.is_yith_wcmg.yith_wcmg_default_zoom = gallery_type.is_yith_wcmg.element.find( '.yith_magnifier_zoom' ).first().attr( 'href' );
31820					gallery_type.is_yith_wcmg.yith_wcmg_default_image = gallery_type.is_yith_wcmg.element.find( '.yith_magnifier_zoom img' ).first().attr( 'src' );
31821
31822					preload_img.onerror = function() {
31823						preload_img_onerror();
31824					};
31825
31826					preload_img.onload = function() {
31827						if ( 'naturalHeight' in this ) {
31828							if ( this.naturalHeight + this.naturalWidth === 0 ) {
31829								this.onerror();
31830								return;
31831							}
31832						} else if ( this.width + this.height === 0 ) {
31833							this.onerror();
31834							return;
31835						}
31836						$current_product_element.find( '#' + tm_current_image_element_id + '_tmimage' ).remove();
31837						$current_product_element.find( '.tm-clone-product-image' ).hide();
31838						clone_image.prop( 'src', preload_img.src ).hide();
31839
31840						img.hide().after( clone_image );
31841
31842						clone_image.css( 'opacity', 0 ).show();
31843
31844						gallery_compatibility_actions( gallery_type, clone_image, preload_img.src );
31845
31846						preloader.animate(
31847							{
31848								opacity: 0
31849							},
31850							750,
31851							'easeOutExpo',
31852							function() {
31853								preloader.remove();
31854							}
31855						);
31856						clone_image.animate(
31857							{
31858								opacity: 1
31859							},
31860							window.tc_epo_image_animation_delay || 1500,
31861							'easeOutExpo',
31862							function() {}
31863						);
31864
31865						jWindow.trigger( 'tm_change_product_image_loaded', {
31866							src: e.src,
31867							element: e.element,
31868							main_product: e.main_product,
31869							epo_holder: e.epo_holder
31870						} );
31871					};
31872
31873					clone_image
31874						.attr( 'id', tm_current_image_element_id + '_tmimage' )
31875						.addClass( 'tm-clone-product-image' )
31876						.hide();
31877
31878					if ( clone_image.attr( 'src-orig' ) ) {
31879						clone_image.attr( 'src-orig', e.src );
31880					}
31881
31882					if ( can_show_image ) {
31883						preload_img.src = e.src;
31884
31885						$form.tc_image_update( e.element, clone_image );
31886
31887						img.data( 'tm-current-image', tm_current_image_element_id );
31888
31889						jWindow.trigger( 'tm_change_product_image_show', {
31890							src: e.src,
31891							element: e.element,
31892							main_product: e.main_product,
31893							epo_holder: e.epo_holder
31894						} );
31895					} else {
31896						clone_image.prop( 'src', e.src ).hide();
31897						img.after( clone_image );
31898					}
31899
31900					jWindow.trigger( 'tm_change_product_image_end', {
31901						src: e.src,
31902						element: e.element,
31903						main_product: e.main_product,
31904						epo_holder: e.epo_holder
31905					} );
31906				}, window.tc_epo_product_image_change_delay || 0 );
31907			} );
31908
31909			main_product.off( 'tm_restore_product_image' ).on( 'tm_restore_product_image', function( evt, e ) {
31910				setTimeout( function() {
31911					var tm_current_image_element_id;
31912					var $main_product;
31913					var $current_product_element;
31914					var variation_element_section;
31915					var is_variation_element;
31916					var current_element;
31917					var current_image_replacement;
31918					var found;
31919					var is_it_visible;
31920					var len;
31921					var el_to_check;
31922					var imgSrc;
31923					var $this_epo_container;
31924					var i;
31925
31926					if ( ! e || ! e.element ) {
31927						return false;
31928					}
31929					if ( TMEPOJS.tm_epo_global_image_recalculate === 'yes' ) {
31930						img = get_main_product_image( epoObject, product_element );
31931					}
31932
31933					jWindow.trigger( 'tm_restore_product_image_pre', {
31934						element: e.element,
31935						main_product: e.main_product,
31936						epo_holder: e.epo_holder
31937					} );
31938					tm_current_image_element_id = e.element.attr( 'id' );
31939					$main_product = e.main_product;
31940					$current_product_element = $main_product.closest( '#product-' + product_id );
31941					variation_element_section = e.element.closest( '.cpf-section' );
31942					is_variation_element = variation_element_section.is( '.tm-epo-variation-section' );
31943					found = false;
31944					imgSrc = img_src_original;
31945					$this_epo_container = e.epo_holder;
31946					if ( is_variation_element ) {
31947						$this_epo_container = variation_element_section;
31948					}
31949
31950					if ( $current_product_element.length <= 0 ) {
31951						$current_product_element = $main_product.closest( '.post-' + product_id );
31952					}
31953
31954					is_it_visible = $current_product_element.find( '#' + tm_current_image_element_id + '_tmimage' ).is( ':visible' );
31955
31956					$current_product_element.find( '#' + tm_current_image_element_id + '_tmimage' ).remove();
31957
31958					if ( $current_product_element.find( '.tm-clone-product-image' ).length === 0 ) {
31959						img.show();
31960						img.data( 'tm-current-image', false );
31961						$form.tc_image_update( false );
31962					} else {
31963						if ( ! is_it_visible ) {
31964							jWindow.trigger( 'tm_restore_product_image_loaded_exit', {
31965								element: e.element,
31966								main_product: e.main_product,
31967								epo_holder: e.epo_holder
31968							} );
31969							return;
31970						}
31971
31972						len = $current_product_element.find( '.tm-clone-product-image' ).length;
31973						tm_current_image_element_id = img.data( 'tm-current-image' );
31974
31975						for ( i = len - 1; i >= 0; i -= 1 ) {
31976							current_image_replacement = $current_product_element.find( '.tm-clone-product-image' ).eq( i );
31977							current_element = current_image_replacement.attr( 'id' ).replace( '_tmimage', '' );
31978							el_to_check = $this_epo_container.find( "[id='" + current_element + "']" );
31979
31980							if ( el_to_check.is( ':checked' ) && el_to_check.closest( '.cpf-element' ).is( ':visible' ) ) {
31981								$current_product_element.find( '.tm-clone-product-image' ).eq( i ).show();
31982								a.attr( 'href', $current_product_element.find( '.tm-clone-product-image' ).eq( i ).prop( 'src' ) );
31983								img.data( 'tm-current-image', current_element );
31984								found = true;
31985								break;
31986							} else {
31987								$current_product_element.find( '.tm-clone-product-image' ).eq( i ).hide();
31988							}
31989						}
31990						if ( ! found ) {
31991							img.show();
31992							img.data( 'tm-current-image', false );
31993							$form.tc_image_update( false );
31994						} else {
31995							$current_product_element.find( '#' + tm_current_image_element_id + '_tmimage' ).remove();
31996						}
31997					}
31998
31999					if ( found ) {
32000						imgSrc = current_image_replacement.attr( 'src' );
32001					}
32002
32003					gallery_compatibility_actions( gallery_type, false, imgSrc, $current_product_element.find( '.tm-clone-product-image' ).filter( ':visible' ).length );
32004
32005					jWindow.trigger( 'tm_restore_product_image_loaded', {
32006						element: e.element,
32007						main_product: e.main_product,
32008						epo_holder: e.epo_holder
32009					} );
32010				}, window.tc_epo_product_image_change_delay || 0 );
32011			} );
32012
32013			main_product.off( 'tm_attempt_product_image' ).on( 'tm_attempt_product_image', function( evt, e ) {
32014				setTimeout( function() {
32015					var $main_product;
32016					var $current_product_element;
32017					var variation_element_section;
32018					var is_variation_element;
32019					var $this_epo_container;
32020					var tm_last_visible_image_element;
32021					var last_activate_field;
32022					var tm_last_visible_image_element_id;
32023					var current_image_replacement;
32024					var current_element;
32025					var found;
32026					var tm_current_image_element_id;
32027					var len;
32028					var imgSrc;
32029					var el_to_check;
32030					var tmcie_id;
32031					var i;
32032
32033					if ( TMEPOJS.tm_epo_global_image_recalculate === 'yes' ) {
32034						img = get_main_product_image( epoObject, product_element );
32035					}
32036
32037					$main_product = e.main_product;
32038					$current_product_element = $main_product.closest( '#product-' + product_id );
32039					if ( e.element ) {
32040						variation_element_section = e.element.closest( '.cpf-section' );
32041					} else {
32042						variation_element_section = $( $main_product.find( '.tm-epo-variation-section' ).first(), e.epo_holder );
32043					}
32044					is_variation_element = variation_element_section.is( '.tm-epo-variation-section' );
32045					$this_epo_container = e.epo_holder;
32046					if ( is_variation_element ) {
32047						$this_epo_container = variation_element_section;
32048					}
32049					tm_last_visible_image_element = $this_epo_container.find( '.tm-product-image:checked,select.tm-product-image' );
32050					last_activate_field = [];
32051					tm_last_visible_image_element_id = '';
32052					found = false;
32053					tm_current_image_element_id = img.data( 'tm-current-image' );
32054					imgSrc = img_src_original;
32055
32056					if ( $current_product_element.length <= 0 ) {
32057						$current_product_element = $main_product.closest( '.post-' + product_id );
32058					}
32059
32060					$this_epo_container = $main_product.find( '.tm-epo-variation-section' ).first().add( e.epo_holder );
32061					tm_last_visible_image_element = $this_epo_container.find( '.tm-product-image:checked,select.tm-product-image' );
32062
32063					tm_last_visible_image_element.toArray().forEach( function( ie ) {
32064						ie = $( ie );
32065						if (
32066							field_is_active( ie ) &&
32067							has_active_changes_product_image( ie ) &&
32068							tm_check_field_match( {
32069								element: ie.closest( '.cpf-element' ),
32070								rule: {
32071									operator: 'isnotempty',
32072									value: ''
32073								},
32074								type: 'element'
32075							} )
32076						) {
32077							last_activate_field.push( ie );
32078						}
32079					} );
32080
32081					if ( last_activate_field.length ) {
32082						tm_last_visible_image_element = last_activate_field[ last_activate_field.length - 1 ];
32083						tm_last_visible_image_element_id = tm_last_visible_image_element.attr( 'id' );
32084					}
32085
32086					if ( last_activate_field.length && tm_last_visible_image_element.length && ( ! tm_current_image_element_id || tm_last_visible_image_element_id !== tm_current_image_element_id ) ) {
32087						tm_last_visible_image_element.last().trigger( 'tm_trigger_product_image' );
32088						return;
32089					}
32090
32091					tmcie_id = $this_epo_container.find( "[id='" + tm_current_image_element_id + "']" ).closest( '.cpf-element' );
32092					if ( ! tm_current_image_element_id || ( tmcie_id.data( 'isactive' ) !== false && tmcie_id.closest( '.cpf-section' ).data( 'isactive' ) !== false ) ) {
32093						return;
32094					}
32095
32096					$current_product_element.find( '#' + tm_current_image_element_id + '_tmimage' ).remove();
32097					len = $current_product_element.find( '.tm-clone-product-image' ).length;
32098
32099					if ( len === 0 ) {
32100						img.show();
32101						img.data( 'tm-current-image', false );
32102						$form.tc_image_update( false );
32103					} else {
32104						for ( i = len - 1; i >= 0; i -= 1 ) {
32105							current_image_replacement = $current_product_element.find( '.tm-clone-product-image' ).eq( i );
32106							current_element = current_image_replacement.attr( 'id' ).replace( '_tmimage', '' );
32107							el_to_check = $this_epo_container.find( "[id='" + current_element + "']" );
32108
32109							if ( el_to_check.is( ':checked' ) && el_to_check.closest( '.cpf-element' ).is( ':visible' ) ) {
32110								$current_product_element.find( '.tm-clone-product-image' ).eq( i ).show();
32111								a.attr( 'href', $current_product_element.find( '.tm-clone-product-image' ).eq( i ).prop( 'src' ) );
32112								img.data( 'tm-current-image', current_element );
32113								found = true;
32114								break;
32115							} else {
32116								$current_product_element.find( '.tm-clone-product-image' ).eq( i ).hide();
32117							}
32118						}
32119
32120						if ( ! found ) {
32121							img.show();
32122							img.data( 'tm-current-image', false );
32123							$form.tc_image_update( false );
32124						}
32125					}
32126
32127					if ( found ) {
32128						imgSrc = current_image_replacement.attr( 'src' );
32129					}
32130
32131					gallery_compatibility_actions( gallery_type, false, imgSrc, $current_product_element.find( '.tm-clone-product-image' ).filter( ':visible' ).length );
32132				}, window.tc_epo_product_image_change_delay || 0 );
32133			} );
32134
32135			$( this_epo_container.find( '.tm-product-image:checked,select.tm-product-image' ) )
32136				.add( main_product.find( '.tm-epo-variation-section' ).first().find( '.tm-product-image:checked,select.tm-product-image' ) )
32137				.toArray().forEach( function( pi ) {
32138					pi = $( pi );
32139					if ( field_is_active( pi ) && pi.val() !== '' ) {
32140						last_active_field.push( pi );
32141					}
32142				} );
32143			if ( last_active_field.length ) {
32144				last_active_field[ last_active_field.length - 1 ].trigger( 'tm_trigger_product_image' );
32145			}
32146		}
32147
32148		jWindow.trigger( 'tm_product_image_loaded' );
32149	}
32150
32151	function tm_product_image( epoObject ) {
32152		var galleryImages;
32153		setTimeout( function() {
32154			if ( TMEPOJS.tm_epo_global_product_image_mode === 'inline' ) {
32155				tm_product_image_inline( epoObject );
32156			} else {
32157				tm_product_image_self( epoObject );
32158			}
32159
32160			if ( $.fn.wc_product_gallery ) {
32161				galleryImages = epoObject.this_epo_container;
32162				if ( epoObject.is_associated ) {
32163					galleryImages = epoObject.main_product;
32164				}
32165				galleryImages.find( '.cpf-type-product-custombundle .tc-associated-product-image' ).tclightbox();
32166			}
32167		}, window.tc_epo_product_image_setup_delay || 0 );
32168	}
32169
32170	function tc_compatibility( epoObject ) {
32171		jWindow.trigger( 'tm-epo-compatibility', {
32172			epo: epoObject
32173		} );
32174	}
32175
32176	// Range picker setup
32177	function setRangePickers( obj ) {
32178		if ( ! noUiSlider ) {
32179			return;
32180		}
32181		obj.find( '.tm-range-picker' )
32182			.toArray()
32183			.forEach( function( picker ) {
32184				var el = $( picker );
32185				var $decimals = el.attr( 'data-step' ).split( '.' );
32186				var $tmfid = obj.find( '#' + $.epoAPI.dom.id( el.attr( 'data-field-id' ) ) );
32187				var $min = parseFloat( el.attr( 'data-min' ) );
32188				var $max = parseFloat( el.attr( 'data-max' ) );
32189				var $start = parseFloat( el.attr( 'data-start' ) );
32190				var $step = parseFloat( el.attr( 'data-step' ) );
32191				var $show_picker_value = el.attr( 'data-show-picker-value' );
32192				var $show_label = el.closest( 'li' ).find( '.tm-show-picker-value' );
32193				var $noofpips = parseFloat( el.attr( 'data-noofpips' ) );
32194				var $pips = null;
32195				var $tmh;
32196
32197				if ( el.data( 'tc-picker-init' ) ) {
32198					return;
32199				}
32200				el.data( 'tc-picker-init', 1 );
32201
32202				if ( $decimals.length === 1 ) {
32203					$decimals = 0;
32204				} else {
32205					$decimals = $decimals[ 1 ].length;
32206				}
32207				if ( ! Number.isFinite( $min ) ) {
32208					$min = 0;
32209				}
32210				if ( ! Number.isFinite( $max ) ) {
32211					$max = 0;
32212				}
32213				if ( $max <= $min ) {
32214					$max = parseFloat( $max ) + 1;
32215				}
32216				$start = $.epoAPI.math.unformat( $start, tcAPI.localDecimalSeparator );
32217				if ( ! Number.isFinite( $start ) ) {
32218					$start = 0;
32219				}
32220				$start = formatPrice( $start, { precision: $decimals } );
32221				if ( ! Number.isFinite( $step ) ) {
32222					$step = 0;
32223				}
32224				if ( ! Number.isFinite( $noofpips ) ) {
32225					$noofpips = 10;
32226				}
32227				if ( $noofpips < 2 ) {
32228					$noofpips = 2;
32229				}
32230
32231				if ( el.attr( 'data-pips' ) === 'yes' ) {
32232					$pips = {
32233						mode: 'count',
32234						values: $noofpips,
32235						filter: function( value, type ) {
32236							value = parseFloat( $.epoAPI.math.toFixed( value, $decimals ) );
32237
32238							if ( $step <= 0 ) {
32239								return 0;
32240							}
32241
32242							if ( type === 1 ) {
32243								if ( ! Number.isInteger( value ) ) {
32244									return 2;
32245								}
32246							}
32247
32248							return type;
32249						},
32250						format: {
32251							from: function( value ) {
32252								return $.epoAPI.math.unformat( value, tcAPI.localInputDecimalSeparator );
32253							},
32254							to: function( value ) {
32255								return formatPrice( value, { precision: $decimals } );
32256							}
32257						},
32258						density: 2
32259					};
32260				}
32261
32262				noUiSlider.create( el.get( 0 ), {
32263					direction: TMEPOJS.text_direction,
32264					start: $start,
32265					step: $step,
32266					connect: 'lower',
32267					// Configure tapping, or make the selected range dragable.
32268					behaviour: 'tap',
32269					// Full number format support.
32270					format: {
32271						from: function( value ) {
32272							return $.epoAPI.math.unformat( value, tcAPI.localDecimalSeparator );
32273						},
32274						to: function( value ) {
32275							return formatPrice( value, { precision: $decimals } );
32276						}
32277					},
32278					// Support for non-linear ranges by adding intervals.
32279					range: {
32280						min: [ $min ],
32281						max: [ $max ]
32282					},
32283					pips: $pips,
32284					tooltips: {
32285						from: function( value ) {
32286							return $.epoAPI.math.unformat( value, tcAPI.localInputDecimalSeparator );
32287						},
32288						to: function( value ) {
32289							return formatPrice( value, { precision: $decimals } );
32290						}
32291					}
32292				} );
32293
32294				$tmh = el.find( '.noui-handle-lower' );
32295				el.get( 0 ).noUiSlider.on( 'slide', function() {
32296					$tmfid.trigger( 'change.cpf' );
32297				} );
32298				el.get( 0 ).noUiSlider.on( 'update', function( values, handle ) {
32299					values[ handle ] = $.epoAPI.math.unformat( values[ handle ], tcAPI.localDecimalSeparator );
32300					handle = 0; //fixes rtl issue.
32301					if ( $show_picker_value !== 'left' && $show_picker_value !== 'right' ) {
32302						$tmh.attr(
32303							'title',
32304							formatPrice( values[ handle ], { precision: $decimals } )
32305						);
32306					}
32307					$tmfid.val( values[ handle ] ).trigger( 'change' );
32308					if ( $show_picker_value !== '' ) {
32309						$show_label.html(
32310							formatPrice( values[ handle ], { precision: $decimals } )
32311						);
32312					}
32313				} );
32314
32315				if ( $show_picker_value !== '' ) {
32316					$show_label.html( $start );
32317				}
32318
32319				if ( $show_picker_value !== 'left' && $show_picker_value !== 'right' ) {
32320					$tmh.attr( 'title', $start );
32321					el.addClass( 'noui-show-tooltip' );
32322				}
32323			} );
32324	}
32325
32326	// Range picker event setup
32327	function setRangePickersEvents() {
32328		if ( ! noUiSlider || jDocument.data( 'setRangePickersEvents' ) ) {
32329			return;
32330		}
32331		jDocument.on( 'click', '.tm-show-picker-value', function() {
32332			var $this = $( this );
32333			var li = $this.closest( '.tmcp-field-wrap' );
32334			var value = li.find( '.tmcp-range' ).val();
32335			var edit;
32336			var html5Slider = $this.closest( '.tmcp-field-wrap' ).find( '.tm-range-picker' );
32337
32338			$this.addClass( 'tc-hidden' ).after( $( '<input type="number" class="tm-show-picker-value-edit">' ) );
32339			edit = li.find( '.tm-show-picker-value-edit' );
32340			edit.focus().val( value ).wrap( '<div class="tm-show-picker-value-edit-wrap"></div>' );
32341			edit.attr( 'step', html5Slider.attr( 'data-step' ) );
32342			edit.attr( 'min', html5Slider.attr( 'data-min' ) );
32343			edit.attr( 'max', html5Slider.attr( 'data-max' ) );
32344			$( '.tm-show-picker-value-edit' ).focus().on( 'focusout', function() {
32345				$this = li.find( '.tm-show-picker-value-edit' );
32346				$this.closest( '.tmcp-field-wrap' ).find( '.tm-show-picker-value' ).removeClass( 'tc-hidden' );
32347				$this.closest( '.tm-show-picker-value-edit-wrap' ).remove();
32348			} );
32349		} );
32350		jDocument.on( 'change input', '.tm-show-picker-value-edit', function() {
32351			var $this = $( this );
32352			var html5Slider = $this.closest( '.tmcp-field-wrap' ).find( '.tm-range-picker' );
32353			var $decimals = html5Slider.attr( 'data-step' ).split( '.' );
32354			var value = $this.val();
32355			if ( $decimals.length === 1 ) {
32356				$decimals = 0;
32357			} else {
32358				$decimals = $decimals[ 1 ].length;
32359			}
32360
32361			html5Slider.get( 0 ).noUiSlider.set( [ formatPrice( value, { precision: $decimals } ), null ] );
32362		} );
32363		jDocument.on( 'keydown', '.tm-show-picker-value-edit', function( event ) {
32364			var key;
32365
32366			key = event.which;
32367			if ( key === 13 ) {
32368				event.preventDefault();
32369			}
32370		} );
32371		jDocument.data( 'setRangePickersEvents', 1 );
32372	}
32373
32374	function validate_date_with_options( date, inputElement ) {
32375		var input = $( inputElement );
32376		var inst = $.tm_datepicker._getInst( input[ 0 ] );
32377		var enabled_only_dates = input.data( 'tc-enabled_only_dates' );
32378		var disabled_weekdays = input.data( 'tc-disabled_weekdays' );
32379		var disabled_months = input.data( 'tc-disabled_months' );
32380		var disabled_dates = input.data( 'tc-disabled_dates' );
32381		var format = input.data( 'tc-format' );
32382		var day = date.getDay();
32383		var month = date.getDay() + 1;
32384		var string;
32385
32386		if ( ! $.tm_datepicker._isInRange( inst, date ) ) {
32387			return false;
32388		}
32389		if ( enabled_only_dates !== '' ) {
32390			string = $.tm_datepicker.formatDate( format, date );
32391			return enabled_only_dates.indexOf( string ) !== -1;
32392		}
32393		if ( disabled_weekdays.indexOf( day.toString() ) !== -1 ) {
32394			return false;
32395		}
32396		if ( disabled_months.indexOf( month.toString() ) !== -1 ) {
32397			return false;
32398		}
32399		if ( disabled_dates !== '' ) {
32400			string = $.tm_datepicker.formatDate( format, date );
32401			return disabled_dates.indexOf( string ) === -1;
32402		}
32403		return true;
32404	}
32405
32406	function correctDate( days ) {
32407		var sign, testDate, count, added, noOfDaysToAdd;
32408		if ( days.toString().isNumeric() ) {
32409			sign = days === 0 ? days : ( days > 0 ? 1 : -1 );
32410			if ( sign !== 0 ) {
32411				testDate = new Date();
32412				count = 1;
32413				added = false;
32414				noOfDaysToAdd = Math.abs( days );
32415				while ( count <= noOfDaysToAdd ) {
32416					if ( added === false ) {
32417						added = 0;
32418					}
32419					testDate.setDate( testDate.getDate() + ( 1 * sign ) );
32420					added++;
32421					if ( testDate.getDay() !== 0 && testDate.getDay() !== 6 ) {
32422						count++;
32423					}
32424				}
32425				if ( added !== false ) {
32426					days = added * sign;
32427				}
32428			}
32429		}
32430		return days;
32431	}
32432	// Date and time picker setup
32433	function tm_set_datepicker( obj ) {
32434		var inputIds;
32435		var elem;
32436		var timepickerSelector = '.tm-epo-timepicker';
32437
32438		if ( ! $.tm_datepicker ) {
32439			return;
32440		}
32441
32442		inputIds = $( 'input' )
32443			.map( function() {
32444				return this.id;
32445			} )
32446			.get()
32447			.join( ' ' );
32448
32449		elem = document.createElement( 'input' );
32450		elem.setAttribute( 'type', 'date' );
32451
32452		if ( elem.type === 'text' ) {
32453			timepickerSelector = '.tm-epo-system-timepicker';
32454		}
32455
32456		obj.find( timepickerSelector )
32457			.toArray()
32458			.forEach( function( el ) {
32459				var field = $( el );
32460				var _mintime = null;
32461				var _maxtime = null;
32462				var format = field.attr( 'data-time-format' ).trim();
32463				var date_theme = field.attr( 'data-time-theme' ).trim();
32464				var date_theme_size = field.attr( 'data-time-theme-size' ).trim();
32465				var date_theme_position = field.attr( 'data-time-theme-position' ).trim();
32466				var data_translation_hour = field.attr( 'data-tranlation-hour' ).trim();
32467				var data_translation_minute = field.attr( 'data-tranlation-minute' ).trim();
32468				var data_translation_second = field.attr( 'data-tranlation-second' ).trim();
32469
32470				field.attr( 'type', 'text' );
32471
32472				if ( field.attr( 'data-min-time' ).trim() !== '' ) {
32473					_mintime = field.attr( 'data-min-time' ).trim();
32474				}
32475				if ( field.attr( 'data-max-time' ).trim() !== '' ) {
32476					_maxtime = field.attr( 'data-max-time' ).trim();
32477				}
32478
32479				if ( field.attr( 'data-custom-time-format' ).trim() !== '' ) {
32480					format = field.attr( 'data-custom-time-format' ).trim();
32481				}
32482				if ( ! data_translation_hour ) {
32483					data_translation_hour = TMEPOJS.hourText;
32484				}
32485				if ( ! data_translation_minute ) {
32486					data_translation_minute = TMEPOJS.minuteText;
32487				}
32488				if ( ! data_translation_second ) {
32489					data_translation_second = TMEPOJS.secondText;
32490				}
32491
32492				field.tm_timepicker( Object.assign( {
32493					isRTL: TMEPOJS.isRTL,
32494					hourText: data_translation_hour,
32495					minuteText: data_translation_minute,
32496					secondText: data_translation_second,
32497					timeFormat: format,
32498					minTime: _mintime,
32499					maxTime: _maxtime,
32500					closeText: TMEPOJS.closeText,
32501					showOn: 'both',
32502					buttonText: '',
32503
32504					beforeShow: function( input, inst ) {
32505						$( inst.dpDiv )
32506							.removeClass( inputIds )
32507							.removeClass( 'tm-ui-skin-epo tm-ui-skin-epo-black tm-datepicker-medium tm-datepicker-small tm-datepicker-large tm-datepicker-normal tm-datepicker-top tm-datepicker-bottom' )
32508							.addClass( this.id + ' tm-bsbb-all tm-ui-skin-' + date_theme + ' tm-timepicker tm-datepicker tm-datepicker-' + date_theme_position + ' tm-datepicker-' + date_theme_size )
32509							.appendTo( 'body' );
32510
32511						jDocument.off( 'click', '.tm-ui-dp-overlay' ).on( 'click', '.tm-ui-dp-overlay', function() {
32512							field.tm_timepicker( 'hide' );
32513						} );
32514						jBody.addClass( 'tm-static' );
32515						field.prop( 'readonly', true );
32516
32517						jWindow.trigger( {
32518							type: 'tm-timepicker-beforeShow',
32519							input: input,
32520							inst: inst
32521						} );
32522					},
32523					onClose: function() {
32524						jBody.removeClass( 'tm-static' );
32525						field.prop( 'readonly', false );
32526						field.trigger( 'change' );
32527					}
32528				}, window.tmTimepickerSettings || window.timepicker_settings || {} ) );
32529				$( '#ui-tm-datepicker-div' ).hide();
32530			} );
32531
32532		obj.find( '.tm-epo-datepicker' )
32533			.toArray()
32534			.forEach( function( el ) {
32535				var field = $( el );
32536				var startDate = parseInt( field.attr( 'data-start-year' ).trim(), 10 );
32537				var endDate = parseInt( field.attr( 'data-end-year' ).trim(), 10 );
32538				var minDate = field.attr( 'data-min-date' ).trim();
32539				var maxDate = field.attr( 'data-max-date' ).trim();
32540				var disabled_dates = field.attr( 'data-disabled-dates' ).trim();
32541				var enabled_only_dates = field.attr( 'data-enabled-only-dates' ).trim();
32542				var exlude_disabled = field.attr( 'data-exlude-disabled' ).trim();
32543				var disabled_weekdays = field.attr( 'data-disabled-weekdays' ).trim().split( ',' );
32544				var disabled_months = field.attr( 'data-disabled-months' ).trim().split( ',' );
32545				var format = field.attr( 'data-date-format' ).trim();
32546				var show = field.attr( 'data-date-showon' ).trim();
32547				var default_date = field.attr( 'data-date-defaultdate' ).trim();
32548				var date_theme = field.attr( 'data-date-theme' ).trim();
32549				var date_theme_size = field.attr( 'data-date-theme-size' ).trim();
32550				var date_theme_position = field.attr( 'data-date-theme-position' ).trim();
32551				var $split;
32552				var $index;
32553				var $split2;
32554				var $index2;
32555
32556				if ( disabled_dates !== '' ) {
32557					$split = disabled_dates.split( ',' );
32558					$index = disabled_dates.indexOf( ',' );
32559
32560					if ( $index !== -1 && $split.length > 0 ) {
32561						disabled_dates = $split;
32562					}
32563				}
32564				if ( enabled_only_dates !== '' ) {
32565					$split2 = enabled_only_dates.split( ',' );
32566					$index2 = enabled_only_dates.indexOf( ',' );
32567
32568					if ( $index2 !== -1 && $split2.length > 0 ) {
32569						enabled_only_dates = $split2;
32570					}
32571				}
32572
32573				if ( minDate === '' ) {
32574					if ( startDate === '' ) {
32575						minDate = null;
32576					} else {
32577						minDate = new Date( startDate, 1 - 1, 1 );
32578					}
32579				} else if ( exlude_disabled ) {
32580					minDate = correctDate( minDate );
32581				}
32582				if ( maxDate === '' ) {
32583					if ( endDate === '' ) {
32584						maxDate = null;
32585					} else {
32586						maxDate = new Date( endDate, 12 - 1, 31 );
32587					}
32588				} else if ( exlude_disabled ) {
32589					maxDate = correctDate( maxDate );
32590				}
32591
32592				field.data( 'tc-enabled_only_dates', enabled_only_dates );
32593				field.data( 'tc-disabled_weekdays', disabled_weekdays );
32594				field.data( 'tc-disabled_months', disabled_months );
32595				field.data( 'tc-disabled_dates', disabled_dates );
32596				field.data( 'tc-format', format );
32597
32598				field.tm_datepicker( Object.assign( {
32599					monthNames: TMEPOJS.monthNames,
32600					monthNamesShort: TMEPOJS.monthNamesShort,
32601					dayNames: TMEPOJS.dayNames,
32602					dayNamesShort: TMEPOJS.dayNamesShort,
32603					dayNamesMin: TMEPOJS.dayNamesMin,
32604					isRTL: TMEPOJS.isRTL,
32605					showOtherMonths: true,
32606					selectOtherMonths: true,
32607					showOn: show,
32608					defaultDate: default_date,
32609					buttonText: '',
32610					showButtonPanel: true,
32611					firstDay: TMEPOJS.first_day,
32612					closeText: TMEPOJS.closeText,
32613					currentText: TMEPOJS.currentText,
32614					dateFormat: format,
32615					minDate: minDate,
32616					maxDate: maxDate,
32617					onSelect: function() {
32618						var input = $( this );
32619						var id = '#' + $.epoAPI.dom.id( input.attr( 'id' ) );
32620						var date = input.tm_datepicker( 'getDate' );
32621						var day = '';
32622						var month = '';
32623						var year = '';
32624						var day_field = obj.find( id + '_day' );
32625						var month_field = obj.find( id + '_month' );
32626						var year_field = obj.find( id + '_year' );
32627						var string;
32628						var ld;
32629
32630						if ( date ) {
32631							day = date.getDate();
32632							month = date.getMonth() + 1;
32633							year = date.getFullYear();
32634							string = $.tm_datepicker.formatDate( format, date );
32635							if (
32636								disabled_months.indexOf( month.toString() ) !== -1 ||
32637								disabled_weekdays.indexOf( date.getDay().toString() ) !== -1 ||
32638								disabled_dates.indexOf( string ) !== -1 ||
32639								( enabled_only_dates !== '' && enabled_only_dates.indexOf( string ) === -1 )
32640							) {
32641								ld = input.data( 'tm-last-date' );
32642								if ( input.data( 'tm-last-date' ) ) {
32643									ld = input.data( 'tm-last-date' );
32644								} else {
32645									ld = '';
32646								}
32647								input.val( ld );
32648								input.tm_datepicker( 'setDate', ld );
32649								if ( ld ) {
32650									date = input.tm_datepicker( 'getDate' );
32651									day = date.getDate();
32652									month = date.getMonth() + 1;
32653									year = date.getFullYear();
32654								} else {
32655									day = '';
32656									month = '';
32657									year = '';
32658								}
32659							}
32660						}
32661
32662						day_field.val( day );
32663						month_field.val( month );
32664						year_field.val( year );
32665
32666						input.data( 'tm-last-date', input.val() );
32667						input.tm_datepicker( 'hide' );
32668					},
32669					beforeShow: function( input, inst ) {
32670						$( inst.dpDiv )
32671							.removeClass( inputIds )
32672							.removeClass( 'tm-datepicker-normal tm-datepicker-top tm-datepicker-bottom' )
32673							.addClass( this.id + ' tm-bsbb-all tm-ui-skin-' + date_theme + ' tm-datepicker tm-datepicker-' + date_theme_position + ' tm-datepicker-' + date_theme_size )
32674							.appendTo( 'body' );
32675
32676						jDocument.off( 'click', '.tm-ui-dp-overlay' ).on( 'click', '.tm-ui-dp-overlay', function() {
32677							field.tm_datepicker( 'hide' );
32678						} );
32679						jDocument.off( 'click', '.ui-tm-datepicker-current' ).on( 'click', '.ui-tm-datepicker-current', function() {
32680							var tempDate = new Date(),
32681								today = $.tm_datepicker._daylightSavingAdjust( new Date( tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate() ) );
32682							var day = today.getDay();
32683							var month = today.getMonth() + 1;
32684							var id = '#' + inst.id.replace( /\\\\/g, '\\' );
32685							var check = false;
32686							var string;
32687							var date = field.tm_datepicker( 'getDate' );
32688
32689							if ( enabled_only_dates !== '' ) {
32690								string = $.tm_datepicker.formatDate( format, date );
32691								check = enabled_only_dates.indexOf( string ) !== -1;
32692							} else if ( disabled_months.indexOf( month.toString() ) !== -1 || disabled_weekdays.indexOf( day.toString() ) !== -1 ) {
32693								check = false;
32694							} else {
32695								if ( disabled_dates !== '' ) {
32696									string = $.tm_datepicker.formatDate( format, date );
32697									return [ disabled_dates.indexOf( string ) === -1, '' ];
32698								}
32699								check = true;
32700							}
32701							if ( check ) {
32702								$.tm_datepicker._setDate( inst, today );
32703								$.tm_datepicker._gotoToday( id );
32704							}
32705						} );
32706						jBody.addClass( 'tm-static' );
32707
32708						jWindow.trigger( {
32709							type: 'tm-datepicker-beforeShow',
32710							input: input,
32711							inst: inst
32712						} );
32713					},
32714					onClose: function() {
32715						jBody.removeClass( 'tm-static' );
32716						field.trigger( 'change' );
32717					},
32718					beforeShowDay: function( date ) {
32719						var day = date.getDay();
32720						var month = date.getMonth() + 1;
32721						var string;
32722
32723						if ( enabled_only_dates !== '' ) {
32724							string = $.tm_datepicker.formatDate( format, date );
32725							return [ enabled_only_dates.indexOf( string ) !== -1, '' ];
32726						}
32727						if ( disabled_months.indexOf( month.toString() ) !== -1 || disabled_weekdays.indexOf( day.toString() ) !== -1 ) {
32728							return [ false, '' ];
32729						}
32730						if ( disabled_dates !== '' ) {
32731							string = $.tm_datepicker.formatDate( format, date );
32732							return [ disabled_dates.indexOf( string ) === -1, '' ];
32733						}
32734						return [ true, '' ];
32735					}
32736				}, window.tmDatepickerSettings || {} ) );
32737
32738				$( '#ui-tm-datepicker-div' ).hide();
32739			} );
32740
32741		obj.find( '.tmcp-date-select' )
32742			.on( 'change.cpf', function() {
32743				var id = '#' + $.epoAPI.dom.id( $( this ).attr( 'data-tm-date' ) );
32744				var input = obj.find( id );
32745				var format = input.attr( 'data-date-format' );
32746				var day = obj.find( id + '_day' ).val();
32747				var month = obj.find( id + '_month' ).val();
32748				var year = obj.find( id + '_year' ).val();
32749				var dateFormat = $.tm_datepicker.formatDate( format, new Date( year, parseInt( month, 10 ) - 1, day ) );
32750
32751				if ( day > 0 && month > 0 && year > 0 ) {
32752					input.tm_datepicker( 'setDate', dateFormat );
32753					input.trigger( 'change' );
32754				} else {
32755					input.val( '' );
32756					input.trigger( 'change.cpf' );
32757				}
32758			} )
32759			.on( 'focus.cpf', function() {
32760				var id = '#' + $.epoAPI.dom.id( $( this ).attr( 'data-tm-date' ) );
32761				var input = obj.find( id );
32762				var day_select = obj.find( id + '_day' );
32763				var month_select = obj.find( id + '_month' );
32764				var year_select = obj.find( id + '_year' );
32765				var day = day_select.val();
32766				var month = month_select.val();
32767				var year = year_select.val();
32768				var _select = $( this );
32769
32770				if ( ( year !== '' && month !== '' && day !== '' ) || ( year !== '' && month !== '' && day === '' ) || ( day !== '' && year !== '' && month === '' ) || ( day !== '' && month !== '' && year === '' ) ) {
32771					_select
32772						.find( 'option' )
32773						.toArray()
32774						.forEach( function( element ) {
32775							var option = $( element );
32776							var val = option.val();
32777							var date_string = year + '-' + month + '-' + day;
32778							var d;
32779
32780							if ( _select.is( '.tmcp-date-day' ) ) {
32781								if ( year === '' || month === '' ) {
32782									return;
32783								}
32784								date_string = year + '-' + month + '-' + val;
32785							} else if ( _select.is( '.tmcp-date-month' ) ) {
32786								if ( year === '' || day === '' ) {
32787									return;
32788								}
32789								date_string = year + '-' + val + '-' + day;
32790							} else if ( _select.is( '.tmcp-date-year' ) ) {
32791								if ( day === '' || month === '' ) {
32792									return;
32793								}
32794								date_string = val + '-' + month + '-' + day;
32795							}
32796
32797							if ( val !== '' ) {
32798								try {
32799									d = $.tm_datepicker.parseDate( 'yy-mm-dd', date_string );
32800									if ( d ) {
32801										if ( validate_date_with_options( d, input ) ) {
32802											option.prop( 'disabled', false );
32803										} else {
32804											option.prop( 'disabled', true );
32805										}
32806									}
32807								} catch ( err ) {
32808									window.console.log( err );
32809
32810									option.prop( 'disabled', true );
32811									errorObject = err;
32812								}
32813							}
32814						} );
32815				} else {
32816					day_select.find( 'option' ).prop( 'disabled', false );
32817					month_select.find( 'option' ).prop( 'disabled', false );
32818					year_select.find( 'option' ).prop( 'disabled', false );
32819				}
32820			} );
32821
32822		jWindow.on( 'resizestart', function() {
32823			var activeElement = $( document.activeElement );
32824
32825			if ( activeElement.is( '.hasDatepicker' ) ) {
32826				activeElement.data( 'resizestarted', true );
32827
32828				// we don't use jWindow here because we want the current window width
32829				if ( $( window ).width() < 768 ) {
32830					activeElement.data( 'resizewidth', true );
32831					return;
32832				}
32833				activeElement.tm_datepicker( 'hide' );
32834			}
32835		} );
32836		jWindow.on( 'resizestop', function() {
32837			var activeElement = $( document.activeElement );
32838
32839			if ( activeElement.is( '.hasDatepicker' ) && activeElement.data( 'resizestarted' ) ) {
32840				if ( activeElement.data( 'resizewidth' ) ) {
32841					activeElement.tm_datepicker( 'hide' );
32842				}
32843				activeElement.tm_datepicker( 'show' );
32844			}
32845			activeElement.data( 'resizestarted', false );
32846			activeElement.data( 'resizewidth', false );
32847		} );
32848	}
32849
32850	function apply_submit_events( epoObject ) {
32851		var epoEventId = epoObject.epoEventId;
32852		var main_product = epoObject.main_product;
32853		var type;
32854		var form_is_submit = ! $.tcepo.formSubmitEvents[ epoEventId ].some( function( form_event ) {
32855			return typeof form_event && ( typeof form_event.trigger === 'function' || false ) && ! form_event.trigger();
32856		} );
32857
32858		$.tcepo.formSubmitEvents[ epoEventId ].forEach( function( form_event ) {
32859			type = typeof form_event;
32860			if ( type === 'object' ) {
32861				if ( form_is_submit ) {
32862					form_event.on_true();
32863				} else {
32864					form_event.on_false();
32865				}
32866			}
32867		} );
32868
32869		if ( ! form_is_submit ) {
32870			setTimeout( function() {
32871				main_product.find( tcAPI.addToCartButtonSelector ).first().removeClass( 'disabled' ).removeClass( 'loading' ).prop( 'disabled', false ).removeClass( 'fpd-disabled' );
32872			}, 100 );
32873		}
32874
32875		jWindow.trigger( 'tm-apply-submit-events', {
32876			epo: {
32877				form_is_submit: form_is_submit
32878			}
32879		} );
32880
32881		return form_is_submit;
32882	}
32883
32884	function tm_apply_validation( epoObject ) {
32885		var form = epoObject.form;
32886		var this_epo_container = epoObject.this_epo_container;
32887		var main_product = epoObject.main_product;
32888		var epoEventId = epoObject.epoEventId;
32889		var validation_rules;
32890		var has_rules;
32891
32892		if ( TMEPOJS.tm_epo_global_enable_validation === 'yes' ) {
32893			validation_rules = {};
32894
32895			this_epo_container
32896				.find( '.tmcp-ul-wrap' )
32897				.toArray()
32898				.forEach( function( tmcpulwrap ) {
32899					var field;
32900					var field_name;
32901					var subFieldName;
32902					var subRule;
32903					var productField;
32904
32905					tmcpulwrap = $( tmcpulwrap );
32906					has_rules = tmcpulwrap.data( 'tm-validation' );
32907					if ( has_rules && $.tmType( has_rules ) === 'object' ) {
32908						field = tmcpulwrap.find( '.tm-epo-field' );
32909						field_name = field.first().attr( 'name' );
32910						if ( tmcpulwrap.is( '.tm-extra-product-options-radio.tm-element-ul-radio' ) ) {
32911							field_name = field.last().attr( 'name' );
32912							validation_rules[ field_name ] = has_rules;
32913						} else if ( tmcpulwrap.is( '.tm-extra-product-options-checkbox.tm-element-ul-checkbox' ) ) {
32914							field.toArray().forEach( function( fname ) {
32915								if ( 'required' in has_rules ) {
32916									has_rules.required = function( elem ) {
32917										var len = tmcpulwrap.find( 'input.tm-epo-field.tmcp-checkbox:checked' ).length;
32918										if ( len === 0 ) {
32919											if ( field.last().attr( 'name' ) === $( elem ).attr( 'name' ) ) {
32920												return true;
32921											}
32922											return false;
32923										}
32924										return len <= 0;
32925									};
32926								}
32927								validation_rules[ $( fname ).attr( 'name' ) ] = has_rules;
32928							} );
32929						} else if ( tmcpulwrap.is( '.tm-extra-product-options-product.tm-element-ul-product' ) ) {
32930							field = field.filter( '.tc-epo-field-product' );
32931							if ( field.is( ':checkbox' ) ) {
32932								field.toArray().forEach( function( fname ) {
32933									if ( 'required' in has_rules ) {
32934										productField = $( fname );
32935										if ( productField.is( ':checkbox' ) ) {
32936											has_rules.required = function( elem ) {
32937												var checkedFields = tmcpulwrap.find( 'input.tm-epo-field.tmcp-checkbox:checked' );
32938												var len = checkedFields.length;
32939												var qtyCheck;
32940												if ( len === 0 ) {
32941													if ( field.last().attr( 'name' ) === $( elem ).attr( 'name' ) ) {
32942														return true;
32943													}
32944													return false;
32945												}
32946												qtyCheck = checkedFields.map( function() {
32947													return $( this );
32948												} ).get().some( function( element ) {
32949													return element.closest( '.tc-epo-element-product-holder' ).find( '.tm-qty' ).first().val() <= 0;
32950												} );
32951												if ( len > 0 ) {
32952													return qtyCheck;
32953												}
32954												return len <= 0;
32955											};
32956											if ( productField.attr( 'data-type' ) === 'variable' ) {
32957												subFieldName = productField.closest( '.tc-epo-element-product-holder' ).find( '.product-variation-id' ).first().attr( 'name' );
32958												subRule = {
32959													required: function( elem ) {
32960														var element = $( elem );
32961														var holder = element.closest( '.tc-epo-element-product-holder' );
32962														var checkbox = holder.find( '.tc-epo-field-product' );
32963
32964														if ( field_is_active( checkbox, true ) && checkbox.is( ':checked' ) ) {
32965															return true;
32966														}
32967														return false;
32968													}
32969												};
32970												validation_rules[ subFieldName ] = subRule;
32971											}
32972										}
32973									}
32974									validation_rules[ productField.attr( 'name' ) ] = has_rules;
32975								} );
32976							} else if ( field.is( ':radio' ) ) {
32977								has_rules.product_element_radio_qty = true;
32978								validation_rules[ field.attr( 'name' ) ] = has_rules;
32979								subFieldName = field.closest( '.tc-element-container' ).find( '.product-variation-id' ).first().attr( 'name' );
32980								subRule = {
32981									required: function( elem ) {
32982										var element = $( elem );
32983										var holder = element.closest( '.tc-element-container' );
32984										var radiobutton = holder.find( '.tc-epo-field-product:checked' );
32985
32986										if ( radiobutton.length && radiobutton.attr( 'data-type' ) === 'variable' && field_is_active( radiobutton, true ) && radiobutton.is( ':checked' ) ) {
32987											return true;
32988										}
32989										return false;
32990									}
32991								};
32992								validation_rules[ subFieldName ] = subRule;
32993							} else if ( field.is( 'select' ) ) {
32994								has_rules.product_element_select_qty = true;
32995								validation_rules[ field.attr( 'name' ) ] = has_rules;
32996								subFieldName = field.closest( '.tc-element-container' ).find( '.product-variation-id' ).first().attr( 'name' );
32997								subRule = {
32998									required: function( elem ) {
32999										var element = $( elem );
33000										var holder = element.closest( '.tc-element-container' );
33001										var select = holder.find( '.tc-epo-field-product' );
33002
33003										if ( select.length && select.children( ':selected' ).attr( 'data-type' ) === 'variable' && field_is_active( select, true ) ) {
33004											return true;
33005										}
33006										return false;
33007									}
33008								};
33009								validation_rules[ subFieldName ] = subRule;
33010							}
33011						} else {
33012							validation_rules[ field_name ] = has_rules;
33013						}
33014					}
33015				} );
33016
33017			form.removeData( 'tc_validator' );
33018			form.tc_validate( {
33019				focusInvalid: false,
33020				ignore:
33021					tcAPI.qtySelector +
33022					',.tcdisabled,.tmcp-upload-hidden,#wc_bookings_field_duration,input.tm-qty:hidden[type="number"],input.input-text.qty,.ignore,.variations select,.tc-epo-variable-product-selector,.tm-extra-product-options-variations input,.tm-extra-product-options-variations select,input:not(' + tcAPI.epoSelector + ' input),select:not(' + tcAPI.epoSelector + ' select)',
33023				rules: validation_rules,
33024				errorClass: 'tm-error',
33025				validClass: 'tm-valid',
33026				errorElement: 'label',
33027				errorPlacement: function( error, element ) {
33028					error.addClass( 'tc-cell tcwidth tcwidth-100' );
33029					if ( TMEPOJS.tm_epo_global_error_label_placement === 'before' ) {
33030						element.closest( '.tc-element-container' ).before( error );
33031					} else {
33032						element.closest( '.tc-element-container' ).after( error );
33033					}
33034					return false;
33035				},
33036				invalidHandler: function( event, validator ) {
33037					jWindow.trigger( 'tm-invalidHandler', {
33038						epo: {
33039							validator: validator
33040						}
33041					} );
33042					setTimeout( function() {
33043						if ( ! main_product.find( tcAPI.addToCartButtonSelector ).first().is( '.disabled' ) ) {
33044							main_product.find( tcAPI.addToCartButtonSelector ).first().removeClass( 'loading' ).prop( 'disabled', false ).removeClass( 'fpd-disabled' );
33045							main_product.find( tcAPI.addToCartButtonSelector ).first().removeClass( 'disabled' ).removeClass( 'loading' ).prop( 'disabled', false ).removeClass( 'fpd-disabled' );
33046						}
33047					}, 100 );
33048					if ( validator.errorList && validator.errorList[ 0 ] && validator.errorList[ 0 ].element ) {
33049						goto_error_item( $( validator.errorList[ 0 ].element ), epoEventId );
33050					}
33051				},
33052				submitHandler: function() {
33053					var ajaxSuccessFunc;
33054					if ( ! epoObject.is_quickview ) {
33055						main_product.find( tcAPI.addToCartButtonSelector ).first().addClass( 'disabled' );
33056						ajaxSuccessFunc = function() {
33057							main_product.find( tcAPI.addToCartButtonSelector ).first().removeClass( 'disabled' );
33058							jDocument.off( 'ajaxSuccess', ajaxSuccessFunc );
33059						};
33060						jDocument.on( 'ajaxSuccess', ajaxSuccessFunc );
33061					}
33062					return apply_submit_events( epoObject );
33063				}
33064			} );
33065
33066			// This should handle most ajax based add to cart solutions
33067			form.find( tcAPI.addToCartButtonSelector ).on( 'click', function( e ) {
33068				if ( ! form.tc_validate().form() ) {
33069					e.preventDefault();
33070					e.stopImmediatePropagation();
33071				}
33072			} );
33073
33074			return true;
33075		}
33076		return false;
33077	}
33078
33079	function tm_form_submit_event( epoObject ) {
33080		var form = epoObject.form;
33081		var epoEventId = epoObject.epoEventId;
33082
33083		jWindow.trigger( 'tm-from-submit', {
33084			epo: epoObject,
33085			functions: {
33086				tm_apply_validation: tm_apply_validation,
33087				apply_submit_events: apply_submit_events
33088			}
33089		} );
33090		if ( ! tm_apply_validation( epoObject ) && $.tcepo.formSubmitEvents[ epoEventId ].length ) {
33091			form.on( 'submit', function() {
33092				apply_submit_events( epoObject );
33093			} );
33094		}
33095
33096		jWindow.on( 'tc_apply_validation', function() {
33097			return tm_apply_validation( epoObject );
33098		} );
33099	}
33100
33101	function found_variation_tmepo( dataObject ) {
33102		var epoHolder = dataObject.epoHolder;
33103		var totalsHolder = dataObject.totalsHolder;
33104		var totalsHolderContainer = dataObject.totalsHolderContainer;
33105		var currentCart = dataObject.currentCart;
33106		var variationForm = dataObject.variationForm;
33107		var variation = dataObject.variation;
33108		var variations = totalsHolder.data( 'variations' );
33109		var product_price;
33110
33111		totalsHolder.data( 'current_variation', variation );
33112
33113		/**
33114		 * Currency converters that don't allow multi currency checkout will fail the following if statement
33115		 *
33116		 * if (variation.display_price!=undefined) {
33117		 *     product_price = variation.display_price;
33118		 *     totalsHolder.data('price', product_price);
33119		 * } else ...
33120		 *
33121		 */
33122		// This needs to be the first check as the next one
33123		// fails for taxed products as it displays the backend price only.
33124		if ( variation && 'display_price' in variation && ! totalsHolder.data( 'tm-epo-is-woocs' ) ) {
33125			product_price = variation.display_price;
33126
33127			totalsHolder.data( 'priceIsWithDiscount', '1' );
33128			// Fancy product Designer
33129			totalsHolder.removeData( 'tcprice' );
33130		} else if ( variations && variation && variation.variation_id && variations[ variation.variation_id ] !== undefined ) {
33131			product_price = variations[ variation.variation_id ];
33132
33133			// Fancy product Designer
33134			totalsHolder.removeData( 'tcprice' );
33135		} else if ( variation && $( variation.price_html ).find( '.amount:last' ).length ) {
33136			product_price = $( variation.price_html ).find( '.amount:last' ).text();
33137			product_price = product_price.replace( TMEPOJS.currency_format_thousand_sep, '' );
33138			product_price = product_price.replace( TMEPOJS.currency_format_decimal_sep, '.' );
33139			product_price = product_price.replace( /[^0-9.]/g, '' );
33140			product_price = parseFloat( product_price );
33141
33142			// Fancy product Designer
33143			totalsHolder.removeData( 'tcprice' );
33144		}
33145
33146		product_price = tm_set_backend_price( product_price, totalsHolder, variation );
33147		totalsHolder.data( 'price', product_price );
33148
33149		totalsHolderContainer.find( '.cpf-product-price' ).val( product_price );
33150
33151		setTimeout( function() {
33152			epoHolder.find( 'select.tm-epo-field' ).trigger( 'tm-select-change-html-all-math' );
33153		}, 100 );
33154
33155		// This must be run every time to get correct results for percent price types
33156		// if set norules then discount will not auto work upon chosing a variation
33157		if ( ! variationForm.data( 'tm-styled-variations' ) ) {
33158			currentCart.trigger( {
33159				type: 'tm-epo-update'
33160			} );
33161		}
33162	}
33163
33164	function fetchOptionPrices( epoObject, epoHolder, selector, total, original_total, floatingBoxData, showTotal, forced, setPriceTax, useUndiscountedPrice, mathTotal, cumulative, nopriceCac
33164heSelector, cartQty ) {
33165		var obj;
33166		var noDpd;
33167		var priceArray;
33168		var priceCacheSelector = 'none';
33169		var total_taxed;
33170		var original_total_taxed;
33171		var vat_total = 0;
33172		var currentTotalsContainer;
33173		var epoinline;
33174		var funcTotal;
33175		var cumulativeTotal;
33176
33177		if ( ! nopriceCacheSelector ) {
33178			priceCacheSelector = epoHolder.attr( 'class' ) + epoHolder.attr( 'data-uniqid' ) + selector + epoObject.is_associated + setPriceTax + forced + setPriceTax + useUndiscountedPrice || 'none';
33179		}
33180
33181		if ( priceCacheSelector !== 'none' && priceCache !== true && priceCache !== false && priceCache[ priceCacheSelector ] !== undefined ) {
33182			return priceCache[ priceCacheSelector ];
33183		}
33184
33185		noDpd = useUndiscountedPrice ? 'undiscounted_' : '';
33186		obj = epoHolder.find( selector );
33187		if ( epoObject.is_associated === false ) {
33188			obj = obj.not( tcAPI.associatedEpoSelector + ' ' + selector + ',.cpf-type-variations ' + selector );
33189		}
33190		if ( ! forced ) {
33191			obj = obj.filter( '.tcenabled' );
33192		}
33193
33194		if ( ! total ) {
33195			total = 0;
33196		}
33197		if ( ! original_total ) {
33198			original_total = 0;
33199		}
33200
33201		funcTotal = total;
33202		cumulativeTotal = total;
33203
33204		total_taxed = total;
33205		original_total_taxed = original_total;
33206
33207		if ( ! floatingBoxData ) {
33208			floatingBoxData = [];
33209		}
33210
33211		currentTotalsContainer = epoObject.this_epo_totals_container;
33212
33213		if ( obj.length ) {
33214			obj.toArray().forEach( function( tmcpfield ) {
33215				var field = $( tmcpfield );
33216				var _value = '';
33217				var fieldval;
33218				var field_div = field.closest( '.cpf-element' );
33219				var field_wrap = field.closest( '.tmcp-field-wrap' );
33220				var field_label_show = field_div.attr( 'data-fblabelshow' );
33221				var field_value_show = field_div.attr( 'data-fbvalueshow' );
33222				var field_title = '';
33223				var option_quantity = field_wrap.find( '.tm-qty' ).val();
33224				var option_price;
33225				var option_price_taxed;
33226				var option_original_price;
33227				var option_original_price_taxed;
33228				var liw;
33229				var cri;
33230				var tl;
33231				var options;
33232				var forrangepicker;
33233				var $decimals;
33234				var _valueText = '';
33235				var setter;
33236				var dofloatingBoxData = false;
33237
33238				if ( selector === '.tc-epo-field-product' ) {
33239					if ( field_div.is( '.cpf-type-product-thumbnailmultiple' ) || field_div.is( '.cpf-type-product-checkbox' ) || field_div.is( '.cpf-type-product-custombundle' ) ) {
33240						epoinline = field.closest( '.tc-epo-element-product-holder' ).find( tcAPI.associatedEpoSelector );
33241					} else if ( field_div.is( '.cpf-type-product-thumbnail' ) || field_div.is( '.cpf-type-product-radio' ) || field_div.is( '.cpf-type-product-dropdown' ) ) {
33242						epoinline = field_div.find( '.tc-epo-element-product-container[data-product_id="' + field.val() + '"]' ).find( tcAPI.associatedEpoSelector );
33243					} else if ( field_div.is( '.cpf-type-product-single' ) ) {
33244						epoinline = field_div.find( tcAPI.associatedEpoSelector );
33245					}
33246					currentTotalsContainer = $( '.tc-epo-totals' + '.tm-product-id-' + epoinline.attr( 'data-product-id' ) + '[data-epo-id="' + epoinline.attr( 'data-epo-id' ) + '"]' );
33247				}
33248
33249				if ( currentTotalsContainer.length ) {
33250					if ( field_label_show === '' ) {
33251						field_title = field_div.find( '.tc-epo-element-label-text' ).html();
33252					}
33253
33254					if ( option_quantity === undefined ) {
33255						option_quantity = '';
33256					}
33257					if ( mathTotal ) {
33258						tm_element_epo_rules( epoObject, field, undefined, undefined, undefined, undefined, funcTotal, cumulativeTotal );
33259					}
33260					if ( field.is( ':checkbox, :radio, :input' ) ) {
33261						option_price = 0;
33262						option_price_taxed = 0;
33263						option_original_price = 0;
33264						option_original_price_taxed = 0;
33265						if ( field.is( '.tmcp-checkbox, .tmcp-radio' ) ) {
33266							if ( forced || field.is( ':checked' ) ) {
33267								option_price = field.data( noDpd + 'raw_price' );
33268								option_price_taxed = field.data( noDpd + 'price' );
33269								option_original_price = field.data( noDpd + 'raw_original_price' );
33270								option_original_price_taxed = field.data( noDpd + 'original_price' );
33271								showTotal = true;
33272								field.data( 'isset', 1 );
33273								liw = field.closest( 'li.tmcp-field-wrap' );
33274								cri = liw.find( '.tc-image' );
33275								_value = '';
33276								_valueText = '';
33277
33278								tl = field.closest( 'li.tmcp-field-wrap' ).find( '.tc-label-text' );
33279								if ( tl.length ) {
33280									_value = tl.html();
33281									_valueText = _value;
33282								}
33283
33284								if ( cri.length ) {
33285									cri = cri.closest( '.tc-label-wrap' ).clone().addClass( 'tc-img-floating' );
33286									cri.find( '.tc-label, .tc-epo-style-wrapper, .tc-input-wrap' ).remove();
33287									_value = cri[ 0 ].outerHTML + '<span class="tc-label-text">' + _value + '</span>';
33288								}
33289								dofloatingBoxData = true;
33290							} else {
33291								field.data( 'isset', 0 );
33292							}
33293						} else if ( field.is( '.tmcp-select' ) ) {
33294							setter = field.find( 'option:selected' );
33295							option_price = setter.data( noDpd + 'raw_price' );
33296							option_price_taxed = setter.data( noDpd + 'price' );
33297							option_original_price = setter.data( noDpd + 'raw_original_price' );
33298							option_original_price_taxed = setter.data( noDpd + 'original_price' );
33299
33300							options = field.children( 'option:selected' );
33301							if ( ! ( options.val() === '' && options.attr( 'data-rulestype' ) === '' ) ) {
33302								showTotal = true;
33303							}
33304
33305							field.find( 'option' ).data( 'isset', 0 );
33306							setter.data( 'isset', 1 );
33307
33308							if ( ! ( setter.val() === '' && setter.attr( 'data-rulestype' ) === '' ) ) {
33309								_value = setter.attr( 'data-text' );
33310								dofloatingBoxData = true;
33311								_valueText = _value;
33312							}
33313						} else if ( field.is( '.tmcp-selectmultiple' ) ) {
33314							setter = field.find( 'option:selected' );
33315							setter.toArray().forEach( function( setterel ) {
33316								setterel = $( setterel );
33317								option_price = option_price + $.epoAPI.math.toFloat( setterel.data( noDpd + 'raw_price' ) );
33318								option_price_taxed = option_price_taxed + $.epoAPI.math.toFloat( setterel.data( noDpd + 'price' ) );
33319								option_original_price = option_original_price + $.epoAPI.math.toFloat( setterel.data( noDpd + 'raw_original_price' ) );
33320								option_original_price_taxed = option_original_price_taxed + $.epoAPI.math.toFloat( setterel.data( noDpd + 'original_price' ) );
33321
33322								options = field.children( 'option:selected' );
33323								if ( ! ( options.val() === '' && options.attr( 'data-rulestype' ) === '' ) ) {
33324									showTotal = true;
33325								}
33326
33327								field.find( 'option' ).data( 'isset', 0 );
33328								setterel.data( 'isset', 1 );
33329
33330								if ( ! ( setterel.val() === '' && setterel.attr( 'data-rulestype' ) === '' ) ) {
33331									_value = setterel.attr( 'data-text' );
33332									dofloatingBoxData = true;
33333									_valueText = _value;
33334								}
33335							} );
33336						} else {
33337							fieldval = field.val();
33338							if ( field.is( "[type='file']" ) ) {
33339								fieldval = fieldval.replace( 'C:\\fakepath\\', '' );
33340							}
33341							if ( fieldval ) {
33342								if ( field.is( '.tmcp-range' ) && fieldval === '0' ) {
33343									field.data( 'isset', 0 );
33344								} else {
33345									option_price = field.data( noDpd + 'raw_price' );
33346									option_price_taxed = field.data( noDpd + 'price' );
33347									option_original_price = field.data( noDpd + 'raw_original_price' );
33348									option_original_price_taxed = field.data( noDpd + 'original_price' );
33349									showTotal = true;
33350									field.data( 'isset', 1 );
33351
33352									_value = fieldval;
33353									if ( field.is( '.tmcp-range' ) ) {
33354										forrangepicker = field.closest( '.tmcp-field-wrap' ).find( '.tm-range-picker' );
33355										$decimals = forrangepicker.attr( 'data-step' ).split( '.' );
33356										if ( $decimals.length === 1 ) {
33357											$decimals = 0;
33358										} else {
33359											$decimals = $decimals[ 1 ].length;
33360										}
33361										_value = formatPrice( _value, { precision: $decimals } );
33362									}
33363									dofloatingBoxData = true;
33364									if ( field.is( '.tmcp-dynamic' ) ) {
33365										field_label_show = 'hidden';
33366										field_value_show = 'hidden';
33367									}
33368									_valueText = fieldval;
33369								}
33370							} else {
33371								field.data( 'isset', 0 );
33372							}
33373						}
33374						if ( ! option_price ) {
33375							option_price = 0;
33376						}
33377						if ( ! option_original_price ) {
33378							option_original_price = 0;
33379						}
33380						if ( ! option_price_taxed ) {
33381							option_price_taxed = 0;
33382						}
33383						if ( ! option_original_price_taxed ) {
33384							option_original_price_taxed = 0;
33385						}
33386						if ( dofloatingBoxData ) {
33387							floatingBoxData.push( {
33388								title: field_title,
33389								value: _value,
33390								valueText: _valueText,
33391								price: option_price_taxed,
33392								original_price: option_original_price_taxed,
33393								quantity: option_quantity,
33394								label_show: field_label_show,
33395								value_show: field_value_show,
33396								input_type: field.attr( 'type' )
33397							} );
33398						}
33399						if ( undefined !== cartQty && cartQty > 0 && field.is( '.tc-is-dnmpbq' ) ) {
33400							option_price = option_price / cartQty;
33401						}
33402
33403						option_price_taxed = tm_set_tax_price( option_price, currentTotalsContainer, field, undefined, undefined, undefined, true );
33404						option_original_price_taxed = tm_set_tax_price( option_original_price, currentTotalsContainer, field, undefined, undefined, undefined, true );
33405
33406						if ( setPriceTax ) {
33407							option_price = option_price_taxed;
33408							option_original_price = option_original_price_taxed;
33409						}
33410
33411						total = $.epoAPI.math.toFloat( total ) + $.epoAPI.math.toFloat( option_price );
33412						total = $.epoAPI.math.toFloat( $.epoAPI.math.round( total, 10 ) );
33413						original_total = $.epoAPI.math.toFloat( original_total ) + $.epoAPI.math.toFloat( option_original_price );
33414						original_total = $.epoAPI.math.toFloat( $.epoAPI.math.round( original_total, 10 ) );
33415
33416						total_taxed = $.epoAPI.math.toFloat( total_taxed ) + $.epoAPI.math.toFloat( option_price_taxed );
33417						total_taxed = $.epoAPI.math.toFloat( $.epoAPI.math.round( total_taxed, 10 ) );
33418						vat_total = vat_total + $.epoAPI.math.toFloat( calculateTaxAmount( option_price_taxed, currentTotalsContainer ) );
33419						original_total_taxed = $.epoAPI.math.toFloat( original_total_taxed ) + $.epoAPI.math.toFloat( option_original_price_taxed );
33420						original_total_taxed = $.epoAPI.math.toFloat( $.epoAPI.math.round( original_total_taxed, 10 ) );
33421
33422						if ( cumulative ) {
33423							cumulativeTotal = total;
33424						}
33425					}
33426				}
33427			} );
33428		}
33429		priceArray = {
33430			total: total,
33431			original_total: original_total,
33432			total_taxed: total_taxed,
33433			original_total_taxed: original_total_taxed,
33434			vat_total: vat_total,
33435			floatingBoxData: floatingBoxData,
33436			showTotal: showTotal,
33437			elementsLength: obj.length
33438		};
33439
33440		if ( priceCache === true ) {
33441			priceCache = {};
33442			priceCache[ priceCacheSelector ] = priceArray;
33443		} else if ( priceCache !== true && priceCache !== false ) {
33444			priceCache[ priceCacheSelector ] = priceArray;
33445		}
33446
33447		return priceArray;
33448	}
33449
33450	function show_product_html( thisEpoObject, thisMainProduct, thisVariableProductContainer, type, $this, currentCart, variableProductContainers, isTrigger, qtyalt ) {
33451		var epoObjectCopy = $.extend( true, {}, thisEpoObject );
33452		var item_tm_extra_product_options = thisVariableProductContainer.find( tcAPI.associatedEpoSelector );
33453		var item = thisVariableProductContainer;
33454		var newEpoObject;
33455		var showOnly = true;
33456
33457		$this.removeData( 'triggeredforced' );
33458
33459		if ( item.closest( '.cpf-element' ).is( '.tc-hidden' ) ) {
33460			return;
33461		}
33462
33463		variableProductContainers.addClass( 'tm-hidden' );
33464		if ( variableProductContainers.length ) {
33465			toggleState( variableProductContainers, true );
33466		}
33467
33468		if ( type === 'variable' ) {
33469			if ( ! thisVariableProductContainer.is( '.tc-init-variations' ) ) {
33470				thisVariableProductContainer.addClass( 'variations_form' );
33471				setTimeout( function() {
33472					newEpoObject = tm_init_epo( item, false, item_tm_extra_product_options.attr( 'data-product-id' ), item_tm_extra_product_options.attr( 'data-epo-id' ), $this, epoObjectCopy );
33473					thisVariableProductContainer.addClass( 'tc-init-variations' );
33474					thisVariableProductContainer.tc_product_variation_form( $this, currentCart, variableProductContainers, newEpoObject );
33475				}, 40 );
33476				showOnly = false;
33477			} else {
33478				thisVariableProductContainer.trigger( 'refresh.tc-variation-form' );
33479			}
33480		} else if ( ! thisVariableProductContainer.is( '.tc-init-product' ) ) {
33481			thisVariableProductContainer.addClass( 'tc-init-product' );
33482			variableProductContainers.find( '.tc-epo-element-variable-product' ).removeClass( 'variations_form' );
33483			variableProductContainers.find( '.tc-epo-element-variations' ).removeClass( 'variations' );
33484
33485			setTimeout( function() {
33486				tm_init_epo( item, false, item_tm_extra_product_options.attr( 'data-product-id' ), item_tm_extra_product_options.attr( 'data-epo-id' ), $this, epoObjectCopy );
33487			}, 20 );
33488			showOnly = false;
33489		}
33490
33491		setTimeout( function() {
33492			if ( variableProductContainers.length ) {
33493				toggleState( thisVariableProductContainer, false );
33494			}
33495			thisVariableProductContainer
33496				.find( ':checkbox:checked, :radio:checked' )
33497				.toArray().forEach( function( vpc ) {
33498					$( vpc ).closest( '.tmcp-field-wrap' ).addClass( 'tc-active' );
33499				} );
33500		}, 10 );
33501		setTimeout( function() {
33502			if ( qtyalt.length ) {
33503				qtyalt.trigger( 'change' );
33504			}
33505			if ( thisVariableProductContainer.find( tcAPI.epoSelector ).length === 0 ) {
33506				thisVariableProductContainer.addClass( 'no-epo' );
33507			}
33508			thisVariableProductContainer.removeClass( 'tm-hidden' );
33509			if ( showOnly ) {
33510				jWindow.trigger( 'cpflogicdone' );
33511				thisVariableProductContainer.find( '.tm-quantity' ).trigger( 'showhide.cpfcustom' );
33512			}
33513			if ( isTrigger === undefined && TMEPOJS.tm_epo_global_product_element_scroll === 'yes' ) {
33514				jWindow.tcScrollTo( thisVariableProductContainer, 200, $.epoAPI.math.toFloat
33514( TMEPOJS.tm_epo_global_product_element_scroll_offset ) );
33515			}
33516		}, 210 );
33517	}
33518
33519	function setQtyData( qtyElement, value ) {
33520		var data = qtyElement.data();
33521		if ( value === undefined ) {
33522			value = qtyElement.val();
33523		}
33524		if ( data ) {
33525			Object.keys( data ).filter( function( key ) {
33526				return key.indexOf( 'epopreviousvalue' ) === 0;
33527			} ).forEach( function( key ) {
33528				qtyElement.data( key, value );
33529			} );
33530		}
33531	}
33532
33533	function epoEventHandlers( epoObject, cartContainer, alternativeCart ) {
33534		// if cartContainer & alternativeCart is defined we are on a non default product (eg. composite product)
33535		var product_id = epoObject.product_id;
33536		var main_product = epoObject.main_product;
33537		var main_cart = epoObject.main_cart;
33538		var this_epo_container = epoObject.this_epo_container;
33539		var this_totals_container = epoObject.this_totals_container;
33540		var this_epo_totals_container = epoObject.this_epo_totals_container;
33541		var epoEventId = epoObject.epoEventId;
33542		var main_epo_inside_form = epoObject.main_epo_inside_form;
33543		var epo_id_selector = epoObject.epo_id_selector;
33544		var epo_id = epoObject.epo_id;
33545		var product_id_selector = epoObject.product_id_selector;
33546		var itemId = 'main';
33547		var epoHolder;
33548		var totalsHolderContainer;
33549		var totalsHolder;
33550		var currentCart;
33551		var variation_id_selector;
33552		var this_product_type;
33553		var variationForm;
33554		var qtyElement;
33555		var finalTotalBoxMode;
33556		var finalTotalBoxShowFinal;
33557		var finalTotalBoxShowOptions;
33558		var eventName = epoObject.is_associated ? 'tc-variation-form' : 'wc-variation-form';
33559		var eventNamePrefix = epoObject.is_associated ? 'tc_' : '';
33560		var epoVariationSection;
33561		var thismaxlength;
33562		var epoFieldAll;
33563		var selectSelector;
33564		var epoFieldText;
33565		var epoResetRadio;
33566		var epoFieldUpload;
33567		var epoFieldHasClearButton;
33568		var tmQty;
33569		var tmQuantity;
33570
33571		// Non default product (eg. composite product)
33572		if ( alternativeCart && cartContainer ) {
33573			itemId = $.epoAPI.applyFilter( 'tc_get_item_id', cartContainer.attr( 'data-item_id' ), cartContainer );
33574			epoHolder = main_product.find( '.tm-extra-product-options.tm-cart-' + itemId );
33575			totalsHolderContainer = main_product.find( '.tm-totals-form-' + itemId );
33576			totalsHolder = main_product.find( '.tm-epo-totals.tm-cart-' + itemId );
33577			variationForm = cartContainer.find( '.variations_form' ).first();
33578		// Default product
33579		} else {
33580			if ( ! main_cart || main_cart.length === 0 ) {
33581				if ( this_epo_container.is( '.tc-shortcode' ) ) {
33582					main_cart = main_product;
33583				} else {
33584					main_cart = get_main_cart( main_product, main_product, 'form', product_id );
33585				}
33586			}
33587			cartContainer = main_cart.parent();
33588			epoHolder = this_epo_container;
33589			totalsHolderContainer = this_totals_container;
33590			totalsHolder = this_epo_totals_container;
33591			variationForm = epoObject.variations_form;
33592		}
33593
33594		if ( epoObject.is_associated ) {
33595			itemId = epoHolder.attr( 'data-cart-id' );
33596		}
33597
33598		currentCart = alternativeCart || main_cart;
33599		totalsHolder.data( 'tm_for_cart', currentCart );
33600
33601		variation_id_selector = getVariationIdSelector( currentCart );
33602		qtyElement = getQtyElement( currentCart );
33603
33604		totalsHolder.data( 'variationIdElement', getVariationIdElement( currentCart, '.wceb_picker_wrap ' + variation_id_selector ) );
33605		totalsHolder.data( 'qty_element', qtyElement );
33606
33607		this_product_type = totalsHolder.data( 'type' );
33608
33609		variationForm.data( 'tc_product_id', product_id );
33610
33611		finalTotalBoxMode = totalsHolder.attr( 'data-tm-epo-final-total-box' );
33612		finalTotalBoxShowFinal = totalsHolder.attr( 'data-tm-epo-show-final-total' );
33613		finalTotalBoxShowOptions = totalsHolder.attr( 'data-tm-epo-show-options-total' );
33614
33615		jWindow.trigger( 'epoEventHandlersStart', {
33616			epo: epoObject,
33617			cart: currentCart,
33618			alternativeCart: alternativeCart,
33619			cartContainer: cartContainer,
33620			totalsHolderContainer: totalsHolderContainer,
33621			totalsHolder: totalsHolder,
33622			epoHolder: epoHolder
33623		} );
33624
33625		jWindow.on( 'epoCalculateRules', function( event, dataObject ) {
33626			if ( event && dataObject && dataObject.currentCart ) {
33627				tm_epo_rules( epoObject, dataObject.currentCart );
33628			}
33629		} );
33630
33631		if ( currentCart.is( 'form' ) ) {
33632			currentCart.on( 'reset', function() {
33633				var form = $( this );
33634				setTimeout( function() {
33635					$( form.data( 'epo_id_selector' ) ).find( '.tm-epo-field' ).trigger( 'change' );
33636				}, 1 );
33637			} );
33638		}
33639
33640		tm_epo_rules( epoObject, currentCart );
33641
33642		epoFieldAll = epoHolder.find( '.tm-epo-field' );
33643		if ( ! epoObject.is_associated ) {
33644			epoFieldAll = epoFieldAll.not( tcAPI.epoSelector + ' ' + tcAPI.associatedEpoSelector + ' .tm-epo-field' );
33645		}
33646
33647		epoFieldText = epoHolder.find( '.tm-epo-field.tmcp-textarea,.tm-epo-field.tmcp-textfield' );
33648		if ( ! epoObject.is_associated ) {
33649			epoFieldText = epoFieldText.not( tcAPI.epoSelector + ' ' + tcAPI.associatedEpoSelector + ' .tm-epo-field' );
33650		}
33651
33652		epoResetRadio = epoHolder.find( '.tm-epo-reset-radio' );
33653		if ( ! epoObject.is_associated ) {
33654			epoResetRadio = epoResetRadio.not( tcAPI.epoSelector + ' ' + tcAPI.associatedEpoSelector + ' .tm-epo-reset-radio' );
33655		}
33656
33657		epoFieldUpload = epoHolder.find( '.tm-epo-field.tmcp-upload' );
33658		if ( ! epoObject.is_associated ) {
33659			epoFieldUpload = epoFieldUpload.not( tcAPI.epoSelector + ' ' + tcAPI.associatedEpoSelector + ' .tm-epo-field.tmcp-upload' );
33660		}
33661
33662		epoFieldHasClearButton = epoHolder.find( '.tm-has-clearbutton .tm-epo-field' );
33663		if ( ! epoObject.is_associated ) {
33664			epoFieldHasClearButton = epoFieldHasClearButton.not( tcAPI.epoSelector + ' ' + tcAPI.associatedEpoSelector + ' .tm-has-clearbutton .tm-epo-field' );
33665		}
33666
33667		tmQty = epoHolder.find( '.tm-quantity .tm-qty' );
33668		if ( ! epoObject.is_associated ) {
33669			tmQty = tmQty.not( tcAPI.epoSelector + ' ' + tcAPI.associatedEpoSelector + ' .tm-quantity .tm-qty' );
33670		}
33671
33672		tmQuantity = epoHolder.find( '.tm-quantity' );
33673		if ( ! epoObject.is_associated ) {
33674			tmQuantity = tmQuantity.not( tcAPI.epoSelector + ' ' + tcAPI.associatedEpoSelector + ' .tm-quantity' );
33675		}
33676
33677		selectSelector = epoHolder.find( 'select.tm-epo-field' );
33678		if ( ! epoObject.is_associated ) {
33679			selectSelector = selectSelector.not( tcAPI.epoSelector + ' ' + tcAPI.associatedEpoSelector + ' select.tm-epo-field' );
33680		}
33681		// update price amount for select elements
33682		selectSelector
33683			.off( 'tm-select-change-html' )
33684			.on( 'tm-select-change-html', function() {
33685				var field = $( this );
33686				var e_tip;
33687				var e_description;
33688				var sign;
33689				var formatted_price;
33690
33691				if ( field.is( '.tc-epo-field-product' ) || field.is( '.tmcp-selectmultiple' ) ) {
33692					return;
33693				}
33694
33695				if ( main_cart && main_cart.data( 'per_product_pricing' ) !== undefined && ! main_cart.data( 'per_product_pricing' ) ) {
33696					return;
33697				}
33698
33699				formatted_price = tm_set_price( field.find( 'option:selected' ).data( 'price' ), totalsHolder, true, field );
33700				e_tip = field.closest( '.tmcp-field-wrap' ).find( '.tc-tooltip' );
33701				e_description = field.closest( '.tmcp-field-wrap' ).find( '.tc-inline-description' );
33702
33703				// Prices are already taxed
33704				tm_update_price( {
33705					epoObject: epoObject,
33706					obj: field.closest( '.tmcp-field-wrap' ).find( '.tc-price' ),
33707					price: field.find( 'option:selected' ).data( 'price' ),
33708					original_price: field.find( 'option:selected' ).data( 'original_price' ),
33709					force: false,
33710					useFormattedPrice: false
33711				} );
33712
33713				if ( e_tip.length > 0 ) {
33714					e_tip.attr( 'data-tm-tooltip-html', field.find( 'option:selected' ).attr( 'data-tm-tooltip-html' ) ).trigger( 'tc-tooltip-html-changed' );
33715				}
33716
33717				if ( e_description.length > 0 ) {
33718					if ( field.find( 'option:selected' ).attr( 'data-tm-tooltip-html' ) ) {
33719						e_description.html( field.find( 'option:selected' ).attr( 'data-tm-tooltip-html' ) );
33720					} else {
33721						e_description.html( '' );
33722					}
33723				}
33724
33725				if (
33726					( field.find( 'option:selected' ).attr( 'data-hide-amount' ) === '0' || TMEPOJS.tm_epo_show_price_inside_option_hidden_even === 'yes' ) &&
33727					TMEPOJS.tm_epo_show_price_inside_option === 'yes' &&
33728					field.find( 'option:selected' ).attr( 'data-text' ) &&
33729					( field.find( 'option:selected' ).data( 'price' ) || ( TMEPOJS.tm_epo_no_hide_price_if_original_not_zero === 'yes' && field.find( 'option:selected' ).data( 'original_price' ) ) )
33730				) {
33731					if (
33732						( TMEPOJS.tm_epo_auto_hide_price_if_zero === 'yes' && $.epoAPI.util.isEmpty( field.find( 'option:selected' ).data( 'price' ) ) === false ) ||
33733						( TMEPOJS.tm_epo_auto_hide_price_if_zero !== 'yes' && field.find( 'option:selected' ).attr( 'data-
33733price' ) !== '' )
33734					) {
33735						sign = '';
33736						field.find( 'option:selected' ).html( field.find( 'option:selected' ).attr( 'data-text' ) + ' (' + sign + formatted_price + ')' );
33737					} else {
33738						field.find( 'option:selected' ).html( field.find( 'option:selected' ).attr( 'data-text' ) );
33739					}
33740				}
33741
33742				if ( field.val() === '' ) {
33743					e_tip.addClass( 'tm-hidden' );
33744				} else if ( field.find( 'option:selected' ).attr( 'data-tm-tooltip-html' ) ) {
33745					e_tip.removeClass( 'tm-hidden' );
33746				} else {
33747					e_tip.addClass( 'tm-hidden' );
33748				}
33749			} )
33750			.off( 'tm-math-select-change-html-all tm-select-change-html-all tm-select-change-html-all-math' )
33751			.on( 'tm-math-select-change-html-all tm-select-change-html-all tm-select-change-html-all-math', function( event ) {
33752				var field = $( this );
33753				var e_tip;
33754				var e_description;
33755				var divider;
33756				var thisformatted_price;
33757
33758				if ( field.is( '.tc-epo-field-product' ) ) {
33759					return;
33760				}
33761
33762				e_tip = field.closest( '.tmcp-field-wrap' ).find( '.tc-tooltip' );
33763				e_description = field.closest( '.tmcp-field-wrap' ).find( '.tc-inline-description' );
33764
33765				if ( e_tip.length > 0 ) {
33766					e_tip.attr( 'data-tm-tooltip-html', field.find( 'option:selected' ).attr( 'data-tm-tooltip-html' ) ).trigger( 'tc-tooltip-html-changed' );
33767				}
33768
33769				if ( field.val() === '' ) {
33770					e_tip.addClass( 'tm-hidden' );
33771				} else if ( field.find( 'option:selected' ).attr( 'data-tm-tooltip-html' ) ) {
33772					e_tip.removeClass( 'tm-hidden' );
33773				} else {
33774					e_tip.addClass( 'tm-hidden' );
33775				}
33776
33777				if ( e_description.length > 0 ) {
33778					if ( field.find( 'option:selected' ).attr( 'data-tm-tooltip-html' ) ) {
33779						e_description.html( field.find( 'option:selected' ).attr( 'data-tm-tooltip-html' ) );
33780					} else {
33781						e_description.html( '' );
33782					}
33783				}
33784
33785				if ( main_cart && main_cart.data( 'per_product_pricing' ) !== undefined && ! main_cart.data( 'per_product_pricing' ) ) {
33786					return;
33787				}
33788
33789				if ( TMEPOJS.tm_epo_show_price_inside_option === 'yes' ) {
33790					field.find( 'option' ).toArray().forEach( function( thisoption ) {
33791						thisoption = $( thisoption );
33792						if ( ! thisoption.val() ) {
33793							return true;
33794						}
33795
33796						if ( event.type === 'tm-select-change-html-all' ) {
33797							thisoption.removeClass( 'tm-epo-late-field' ).removeData( 'tm-price-for-late islate' );
33798							tm_element_epo_rules( epoObject, field, undefined, thisoption, 1 );
33799						} else if ( event.type === 'tm-math-select-change-html-all' ) {
33800							tm_element_epo_rules( epoObject, field, undefined, thisoption, 1, true );
33801						} else if ( event.type === 'tm-select-change-html-all-math' ) {
33802							thisoption.removeClass( 'tm-epo-late-field' ).removeData( 'tm-price-for-late islate' );
33803							tm_element_epo_rules( epoObject, field, undefined, thisoption, 1, true );
33804						}
33805						divider = 1;
33806
33807						if ( TMEPOJS.tm_epo_multiply_price_inside_option !== 'yes' ) {
33808							divider = field.data( 'tm-quantity' );
33809						}
33810
33811						if ( ! divider ) {
33812							divider = 1;
33813						}
33814
33815						if ( ( TMEPOJS.tm_epo_auto_hide_price_if_zero === 'yes' && $.epoAPI.util.isEmpty( thisoption.data( 'price' ) ) === false ) || ( TMEPOJS.tm_epo_auto_hide_price_if_zero !== 'yes' && thisoption.attr( 'data-price' ) !== '' ) ) {
33816							thisformatted_price = tm_set_price( thisoption.data( 'price' ) / divider, totalsHolder, true, field );
33817
33818							if ( ( thisoption.attr( 'data-hide-amount' ) === '0' || TMEPOJS.tm_epo_show_price_inside_option_hidden_even === 'yes' ) && thisoption.attr( 'data-text' ) ) {
33819								thisoption.html( thisoption.attr( 'data-text' ) + ' (' + thisformatted_price + ')' );
33820							}
33821						} else {
33822							thisoption.html( thisoption.attr( 'data-text' ) );
33823						}
33824					} );
33825				}
33826			} )
33827			.off( 'tm-select-price-update-html-all' )
33828			.on( 'tm-select-price-update-html-all', function() {
33829				var field = $( this );
33830				var divider;
33831				var thisformatted_price;
33832
33833				if ( field.is( '.tc-epo-field-product' ) ) {
33834					return;
33835				}
33836
33837				if ( main_cart && main_cart.data( 'per_product_pricing' ) !== undefined && ! main_cart.data( 'per_product_pricing' ) ) {
33838					return;
33839				}
33840
33841				if ( TMEPOJS.tm_epo_show_price_inside_option === 'yes' ) {
33842					field.find( 'option' ).toArray().forEach( function( thisoption ) {
33843						thisoption = $( thisoption );
33844						if ( ! thisoption.val() ) {
33845							return true;
33846						}
33847
33848						divider = 1;
33849
33850						if ( TMEPOJS.tm_epo_multiply_price_inside_option !== 'yes' ) {
33851							divider = field.data( 'tm-quantity' );
33852						}
33853
33854						if ( ! divider ) {
33855							divider = 1;
33856						}
33857
33858						if ( ( TMEPOJS.tm_epo_auto_hide_price_if_zero === 'yes' && $.epoAPI.util.isEmpty( thisoption.data( 'price' ) ) === false ) || ( TMEPOJS.tm_epo_auto_hide_price_if_zero !== 'yes' && thisoption.attr( 'data-price' ) !== '' ) ) {
33859							thisformatted_price = tm_set_price( thisoption.data( 'price' ) / divider, totalsHolder, true, field );
33860
33861							if ( ( thisoption.attr( 'data-hide-amount' ) === '0' || TMEPOJS.tm_epo_show_price_inside_option_hidden_even === 'yes' ) && thisoption.attr( 'data-text' ) ) {
33862								thisoption.html( thisoption.attr( 'data-text' ) + ' (' + thisformatted_price + ')' );
33863							}
33864						} else {
33865							thisoption.html( thisoption.attr( 'data-text' ) );
33866						}
33867					} );
33868				}
33869			} )
33870			.off( 'tm-select-change' )
33871			.on( 'tm-select-change', function() {
33872				var field = $( this );
33873				var thisElementId = field.closest( '.cpf-element' ).attr( 'data-uniqid' );
33874
33875				if ( field.is( '.tc-epo-field-product' ) ) {
33876					return;
33877				}
33878
33879				if ( main_cart && main_cart.data( 'per_product_pricing' ) !== undefined && ! main_cart.data( 'per_product_pricing' ) ) {
33880					return;
33881				}
33882
33883				field.removeData( 'addedtcEpoBeforeOptionPriceCalculation' );
33884				field.removeData( 'addedfieldtcEpoBeforeOptionPriceCalculation' );
33885				jWindow.off( 'tcEpoBeforeOptionPriceCalculation.math' + thisElementId );
33886				jWindow.off( 'tcEpoAfterNormalOptionPriceCalculation.math' + thisElementId );
33887
33888				field.trigger( 'tm-select-change-html' );
33889				field.trigger( 'tm-select-change-html-all' );
33890
33891				currentCart.trigger( {
33892					type: 'tm-epo-update',
33893					norules: 1,
33894					element: field
33895				} );
33896			} ).trigger( 'tm-select-change-html-all' );
33897
33898		// Element quantity selector
33899		tmQty
33900			.off( 'focus.cpf' )
33901			.on( 'focus.cpf', function() {
33902				var qtyField = $( this );
33903				var field = qtyField.closest( '.tmcp-field-wrap' ).find( '.tm-epo-field' );
33904				var currentVal = parseFloat( qtyField.val() );
33905				var max = parseFloat( qtyField.attr( 'max' ) );
33906				var min = parseFloat( qtyField.attr( 'min' ) );
33907				var step = qtyField.attr( 'step' );
33908				var check1 = tm_limit_c_selection( field, false );
33909				var check2 = tm_exact_c_selection( field, false );
33910				var check3 = true;
33911
33912				// Format values
33913				if ( ! currentVal || currentVal === '' || currentVal === 'NaN' ) {
33914					currentVal = 0;
33915				}
33916				if ( max === '' || max === 'NaN' ) {
33917					max = '';
33918				}
33919				if ( min === '' || min === 'NaN' ) {
33920					min = 0;
33921				}
33922				if ( step === 'any' || step === '' || step === undefined || parseFloat( step ) === 'NaN' ) {
33923					step = 1;
33924				}
33925
33926				if ( currentVal < min || currentVal > max ) {
33927					check3 = false;
33928				}
33929
33930				if ( check1 && check2 && check3 ) {
33931					setQtyData( qtyField, currentVal );
33932				} else {
33933					setQtyData( qtyField, min );
33934				}
33935			} )
33936			.off( 'change.cpf' )
33937			.on( 'change.cpf', function( event, data ) {
33938				var qtyField = $( this );
33939				var field = qtyField.closest( '.tmcp-field-wrap' ).find( '.tm-epo-field' );
33940				var currentVal = parseFloat( qtyField.val() );
33941				var max = parseFloat( qtyField.attr( 'max' ) );
33942				var min = parseFloat( qtyField.attr( 'min' ) );
33943				var step = qtyField.attr( 'step' );
33944				var check1 = tm_limit_c_selection( field, false );
33945				var check2 = tm_exact_c_selection( field, false );
33946				var check3 = true;
33947
33948				// Format values
33949				if ( ! currentVal || currentVal === '' || currentVal === 'NaN' ) {
33950					currentVal = 0;
33951				}
33952				if ( max === '' || max === 'NaN' ) {
33953					max = '';
33954				}
33955				if ( min === '' || min === 'NaN' ) {
33956					min = 0;
33957				}
33958				if ( step === 'any' || step === '' || step === undefined || parseFloat( step ) === 'NaN' ) {
33959					step = 1;
33960				}
33961
33962				if ( currentVal < min || currentVal > max ) {
33963					check3 = false;
33964				}
33965
33966				if ( check1 && check2 && check3 ) {
33967					if ( ! epoObject.noEpoUpdate ) {
33968						field.data( 'tm-quantity', qtyField.val() ).trigger( 'change', data );
33969					} else {
33970						field.data( 'tm-quantity', qtyField.val() ).trigger( 'change.cpf', data ).trigger( 'change.cpfproduct', data );
33971					}
33972					field.trigger( 'tm-select-change-html-all' );
33973				} else if ( qtyField.data( 'epopreviousvalue' ) ) {
33974					qtyField.val( qtyField.data( 'epopreviousvalue' ) );
33975				} else {
33976					qtyField.val( min );
33977				}
33978
33979				qtyField.trigger( 'cpf-changed' );
33980			} )
33981			.off( 'tmaddquantity' )
33982			.on( 'tmaddquantity', function() {
33983				var qtyField = $( this );
33984				var field = qtyField.closest( '.tmcp-field-wrap' ).find( '.tm-epo-field' );
33985
33986				field.data( 'tm-quantity', qtyField.val() );
33987			} );
33988
33989		// Insert characters remaining for text-areas and text-fields
33990		thismaxlength = epoHolder.find( '.tmcp-textfield.tm-epo-field[maxlength],textarea.tm-epo-field[maxlength]' );
33991		if ( ! epoObject.is_associated ) {
33992			thismaxlength = thismaxlength.not( tcAPI.epoSelector + ' ' + tcAPI.associatedEpoSelector + ' .tm-epo-field' );
33993		}
33994
33995		thismaxlength.toArray().forEach( function( field ) {
33996			var innerWrap;
33997			var html;
33998			field = $( field );
33999			if ( field.data( 'tcmaxlength' ) ) {
34000				return;
34001			}
34002			field.data( 'tcmaxlength', 1 );
34003			innerWrap = field.closest( '.tmcp-field-wrap-inner' );
34004			html = $.epoAPI.template.html( tcAPI.templateEngine.tc_chars_remanining, {
34005				maxlength: field.attr( 'maxlength' ),
34006				characters_remaining: TMEPOJS.i18n_characters_remaining
34007			} );
34008			innerWrap.append( $( html ) );
34009		} );
34010		thismaxlength
34011			.off( 'change.tc_maxlen input.tc_maxlen' )
34012			.on( 'change.tc_maxlen input.tc_maxlen', function() {
34013				var field = $( this );
34014
34015				field
34016					.closest( '.tmcp-field-wrap' )
34017					.find( '.tc-chars-remanining' )
34018					.html( parseInt( field.attr( 'maxlength' ), 10 ) - parseInt( field.val().length, 10 ) );
34019			} );
34020
34021		// Change product image event
34022		epoFieldAll
34023			.off( 'tm_trigger_product_image' )
34024			.on( 'tm_trigger_product_image', function() {
34025				var field = $( this );
34026				var currentElement;
34027				var uic;
34028				var variation_element_section;
34029				var is_variation_element;
34030				var src;
34031
34032				if ( field.is( '.tm-product-image:checkbox, .tm-product-image:radio, select.tm-product-image' ) ) {
34033					uic = field.closest( '.tmcp-field-wrap' ).find( 'label img' );
34034					variation_element_section = field.closest( '.cpf-section' );
34035					is_variation_element = variation_element_section.is( '.tm-epo-variation-section' );
34036
34037					currentElement = field;
34038					if ( field.is( 'select.tm-product-image' ) ) {
34039						currentElement = field.children( 'option:selected' );
34040					}
34041					if (
34042						$( uic ).length > 0 ||
34043						( is_variation_element && currentElement.attr( 'data-image' ) !== undefined ) ||
34044						( currentElement.attr( 'data-image' ) !== undefined && currentElement.attr( 'data-image' ) !== '' ) ||
34045						( currentElement.attr( 'data-imagep' ) !== undefined && currentElement.attr( 'data-imagep' ) !== '' )
34046					) {
34047						if ( field.is( ':checked' ) || ( field.is( 'select.tm-product-image' ) && field.val() !== '' && ( field.find( 'option:selected' ).attr( 'data-rules' ) !== '' || field.is( '.tm-epo-variation-element' ) ) ) ) {
34048							src = $( uic ).first().attr( 'data-original' );
34049
34050							if ( ! src && ! is_variation_element ) {
34051								src = $( uic ).first().attr( 'src' );
34052							}
34053							if ( ! src ) {
34054								src = currentElement.attr( 'data-image' );
34055							}
34056							if ( currentElement.attr( 'data-imagep' ) ) {
34057								src = currentElement.attr( 'data-imagep' );
34058							}
34059							if ( src ) {
34060								main_product.trigger( 'tm_change_product_image', {
34061									src: src,
34062									element: field,
34063									element_current: currentElement,
34064									main_product: main_product,
34065									epo_holder: epoHolder
34066								} );
34067							} else {
34068								main_product.trigger( 'tm_change_product_image', {
34069									src: false,
34070									element: field,
34071									element_current: currentElement,
34072									main_product: main_product,
34073									epo_holder: epoHolder
34074								} );
34075							}
34076						} else {
34077							main_product.trigger( 'tm_restore_product_image', {
34078								element: field,
34079								element_current: currentElement,
34080								main_product: main_product,
34081								epo_holder: epoHolder
34082							} );
34083						}
34084					} else {
34085						main_product.trigger( 'tm_restore_product_image', {
34086							element: field,
34087							element_current: currentElement,
34088							main_product: main_product,
34089							epo_holder: epoHolder
34090						} );
34091					}
34092				} else {
34093					main_product.trigger( 'tm_attempt_product_image', {
34094						element: field,
34095						element_current: currentElement,
34096						main_product: main_product,
34097						epo_holder: epoHolder
34098					} );
34099				}
34100			} );
34101
34102		tmQuantity
34103			.off( 'showhide.cpfcustom' )
34104			.on( 'showhide.cpfcustom', function() {
34105				var quantity_selector = $( this );
34106				var field = quantity_selector.closest( '.tmcp-field-wrap' ).find( '.tm-epo-field' );
34107				var show = false;
34108				var tmqty;
34109				var tmqtyval;
34110				var tmqtymin;
34111				var radios;
34112				var tmqtybuttons;
34113
34114				if ( ! field.is( '.tm-epo-variation-element' ) ) {
34115					if ( field.is( 'select' ) ) {
34116						if ( field.val() !== '' ) {
34117							show = true;
34118						}
34119					} else if ( field.is( ':checkbox' ) ) {
34120						if ( field.is( ':checked' ) ) {
34121							show = true;
34122						}
34123					} else if ( field.is( ':radio' ) ) {
34124						if ( field.is( ':checked' ) ) {
34125							show = true;
34126							if ( TMEPOJS.tm_epo_show_only_active_quantities === 'yes' ) {
34127								radios = field.closest( '.tc-element-inner-wrap' ).find( '.tm-epo-field.tmcp-radio' );
34128								radios.toArray().forEach( function( rd ) {
34129									$( rd ).closest( '.tmcp-field-wrap' ).find( '.tm-quantity' ).hide();
34130								} );
34131							}
34132						}
34133					} else if ( field.val() ) {
34134						show = true;
34135					}
34136
34137					tmqty = quantity_selector.find( '.tm-qty' );
34138					tmqtybuttons = quantity_selector.find( '.tc-qty-button' );
34139					tmqtyval = tmqty.val();
34140					tmqtymin = tmqty.attr( 'min' ) || '';
34141
34142					if ( show ) {
34143						if ( TMEPOJS.tm_epo_show_only_active_quantities === 'yes' ) {
34144							quantity_selector.show();
34145						}
34146
34147						tmqty.removeClass( 'ignore' ).prop( 'disabled', false );
34148						tmqtybuttons.prop( 'disabled', false );
34149					} else {
34150						if ( TMEPOJS.tm_epo_show_only_active_quantities === 'yes' ) {
34151							quantity_selector.hide();
34152							if ( ! tmqtyval ) {
34153								tmqty.val( tmqtymin );
34154							}
34155						}
34156
34157						tmqty.addClass( 'ignore' ).prop( 'disabled', true );
34158						tmqtybuttons.prop( 'disabled', true );
34159					}
34160
34161					if ( epoHolder.is( '.tc-show' ) ) {
34162						setTimeout( function() {
34163							quantity_selector.closest( '.tcowl-carousel' ).trigger( 'refresh.owl.carousel' );
34164						}, 200 );
34165					}
34166				}
34167			} );
34168
34169		epoFieldAll
34170			.off( 'change.cpfcustom' )
34171			.on( 'change.cpfcustom', function() {
34172				$( this ).closest( '.tmcp-field-wrap' ).find( '.tm-quantity' ).trigger( 'showhide.cpfcustom' );
34173			} );
34174
34175		epoFieldAll
34176			.off( 'change.cpf' )
34177			.on( 'change.cpf', function( event, data ) {
34178				var field = $( this );
34179				var is_li = field.closest( '.tmcp-field-wrap' );
34180				var is_ul = field.closest( '.tmcp-ul-wrap' );
34181				var is_replace;
34182				var connector = is_ul.attr( 'data-tm-connector' );
34183
34184				if ( field.is( ':checkbox, :radio' ) ) {
34185					if ( field.is( ':radio' ) ) {
34186						if ( ! data ) {
34187							if ( connector !== undefined ) {
34188								$( '[data-tm-connector="' + connector + '"]' ).find( '.tmcp-field-wrap' ).removeClass( 'tc-active' ).find( '.tm-epo-reset-radio' ).addClass( 'tm-hidden' );
34189							} else {
34190								is_ul.find( '.tmcp-field-wrap' ).removeClass( 'tc-active' );
34191							}
34192						}
34193						if ( connector !== undefined ) {
34194							$( '[data-tm-connector="' + connector + '"]' ).find( '.tmcp-field-wrap' ).removeClass( 'tc-active' ).find( '.tm-epo-reset-radio' ).addClass( 'tm-hidden' );
34195						}
34196					}
34197					if ( field.is( ':checked' ) ) {
34198						is_li.addClass( 'tc-active' );
34199					} else {
34200						is_li.removeClass( 'tc-active' );
34201					}
34202				}
34203
34204				if ( ! field.is( '.tm-epo-variation-element' ) ) {
34205					if ( field.is( '.use_images:checkbox, .use_images:radio' ) && field.attr( 'data-imagec' ) ) {
34206						is_replace = is_li.find( '.radio-image,.checkbox-image' ).first();
34207						if ( is_replace.length > 0 ) {
34208							if ( field.is( ':checked' ) ) {
34209								is_replace.prop( 'src', field.attr( 'data-imagec' ) );
34210							} else {
34211								is_replace.prop( 'src', field.attr( 'data-image' ) );
34212							}
34213						}
34214					}
34215
34216					if ( field.is( '.use_images:radio' ) && ! data ) {
34217						field
34218							.closest( '.cpf-type-radio' )
34219							.find( '.use_images:radio' )
34220							.not( field )
34221							.toArray().forEach( function( r ) {
34222								r = $( r );
34223								r.closest( '.tmcp-field-wrap' ).find( '.radio-image' ).first().prop( 'src', r.attr( 'data-image' ) );
34224							} );
34225					}
34226
34227					if ( field.is( '.tmcp-range' ) ) {
34228						field.trigger( 'change.cpflogic' );
34229					}
34230					if ( field.is( 'select' ) ) {
34231						field.trigger( 'tm-select-change' );
34232					} else {
34233						if ( field.is( '.tmcp-radio' ) ) {
34234							field
34235								.closest( '.cpf-element' )
34236								.find( '.tm-quantity .tm-qty' )
34237								.toArray().forEach( function( tmqty ) {
34238									tmqty = $( tmqty );
34239									if ( ! tmqty.closest( 'li.tmcp-field-wrap' ).find( '.tmcp-radio' ).is( ':checked' ) ) {
34240										tmqty.attr( 'disabled', 'disabled' );
34241									} else {
34242										tmqty.prop( 'disabled', false );
34243									}
34244								} );
34245						}
34246						priceCache = true;
34247						currentCart.trigger( {
34248							type: 'tm-epo-update',
34249							norules: 1,
34250							element: field
34251						} );
34252						priceCache = false;
34253					}
34254				}
34255
34256				field.trigger( 'tm_trigger_product_image' );
34257
34258				if ( epoHolder.is( '.tc-show' ) ) {
34259					setTimeout( function() {
34260						$( '.tm-owl-slider' ).toArray().forEach( function( slider ) {
34261							$( slider ).trigger( 'refresh.owl.carousel' );
34262						} );
34263					}, 200 );
34264				}
34265				main_product.trigger( 'tm_attempt_product_image', {
34266					element: field,
34267					main_product: main_product,
34268					epo_holder: epoHolder
34269				} );
34270			} );
34271
34272		epoFieldAll.filter( '.tm-epo-field:text,input.tm-epo-field[type="number"],textarea.tm-epo-field' )
34273			.off( 'input.cpf' )
34274			.on( 'input.cpf', function() {
34275				$( this ).trigger( 'change.cpf', { autoload: 1 } );
34276			} );
34277
34278		epoFieldAll
34279			.filter( ':checkbox:checked, :radio:checked' )
34280			.toArray().forEach( function( cb ) {
34281				$( cb )
34282					.closest( '.tmcp-field-wrap' )
34283					.addClass( 'tc-active' )
34284					.trigger( 'change.cpf', { autoload: 1 } );
34285			} );
34286
34287		epoFieldHasClearButton
34288			.off( 'change.cpfclearbutton' )
34289			.on( 'change.cpfclearbutton cpfclearbutton', function() {
34290				var field = $( this );
34291				var radioResetElement;
34292				var fieldWrap = field.closest( '.tmcp-field-wrap' );
34293				var fieldSearch;
34294
34295				if ( field.is( ':checked' ) ) {
34296					fieldSearch = field.closest( '.tc-element-inner-wrap' );
34297
34298					if ( fieldSearch.find( '.tc-epo-element-product-li-container' ).length ) {
34299						fieldSearch = fieldSearch.find( '.tmcp-ul-wrap' ).first().children( '.tmcp-field-wrap' ).not( '.tc-epo-element-product-li-container' );
34300					}
34301
34302					radioResetElement = fieldSearch.find( '.tm-epo-reset-radio' );
34303					if ( ! radioResetElement.length ) {
34304						radioResetElement = field.closest( '.tc-element-inner-wrap' ).find( '.tm-epo-reset-radio' ).first();
34305					}
34306					radioResetElement.removeClass( 'tm-hidden' );
34307
34308					fieldWrap.append( radioResetElement );
34309				}
34310			} );
34311
34312		epoResetRadio
34313			.off( 'click.cpf' )
34314			.on( 'click.cpf', function() {
34315				var radioResetElement = $( this );
34316				var fieldContainer = radioResetElement.closest( '.tc-element-inner-wrap' );
34317				var checkedRadios = fieldContainer.find( '.tm-epo-field.tmcp-radio:checked' );
34318
34319				if ( checkedRadios.length ) {
34320					checkedRadios.prop( 'checked', false );
34321					checkedRadios.trigger( 'change', { forced: 1 } );
34322				}
34323
34324				radioResetElement.addClass( 'tm-hidden' );
34325			} );
34326
34327		if ( _ && _.debounce ) {
34328			epoFieldText.on( 'keyup',
34329				_.debounce( function() {
34330					var $this = $( this );
34331					var mainform = currentCart;
34332					if ( epoObject.is_associated ) {
34333						mainform = epoObject.mainEpoObject.form;
34334					}
34335
34336					if ( TMEPOJS.tm_epo_global_enable_validation === 'yes' && $.tc_validator && $this.closest( '.tmcp-ul-wrap' ).data( 'tm-validation' ) && $this.tc_rules() ) {
34337						mainform.tc_validate().element( $this );
34338					}
34339					$this.trigger( 'change.cpf' );
34340					$this.closest( '.tmcp-field-wrap' ).find( '.tm-quantity' ).trigger( 'showhide.cpfcustom' );
34341				}, 10 )
34342			);
34343		}
34344
34345		epoFieldUpload
34346			.off( 'change.cpfv tcupload' )
34347			.on( 'change.cpfv tcupload', function() {
34348				var field = $( this );
34349				var label = field.closest( 'label' );
34350				var li = field.closest( '.tmcp-field-wrap' );
34351				var cpfUploadContainer = li.find( '.cpf-upload-container' );
34352				var name = li.find( '.tm-filename' );
34353				var val = field.val().replace( 'C:\\fakepath\\', '' );
34354				var valHidden = [];
34355				var num_uploads;
34356				var windowURL = window.URL || window.webkitURL;
34357				var files;
34358				var image;
34359				var uploadPreview = li.find( '.tc-upload-preview' );
34360				var addImage;
34361				var uniqid;
34362
34363				if ( cpfUploadContainer.length && name.length <= 0 ) {
34364					name = $( '<span class="tm-filename"></span>' );
34365					label.after( name );
34366				}
34367				if ( val === undefined || val === 'undefined' ) {
34368					val = '';
34369				}
34370
34371				field.next( '.tmcp-upload-hidden' ).remove();
34372
34373				valHidden = field.attr( 'data-file' );
34374				valHidden = valHidden ? valHidden.split( '|' ) : [];
34375				if ( this.files ) {
34376					files = Array.from( this.files );
34377				} else if ( valHidden.length > 0 ) {
34378					files = valHidden;
34379				}
34380
34381				if ( files.length > 1 ) {
34382					name.html( '' + files.length + ' ' + ( files.length === 1 ? TMEPOJS.i18n_file : TMEPOJS.i18n_files ) );
34383				} else if ( files.length === 1 ) {
34384					name.html( val );
34385				} else {
34386					name.empty();
34387				}
34388				if ( ! uploadPreview.length ) {
34389					uploadPreview = $( '<div class="tc-upload-preview tc-hidden"></div>' );
34390					li.append( uploadPreview );
34391				}
34392
34393				uploadPreview.empty();
34394
34395				num_uploads = epoHolder.data( 'num_uploads' );
34396				if ( ! num_uploads ) {
34397					num_uploads = [];
34398				}
34399				uniqid = field.closest( '.cpf-element' ).attr( 'data-uniqid' );
34400				if ( ! num_uploads[ uniqid ] ) {
34401					num_uploads[ uniqid ] = [];
34402				}
34403
34404				if ( ( val || files.length > 0 ) && windowURL && windowURL.createObjectURL ) {
34405					if ( TMEPOJS.tm_epo_upload_inline_image_preview === 'yes' ) {
34406						if ( files.length > 1 ) {
34407							uploadPreview.addClass( 'multiple' );
34408						} else {
34409							uploadPreview.removeClass( 'multiple' );
34410						}
34411						uploadPreview.removeClass( 'tc-hidden' );
34412					}
34413					addImage = function( dataFile, src, i, fileHolder, ext ) {
34414						var size = field[ 0 ].files && field[ 0 ].files[ i ] && field[ 0 ].files[ i ].size ? field[ 0 ].files[ i ].size : 0;
34415						var imageHTML = '<div class="tc-upload-image">';
34416						if ( src ) {
34417							imageHTML = imageHTML + '<img src="' + src + '">';
34418						} else {
34419							imageHTML = imageHTML + '<div class="tc-file-ext-overlay"></div>';
34420						}
34421						imageHTML = imageHTML + '<div class="tc-file-name">' + dataFile.name + '</div>';
34422						if ( ClipboardEvent || DataTransfer ) {
34423							imageHTML = imageHTML + '<button type="button" class="tc-upload-remove"><svg width="26" height="26" viewBox="0 0 26 26" xmlns="http://www.w3.org/2000/svg"><path d="M11.586 13l-2.293 2.293a1 1 0 0 0 1.414 1.414L13 14.414l2.293 2.293a1 1 0 0 0 1.414-1.414L14.414 13l2.293-2.293a1 1 0 0 0-1.414-1.414L13 11.586l-2.293-2.293a1 1 0 0 0-1.414 1.414L11.586 13z" fill="currentColor" fill-rule="nonzero"/></svg></button>';
34424						}
34425						if ( ext ) {
34426							imageHTML = imageHTML + '<div class="tc-file-ext">' + ext + '</div>';
34427						}
34428						imageHTML = imageHTML + '<div class="tc-file-size">' + readableFileSize( size ) + '</div>';
34429						imageHTML = imageHTML + '</div>';
34430						fileHolder.append( imageHTML );
34431					};
34432
34433					files.forEach( function( dataFile, i ) {
34434						var fileHolder;
34435						if ( TMEPOJS.tm_epo_upload_inline_image_preview === 'yes' ) {
34436							fileHolder = $( '<div class="tc-upload-file"></div>' );
34437							uploadPreview.append( fileHolder );
34438							image = new Image();
34439							image.onload = function() {
34440								addImage( dataFile, this.src, i, fileHolder );
34441							};
34442							image.onerror = function() {
34443								addImage( dataFile, false, i, fileHolder, dataFile.name.split( '.' ).pop() );
34444							};
34445							if ( valHidden.length > 0 ) {
34446								image.src = valHidden[ i ];
34447							} else {
34448								image.src = windowURL.createObjectURL( dataFile );
34449								windowURL.revokeObjectURL( dataFile );
34450							}
34451						}
34452						num_uploads[ uniqid ].push( dataFile.name );
34453					} );
34454					epoHolder.data( 'num_uploads', num_uploads );
34455				} else {
34456					delete num_uploads[ uniqid ];
34457				}
34458				if ( num_uploads[ uniqid ] && num_uploads[ uniqid ].length === 0 ) {
34459					delete num_uploads[ uniqid ];
34460				}
34461			} );
34462
34463		jDocument.on( 'click', '.tc-upload-remove', function() {
34464			var $this = $( this );
34465			var val;
34466			var cpfElement;
34467			var input;
34468			var fileWrap;
34469			var li;
34470			var uploadPreview;
34471			var name;
34472			var index;
34473			var files;
34474			var dT;
34475			var num_uploads;
34476			var uniqid;
34477			var uploadHidden;
34478			var dataFiles;
34479
34480			if ( ClipboardEvent || DataTransfer ) {
34481				cpfElement = $this.closest( '.cpf-element' );
34482				input = cpfElement.find( '.tm-epo-field' );
34483				fileWrap = $this.closest( '.tc-upload-file' );
34484				li = $this.closest( '.tmcp-field-wrap' );
34485				uploadPreview = li.find( '.tc-upload-preview' );
34486				name = li.find( '.tm-filename' );
34487				index = fileWrap.index();
34488				val = input.val().replace( 'C:\\fakepath\\', '' );
34489				if ( val === undefined || val === 'undefined' ) {
34490					val = '';
34491				}
34492
34493				files = Array.from( input[ 0 ].files );
34494				files.splice( index, 1 );
34495				dT = new ClipboardEvent( '' ).clipboardData || new DataTransfer();
34496
34497				files.forEach( function( dataFile ) {
34498					dT.items.add( dataFile );
34499				} );
34500
34501				if ( files.length > 1 ) {
34502					uploadPreview.addClass( 'multiple' );
34503				} else {
34504					uploadPreview.removeClass( 'multiple' );
34505				}
34506
34507				input[ 0 ].files = dT.files;
34508				fileWrap.remove();
34509				if ( dT.files.length > 1 ) {
34510					name.html( '' + dT.files.length + ' ' + ( dT.files.length === 1 ? TMEPOJS.i18n_file : TMEPOJS.i18n_files ) );
34511				} else if ( dT.files.length === 1 ) {
34512					name.html( val );
34513				} else {
34514					name.empty();
34515				}
34516
34517				num_uploads = epoHolder.data( 'num_uploads' );
34518				if ( ! num_uploads ) {
34519					num_uploads = [];
34520				}
34521				uniqid = cpfElement.closest( '.cpf-element' ).attr( 'data-uniqid' );
34522				if ( ! num_uploads[ uniqid ] ) {
34523					num_uploads[ uniqid ] = [];
34524				}
34525
34526				num_uploads[ uniqid ].splice( index, 1 );
34527				if ( num_uploads[ uniqid ].length === 0 ) {
34528					delete num_uploads[ uniqid ];
34529					input.val( '' );
34530				}
34531
34532				uploadHidden = input.next( '.tmcp-upload-hidden' );
34533				if ( uploadHidden.length ) {
34534					dataFiles = uploadHidden.val().split( '|' );
34535					if ( dataFiles.length > 0 ) {
34536						dataFiles.splice( index, 1 );
34537					}
34538					if ( dataFiles.length === 0 ) {
34539						uploadHidden.remove();
34540					} else {
34541						uploadHidden.val( dataFiles.join( '|' ) );
34542					}
34543				}
34544
34545				epoHolder.data( 'num_uploads', num_uploads );
34546			}
34547		} );
34548
34549		qtyElement
34550			.off( 'change.cpf input.cpf' )
34551			.on( 'change.cpf input.cpf', function() {
34552				var field = $( this );
34553
34554				currentCart.trigger( 'tm-epo-check-dpd' );
34555				setQtyData( field );
34556				// It is required than when you update the product quantity
34557				// to recalculate the option price to accommodate price types
34558				// that depend on quantity
34559				currentCart.trigger( {
34560					type: 'tm-epo-update',
34561					norules: 2
34562				} );
34563			} );
34564		setQtyData( qtyElement );
34565		// Support for math formula on associated products when they are initially hidden
34566		epoHolder
34567			.find( '.cpf-type-product' )
34568			.off( 'tc-logic' )
34569			.on( 'tc-logic', function() {
34570				var $this = $( this );
34571				if ( $this.find( '.tc-init' ).length ) {
34572					return;
34573				}
34574				if ( $this.data( 'isactive' ) && ! $this.data( 'donetclogic' ) ) {
34575					$this.data( 'donetclogic', true );
34576					$this.find( '.tc-epo-field-product' ).trigger( 'change.cpfproduct', { forced: 3 } );
34577				}
34578			} );
34579
34580		// Product element checkbox required status fix
34581		epoHolder
34582			.find( '.cpf-type-product .tc-epo-field-product.tc-epo-field-product-checkbox' )
34583			.off( 'change.cpfrequired' )
34584			.on( 'change.cpfrequired', function() {
34585				var $this = $( this );
34586				var cpfElement = $this.closest( '.cpf-element' );
34587				var checkboxes;
34588				var checkboxesChecked;
34589				var checkboxesNotChecked;
34590				if ( cpfElement.is( '.tc-is-required' ) ) {
34591					checkboxes = cpfElement.find( '.tmcp-field.tc-epo-field-product-checkbox' );
34592					checkboxesNotChecked = checkboxes.not( ':checked' );
34593					checkboxesChecked = cpfElement.find( '.tmcp-field.tc-epo-field-product-checkbox:checked' );
34594
34595					if ( checkboxesChecked.length > 0 ) {
34596						checkboxesNotChecked.prop( 'required', false );
34597						checkboxesChecked.prop( 'required', true );
34598					} else {
34599						checkboxes.attr( 'required', true );
34600						checkboxes.prop( 'required', true );
34601					}
34602				}
34603			} );
34604
34605		// Product element variable product support
34606		epoHolder
34607			.find( '.cpf-type-product .tc-epo-field-product' )
34608			.off( 'change.cpfproduct' )
34609			.on( 'change.cpfproduct', function( e, data ) {
34610				var $this = $( this );
34611				var value;
34612				var type;
34613				var selected;
34614				var variableProductContainers;
34615				var thisVariableProductContainer;
34616				var elementContainer = $this.closest( '.cpf-element' );
34617				var productContainerWraps = elementContainer.find( '.tc-epo-element-product-container-wrap' );
34618				var thisProductContainerWrap = $this.closest( '.tmcp-field-wrap' ).find( '.tc-epo-element-product-container-wrap' );
34619				var hasProductContainerWrap = thisProductContainerWrap.length > 0;
34620				var postData;
34621				var skip = false;
34622				var isTrigger = 1000;
34623				var qtyalt;
34624				var associatedSetter = $this;
34625				var associatedElement;
34626				var isFilled;
34627				var productPrice;
34628				var originalProductPrice;
34629				var epoField = elementContainer.find( '.tm-epo-field' ).not( '.tc-epo-element-product-li-container .tm-epo-field' );
34630				var checked = epoField.filter( ':checked' );
34631				var qty;
34632				var counter = $this.attr( 'data-counter' );
34633
34634				if ( false === elementContainer.data( 'isactive' ) ) {
34635					return;
34636				}
34637				if ( data && data.forced === 2 ) {
34638					return;
34639				}
34640
34641				if ( e.isTrigger !== undefined && $this.data( 'triggeredonce' ) && ! ( data && data.forced ) ) {
34642					return;
34643				}
34644
34645				if ( $this.data( 'triggeredforced' ) === 3 ) {
34646					return;
34647				}
34648
34649				$this.data( 'triggeredonce', 1 );
34650
34651				if ( ! $this.is( ':checkbox' ) ) {
34652					isTrigger = e.isTrigger;
34653				}
34654
34655				if ( epoField.is( ':radio' ) ) {
34656					if ( checked.length > 0 ) {
34657						qty = checked.closest( '.tmcp-field-wrap' ).find( 'input.tm-qty' );
34658					}
34659				} else if ( epoField.is( ':checkbox' ) ) {
34660					if ( checked.length > 0 ) {
34661						qty = $this.closest( '.tmcp-field-wrap' ).find( 'input.tm-qty' );
34662					}
34663				} else {
34664					qty = epoField.closest( '.tmcp-field-wrap' ).find( 'input.tm-qty' );
34665				}
34666
34667				if ( $this.is( ':checkbox' ) ) {
34668					if ( $this.is( '.tc-epo-field-product-hidden' ) ) {
34669						if ( $this.is( ':checked' ) ) {
34670							productContainerWraps.addClass( 'tc-active-product' );
34671						} else {
34672							productContainerWraps.removeClass( 'tc-active-product' );
34673						}
34674
34675						value = $this.val();
34676						type = $this.attr( 'data-type' );
34677						elementContainer.find( '.tc-epo-element-product-li-container:not(.tc-hidden-container .tc-epo-element-product-li-container)' ).removeClass( 'tm-hidden' );
34678					} else {
34679						if ( ! $this.is( ':checked' ) ) {
34680							if ( hasProductContainerWrap ) {
34681								thisProductContainerWrap.addClass( 'tm-hidden' );
34682							}
34683							if ( ! ( data && data.forced ) ) {
34684								return;
34685							}
34686							skip = true;
34687						} else if ( hasProductContainerWrap ) {
34688							thisProductContainerWrap.removeClass( 'tm-hidden' );
34689						}
34690						if ( ! skip ) {
34691							value = $this.val();
34692							type = $this.attr( 'data-type' );
34693							elementContainer.find( '.tc-epo-element-product-li-container:not(.tc-hidden-container .tc-epo-element-product-li-container)' ).removeClass( 'tm-hidden' );
34694						}
34695					}
34696				} else if ( $this.is( ':radio' ) ) {
34697					if ( ! $this.is( ':checked' ) ) {
34698						if ( hasProductContainerWrap ) {
34699							thisProductContainerWrap.addClass( 'tm-hidden' );
34700						}
34701						if ( ! ( data && data.forced ) ) {
34702							return;
34703						}
34704						skip = true;
34705					} else if ( hasProductContainerWrap ) {
34706						productContainerWraps.addClass( 'tm-hidden' );
34707						thisProductContainerWrap.removeClass( 'tm-hidden' );
34708					}
34709					if ( ! skip ) {
34710						value = $this.val();
34711						type = $this.attr( 'data-type' );
34712						elementContainer.find( '.tc-epo-element-product-li-container:not(.tc-hidden-container .tc-epo-element-product-li-container)' ).removeClass( 'tm-hidden' );
34713					}
34714				} else if ( $this.is( 'select' ) ) {
34715					selected = $this.children( ':selected' );
34716					associatedSetter = selected;
34717					counter = selected.index();
34718					if ( $this.children( ':first' ).val() === '' ) {
34719						counter--;
34720					}
34721					value = $this.val();
34722					type = selected.attr( 'data-type' );
34723					elementContainer.find( '.tc-epo-element-product-li-container:not(.tc-hidden-container .tc-epo-element-product-li-container)' ).removeClass( 'tm-hidden' );
34724				}
34725
34726				if ( $this.is( '.tc-epo-field-product-checkbox' ) ) {
34727					variableProductContainers = thisProductContainerWrap.find( '.tc-epo-element-product-container' );
34728				} else {
34729					variableProductContainers = elementContainer.find( '.tc-epo-element-product-container' );
34730				}
34731
34732				thisVariableProductContainer = variableProductContainers.filter( '[data-product_id="' + value + '"]' );
34733
34734				qtyalt = thisVariableProductContainer.find( tcAPI.associateQtySelector );
34735
34736				if ( productContainerWraps.html() !== '' && qty && qty.length > 0 && qtyalt.length === 0 ) {
34737					qty.val( 0 );
34738				}
34739				if ( $this.is( ':checkbox' ) ) {
34740					if ( $this.is( '.tc-epo-field-product-hidden' ) ) {
34741						if ( $this.is( ':checked' ) ) {
34742							if ( qtyalt.val() === '0' ) {
34743								if ( qtyalt.attr( 'min' ) !== '0' ) {
34744									qtyalt.val( qtyalt.attr( 'min' ) ).trigger( 'change' );
34745								}
34746							}
34747						} else {
34748							qtyalt.val( 0 );
34749							qtyalt.closest( '.tm-quantity-alt' ).removeClass( 'tm-hidden' );
34750							if ( data.forced !== 1 ) {
34751								qtyalt.closest( '.tm-quantity-alt' ).find( '.single_add_to_cart_product' ).trigger( 'cpfqtybutton' );
34752							}
34753							productContainerWraps.find( tcAPI.associatedEpoCart ).trigger( 'tm-epo-update' );
34754						}
34755					}
34756				} else if ( $this.is( ':radio' ) ) {
34757					if ( $this.val() && ! $this.data( 'set_initial' ) ) {
34758						if ( qtyalt.val() === '0' ) {
34759							if ( qtyalt.attr( 'min' ) !== '0' ) {
34760								qtyalt.val( qtyalt.attr( 'min' ) ).trigger( 'change' );
34761							}
34762							$this.data( 'set_initial', 1 );
34763						}
34764					}
34765				} else if ( $this.is( 'select' ) ) {
34766					if ( $this.val() && ! $this.data( 'set_initial' ) ) {
34767						if ( qtyalt.val() === '0' ) {
34768							if ( qtyalt.attr( 'min' ) !== '0' ) {
34769								qtyalt.val( qtyalt.attr( 'min' ) ).trigger( 'change' );
34770							}
34771							$this.data( 'set_initial', 1 );
34772						}
34773					}
34774				} else {
34775					return;
34776				}
34777
34778				if ( ! value ) {
34779					variableProductContainers.addClass( 'tm-hidden' );
34780					productPrice = $.epoAPI.util.parseJSON( associatedSetter.attr( 'data-rules' ) );
34781					productPrice = productPrice[ 0 ] || 0;
34782					originalProductPrice = $.epoAPI.util.parseJSON( associatedSetter.attr( 'data-original-rules' ) );
34783					originalProductPrice = originalProductPrice || 0;
34784					if ( associatedSetter.is( '.tcenabled' ) ) {
34785						associatedSetter.data( 'associated_price_set', 1 );
34786					}
34787					associatedSetter.data( 'price_set', 1 );
34788					associatedSetter.data( 'raw_price', productPrice );
34789					associatedSetter.data( 'raw_original_price', originalProductPrice );
34790					associatedSetter.data( 'price', productPrice );
34791					associatedSetter.data( 'original_price', originalProductPrice );
34792
34793					associatedElement = associatedSetter;
34794					if ( associatedElement.is( 'option' ) ) {
34795						associatedElement = associatedSetter.closest( 'select' );
34796					}
34797					isFilled = false;
34798					if ( associatedElement.is( '.tc-epo-field-product' ) ) {
34799						if ( associatedElement.is( 'select' ) ) {
34800							if ( associatedElement.val() !== '' ) {
34801								isFilled = true;
34802							}
34803						} else if ( ( associatedElement.is( ':checkbox' ) || associatedElement.is( ':radio' ) ) ) {
34804							if ( associatedElement.is( ':checked' ) ) {
34805								isFilled = true;
34806							}
34807						} else if ( associatedElement.val() !== '' ) {
34808							isFilled = true;
34809						}
34810					}
34811					tm_force_update_price(
34812						epoObject,
34813						associatedSetter
34814							.closest( '.tmcp-field-wrap' )
34815							.find( '.tc-price' )
34816							.not( tcAPI.associatedEpoSelector + ' .tc-price' ),
34817						productPrice,
34818						isFilled ? tm_set_price_totals( productPrice ) : associatedSetter.data( 'price-html' ),
34819						originalProductPrice,
34820						! isFilled
34821					);
34822
34823					qtyElement.trigger( 'change.cpf' );
34824
34825					return;
34826				}
34827
34828				if ( thisVariableProductContainer.length === 0 ) {
34829					if ( data && data.forced === 3 ) {
34830						$this.data( 'triggeredforced', 3 );
34831					}
34832					// parent_id is used for customization purposes.
34833					postData = {
34834						action: 'wc_epo_get_associated_product_html',
34835						product_id: value,
34836						parent_id: epoHolder.attr( 'data-product-id' ),
34837						mode: elementContainer.attr( 'data-mode' ),
34838						layout_mode: elementContainer.attr( 'data-product-layout-mode' ),
34839						uniqid: elementContainer.attr( 'data-uniqid' ),
34840						name: $this.attr( 'name' ),
34841						counter: counter,
34842						quantity_min: elementContainer.attr( 'data-quantity-min' ),
34843						quantity_max: elementContainer.attr( 'data-quantity-max' ),
34844						priced_individually: elementContainer.attr( 'data-priced-individually' ),
34845						discount: elementContainer.attr( 'data-discount' ),
34846						discount_type: elementContainer.attr( 'data-discount-type' ),
34847						discount_exclude_addons: elementContainer.attr( 'data-discount-exclude-
34847addons' ),
34848						show_image: elementContainer.attr( 'data-show-image' ),
34849						show_title: elementContainer.attr( 'data-show-title' ),
34850						show_title_link: elementContainer.attr( 'data-show-title-link' ),
34851						show_price: elementContainer.attr( 'data-show-price' ),
34852						show_description: elementContainer.attr( 'data-show-description' ),
34853						show_meta: elementContainer.attr( 'data-show-meta' ),
34854						disable_epo: elementContainer.attr( 'data-disable-epo' )
34855					};
34856
34857					elementContainer.block( { message: null, overlayCSS: { background: '#fff', opacity: 0.6 } } );
34858					$.ajax( {
34859						url: TMEPOJS.ajax_url,
34860						type: 'POST',
34861						data: postData,
34862						dataType: 'json',
34863						success: function( response ) {
34864							if ( response.result === 200 ) {
34865								thisVariableProductContainer = $( response.html );
34866								if ( hasProductContainerWrap ) {
34867									$this.closest( '.tmcp-field-wrap' ).find( '.tc-epo-element-product-container-wrap' ).empty().append( thisVariableProductContainer );
34868								} else {
34869									elementContainer.find( '.tc-epo-element-product-container-wrap' ).append( thisVariableProductContainer );
34870								}
34871								show_product_html( epoObject, main_product, thisVariableProductContainer, type, $this, currentCart, variableProductContainers, isTrigger, qtyalt );
34872								thisVariableProductContainer.find( tcAPI.associateQtySelector ).trigger( 'change' );
34873								jWindow.trigger( 'tc_apply_validation' );
34874							}
34875						},
34876						complete: function() {
34877							elementContainer.unblock();
34878						}
34879					} );
34880				} else {
34881					show_product_html( epoObject, main_product, thisVariableProductContainer, type, $this, currentCart, variableProductContainers, isTrigger, qtyalt );
34882					thisVariableProductContainer.find( tcAPI.associateQtySelector ).trigger( 'change' );
34883				}
34884			} );
34885
34886		jDocument
34887			.off( 'click.cpfqtybutton cpfqtybutton', '.cpf-type-product .single_add_to_cart_product' )
34888			.on( 'click.cpfqtybutton cpfqtybutton', '.cpf-type-product .single_add_to_cart_product', function() {
34889				var $this = $( this );
34890				var qtyalt = $this.closest( '.tm-quantity-alt' ).find( tcAPI.associateQtySelector );
34891				var isAdd = $this.is( '.alt' );
34892				var productContainerWraps = $this.closest( '.cpf-element' ).find( '.tc-epo-element-product-container-wrap' );
34893				var qtyMin;
34894				var qtyMax;
34895
34896				if ( $this.data( 'inittriggeredonce' ) ) {
34897					qtyMin = $.epoAPI.math.toInt( qtyalt.attr( 'data-min' ) );
34898					qtyMax = $.epoAPI.math.toInt( qtyalt.attr( 'data-max' ) );
34899					if ( isAdd ) {
34900						productContainerWraps.addClass( 'tc-active-product' );
34901						qtyalt.attr( 'min', qtyMin );
34902						if ( qtyMax ) {
34903							qtyalt.attr( 'max', qtyMax );
34904						}
34905						if ( qtyalt.val() === '0' ) {
34906							if ( qtyalt.attr( 'min' ) === '0' ) {
34907								qtyalt.val( 1 ).trigger( 'change' );
34908							} else {
34909								qtyalt.val( qtyalt.attr( 'min' ) ).trigger( 'change' );
34910							}
34911						}
34912						$this.removeClass( 'alt' ).text( $this.attr( 'data-remove' ) );
34913					} else {
34914						productContainerWraps.removeClass( 'tc-active-product' );
34915						qtyalt.val( 0 ).trigger( 'change' );
34916						qtyalt.closest( '.tm-quantity-alt' ).removeClass( 'tm-hidden' );
34917						$this.addClass( 'alt' ).text( $this.attr( 'data-add' ) );
34918					}
34919				}
34920
34921				$this.data( 'inittriggeredonce', 1 );
34922
34923				productContainerWraps.find( tcAPI.associatedEpoCart ).trigger( 'tm-epo-update' );
34924			} );
34925
34926		jDocument
34927			.off( 'change.cpfqtyalt input.cpfqtyalt', '.cpf-type-product ' + tcAPI.associateQtySelector )
34928			.on( 'change.cpfqtyalt input.cpfqtyalt', '.cpf-type-product ' + tcAPI.associateQtySelector, function() {
34929				var $this = $( this );
34930				var epoField = $this.closest( '.cpf-element' ).find( '.tm-epo-field' ).not( '.tc-epo-element-product-li-container .tm-epo-field' );
34931				var addButton = $this.closest( '.tm-quantity-alt' ).find( '.single_add_to_cart_product' );
34932				var checked = epoField.filter( ':checked' );
34933				var qty;
34934				var qtyMin;
34935				var qtyMax;
34936				var associatedEpoCart;
34937
34938				qtyMin = $.epoAPI.math.toInt( $this.attr( 'min' ) );
34939				qtyMax = $.epoAPI.math.toInt( $this.attr( 'max' ) );
34940
34941				if ( epoField.is( '.tc-epo-field-product-hidden' ) && epoField.is( ':checkbox' ) ) {
34942					if ( checked.length === 0 && $this.val() !== '0' ) {
34943						epoField.prop( 'checked', true ).trigger( 'change' );
34944						checked = epoField.filter( ':checked' );
34945					} else if ( $this.val() === '0' && checked.length ) {
34946						epoField.prop( 'checked', false ).trigger( 'change' );
34947					}
34948				}
34949
34950				if ( epoField.is( ':radio' ) ) {
34951					if ( checked.length === 0 ) {
34952						return;
34953					}
34954					qty = checked.closest( '.tmcp-field-wrap' ).find( 'input.tm-qty' ).not( '.tc-element-qty' );
34955				} else if ( epoField.is( ':checkbox' ) ) {
34956					if ( checked.length === 0 ) {
34957						return;
34958					}
34959					qty = $this.closest( '.tmcp-field-wrap' ).find( 'input.tm-qty' ).not( '.tc-element-qty' );
34960				} else {
34961					qty = epoField.closest( '.tmcp-field-wrap' ).find( 'input.tm-qty' ).not( '.tc-element-qty' );
34962				}
34963
34964				if ( qty.length === 0 ) {
34965					return;
34966				}
34967
34968				qty.attr( 'min', qtyMin );
34969				if ( qtyMax ) {
34970					qty.attr( 'max', qtyMax );
34971				}
34972				associatedEpoCart = $this.closest( '.tc-epo-element-product-container' ).find( tcAPI.associatedEpoCart );
34973				associatedEpoCart.trigger( 'tm-epo-update' );
34974
34975				qty.val( $this.val() );
34976
34977				if ( $.epoAPI.math.toFloat( $this.val() ) > 0 ) {
34978					associatedEpoCart.find( '.tm-epo-field.tcenabled' ).removeClass( 'ignore' );
34979					$this.removeClass( 'ignore' );
34980				} else {
34981					associatedEpoCart.find( '.tm-epo-field.tcenabled' ).addClass( 'ignore' );
34982					$this.addClass( 'ignore' );
34983				}
34984				epoField.trigger( 'change.cpfproduct', { forced: 2 } );
34985				if ( addButton.data( 'inittriggeredonce' ) ) {
34986					if ( $this.val() === '0' ) {
34987						addButton.removeClass( 'alt' );
34988					} else {
34989						addButton.addClass( 'alt' );
34990					}
34991				}
34992				addButton.trigger( 'cpfqtybutton' );
34993			} );
34994
34995		jDocument
34996			.off( 'click.cbsmel' )
34997			.on( 'click.cbsmel', '.tc-choice-button-selector', function( e ) {
34998				var $field = $( this ).closest( '.tmcp-field-wrap' ).find( '.tm-epo-field' );
34999				e.stopPropagation();
35000				if ( $field.length ) {
35001					$field.trigger( 'click' );
35002				}
35003			} );
35004
35005		// Global update event
35006		currentCart.off( 'tm-epo-update' ).on( 'tm-epo-update', function( event ) {
35007			var cart = $( this );
35008			var bundleid;
35009			var productPrice = false;
35010			var rawProductPrice = 0;
35011			var productRegularPrice = false;
35012			var rawProductRegularPrice = 0;
35013			var total = 0;
35014			var original_total = 0;
35015			var showTotal = false;
35016			var cartQty;
35017			var elementQty = 1;
35018			var priceOverrideMode;
35019			var perProductPricing = true;
35020			var floatingBoxData = [];
35021			var currentVariation;
35022			var cart_fee_options_total = 0;
35023			var cart_fee_options_original_total = 0;
35024			var _total;
35025			var _original_total;
35026			var late_total_price;
35027			var tc_totals_ob = {};
35028			var formatted_options_total;
35029			var formatted_options_original_total;
35030			var formatted_fees_total;
35031			var formatted_fees_original_total;
35032			var formatted_final_total;
35033			var formatted_final_original_total;
35034			var extraFee = 0;
35035			var raw_extraFee = 0;
35036			var product_total_price;
35037			var product_total_original_price;
35038			var calculateFinalProductPrice;
35039			var total_plus_fee;
35040			var original_total_plus_fee;
35041			var product_total_price_without_options;
35042			var unit_price;
35043			var unit_original_price;
35044			var formatted_unit_price;
35045			var formatted_unit_original_price;
35046			var html;
35047			var show_options_total = false;
35048			var show_vat_options_total = false;
35049			var show_fees_total = false;
35050			var formatted_extra_fee = '';
35051			var show_extra_fee = false;
35052			var show_final_total = false;
35053			var hide_native_price;
35054			var update_native_html;
35055			var _fprice;
35056			var _f_regular_price;
35057			var customerPriceFormat;
35058			var currentEpoObject;
35059			var raw_total;
35060			var raw__total;
35061			var raw_original_total;
35062			var raw__original_total;
35063			var raw_cart_fee_options_total;
35064			var raw_cart_fee_options_original_total;
35065			var raw_total_plus_fee;
35066			var raw_original_total_plus_fee;
35067			var raw_product_total_price_without_options;
35068			var raw_product_total_price;
35069			var raw_product_total_original_price;
35070			var fetch;
35071			var customer_price_format_wrap_start = '';
35072			var customer_price_format_wrap_end = '';
35073			var associatedSetter;
35074			var associatedPrice;
35075			var associatedRawPrice;
35076			var associatedOriginalPrice;
35077			var associatedRawOriginalPrice;
35078			var associatedFormattedPrice;
35079			var nativeProductPriceSelector;
35080			var elementsLength;
35081			var productFieldSyncVatTotal = 0;
35082			var productFieldSyncTotal = 0;
35083			var productFieldSyncOriginalTotal = 0;
35084			var productFieldSyncTotalTaxed = 0;
35085			var productFieldSyncOriginalTotalTaxed = 0;
35086			var totalProductField = 0;
35087			var originalTotalProductField = 0;
35088			var totalProductFieldTaxed = 0;
35089			var originalTotalProductFieldTaxed = 0;
35090			var _ftotal;
35091			var _foriginal_total;
35092			var totalsHolder_tc_totals_ob;
35093			var temp;
35094			var vat_options_total;
35095			var vat_options_total_plus_fee;
35096			var formatted_vat_options_total;
35097			var formatted_vat_options_total_plus_fee;
35098			var vat_total;
35099			var formatted_vat_total;
35100			var vat_product_base;
35101			var formatted_vat_product_base;
35102			var pricesIncludeTax;
35103			var dynamicCheck;
35104			var dynamicProductPrice = 0;
35105
35106			if ( event.epoObject ) {
35107				currentEpoObject = $.extend( true, {}, event.epoObject );
35108			} else {
35109				currentEpoObject = $.extend( true, {}, epoObject );
35110			}
35111
35112			if ( ! currentEpoObject ) {
35113				return;
35114			}
35115
35116			if ( currentEpoObject.noEpoUpdate ) {
35117				return;
35118			}
35119
35120			jWindow.trigger( 'tcEpoUpdateStart', {
35121				epo: currentEpoObject,
35122				alternativeCart: currentEpoObject,
35123				this_product_type: this_product_type,
35124				cart: cart,
35125				totalsHolder: totalsHolder,
35126				fetchOptionPrices: fetchOptionPrices,
35127				epoHolder: epoHolder
35128			} );
35129
35130			bundleid = $.epoAPI.applyFilter( 'tc_get_bundleid', cart.attr( 'data-
35130product_id' ), cart, currentEpoObject );
35131			priceOverrideMode = totalsHolder.attr( 'data-price-override' );
35132			cartQty = getCurrentQty( cart );
35133			currentVariation = getCurrentVariation( cart );
35134
35135			if ( currentEpoObject.associated_connect && currentEpoObject.associated_connect.length === 1 ) {
35136				cartQty = parseFloat(
35137					currentEpoObject.main_product
35138						.find( tcAPI.associateQtySelector )
35139						.not( tcAPI.associatedEpoSelector + ' ' + tcAPI.qtySelector )
35140						.last()
35141						.val()
35142				);
35143			}
35144
35145			event.stopImmediatePropagation();
35146
35147			productPrice = $.epoAPI.applyFilter( 'tcGetCurrentProductPrice', tm_calculate_product_price( totalsHolder, true ), currentCart, totalsHolder );
35148
35149			rawProductPrice = productPrice;
35150
35151			productRegularPrice = $.epoAPI.applyFilter( 'tcGetCurrentProductRegularPrice', tm_calculate_product_regular_price( totalsHolder, true ), currentCart, totalsHolder );
35152
35153			rawProductRegularPrice = productRegularPrice;
35154
35155			productPrice = $.epoAPI.applyFilter( 'tcCalculateCurrentProductPrice', productPrice, {
35156				epo: currentEpoObject,
35157				alternativeCart: alternativeCart,
35158				cart: cart,
35159				main_product: main_product
35160			} );
35161
35162			if ( ! Number.isFinite( cartQty ) || totalsHolder.attr( 'data-is-sold-individually' ) || getQtyElement( cart ).length === 0 ) {
35163				cartQty = 1;
35164			}
35165			if ( TMEPOJS.tm_epo_total_price_as_unit_price === 'yes' ) {
35166				cartQty = 1;
35167			}
35168
35169			// needed for inital math calculation
35170			totalsHolder_tc_totals_ob = {
35171				qty: cartQty,
35172				product_price: rawProductPrice,
35173				original_product_price: rawProductRegularPrice
35174			};
35175			totalsHolder.data( 'totalsHolder_tc_totals_ob', totalsHolder_tc_totals_ob );
35176
35177			if ( ! event.norules ) {
35178				tm_epo_rules( currentEpoObject, cart );
35179			} else if ( event.norules ) {
35180				if ( event.norules === 1 ) {
35181					tm_element_epo_rules( currentEpoObject, event.element );
35182				}
35183				$.tcepo.lateFieldsPrices[ epoEventId ] = [];
35184
35185				epoHolder
35186					.find( '.tm-epo-late-field' )
35187					.toArray()
35188					.forEach( function( setter ) {
35189						setter = $( setter );
35190						setter.data( 'price', 0 );
35191						$.tcepo.lateFieldsPrices[ epoEventId ].push( {
35192							setter: setter,
35193							price: setter.data( 'tm-price-for-late' ),
35194							original_price: setter.data( 'tm-original-price-for-late' ),
35195							bundleid: bundleid,
35196							pricetype: get_type( currentEpoObject, setter, 'price_type' )
35197						} );
35198					} );
35199			}
35200
35201			// Check for dynamic elements.
35202			dynamicCheck = epoHolder.find( '.tmcp-dynamic.dynamic-product-price:not(.tc-is-math-special, .tc-is-math-cumulative)' );
35203			if ( currentEpoObject.is_associated === false ) {
35204				dynamicCheck = dynamicCheck.not( tcAPI.associatedEpoSelector + ' ' + '.tmcp-dynamic.dynamic-product-price:not(.tc-is-math-special, .tc-is-math-cumulative)' );
35205			}
35206			dynamicCheck = dynamicCheck.filter( '.tcenabled' );
35207			if ( dynamicCheck.length > 0 ) {
35208				fetch = fetchOptionPrices( currentEpoObject, epoHolder, '.tmcp-dynamic.dynamic-product-price:not(.tc-is-math-special, .tc-is-math-cumulative)', 0, 0, floatingBoxData, showTotal, undefined, undefined, undefined, true, undefined, undefined, cartQty );
35209				productPrice = fetch.total;
35210				dynamicProductPrice = productPrice;
35211				rawProductRegularPrice = fetch.original_total;
35212				rawProductPrice = dynamicProductPrice;
35213				productRegularPrice = dynamicProductPrice;
35214				totalsHolderContainer.find( '.cpf-dynamic-product-price' ).val( dynamicProductPrice );
35215				totalsHolder_tc_totals_ob.dynamic_product_price = dynamicProductPrice;
35216				totalsHolder.data( 'totalsHolder_tc_totals_ob', totalsHolder_tc_totals_ob );
35217			}
35218
35219			if ( currentEpoObject.associated_connect && currentEpoObject.associated_connect.length === 1 ) {
35220				associatedSetter = currentEpoObject.associated_connect;
35221				if ( currentEpoObject.associated_connect.is( 'select' ) ) {
35222					associatedSetter = currentEpoObject.associated_connect.find( 'option:selected' );
35223				}
35224			}
35225
35226			// No reason to continue if the product price is invalid
35227			if ( productPrice === false ) {
35228				totalsHolder.data( 'tm-floating-box-data', [] );
35229				totalsHolder.empty();
35230				if ( currentEpoObject.associated_connect && currentEpoObject.associated_connect.length === 1 ) {
35231					tm_force_update_price(
35232						epoObject,
35233						associatedSetter
35234							.closest( '.tmcp-field-wrap' )
35235							.find( '.tc-price' )
35236							.not( tcAPI.associatedEpoSelector + ' .tc-price' ),
35237						0,
35238						'',
35239						0
35240					);
35241				}
35242				main_cart.trigger( 'tm-epo-short-update', {
35243					container: cartContainer
35244				} );
35245				return;
35246			}
35247
35248			elementQty = $.epoAPI.applyFilter( 'tcAlterElementQty', elementQty, {
35249				epo: currentEpoObject,
35250				alternativeCart: alternativeCart,
35251				currentCart: currentCart,
35252				main_product: main_product
35253			} );
35254
35255			if ( currentCart.data( 'per_product_pricing' ) !== undefined ) {
35256				perProductPricing = currentCart.data( 'per_product_pricing' );
35257			}
35258
35259			perProductPricing = $.epoAPI.applyFilter( 'tcCalculatePerProductPricing', perProductPricing, {
35260				epo: currentEpoObject,
35261				alternativeCart: alternativeCart,
35262				cart: cart,
35263				main_product: main_product
35264			} );
35265
35266			if ( main_epo_inside_form && TMEPOJS.tm_epo_totals_box_placement === 'woocommerce_before_add_to_cart_button' ) {
35267				if ( ( this_product_type === 'variable' || this_product_type === 'variable-subscription' ) && ! totalsHolder.data( 'moved_inside' ) ) {
35268					totalsHolder.data( 'moved_inside', 1 );
35269				}
35270			}
35271
35272			jWindow.trigger( 'tcEpoBeforeOptionPriceCalculation', {
35273				epo: currentEpoObject,
35274				alternativeCart: currentEpoObject,
35275				this_product_type: this_product_type,
35276				cart: cart,
35277				totalsHolder: totalsHolder,
35278				fetchOptionPrices: fetchOptionPrices,
35279				epoHolder: epoHolder,
35280				total: total,
35281				original_total: original_total,
35282				floatingBoxData: floatingBoxData,
35283				showTotal: showTotal,
35284				cart_fee_options_total: cart_fee_options_total,
35285				cart_fee_options_original_total: cart_fee_options_original_total
35286			} );
35287
35288			if ( ! currentEpoObject.associated_connect && ! event.noassociated ) {
35289				if ( TMEPOJS.tm_epo_global_product_element_quantity_sync === 'yes' && ! cart.is( $.tcAPI().associatedEpoCart ) ) {
35290					cart.find( $.tcAPI().associatedEpoCart ).filter( ':visible' ).toArray().forEac
35290h( function( acart ) {
35291						$( acart ).trigger( {
35292							type: 'tm-epo-update',
35293							noassociated: 1
35294						} );
35295					} );
35296				}
35297			}
35298
35299			fetch = fetchOptionPrices( currentEpoObject, epoHolder, '.tmcp-field:not(.tmcp-dynamic,.tc-epo-field-product, .tc-is-math-special, .tc-is-math-cumulative)', total, original_total, floatingBoxData, showTotal, undefined, undefined, undefined, undefined, undefined, undefined, cartQty );
35300			total = fetch.total;
35301			original_total = fetch.original_total;
35302			floatingBoxData = fetch.floatingBoxData;
35303			showTotal = fetch.showTotal;
35304			elementsLength = fetch.elementsLength;
35305
35306			fetch = fetchOptionPrices( currentEpoObject, epoHolder, '.tc-epo-field-product', productFieldSyncTotal, productFieldSyncOriginalTotal, floatingBoxData, showTotal, undefined, undefined, undefined, undefined, undefined, undefined, cartQty );
35307			productFieldSyncTotal = fetch.total;
35308			productFieldSyncOriginalTotal = fetch.original_total;
35309			productFieldSyncTotalTaxed = fetch.total_taxed;
35310			productFieldSyncOriginalTotalTaxed = fetch.original_total_taxed;
35311			productFieldSyncVatTotal = fetch.vat_total;
35312			floatingBoxData = fetch.floatingBoxData;
35313			showTotal = fetch.showTotal;
35314			elementsLength = elementsLength + fetch.elementsLength;
35315
35316			totalsHolder.data( 'tm-floating-box-data', floatingBoxData );
35317
35318			fetch = fetchOptionPrices( currentEpoObject, epoHolder, '.tmcp-fee-field', cart_fee_options_total, cart_fee_options_original_total, floatingBoxData, showTotal, false, true, undefined, undefined, undefined, undefined, cartQty );
35319			cart_fee_options_total = fetch.total;
35320			cart_fee_options_original_total = fetch.original_total;
35321			floatingBoxData = fetch.floatingBoxData;
35322			showTotal = fetch.showTotal;
35323			elementsLength = elementsLength + fetch.elementsLength;
35324
35325			jWindow.trigger( 'tcEpoAfterNormalOptionPriceCalculation', {
35326				epo: currentEpoObject,
35327				alternativeCart: currentEpoObject,
35328				this_product_type: this_product_type,
35329				cart: cart,
35330				totalsHolder: totalsHolder,
35331				fetchOptionPrices: fetchOptionPrices,
35332				epoHolder: epoHolder,
35333				total: total,
35334				original_total: original_total,
35335				floatingBoxData: floatingBoxData,
35336				showTotal: showTotal,
35337				cart_fee_options_total: cart_fee_options_total,
35338				cart_fee_options_original_total: cart_fee_options_original_total
35339			} );
35340
35341			// Original price + options price type requires this here.
35342			_total = total;
35343			_original_total = original_total;
35344
35345			late_total_price = add_late_fields_prices( currentEpoObject, parseFloat( productPrice ), parseFloat( rawProductRegularPrice ), parseFloat( _total + productFieldSyncTotal ), parseFloat( _original_total + productFieldSyncOriginalTotal ), bundleid, totalsHolder );
35346
35347			fetch = fetchOptionPrices( currentEpoObject, epoHolder, '.tmcp-field.tc-is-math-special:not(.tmcp-dynamic)', total, original_total, floatingBoxData, showTotal, undefined, undefined, undefined, true, undefined, undefined, cartQty );
35348			total = fetch.total;
35349			original_total = fetch.original_total;
35350			floatingBoxData = fetch.floatingBoxData;
35351			showTotal = fetch.showTotal;
35352			elementsLength = elementsLength + fetch.elementsLength;
35353
35354			_total = total;
35355			_original_total = original_total;
35356
35357			_total = _total + late_total_price.normal[ 0 ];
35358			_original_total = _original_total + late_total_price.normal[ 1 ];
35359
35360			cart_fee_options_total = cart_fee_options_total + late_total_price.fees[ 0 ];
35361			cart_fee_options_original_total = cart_fee_options_original_total + late_total_price.fees[ 1 ];
35362
35363			total = _total;
35364			original_total = _original_total;
35365			fetch = fetchOptionPrices( currentEpoObject, epoHolder, '.tmcp-field.tc-is-math-cumulative:not(.tmcp-dynamic)', total, original_total, floatingBoxData, showTotal, undefined, undefined, undefined, true, true, undefined, cartQty );
35366			total = fetch.total;
35367			original_total = fetch.original_total;
35368			floatingBoxData = fetch.floatingBoxData;
35369			showTotal = fetch.showTotal;
35370			elementsLength = elementsLength + fetch.elementsLength;
35371
35372			_total = total;
35373			_original_total = original_total;
35374
35375			// Check for dynamic elements.
35376			dynamicCheck = epoHolder.find( '.tmcp-dynamic.dynamic-product-price.tc-is-math-special' );
35377			if ( currentEpoObject.is_associated === false ) {
35378				dynamicCheck = dynamicCheck.not( tcAPI.associatedEpoSelector + ' ' + '.tmcp-dynamic.dynamic-product-price.tc-is-math-special' );
35379			}
35380			dynamicCheck = dynamicCheck.filter( '.tcenabled' );
35381			if ( dynamicCheck.length > 0 ) {
35382				fetch = fetchOptionPrices( currentEpoObject, epoHolder, '.tmcp-field.tmcp-dynamic.dynamic-product-price.tc-is-math-special', dynamicProductPrice + total, dynamicProductPrice + original_total, floatingBoxData, showTotal, undefined, undefined, undefined, true, undefined, undefined, cartQty );
35383				dynamicProductPrice = $.epoAPI.math.toFloat( fetch.total ) - $.epoAPI.math.toFloat( total );
35384				productPrice = dynamicProductPrice;
35385				rawProductRegularPrice = $.epoAPI.math.toFloat( fetch.original_total ) - $.epoAPI.math.toFloat( original_total );
35386				rawProductPrice = dynamicProductPrice;
35387				productRegularPrice = dynamicProductPrice;
35388				totalsHolderContainer.find( '.cpf-dynamic-product-price' ).val( dynamicProductPrice );
35389				totalsHolder_tc_totals_ob.dynamic_product_price = dynamicProductPrice;
35390				totalsHolder.data( 'totalsHolder_tc_totals_ob', totalsHolder_tc_totals_ob );
35391			}
35392
35393			dynamicCheck = epoHolder.find( '.tmcp-dynamic.dynamic-product-price.tc-is-math-cumulative' );
35394			if ( currentEpoObject.is_associated === false ) {
35395				dynamicCheck = dynamicCheck.not( tcAPI.associatedEpoSelector + ' ' + '.tmcp-dynamic.dynamic-product-price.tc-is-math-cumulative' );
35396			}
35397			dynamicCheck = dynamicCheck.filter( '.tcenabled' );
35398			if ( dynamicCheck.length > 0 ) {
35399				fetch = fetchOptionPrices( currentEpoObject, epoHolder, '.tmcp-field.tmcp-dynamic.dynamic-product-price.tc-is-math-cumulative', dynamicProductPrice + total, dynamicProductPrice + original_total, floatingBoxData, showTotal, undefined, undefined, undefined, true, undefined, undefined, cartQty );
35400				dynamicProductPrice = $.epoAPI.math.toFloat( fetch.total ) - $.epoAPI.math.toFloat( total );
35401				productPrice = dynamicProductPrice;
35402				rawProductRegularPrice = $.epoAPI.math.toFloat( fetch.original_total ) - $.epoAPI.math.toFloat( original_total );
35403				rawProductPrice = dynamicProductPrice;
35404				productRegularPrice = dynamicProductPrice;
35405				totalsHolderContainer.find( '.cpf-dynamic-product-price' ).val( dynamicProductPrice );
35406				totalsHolder_tc_totals_ob.dynamic_product_price = dynamicProductPrice;
35407				totalsHolder.data( 'totalsHolder_tc_totals_ob', totalsHolder_tc_totals_ob );
35408			}
35409
35410			// Check if there are any dynamic product price elements
35411			dynamicCheck = epoHolder.find( '.tmcp-dynamic.dynamic-product-price' );
35412			if ( currentEpoObject.is_associated === false ) {
35413				dynamicCheck = dynamicCheck.not( tcAPI.associatedEpoSelector + ' ' + '.tmcp-dynamic.dynamic-product-price' );
35414			}
35415			dynamicCheck = dynamicCheck.filter( '.tcenabled' );
35416			if ( ! dynamicCheck.length > 0 ) {
35417				totalsHolderContainer.find( '.cpf-dynamic-product-price' ).val( '' );
35418			}
35419
35420			// Dynamic Calculation
35421			dynamicProductPrice = 0;
35422			dynamicCheck = epoHolder.find( '.tmcp-dynamic.calculation:not(.tc-is-math-special, .tc-is-math-cumulative)' );
35423			if ( currentEpoObject.is_associated === false ) {
35424				dynamicCheck = dynamicCheck.not( tcAPI.associatedEpoSelector + ' ' + '.tmcp-dynamic.calculation:not(.tc-is-math-special, .tc-is-math-cumulative)' );
35425			}
35426			dynamicCheck = dynamicCheck.filter( '.tcenabled' );
35427			if ( dynamicCheck.length > 0 ) {
35428				dynamicCheck.toArray().forEach( function( setter ) {
35429					var f;
35430					var ulWrap;
35431					var setterVal;
35432					setter = $( setter );
35433					ulWrap = setter.closest( '.tmcp-ul-wrap' );
35434					f = fetchOptionPrices( epoObject, ulWrap, '.tmcp-dynamic.calculation:not(.tc-is-math-special, .tc-is-math-cumulative)', dynamicProductPrice, dynamicProductPrice, [], true, true, undefined, undefined, undefined, true, undefined, undefined, cartQty );
35435					setterVal = f.total;
35436					if ( setter.is( '.result-as-price' ) ) {
35437						setterVal = tm_set_price_totals( setterVal, totalsHolder, true );
35438					}
35439					ulWrap.find( '.tc-result' ).html( setterVal );
35440				} );
35441			}
35442
35443			dynamicCheck = epoHolder.find( '.tmcp-dynamic.calculation.tc-is-math-special' );
35444			if ( currentEpoObject.is_associated === false ) {
35445				dynamicCheck = dynamicCheck.not( tcAPI.associatedEpoSelector + ' ' + '.tmcp-dynamic.calculation.tc-is-math-special' );
35446			}
35447			dynamicCheck = dynamicCheck.filter( '.tcenabled' );
35448			if ( dynamicCheck.length > 0 ) {
35449				dynamicCheck.toArray().forEach( function( setter ) {
35450					var f;
35451					var ulWrap;
35452					var setterVal;
35453					setter = $( setter );
35454					ulWrap = setter.closest( '.tmcp-ul-wrap' );
35455					f = fetchOptionPrices( epoObject, ulWrap, '.tmcp-field.tmcp-dynamic.calculation.tc-is-math-special', dynamicProductPrice + total, dynamicProductPrice + original_total, [], true, true, undefined, undefined, true );
35456					setterVal = $.epoAPI.math.toFloat( f.total ) - $.epoAPI.math.toFloat( total );
35457					if ( setter.is( '.result-as-price' ) ) {
35458						setterVal = tm_set_price_totals( setterVal, totalsHolder, true );
35459					}
35460					ulWrap.find( '.tc-result' ).html( setterVal );
35461				} );
35462			}
35463
35464			dynamicCheck = epoHolder.find( '.tmcp-dynamic.calculation.tc-is-math-cumulative' );
35465			if ( currentEpoObject.is_associated === false ) {
35466				dynamicCheck = dynamicCheck.not( tcAPI.associatedEpoSelector + ' ' + '.tmcp-dynamic.calculation.tc-is-math-cumulative' );
35467			}
35468			dynamicCheck = dynamicCheck.filter( '.tcenabled' );
35469			if ( dynamicCheck.length > 0 ) {
35470				dynamicCheck.toArray().forEach( function( setter ) {
35471					var f;
35472					var ulWrap;
35473					var setterVal;
35474					setter = $( setter );
35475					ulWrap = setter.closest( '.tmcp-ul-wrap' );
35476					f = fetchOptionPrices( epoObject, ulWrap, '.tmcp-field.tmcp-dynamic.calculation.tc-is-math-cumulative', dynamicProductPrice + total, dynamicProductPrice + original_total, [], true, true, undefined, undefined, true, undefined, undefined, undefined, cartQty );
35477					setterVal = $.epoAPI.math.toFloat( f.total ) - $.epoAPI.math.toFloat( total );
35478					if ( setter.is( '.result-as-price' ) ) {
35479						setterVal = tm_set_price_totals( setterVal, totalsHolder, true );
35480					}
35481					ulWrap.find( '.tc-result' ).html( setterVal );
35482				} );
35483			}
35484
35485			// Dynamic Weight
35486			dynamicProductPrice = 0;
35487			dynamicCheck = epoHolder.find( '.tmcp-dynamic.change-product-weight:not(.tc-is-math-special, .tc-is-math-cumulative)' );
35488			if ( currentEpoObject.is_associated === false ) {
35489				dynamicCheck = dynamicCheck.not( tcAPI.associatedEpoSelector + ' ' + '.tmcp-dynamic.change-product-weight:not(.tc-is-math-special, .tc-is-math-cumulative)' );
35490			}
35491			dynamicCheck = dynamicCheck.filter( '.tcenabled' );
35492			if ( dynamicCheck.length > 0 ) {
35493				dynamicCheck.toArray().forEach( function( setter ) {
35494					var f;
35495					var ulWrap;
35496					setter = $( setter );
35497					ulWrap = setter.closest( '.tmcp-ul-wrap' );
35498					f = fetchOptionPrices( epoObject, ulWrap, '.tmcp-dynamic.change-product-weight:not(.tc-is-math-special, .tc-is-math-cumulative)', dynamicProductPrice, dynamicProductPrice, [], true, true, undefined, undefined, undefined, undefined, undefined, undefined, cartQty );
35499					ulWrap.find( '.tc-result' ).html( f.total );
35500				} );
35501			}
35502
35503			dynamicCheck = epoHolder.find( '.tmcp-dynamic.change-product-weight.tc-is-math-special' );
35504			if ( currentEpoObject.is_associated === false ) {
35505				dynamicCheck = dynamicCheck.not( tcAPI.associatedEpoSelector + ' ' + '.tmcp-dynamic.change-product-weight.tc-is-math-special' );
35506			}
35507			dynamicCheck = dynamicCheck.filter( '.tcenabled' );
35508			if ( dynamicCheck.length > 0 ) {
35509				dynamicCheck.toArray().forEach( function( setter ) {
35510					var f;
35511					var ulWrap;
35512					setter = $( setter );
35513					ulWrap = setter.closest( '.tmcp-ul-wrap' );
35514					f = fetchOptionPrices( epoObject, ulWrap, '.tmcp-field.tmcp-dynamic.change-product-weight.tc-is-math-special', dynamicProductPrice + total, dynamicProductPrice + original_total, [], true, true, undefined, undefined, true, undefined, undefined, undefined, cartQty );
35515					ulWrap.find( '.tc-result' ).html( $.epoAPI.math.toFloat( f.total ) - $.epoAPI.math.toFloat( total ) );
35516				} );
35517			}
35518
35519			dynamicCheck = epoHolder.find( '.tmcp-dynamic.change-product-weight.tc-is-math-cumulative' );
35520			if ( currentEpoObject.is_associated === false ) {
35521				dynamicCheck = dynamicCheck.not( tcAPI.associatedEpoSelector + ' ' + '.tmcp-dynamic.change-product-weight.tc-is-math-cumulative' );
35522			}
35523			dynamicCheck = dynamicCheck.filter( '.tcenabled' );
35524			if ( dynamicCheck.length > 0 ) {
35525				dynamicCheck.toArray().forEach( function( setter ) {
35526					var f;
35527					var ulWrap;
35528					setter = $( setter );
35529					ulWrap = setter.closest( '.tmcp-ul-wrap' );
35530					f = fetchOptionPrices( epoObject, ulWrap, '.tmcp-field.tmcp-dynamic.change-product-weight.tc-is-math-cumulative', dynamicProductPrice + total, dynamicProductPrice + original_total, [], true, true, undefined, undefined, true, undefined, undefined, undefined, cartQty );
35531					ulWrap.find( '.tc-result' ).html( $.epoAPI.math.toFloat( f.total ) - $.epoAPI.math.toFloat( total ) );
35532				} );
35533			}
35534
35535			// Check for dynamic override elements
35536			dynamicProductPrice = 0;
35537			dynamicCheck = epoHolder.find( '.tmcp-dynamic.override-product-price:not(.tc-is-math-special, .tc-is-math-cumulative)' );
35538			if ( currentEpoObject.is_associated === false ) {
35539				dynamicCheck = dynamicCheck.not( tcAPI.associatedEpoSelector + ' ' + '.tmcp-dynamic.override-product-price:not(.tc-is-math-special, .tc-is-math-cumulative)' );
35540			}
35541			dynamicCheck = dynamicCheck.filter( '.tcenabled' );
35542			if ( dynamicCheck.length > 0 ) {
35543				fetch = fetchOptionPrices( currentEpoObject, epoHolder, '.tmcp-dynamic.override-product-price:not(.tc-is-math-special, .tc-is-math-cumulative)', dynamicProductPrice, dynamicProductPrice, floatingBoxData, showTotal, undefined, undefined, undefined, undefined, undefined, undefined, cartQty );
35544				dynamicProductPrice = fetch.total;
35545				productPrice = dynamicProductPrice;
35546				rawProductRegularPrice = fetch.original_total;
35547				rawProductPrice = dynamicProductPrice;
35548				productRegularPrice = dynamicProductPrice;
35549				_total = dynamicProductPrice;
35550				_original_total = dynamicProductPrice;
35551				totalsHolderContainer.find( '.cpf-override-product-price' ).val( dynamicProductPrice );
35552				totalsHolder_tc_totals_ob.override_product_price = dynamicProductPrice;
35553				totalsHolder.data( 'totalsHolder_tc_totals_ob', totalsHolder_tc_totals_ob );
35554			}
35555
35556			dynamicCheck = epoHolder.find( '.tmcp-dynamic.override-product-price.tc-is-math-special' );
35557			if ( currentEpoObject.is_associated === false ) {
35558				dynamicCheck = dynamicCheck.not( tcAPI.associatedEpoSelector + ' ' + '.tmcp-dynamic.override-product-price.tc-is-math-special' );
35559			}
35560			dynamicCheck = dynamicCheck.filter( '.tcenabled' );
35561			if ( dynamicCheck.length > 0 ) {
35562				fetch = fetchOptionPrices( currentEpoObject, epoHolder, '.tmcp-field.tmcp-dynamic.override-product-price.tc-is-math-special', dynamicProductPrice + total, dynamicProductPrice + original_total, floatingBoxData, showTotal, undefined, undefined, undefined, true, undefined, undefined, cartQty );
35563				dynamicProductPrice = $.epoAPI.math.toFloat( fetch.total ) - $.epoAPI.math.toFloat( total );
35564				productPrice = dynamicProductPrice;
35565				rawProductRegularPrice = $.epoAPI.math.toFloat( fetch.original_total ) - $.epoAPI.math.toFloat( original_total );
35566				rawProductPrice = dynamicProductPrice;
35567				productRegularPrice = dynamicProductPrice;
35568				_total = dynamicProductPrice;
35569				_original_total = dynamicProductPrice;
35570				totalsHolderContainer.find( '.cpf-override-product-price' ).val( dynamicProductPrice );
35571				totalsHolder_tc_totals_ob.override_product_price = dynamicProductPrice;
35572				totalsHolder.data( 'totalsHolder_tc_totals_ob', totalsHolder_tc_totals_ob );
35573			}
35574
35575			dynamicCheck = epoHolder.find( '.tmcp-dynamic.override-product-price.tc-is-math-cumulative' );
35576			if ( currentEpoObject.is_associated === false ) {
35577				dynamicCheck = dynamicCheck.not( tcAPI.associatedEpoSelector + ' ' + '.tmcp-dynamic.override-product-price.tc-is-math-cumulative' );
35578			}
35579			dynamicCheck = dynamicCheck.filter( '.tcenabled' );
35580			if ( dynamicCheck.length > 0 ) {
35581				fetch = fetchOptionPrices( currentEpoObject, epoHolder, '.tmcp-field.tmcp-dynamic.override-product-price.tc-is-math-cumulative', dynamicProductPrice + total, dynamicProductPrice + original_total, floatingBoxData, showTotal, undefined, undefined, undefined, true, undefined, undefined, cartQty );
35582				dynamicProductPrice = $.epoAPI.math.toFloat( fetch.total ) - $.epoAPI.math.toFloat( total );
35583				productPrice = dynamicProductPrice;
35584				rawProductRegularPrice = $.epoAPI.math.toFloat( fetch.original_total ) - $.epoAPI.math.toFloat( original_total );
35585				rawProductPrice = dynamicProductPrice;
35586				productRegularPrice = dynamicProductPrice;
35587				_total = dynamicProductPrice;
35588				_original_total = dynamicProductPrice;
35589				totalsHolderContainer.find( '.cpf-override-product-price' ).val( dynamicProductPrice );
35590				totalsHolder_tc_totals_ob.override_product_price = dynamicProductPrice;
35591				totalsHolder.data( 'totalsHolder_tc_totals_ob', totalsHolder_tc_totals_ob );
35592			}
35593
35594			// Check if there are any dynamic override elements
35595			dynamicCheck = epoHolder.find( '.tmcp-dynamic.override-product-price' );
35596			if ( currentEpoObject.is_associated === false ) {
35597				dynamicCheck = dynamicCheck.not( tcAPI.associatedEpoSelector + ' ' + '.tmcp-dynamic.override-product-price' );
35598			}
35599			dynamicCheck = dynamicCheck.filter( '.tcenabled' );
35600			if ( ! dynamicCheck.length > 0 ) {
35601				totalsHolderContainer.find( '.cpf-override-product-price' ).val( '' );
35602			} else {
35603				_total = 0;
35604				_original_total = 0;
35605			}
35606
35607			jWindow.trigger( 'tcEpoAfterOptionPriceCalculation', {
35608				epo: currentEpoObject,
35609				alternativeCart: currentEpoObject,
35610				this_product_type: this_product_type,
35611				cart: cart,
35612				totalsHolder: totalsHolder,
35613				fetchOptionPrices: fetchOptionPrices,
35614				epoHolder: epoHolder,
35615				total: total,
35616				original_total: original_total,
35617				floatingBoxData: floatingBoxData,
35618				showTotal: showTotal,
35619				cart_fee_options_total: cart_fee_options_total,
35620				cart_fee_options_original_total: cart_fee_options_original_total
35621			} );
35622
35623			setTimeout( function() {
35624				epoHolder.find( 'select.tm-epo-field' ).trigger( 'tm-select-price-update-html-all' );
35625			}, 1 );
35626
35627			$.tcepo.oneOptionIsSelected[ epoEventId ] = showTotal;
35628			currentEpoObject = addShowHidetoEpoObject( currentEpoObject, epoEventId, elementsLength );
35629			tm_show_hide_add_to_cart_button( main_product, currentEpoObject );
35630
35631			if ( cart_fee_options_total > 0 ) {
35632				showTotal = true;
35633			}
35634
35635			if ( alternativeCart && ! perProductPricing ) {
35636				showTotal = false;
35637			}
35638
35639			if (
35640				finalTotalBoxMode === 'enable' ||
35641				finalTotalBoxMode === 'hideifoptionsiszero' ||
35642				finalTotalBoxMode === 'hideiftotaliszero'
35643			) {
35644				showTotal = true;
35645			}
35646
35647			if ( cartQty > 1 ) {
35648				showTotal = true;
35649			}
35650			if ( ( this_product_type === 'variable' || this_product_type === 'variable-subscription' ) && ! $.epoAPI.math.toFloat( currentVariation ) ) {
35651				showTotal = false;
35652			}
35653
35654			if ( finalTotalBoxMode === 'disable' ) {
35655				showTotal = false;
35656			}
35657			if ( TMEPOJS.tm_epo_change_variation_price === 'yes' || TMEPOJS.tm_epo_change_original_price === 'yes' ) {
35658				showTotal = true;
35659			}
35660
35661			if ( currentEpoObject.is_associated && ! perProductPricing ) {
35662				showTotal = false;
35663			}
35664
35665			product_total_price = parseFloat( productPrice * cartQty );
35666
35667			if ( TMEPOJS.extraFee ) {
35668				extraFee = parseFloat( TMEPOJS.extraFee );
35669				if ( ! Number.isFinite( extraFee ) ) {
35670					extraFee = 0;
35671				}
35672			}
35673
35674			calculateFinalProductPrice = $.epoAPI.applyFilter( 'tcCalculateFinalProductPrice', false, {
35675				alternativeCart: alternativeCart,
35676				product_price: productPrice,
35677				product_total_price: product_total_price,
35678				v_product_price: rawProductPrice,
35679				tm_set_tax_price: tm_set_tax_price,
35680				main_product: main_product,
35681				totalsHolder: totalsHolder,
35682				cartQty: cartQty
35683			} );
35684
35685			if (
35686				calculateFinalProductPrice !== false &&
35687				typeof calculateFinalProductPrice === 'object' &&
35688				Object.prototype.hasOwnProperty.call( calculateFinalProductPrice, 'productPrice' ) &&
35689				Object.prototype.hasOwnProperty.call( calculateFinalProductPrice, 'productTotalPrice' )
35690			) {
35691				productPrice = calculateFinalProductPrice.productPrice;
35692				product_total_price = calculateFinalProductPrice.productTotalPrice;
35693			}
35694
35695			_total = $.epoAPI.applyFilter( 'tc_adjust_options_price_per_unit', _total, product_total_price );
35696			total = parseFloat( _total * cartQty * elementQty );
35697			total = $.epoAPI.applyFilter( 'tc_adjust_options_total_price', total, cartQty, elementQty, _total, totalsHolder );
35698			if ( TMEPOJS.tm_epo_global_product_element_quantity_sync === 'no' ) {
35699				totalProductField = parseFloat( productFieldSyncTotal );
35700				totalProductFieldTaxed = parseFloat( productFieldSyncTotalTaxed );
35701			} else {
35702				totalProductField = parseFloat( productFieldSyncTotal * cartQty * elementQty );
35703				totalProductFieldTaxed = parseFloat( productFieldSyncTotalTaxed * cartQty * elementQty );
35704			}
35705			totalProductField = $.epoAPI.applyFilter( 'tc_adjust_options_productfield_total_price', totalProductField, cartQty, elementQty, _total, totalsHolder );
35706			totalProductFieldTaxed = $.epoAPI.applyFilter( 'tc_adjust_options_productfield_total_price_taxed', totalProductFieldTaxed, cartQty, elementQty, _total, totalsHolder );
35707
35708			_original_total = $.epoAPI.applyFilter( 'tc_adjust_options_price_per_unit', _original_total, product_total_price );
35709			original_total = parseFloat( _original_total * cartQty * elementQty );
35710			original_total = $.epoAPI.applyFilter( 'tc_adjust_options_original_total_price', original_total, cartQty, elementQty, _original_total, totalsHolder );
35711			if ( TMEPOJS.tm_epo_global_product_element_quantity_sync === 'no' ) {
35712				originalTotalProductField = parseFloat( productFieldSyncOriginalTotal );
35713				originalTotalProductFieldTaxed = parseFloat( productFieldSyncOriginalTotalTaxed );
35714			} else {
35715				originalTotalProductField = parseFloat( productFieldSyncOriginalTotal * cartQty * elementQty );
35716				originalTotalProductFieldTaxed = parseFloat( productFieldSyncOriginalTotalTaxed * cartQty * elementQty );
35717			}
35718			originalTotalProductField = $.epoAPI.applyFilter( 'tc_adjust_options_productfield_original_total_price', originalTotalProductField, cartQty, elementQty, _original_total, totalsHolder );
35719			originalTotalProductFieldTaxed = $.epoAPI.applyFilter( 'tc_adjust_options_productfield_original_total_price_taxed', originalTotalProductFieldTaxed, cartQty, elementQty, _original_total, totalsHolder );
35720
35721			if ( priceOverrideMode === '1' && parseFloat( total ) > 0 ) {
35722				productPrice = 0;
35723				rawProductPrice = 0;
35724				product_total_price = 0;
35725			}
35726
35727			product_total_price = $.epoAPI.applyFilter( 'tc_adjust_product_total_price_without_options', product_total_price );
35728
35729			total = $.epoAPI.applyFilter( 'tcAdjustTotal', total, {
35730				epo: currentEpoObject,
35731				totalsHolder: totalsHolder,
35732				cart: cart,
35733				main_product: main_product
35734			} );
35735			totalProductField = $.epoAPI.applyFilter( 'tcAdjustProductFieldTotal', totalProductField, {
35736				epo: currentEpoObject,
35737				totalsHolder: totalsHolder,
35738				cart: cart,
35739				main_product: main_product
35740			} );
35741			totalProductFieldTaxed = $.epoAPI.applyFilter( 'tcAdjustProductFieldTotalTaxed', totalProductFieldTaxed, {
35742				epo: currentEpoObject,
35743				totalsHolder: totalsHolder,
35744				cart: cart,
35745				main_product: main_product
35746			} );
35747			original_total = $.epoAPI.applyFilter( 'tcAdjustOriginalTotal', original_total, {
35748				epo: currentEpoObject,
35749				totalsHolder: totalsHolder,
35750				cart: cart,
35751				main_product: main_product
35752			} );
35753			originalTotalProductField = $.epoAPI.applyFilter( 'tcAdjustProductFieldOriginalTotal', originalTotalProductField, {
35754				epo: currentEpoObject,
35755				totalsHolder: totalsHolder,
35756				cart: cart,
35757				main_product: main_product
35758			} );
35759			originalTotalProductFieldTaxed = $.epoAPI.applyFilter( 'tcAdjustProductFieldOriginalTotalTaxed', originalTotalProductFieldTaxed, {
35760				epo: currentEpoObject,
35761				totalsHolder: totalsHolder,
35762				cart: cart,
35763				main_product: main_product
35764			} );
35765
35766			total = parseFloat( $.epoAPI.applyFilter( 'tc_adjust_total', total, totalsHolder ) );
35767			cart_fee_options_total = parseFloat( $.epoAPI.applyFilter( 'tc_adjust_totals_fee', cart_fee_options_total, totalsHolder ) );
35768			original_total = parseFloat( $.epoAPI.applyFilter( 'tc_adjust_original_total', original_total, totalsHolder ) );
35769			cart_fee_options_original_total = parseFloat( $.epoAPI.applyFilter( 'tc_adjust_original_total_fee', cart_fee_options_original_total, totalsHolder ) );
35770			raw_extraFee = extraFee;
35771
35772			total_plus_fee = total + cart_fee_options_total;
35773			original_total_plus_fee = original_total + cart_fee_options_original_total;
35774
35775			raw_total = total;
35776			raw__total = _total;
35777
35778			raw_cart_fee_options_total = cart_fee_options_total;
35779			raw_total_plus_fee = total_plus_fee;
35780
35781			raw_original_total = original_total;
35782			raw__original_total = _original_total;
35783
35784			raw_cart_fee_options_original_total = cart_fee_options_original_total;
35785			raw_original_total_plus_fee = original_total_plus_fee;
35786
35787			raw_product_total_price_without_options = product_total_price;
35788			raw_product_total_price = parseFloat( product_total_price + total_plus_fee + raw_extraFee );
35789			raw_product_total_original_price = parseFloat( rawProductRegularPrice + original_total_plus_fee + raw_extraFee );
35790
35791			productPrice = tm_set_tax_price( productPrice, totalsHolder, undefined, undefined, undefined, undefined, true );
35792			productRegularPrice = tm_set_tax_price( productRegularPrice, totalsHolder, undefined, undefined, undefined, undefined, true );
35793			product_total_price = tm_set_tax_price( product_total_price, totalsHolder, undefined, undefined, undefined, undefined, true );
35794
35795			total = tm_set_tax_price( total, totalsHolder, undefined, undefined, undefined, undefined, true );
35796			_total = tm_set_tax_price( _total, totalsHolder, undefined, undefined, undefined, undefined, true );
35797
35798			total_plus_fee = parseFloat( cart_fee_options_total ) + parseFloat( total );
35799
35800			original_total = tm_set_tax_price( original_total, totalsHolder, undefined, undefined, undefined, undefined, true );
35801			_original_total = tm_set_tax_price( _original_total, totalsHolder, undefined, undefined, undefined, undefined, true );
35802
35803			original_total_plus_fee = parseFloat( cart_fee_options_original_total ) + parseFloat( original_total );
35804
35805			extraFee = tm_set_tax_price( extraFee, totalsHolder, undefined, undefined, undefined, undefined, true );
35806
35807			// Calculate product price
35808			product_total_price_without_options = product_total_price;
35809			product_total_price = $.epoAPI.applyFilter( 'tc_adjust_product_total_price', parseFloat( product_total_price + total_plus_fee + extraFee ), product_total_price_without_options, total_plus_fee, extraFee, total, cart_fee_options_total, totalsHolder );
35810			product_total_original_price = $.epoAPI.applyFilter( 'tc_adjust_product_total_original_price', parseFloat( product_total_price_without_options + original_total_plus_fee + extraFee ), product_total_price_without_options, original_total_plus_fee, extraFee, original_total, cart_fee_options_original_total, totalsHolder );
35811
35812			// Calculate vat options total
35813			pricesIncludeTax = totalsHolder.attr( 'data-prices-include-tax' ) || TMEPOJS.prices_include_tax;
35814			if ( pricesIncludeTax === '1' ) {
35815				vat_options_total = productFieldSyncVatTotal + parseFloat( calculateTaxAmount( total, totalsHolder ) );
35816				vat_options_total_plus_fee = productFieldSyncVatTotal + parseFloat( calculateTaxAmount( total_plus_fee, totalsHolder ) );
35817				vat_total = productFieldSyncVatTotal + parseFloat( calculateTaxAmount( product_total_price, totalsHolder ) );
35818				vat_product_base = parseFloat( calculateTaxAmount( product_total_price_without_options, totalsHolder ) );
35819			} else {
35820				vat_options_total = productFieldSyncVatTotal + parseFloat( total - raw_total );
35821				vat_options_total_plus_fee = productFieldSyncVatTotal + parseFloat( total_plus_fee - raw_total_plus_fee );
35822				vat_total = productFieldSyncVatTotal + parseFloat( product_total_price - raw_product_total_price );
35823				vat_product_base = parseFloat( product_total_price_without_options - raw_product_total_price_without_options );
35824			}
35825
35826			raw_total = raw_total + totalProductField;
35827			raw__total = raw__total + totalProductField;
35828			raw_total_plus_fee = raw_total_plus_fee + totalProductField;
35829			raw_original_total = raw_original_total + originalTotalProductField;
35830			raw__original_total = raw__original_total + originalTotalProductField;
35831			raw_original_total_plus_fee = raw_original_total_plus_fee + originalTotalProductField;
35832			raw_product_total_price = raw_product_total_price + totalProductField;
35833			raw_product_total_original_price = raw_product_total_original_price + originalTotalProductField;
35834			total = total + totalProductFieldTaxed;
35835			_total = _total + totalProductFieldTaxed;
35836			total_plus_fee = total_plus_fee + totalProductFieldTaxed;
35837			original_total = original_total + originalTotalProductFieldTaxed;
35838			_original_total = _original_total + originalTotalProductFieldTaxed;
35839			original_total_plus_fee = original_total_plus_fee + originalTotalProductFieldTaxed;
35840			product_total_price = product_total_price + totalProductFieldTaxed;
35841			product_total_original_price = product_total_original_price + originalTotalProductFieldTaxed;
35842
35843			// Calculate unit price
35844			unit_price = parseFloat( productPrice + parseFloat( _total ) );
35845			unit_original_price = parseFloat( productPrice + parseFloat( _original_total ) );
35846			if ( TMEPOJS.tm_epo_fees_on_unit_price === 'yes' ) {
35847				unit_price = parseFloat( productPrice + parseFloat( _total ) + parseFloat( parseFloat( cart_fee_options_total ) / cartQty ) );
35848				unit_original_price = parseFloat( productPrice + parseFloat( _original_total ) + parseFloat( parseFloat( cart_fee_options_original_total ) / cartQty ) );
35849			}
35850
35851			// Format unit price
35852			formatted_unit_price = tm_set_price_totals( unit_price, totalsHolder, true );
35853			formatted_unit_original_price = tm_set_price_totals( unit_original_price, totalsHolder, true );
35854
35855			// Format extra fee
35856			if ( extraFee ) {
35857				show_extra_fee = true;
35858				formatted_extra_fee = tm_set_price_totals( extraFee, totalsHolder, true );
35859			}
35860
35861			// Format final total
35862			formatted_final_total = tm_set_price_totals( product_total_price, totalsHolder, true );
35863			formatted_final_original_total = tm_set_price_totals( product_total_original_price, totalsHolder, true );
35864
35865			// Format options total
35866			formatted_options_total = tm_set_price_totals( total, totalsHolder, true );
35867
35868			// Format fees total
35869			formatted_fees_total = tm_set_price_totals( cart_fee_options_total, totalsHolder, true );
35870
35871			// Format options original total
35872			formatted_options_original_total = tm_set_price_totals( original_total, totalsHolder, true );
35873
35874			// Format fees original total
35875			formatted_fees_original_total = tm_set_price_totals( cart_fee_options_original_total, totalsHolder, true );
35876
35877			// Format vat options total
35878			formatted_vat_options_total = tm_set_price_totals( vat_options_total );
35879			formatted_vat_options_total_plus_fee = tm_set_price_totals( vat_options_total_plus_fee );
35880			formatted_vat_total = tm_set_price_totals( vat_total );
35881			formatted_vat_product_base = tm_set_price_totals( vat_product_base );
35882
35883			// Backwards compatibility
35884			formatted_unit_price = $.epoAPI.applyFilter( 'tc_adjust_formatted_unit_price', formatted_unit_price, productPrice, _total, cart_fee_options_total, cartQty );
35885			formatted_options_total = $.epoAPI.applyFilter( 'tc_adjust_formatted_options_total', formatted_options_total, total, _total, cartQty );
35886			formatted_fees_total = $.epoAPI.applyFilter( 'tc_adjust_formatted_fees_total', formatted_fees_total, cart_fee_options_total );
35887			formatted_final_total = $.epoAPI.applyFilter( 'tc_adjust_formatted_final_total', formatted_final_total, product_total_price, product_total_price_without_options, total_plus_fee, extraFee, cartQty );
35888
35889			formatted_unit_price = $.epoAPI.applyFilter( 'tcAdjustFormattedUnitPrice', formatted_unit_price, {
35890				epo: currentEpoObject,
35891				totalsHolder: totalsHolder,
35892				event: event,
35893				unit_price: unit_price,
35894				productPrice: productPrice,
35895				_total: _total,
35896				total_plcart_fee_options_totals_fee: cart_fee_options_total,
35897				cartQty: cartQty
35898			} );
35899			formatted_unit_original_price = $.epoAPI.applyFilter( 'tcAdjustFormattedUnitOriginalPrice', formatted_unit_original_price, {
35900				epo: currentEpoObject,
35901				totalsHolder: totalsHolder,
35902				event: event,
35903				unit_original_price: unit_original_price,
35904				productPrice: productPrice,
35905				_original_total: _original_total,
35906				total_plcart_fee_options_totals_fee: cart_fee_options_total,
35907				cartQty: cartQty
35908			} );
35909			formatted_options_total = $.epoAPI.applyFilter( 'tcAdjustFormattedOptionsTotal', formatted_options_total, {
35910				epo: currentEpoObject,
35911				totalsHolder: totalsHolder,
35912				event: event,
35913				total: total,
35914				_total: _total,
35915				total_plus_fee: total_plus_fee,
35916				extraFee: extraFee,
35917				cartQty: cartQty
35918			} );
35919			formatted_options_original_total = $.epoAPI.applyFilter( 'tcAdjustFormattedOptionsOriginalTotal', formatted_options_original_total, {
35920				epo: currentEpoObject,
35921				totalsHolder: totalsHolder,
35922				event: event,
35923				original_total: original_total,
35924				_original_total: _original_total,
35925				original_total_plus_fee: original_total_plus_fee,
35926				extraFee: extraFee,
35927				cartQty: cartQty
35928			} );
35929			formatted_fees_total = $.epoAPI.applyFilter( 'tcAdjustFormattedFeesTotal', formatted_fees_total, {
35930				epo: currentEpoObject,
35931				totalsHolder: totalsHolder,
35932				event: event,
35933				cart_fee_options_total: cart_fee_options_total,
35934				extraFee: extraFee,
35935				cartQty: cartQty
35936			} );
35937			formatted_fees_original_total = $.epoAPI.applyFilter( 'tcAdjustFormattedFeesOriginalTotal', formatted_fees_original_total, {
35938				epo: currentEpoObject,
35939				totalsHolder: totalsHolder,
35940				event: event,
35941				cart_fee_options_original_total: cart_fee_options_original_total,
35942				extraFee: extraFee,
35943				cartQty: cartQty
35944			} );
35945			formatted_final_total = $.epoAPI.applyFilter( 'tcAdjustFormattedFinalTotal', formatted_final_total, {
35946				epo: currentEpoObject,
35947				totalsHolder: totalsHolder,
35948				event: event,
35949				product_total_price: product_total_price,
35950				product_total_price_without_options: product_total_price_without_options,
35951				total_plus_fee: total_plus_fee,
35952				extraFee: extraFee,
35953				cartQty: cartQty
35954			} );
35955			formatted_final_original_total = $.epoAPI.applyFilter( 'tcAdjustFormattedFinalOriginalTotal', formatted_final_original_total, {
35956				epo: currentEpoObject,
35957				totalsHolder: totalsHolder,
35958				event: event,
35959				product_total_original_price: product_total_original_price,
35960				product_total_price_without_options: product_total_price_without_options,
35961				original_total_plus_fee: original_total_plus_fee,
35962				extraFee: extraFee,
35963				cartQty: cartQty
35964			} );
35965
35966			if ( ( total > 0 && finalTotalBoxShowOptions === 'showtgz' ) || finalTotalBoxShowOptions === 'show' ) {
35967				show_options_total = true;
35968				if ( TMEPOJS.tm_epo_enable_vat_options_total === 'yes' ) {
35969					show_vat_options_total = true;
35970				}
35971			}
35972			if ( cart_fee_options_total !== 0 ) {
35973				show_fees_total = true;
35974			}
35975
35976			if ( formatted_final_total && finalTotalBoxShowFinal === 'show' ) {
35977				show_final_total = true;
35978			}
35979
35980			tc_totals_ob = {
35981				qty: cartQty,
35982				product_price: productPrice,
35983				raw_product_price: rawProductPrice,
35984				product_regular_price: productRegularPrice,
35985				raw_product_regular_price: rawProductRegularPrice,
35986				formatted_product_price: tm_set_price_totals( parseFloat( productPrice ) * cartQty ),
35987
35988				late_total_prices: late_total_price,
35989				late_total_price: tm_set_tax_price( late_total_price[ 0 ], totalsHolder ),
35990				late_total_original_price: tm_set_tax_price( late_total_price[ 1 ], totalsHolder ),
35991
35992				raw_options_price_per_unit: raw__total,
35993				raw_options_total_price: raw_total,
35994				raw_cart_fee_options_total_price: raw_cart_fee_options_total,
35995				raw_total_plus_fee: raw_total_plus_fee,
35996
35997				raw_options_original_price_per_unit: raw__original_total,
35998				raw_options_original_total_price: raw_original_total,
35999				raw_cart_fee_options_original_total_price: raw_cart_fee_options_original_total,
36000				raw_original_total_plus_fee: raw_original_total_plus_fee,
36001
36002				raw_product_total_price: raw_product_total_price,
36003				raw_product_total_original_price: raw_product_total_original_price,
36004				raw_product_total_price_without_options: raw_product_total_price_without_options,
36005
36006				options_price_per_unit: _total,
36007				options_total_price: total,
36008				cart_fee_options_total_price: cart_fee_options_total,
36009				total_plus_fee: total_plus_fee,
36010
36011				options_original_price_per_unit: _original_total,
36012				options_original_total_price: original_total,
36013				cart_fee_options_total_original_price: cart_fee_options_original_total,
36014				original_total_plus_fee: original_total_plus_fee,
36015
36016				vat_options_total: vat_options_total,
36017				vat_options_total_plus_fee: vat_options_total_plus_fee,
36018				formatted_vat_options_total: formatted_vat_options_total,
36019				formatted_vat_options_total_plus_fee: formatted_vat_options_total_plus_fee,
36020				vat_total: vat_total,
36021				formatted_vat_total: formatted_vat_total,
36022				vat_product_base: vat_product_base,
36023				formatted_vat_product_base: formatted_vat_product_base,
36024
36025				product_total_price: product_total_price,
36026				product_total_original_price: product_total_original_price,
36027				product_total_price_without_options: product_total_price_without_options,
36028
36029				product_unit_price: unit_price,
36030				product_unit_original_price: unit_original_price,
36031				formatted_unit_price: formatted_unit_price,
36032				formatted_options_total: formatted_options_total,
36033				formatted_fees_total: formatted_fees_total,
36034				formatted_final_total: formatted_final_total,
36035
36036				formatted_unit_original_price: formatted_unit_original_price,
36037				formatted_options_original_total: formatted_options_original_total,
36038				formatted_fees_original_total: formatted_fees_original_total,
36039				formatted_final_original_total: formatted_final_original_total,
36040
36041				formatted_extra_fee: formatted_extra_fee,
36042
36043				show_options_total: show_options_total,
36044				show_fees_total: show_fees_total,
36045				show_extra_fee: show_extra_fee,
36046				show_final_total: show_final_total,
36047				show_options_vat: show_vat_options_total,
36048
36049				show_unit_price: TMEPOJS.tm_epo_show_unit_price === 'yes',
36050				unit_price: currentEpoObject.is_associated ? TMEPOJS.i18n_item_unit_price : TMEPOJS.i18n_unit_price,
36051				options_total: currentEpoObject.is_associated ? TMEPOJS.i18n_item_options_total : TMEPOJS.i18n_options_total,
36052				options_vat_total: currentEpoObject.is_associated ? TMEPOJS.i18n_item_vat_options_total : TMEPOJS.i18n_vat_options_total,
36053				final_total: currentEpoObject.is_associated ? TMEPOJS.i18n_item_final_total : TMEPOJS.i18n_final_total,
36054				fees_total: TMEPOJS.i18n_fees_total,
36055				extra_fee: TMEPOJS.i18n_extra_fee,
36056
36057				totals_box_before_unit_price: TMEPOJS.totals_box_before_unit_price,
36058				totals_box_after_unit_price: TMEPOJS.totals_box_after_unit_price,
36059				totals_box_before_vat_options_totals_price: TMEPOJS.totals_box_before_vat_options_totals_price,
36060				totals_box_after_vat_options_totals_price: TMEPOJS.totals_box_after_vat_options_totals_price,
36061				totals_box_before_options_totals_price: TMEPOJS.totals_box_before_options_totals_price,
36062				totals_box_after_options_totals_price: TMEPOJS.totals_box_after_options_totals_price,
36063				totals_box_before_fee_totals_price: TMEPOJS.totals_box_before_fee_totals_price,
36064				totals_box_after_fee_totals_price: TMEPOJS.totals_box_after_fee_totals_price,
36065				totals_box_before_extra_fee_price: TMEPOJS.totals_box_before_extra_fee_price,
36066				totals_box_after_extra_fee_price: TMEPOJS.totals_box_after_extra_fee_price,
36067				totals_box_before_final_totals_price: TMEPOJS.totals_box_before_final_totals_price,
36068				totals_box_after_final_totals_price: TMEPOJS.totals_box_after_final_totals_price
36069			};
36070
36071			if ( TMEPOJS.customer_price_format ) {
36072				customer_price_format_wrap_start = TMEPOJS.customer_price_format_wrap_start;
36073				customer_price_format_wrap_end = TMEPOJS.customer_price_format_wrap_end;
36074				customerPriceFormat = TMEPOJS.customer_price_format;
36075			}
36076
36077			if ( formatted_options_total && total >= 0 ) {
36078				_ftotal = formatPrice( total );
36079				_foriginal_total = formatPrice( original_total );
36080				if ( customerPriceFormat ) {
36081					_ftotal = customerPriceFormat.replaceAll( '__PRICE__', _ftotal ).replaceAll( '__CODE__', TMEPOJS.current_currency );
36082					if ( ! totalsHolder.data( 'is-on-sale' ) ) {
36083						_foriginal_total = customerPriceFormat.replaceAll( '__PRICE__', _foriginal_total ).replaceAll( '__CODE__', TMEPOJS.current_currency );
36084					}
36085				}
36086				_ftotal = $.epoAPI.applyFilter( 'tcFilterFormattedFTotal', _ftotal, {
36087					epo: currentEpoObject,
36088					totalsHolder: totalsHolder,
36089					event: event,
36090					total: total,
36091					_total: _total,
36092					total_plus_fee: total_plus_fee,
36093					extraFee: extraFee,
36094					cartQty: cartQty
36095				} );
36096				_foriginal_total = $.epoAPI.applyFilter( 'tcFilterFormattedFOriginalTotal', _foriginal_total, {
36097					epo: currentEpoObject,
36098					totalsHolder: totalsHolder,
36099					event: event,
36100					original_total: original_total,
36101					total: original_total,
36102					_total: _total,
36103					total_plus_fee: total_plus_fee,
36104					extraFee: extraFee,
36105					cartQty: cartQty
36106				} );
36107				if ( TMEPOJS.tm_epo_enable_original_final_total === 'yes' && total !== original_total ) {
36108					tc_totals_ob.formatted_options_total = $.epoAPI.util.decodeHTML(
36109						$.epoAPI.template.html( tcAPI.templateEngine.tc_formatted_sale_price, {
36110							price: _foriginal_total,
36111							sale_price: _ftotal,
36112							customer_price_format_wrap_start: customer_price_format_wrap_start,
36113							customer_price_format_wrap_end: customer_price_format_wrap_end
36114						} )
36115					);
36116					tc_totals_ob._foriginal_total = _foriginal_total;
36117					tc_totals_ob._ftotal = _ftotal;
36118				} else {
36119					tc_totals_ob.formatted_options_total = $.epoAPI.util.decodeHTML(
36120						$.epoAPI.util.decodeHTML(
36121							$.epoAPI.template.html( tcAPI.templateEngine.tc_formatted_price, {
36122								price: _ftotal,
36123								customer_price_format_wrap_start: customer_price_format_wrap_start,
36124								customer_price_format_wrap_end: customer_price_format_wrap_end
36125							} )
36126						)
36127					);
36128				}
36129			}
36130
36131			if ( formatted_final_total && product_total_price >= 0 ) {
36132				_fprice = formatPrice( product_total_price );
36133				if ( priceOverrideMode === '1' && parseFloat( total ) > 0 ) {
36134					_f_regular_price = parseFloat( raw_original_total + extraFee );
36135				} else {
36136					_f_regular_price = parseFloat( parseFloat( rawProductRegularPrice * cartQty ) + raw_original_total );
36137				}
36138				if ( customerPriceFormat ) {
36139					_fprice = customerPriceFormat.replaceAll( '__PRICE__', _fprice ).replaceAll( '__CODE__', TMEPOJS.current_currency );
36140					if ( ! totalsHolder.data( 'is-on-sale' ) ) {
36141						_f_regular_price = customerPriceFormat.replaceAll( '__PRICE__', _f_regular_price ).replaceAll( '__CODE__', TMEPOJS.current_currency );
36142					}
36143				}
36144
36145				_fprice = $.epoAPI.applyFilter( 'tc_adjust_native_price', _fprice, product_total_price );
36146				_f_regular_price = tm_set_tax_price( _f_regular_price, totalsHolder, undefined, undefined, undefined, undefined, true ) + parseFloat( cart_fee_options_original_total ) + parseFloat( extraFee );
36147				_f_regular_price = formatPrice( _f_regular_price );
36148				_f_regular_price = $.epoAPI.applyFilter( 'tc_adjust_native_regular_price', _f_regular_price, product_total_price );
36149
36150				if ( TMEPOJS.tm_epo_enable_original_final_total =
36150== 'yes' && ( ( totalsHolder.data( 'is-on-sale' ) && priceOverrideMode !== '1' ) || _f_regular_price !== _fprice ) ) {
36151					tc_totals_ob.formatted_final_total = $.epoAPI.util.decodeHTML(
36152						$.epoAPI.template.html( tcAPI.templateEngine.tc_formatted_sale_price, {
36153							price: _f_regular_price,
36154							sale_price: _fprice,
36155							customer_price_format_wrap_start: customer_price_format_wrap_start,
36156							customer_price_format_wrap_end: customer_price_format_wrap_end
36157						} )
36158					);
36159				} else {
36160					tc_totals_ob.formatted_final_total = $.epoAPI.util.decodeHTML(
36161						$.epoAPI.util.decodeHTML(
36162							$.epoAPI.template.html( tcAPI.templateEngine.tc_formatted_price, {
36163								price: _fprice,
36164								customer_price_format_wrap_start: customer_price_format_wrap_start,
36165								customer_price_format_wrap_end: customer_price_format_wrap_end
36166							} )
36167						)
36168					);
36169				}
36170				tc_totals_ob._f_regular_price = _f_regular_price;
36171				tc_totals_ob._fprice = _fprice;
36172			}
36173
36174			// Add the suffix.
36175			tc_totals_ob.formatted_unit_price = tc_totals_ob.formatted_unit_price + $.epoAPI.util.decodeHTML(
36176				$.epoAPI.util.decodeHTML(
36177					$.epoAPI.template.html( tcAPI.templateEngine.suffix )
36178				)
36179			);
36180
36181			tc_totals_ob.formatted_options_total = tc_totals_ob.formatted_options_total + $.epoAPI.util.decodeHTML(
36182				$.epoAPI.util.decodeHTML(
36183					$.epoAPI.template.html( tcAPI.templateEngine.suffix )
36184				)
36185			);
36186
36187			tc_totals_ob.formatted_final_total = tc_totals_ob.formatted_final_total + $.epoAPI.util.decodeHTML(
36188				$.epoAPI.util.decodeHTML(
36189					$.epoAPI.template.html( tcAPI.templateEngine.suffix )
36190				)
36191			);
36192
36193			tc_totals_ob = replace_suffixes( 'formatted_fees_original_total', 'cart_fee_options_total_original_price', tc_totals_ob, totalsHolder );
36194			tc_totals_ob = replace_suffixes( 'formatted_fees_total', 'cart_fee_options_total_price', tc_totals_ob, totalsHolder );
36195			tc_totals_ob = replace_suffixes( 'formatted_final_original_total', 'product_total_original_price', tc_totals_ob, totalsHolder );
36196			tc_totals_ob = replace_suffixes( 'formatted_final_total', 'product_total_price', tc_totals_ob, totalsHolder );
36197			tc_totals_ob = replace_suffixes( 'formatted_options_original_total', 'options_original_total_price', tc_totals_ob, totalsHolder );
36198			tc_totals_ob = replace_suffixes( 'formatted_options_total', 'options_total_price', tc_totals_ob, totalsHolder );
36199			tc_totals_ob = replace_suffixes( 'formatted_unit_original_price', 'product_unit_original_price', tc_totals_ob, totalsHolder );
36200			tc_totals_ob = replace_suffixes( 'formatted_unit_price', 'product_unit_price', tc_totals_ob, totalsHolder );
36201
36202			if ( $.tcepo.showHideTotal !== undefined && $.tcepo.showHideTotal[ epoEventId ] !== undefined ) {
36203				showTotal = $.tcepo.showHideTotal[ epoEventId ];
36204			}
36205
36206			tc_totals_ob = $.epoAPI.applyFilter( 'tc_adjust_tc_totals_ob', tc_totals_ob, {
36207				epo_object: currentEpoObject,
36208				showTotal: showTotal,
36209				epoHolder: epoHolder,
36210				totalsHolder: totalsHolder,
36211				replace_suffixes: replace_suffixes,
36212				replace_suffixes_in_html: replace_suffixes_in_html,
36213				tm_set_tax_price: tm_set_tax_price,
36214				tm_set_price: tm_set_price,
36215				tm_set_price_totals: tm_set_price_totals,
36216				product_total_price: product_total_price,
36217				product_price: productPrice,
36218				qty: cartQty
36219			} );
36220
36221			if ( tc_totals_ob.showTotal !== undefined ) {
36222				showTotal = tc_totals_ob.showTotal;
36223			}
36224
36225			currentEpoObject.tc_totals_ob = tc_totals_ob;
36226
36227			showTotal = $.epoAPI.applyFilter( 'tcFinalTotalsBoxVisibility', showTotal, {
36228				epo: currentEpoObject,
36229				alternativeCart: alternativeCart,
36230				cart: cart,
36231				main_product: main_product,
36232				totalsHolder: totalsHolder,
36233				this_epo_totals_container: this_epo_totals_container
36234			} );
36235
36236			html = $.epoAPI.template.html( tcAPI.templateEngine.tc_final_totals, tc_totals_ob );
36237
36238			totalsHolder.data( 'tm-html', html );
36239			totalsHolder.data( 'tc_totals_ob', tc_totals_ob );
36240
36241			if ( currentEpoObject.associated_connect && currentEpoObject.associated_connect.length === 1 ) {
36242				if ( currentEpoObject.associated_connect.attr( 'data-no-price' ) === '1' ) {
36243					associatedPrice = 0;
36244					associatedRawPrice = 0;
36245					associatedOriginalPrice = 0;
36246					associatedRawOriginalPrice = 0;
36247					associatedFormattedPrice = '';
36248					tm_force_update_price(
36249						epoObject,
36250						associatedSetter
36251							.closest( '.tmcp-field-wrap' )
36252							.find( '.tc-price' )
36253							.not( tcAPI.associatedEpoSelector + ' .tc-price' ),
36254						associatedPrice,
36255						associatedFormattedPrice,
36256						associatedOriginalPrice
36257					);
36258				} else {
36259					associatedPrice = tc_totals_ob.product_price + tc_totals_ob.options_price_per_unit + ( tc_totals_ob.cart_fee_options_total_price / tc_totals_ob.qty );
36260					associatedRawPrice = tc_totals_ob.raw_product_price + tc_totals_ob.raw_options_price_per_unit + ( tc_totals_ob.raw_cart_fee_options_total_price / tc_totals_ob.qty );
36261					associatedOriginalPrice = tc_totals_ob.product_regular_price + tc_totals_ob.options_original_price_per_unit + ( tc_totals_ob.cart_fee_options_total_original_price / tc_totals_ob.qty );
36262					associatedRawOriginalPrice = tc_totals_ob.raw_product_regular_price + tc_totals_ob.raw_options_original_price_per_unit + ( tc_totals_ob.raw_cart_fee_options_original_total_price / tc_totals_ob.qty );
36263
36264					associatedFormattedPrice = tm_set_price( associatedPrice, currentEpoObject.this_epo_totals_container, false, associatedSetter );
36265
36266					// Mainly used to update the price of the associated element when the associated product is variable.
36267					tm_force_update_price(
36268						epoObject,
36269						associatedSetter
36270							.closest( '.tmcp-field-wrap' )
36271							.find( '.tc-price' )
36272							.not( tcAPI.associatedEpoSelector + ' .tc-price' ),
36273						associatedPrice,
36274						associatedFormattedPrice,
36275						associatedOriginalPrice
36276					);
36277
36278					currentEpoObject.associated_connect.data( 'tm-quantity', tc_totals_ob.qty );
36279
36280					if ( currentEpoObject.associated_connect.data( 'tm-quantity' ) ) {
36281						associatedPrice = associatedPrice * parseFloat( currentEpoObject.associated_connect.data( 'tm-quantity' ) );
36282						associatedRawPrice = associatedRawPrice * parseFloat( currentEpoObject.associated_connect.data( 'tm-quantity' ) );
36283						associatedOriginalPrice = associatedOriginalPrice * parseFloat( currentEpoObject.associated_connect.data( 'tm-quantity' ) );
36284						associatedRawOriginalPrice = associatedRawOriginalPrice * parseFloat( currentEpoObject.associated_connect.data( 'tm-quantity' ) );
36285					}
36286
36287					associatedFormattedPrice = tm_set_price( associatedPrice, currentEpoObject.this_epo_totals_container, false, associatedSetter );
36288				}
36289
36290				currentEpoObject.associated_connect.data( 'price_set', 1 );
36291				if ( associatedSetter.is( '.tcenabled' ) ) {
36292					associatedSetter.data( 'associated_price_set', 1 );
36293				}
36294				associatedSetter.data( 'price_set', 1 );
36295				associatedSetter.data( 'raw_price', associatedRawPrice );
36296				associatedSetter.data( 'raw_original_price', associatedRawOriginalPrice );
36297				associatedSetter.data( 'price', tm_set_tax_price( associatedPrice, currentEpoObject.this_epo_totals_container, associatedSetter ) );
36298				associatedSetter.data( 'original_price', tm_set_tax_price( associatedOriginalPrice, currentEpoObject.this_epo_totals_container, associatedSetter ) );
36299
36300				currentEpoObject.associated_connect.data( 'price-changed', 1 );
36301
36302				setTimeout( function() {
36303					if ( ! ( currentEpoObject.associated_connect && event.noassociated ) ) {
36304						currentEpoObject.mainEpoObject.main_cart.trigger( {
36305							type: 'tm-epo-update',
36306							norules: 2
36307						} );
36308					}
36309				}, 20 );
36310			}
36311
36312			jWindow.trigger( 'tcEpoAfterCalculateTotals', {
36313				epo: currentEpoObject,
36314				alternativeCart: alternativeCart,
36315				this_product_type: this_product_type,
36316				cart: cart,
36317				bundleid: bundleid,
36318				totalsObject: tc_totals_ob,
36319				main_product: main_product,
36320				per_product_pricing: perProductPricing,
36321				event: event
36322			} );
36323
36324			hide_native_price = $.epoAPI.applyFilter( 'hide_native_price', true );
36325
36326			if ( cartQty > 0 ) {
36327				// hide native prices
36328				if ( TMEPOJS.tm_epo_change_variation_price === 'yes' ) {
36329					if ( hide_native_price === true && finalTotalBoxMode !== 'disable' ) {
36330						tm_get_native_prices_block( cart ).hide();
36331					} else {
36332						tm_get_native_prices_block( cart ).show();
36333					}
36334				}
36335
36336				if ( ! showTotal ||
36337					finalTotalBoxMode === 'disable' ||
36338					( finalTotalBoxMode === 'hideiftotaliszero' && product_total_price === 0 ) ||
36339					( finalTotalBoxMode === 'hideifoptionsiszero' && total_plus_fee === 0 )
36340				) {
36341					html = '';
36342					totalsHolder.html( html );
36343					totalsHolder.data( 'tm-floating-box-data', [] );
36344				} else {
36345					totalsHolder.html( html );
36346
36347					jWindow.trigger( 'tc-totals-container', {
36348						epo: tc_totals_ob,
36349						totals_holder: totalsHolder,
36350						data: {
36351							epo_object: currentEpoObject,
36352							tm_set_price: tm_set_price,
36353							tm_set_price_totals: tm_set_price_totals,
36354							product_total_price: product_total_price,
36355							product_price: productPrice,
36356							qty: cartQty
36357						},
36358						tm_epo_js: TMEPOJS
36359					} );
36360				}
36361
36362				if ( formatted_final_total && product_total_price >= 0 ) {
36363					if ( TMEPOJS.tm_epo_change_variation_price === 'yes' ) {
36364						setTimeout( function() {
36365							update_native_html = tm_get_native_prices_block( cart );
36366							if ( totalsHolder.data( 'is-on-sale' ) ) {
36367								temp = $.epoAPI.util.decodeHTML(
36368									$.epoAPI.template.html( tcAPI.templateEngine.tc_formatted_sale_price, {
36369										price: _f_regular_price,
36370										sale_price: _fprice,
36371										customer_price_format_wrap_start: customer_price_format_wrap_start,
36372										customer_price_format_wrap_end: customer_price_format_wrap_end,
36373										before_price_text: TMEPOJS.variation_price_before_price_text,
36374										after_price_text: TMEPOJS.variation_price_after_price_text
36375									} )
36376								);
36377								temp = temp + $.epoAPI.util.decodeHTML(
36378									$.epoAPI.util.decodeHTML(
36379										$.epoAPI.template.html( tcAPI.templateEngine.main_suffix )
36380									)
36381								);
36382								temp = {
36383									formatted: temp,
36384									raw: tc_totals_ob.product_total_price
36385								};
36386								temp = replace_suffixes( 'formatted', 'raw', temp, totalsHolder );
36387								temp = temp.formatted;
36388								update_native_html
36389									.html( temp )
36390									.show();
36391							} else {
36392								temp = $.epoAPI.util.decodeHTML(
36393									$.epoAPI.template.html( tcAPI.templateEngine.tc_formatted_price, {
36394										price: _fprice,
36395										customer_price_format_wrap_start: customer_price_format_wrap_start,
36396										customer_price_format_wrap_end: customer_price_format_wrap_end,
36397										before_price_text: TMEPOJS.variation_price_before_price_text,
36398										after_price_text: TMEPOJS.variation_price_after_price_text
36399									} )
36400								);
36401								temp = temp + $.epoAPI.util.decodeHTML(
36402									$.epoAPI.util.decodeHTML(
36403										$.epoAPI.template.html( tcAPI.templateEngine.main_suffix )
36404									)
36405								);
36406								temp = {
36407									formatted: temp,
36408									raw: tc_totals_ob.product_total_price
36409								};
36410								temp = replace_suffixes( 'formatted', 'raw', temp, totalsHolder );
36411								temp = temp.formatted;
36412								update_native_html
36413									.html( temp )
36414									.show();
36415							}
36416						}, 310 ); // WooCommerce uses 300ms timeout for changing the price.
36417					}
36418
36419					if ( TMEPOJS.tm_epo_change_original_price === 'yes' ) {
36420						if ( ! alternativeCart || main_product.find( '.cpf-bto-price' ).length === 0 ) {
36421							if ( currentEpoObject.associated_connect ) {
36422								nativeProductPriceSelector = currentEpoObject.main_product.find( tcAPI.associatedNativeProductPriceSelector );
36423							} else {
36424								// shop/archive pages.
36425								nativeProductPriceSelector = currentEpoObject.main_product.find( tcAPI.nativeProductPriceSelector );
36426								// product page.
36427								if ( nativeProductPriceSelector.length === 0 ) {
36428									nativeProductPriceSelector = $( tcAPI.nativeProductPriceSelector );
36429								}
36430							}
36431
36432							if ( nativeProductPriceSelector.length === 0 && main_product.is( '.tc-after-shop-loop' ) ) {
36433								nativeProductPriceSelector = main_product.closest( '.product' ).find( '.price' ).not( '.tc-after-shop-loop .price' );
36434							}
36435							if ( nativeProductPriceSelector.data( 'tc-original-html' ) === undefined ) {
36436								nativeProductPriceSelector.data( 'tc-original-html', nativeProductPriceSelector.html() );
36437							}
36438							if ( product_total_price > 0 ) {
36439								if ( totalsHolder.data( 'is-on-sale' ) ) {
36440									temp = $.epoAPI.util.decodeHTML(
36441										$.epoAPI.template.html( tcAPI.templateEngine.tc_formatted_sale_price, {
36442											price: _f_regular_price,
36443											sale_price: _fprice,
36444											customer_price_format_wrap_start: customer_price_format_wrap_start,
36445											customer_price_format_wrap_end: customer_price_format_wrap_end,
36446											before_price_text: TMEPOJS.product_price_before_price_text,
36447											after_price_text: TMEPOJS.product_price_after_price_text
36448										} )
36449									);
36450									temp = temp + $.epoAPI.util.decodeHTML(
36451										$.epoAPI.util.decodeHTML(
36452											$.epoAPI.template.html( tcAPI.templateEngine.main_suffix )
36453										)
36454									);
36455									temp = {
36456										formatted: temp,
36457										raw: tc_totals_ob.product_total_price
36458									};
36459									temp = replace_suffixes( 'formatted', 'raw', temp, totalsHolder );
36460									temp = temp.formatted;
36461									nativeProductPriceSelector
36462										.html( temp )
36463										.show();
36464								} else {
36465									temp = $.epoAPI.util.decodeHTML(
36466										$.epoAPI.template.html( tcAPI.templateEngine.tc_formatted_price, {
36467											price: _fprice,
36468											customer_price_format_wrap_start: customer_price_format_wrap_start,
36469											customer_price_format_wrap_end: customer_price_format_wrap_end,
36470											before_price_text: TMEPOJS.product_price_before_price_text,
36471											after_price_text: TMEPOJS.product_price_after_price_text
36472										} )
36473									);
36474									temp = temp + $.epoAPI.util.decodeHTML(
36475										$.epoAPI.util.decodeHTML(
36476											$.epoAPI.template.html( tcAPI.templateEngine.main_suffix )
36477										)
36478									);
36479									temp = {
36480										formatted: temp,
36481										raw: tc_totals_ob.product_total_price
36482									};
36483									temp = replace_suffixes( 'formatted', 'raw', temp, totalsHolder );
36484									temp = temp.formatted;
36485									nativeProductPriceSelector
36486										.html( temp )
36487										.show();
36488								}
36489							} else if ( this_product_type && this_product_type !== 'composite' ) {
36490								if ( currentEpoObject.associated_connect ) {
36491									if ( currentEpoObject.variations_form.length ) {
36492										temp = currentEpoObject.this_epo_totals_container.data( 'variationIdElement' );
36493										if ( temp.length > 0 ) {
36494											temp = parseFloat( temp.val() );
36495											if ( ! ( ! temp || temp === 0 ) ) {
36496												nativeProductPriceSelector.html( $.epoAPI.util.decodeHTML( TMEPOJS.assoc_current_free_text ) );
36497											}
36498										}
36499									} else {
36500										nativeProductPriceSelector.html( $.epoAPI.util.decodeHTML( TMEPOJS.assoc_current_free_text ) );
36501									}
36502								} else {
36503									nativeProductPriceSelector.html( $.epoAPI.util.decodeHTML( TMEPOJS.current_free_text ) );
36504								}
36505							}
36506						}
36507
36508						jWindow.trigger( 'tcEpoMaybeChangePriceHtml', {
36509							epo: currentEpoObject,
36510							alternativeCart: alternativeCart,
36511							this_product_type: this_product_type,
36512							cart: cart,
36513							bundleid: bundleid,
36514							totalsObject: tc_totals_ob,
36515							main_product: main_product,
36516							nativePrice: _fprice
36517						} );
36518					}
36519				}
36520
36521				if ( alternativeCart ) {
36522					main_cart.trigger( {
36523						type: 'tm-epo-update',
36524						norules: 1
36525					} );
36526				} else {
36527					this_epo_totals_container.data( 'is_active', true );
36528				}
36529			} else {
36530				if ( currentEpoObject.associated_connect ) {
36531					nativeProductPriceSelector = currentEpoObject.main_product.find( tcAPI.associatedNativeProductPriceSelector );
36532					nativeProductPriceSelector.html( nativeProductPriceSelector.data( 'tc-original-html' ) );
36533				}
36534
36535				tm_get_native_prices_block( cart ).toArray().forEach( function( npb ) {
36536					var $npb = $( npb );
36537					if ( ! $npb.data( 'tm-original-html' ) ) {
36538						$npb.data( 'tm-original-html', $npb.html() );
36539					} else {
36540						$npb.html( $npb.data( 'tm-original-html' ) );
36541					}
36542				} );
36543
36544				if ( rawProductPrice === 0 && TMEPOJS.tm_epo_remove_free_price_label === 'yes' ) {
36545					tm_get_native_prices_block( cart ).hide();
36546				} else if ( TMEPOJS.tm_epo_change_variation_price === 'yes' && ! ( hide_native_price === true && finalTotalBoxMode !== 'disable' ) ) {
36547					tm_get_native_prices_block( cart ).show();
36548				}
36549
36550				totalsHolder.empty();
36551
36552				if ( alternativeCart ) {
36553					main_cart.trigger( {
36554						type: 'tm-epo-update',
36555						norules: 1
36556					} );
36557				}
36558			}
36559
36560			main_cart.trigger( 'tm-epo-after-update', {
36561				container: cartContainer
36562			} );
36563
36564			jWindow.trigger( 'tc-epo-after-update', {
36565				epo: tc_totals_ob,
36566				totals_holder: totalsHolder,
36567				data: {
36568					epo_object: currentEpoObject,
36569					add_late_fields_prices: add_late_fields_prices,
36570					tm_set_price: tm_set_price,
36571					tm_set_price_totals: tm_set_price_totals,
36572					product_total_price: product_total_price,
36573					replace_suffixes_in_html: replace_suffixes_in_html,
36574					product_price: productPrice,
36575					qty: cartQty,
36576					bundleid: bundleid,
36577					currentCart: currentCart
36578				},
36579				tm_epo_js: TMEPOJS
36580			} );
36581		} );
36582
36583		if ( this_product_type === 'variable' || this_product_type === 'variable-subscription' ) {
36584			epoVariationSection = epoHolder.find( '.tm-epo-variation-section' ).first();
36585
36586			// Custom variation events
36587			epoVariationSection
36588				.find( '.tm-epo-reset-variation' )
36589				.off( 'click.cpfv' )
36590				.on( 'click.cpfv', function() {
36591					var field = $( this );
36592					var id = $.epoAPI.dom.id( field.attr( 'data-tm-for-variation' ) );
36593					var section = field.closest( '.cpf-type-variations' );
36594					var inputs = field.closest( '.cpf-element' ).find( '.tm-epo-variation-element' );
36595					var lis = field.closest( '.cpf-element' ).find( '.tmcp-field-wrap' );
36596
36597					inputs.prop( 'checked', false );
36598					lis.removeClass( 'tc-active' );
36599					variationForm
36600						.find( "[data-attribute_name='attribute_" + id + "']" )
36601						.val( '' )
36602						.trigger( 'change' );
36603					variationForm.find( "[data-attribute_name='attribute_" + id + "']" ).trigger( 'focusin' );
36604
36605					main_product
36606						.find( '.cpf-type-variations' )
36607						.not( section )
36608						.toArray().forEach( function( el ) {
36609							variationForm.find( "[data-attribute_name='attribute_" + $.epoAPI.dom.id( $( el ).find( '.tm-epo-variation-element' ).first().attr( 'data-tm-for-variation' ) ) + "']" ).trigger( 'focusin' );
36610						} );
36611					field.trigger( 'blur' );
36612					variationForm.trigger( 'woocommerce_update_variation_values_tmlogic' );
36613				} );
36614
36615			epoVariationSection
36616				.find( 'input.tm-epo-variation-element,input.tm-epo-variation-element + span' )
36617				.off( 'mouseup.cpfv' )
36618				.on( 'mouseup.cpfv', function() {
36619					var field = $( this );
36620					var id;
36621
36622					if ( field.is( 'span' ) ) {
36623						field = field.prev( 'input' );
36624					}
36625					if ( field.attr( 'disabled' ) ) {
36626						variationForm.find( '.reset_variations' ).trigger( 'click' );
36627					}
36628					id = $.epoAPI.dom.id( field.attr( 'data-tm-for-variation' ) );
36629					variationForm.find( "[data-attribute_name='attribute_" + id + "']" ).trigger( 'focusin' );
36630				} );
36631
36632			epoVariationSection
36633				.off( 'click.' + eventName + '.tmepo', '.reset_variations, .tc-epo-element-variable-reset-variations' )
36634				.on( 'click.' + eventName + '.tmepo', '.reset_variations, .tc-epo-element-variable-reset-variations', { _epoObject: epoObject }, function( event ) {
36635					var _nativeProductPriceSelector;
36636					if ( TMEPOJS.tm_epo_change_original_price === 'yes' ) {
36637						if ( ! alternativeCart || main_product.find( '.cpf-bto-price' ).length === 0 ) {
36638							if ( event.data._epoObject.associated_connect ) {
36639								_nativeProductPriceSelector = event.data._epoObject.main_product.find( tcAPI.associatedNativeProductPriceSelector );
36640							} else {
36641								_nativeProductPriceSelector = $( tcAPI.nativeProductPriceSelector );
36642							}
36643							if ( _nativeProductPriceSelector.data( 'tc-original-html' ) ) {
36644								_nativeProductPriceSelector.html( _nativeProductPriceSelector.data( 'tc-original-html' ) );
36645							}
36646						}
36647					}
36648					variationForm.find( '.variations .reset_variations, .tc-epo-element-variable-reset-variations' ).first().trigger( 'click' );
36649				} );
36650
36651			epoVariationSection
36652				.find( '.tm-epo-variation-element' )
36653				.off( 'change.cpfv tm_epo_variation_element_change' )
36654				.on( 'change.cpfv tm_epo_variation_element_change', function( e ) {
36655					var field = $( this );
36656					var id = $.epoAPI.dom.id( field.attr( 'data-tm-for-variation' ) );
36657					var value = field.val();
36658					var section = field.closest( '.cpf-type-variations' );
36659					var nativeSelect = variationForm.find( "[data-attribute_name='attribute_" + id + "']" );
36660					var exists;
36661
36662					if ( field.closest( '.tm-epo-variation-section' ).is( '.tm-hidden' ) ) {
36663						return;
36664					}
36665
36666					if ( ! ( e && e.type && e.type === 'tm_epo_variation_element_change' ) ) {
36667						exists = false;
36668						nativeSelect.each( function() {
36669							if ( this.value === value ) {
36670								exists = true;
36671								return false;
36672							}
36673						} );
36674						if ( ! exists ) {
36675							nativeSelect.trigger( 'focusin' );
36676						}
36677						nativeSelect.val( value ).trigger( 'change' );
36678					}
36679
36680					if ( ! value ) {
36681						nativeSelect.trigger( 'focusin' );
36682					}
36683
36684					main_product
36685						.find( '.cpf-type-variations' )
36686						.not( section )
36687						.toArray().forEach( function( vr ) {
36688							variationForm.find( '#' + $.epoAPI.dom.id( $( vr ).find( '.tm-epo-variation-element' ).first().attr( 'data-tm-for-variation' ) ) ).trigger( 'focusin' );
36689						} );
36690
36691					field.trigger( 'blur' );
36692					variationForm.trigger( 'woocommerce_update_variation_values_tmlogic' );
36693				} )
36694				.off( 'focusin.cpfv' )
36695				.on( 'focusin.cpfv', function() {
36696					var field = $( this );
36697					var id;
36698
36699					if ( ! field.is( 'select' ) ) {
36700						return;
36701					}
36702
36703					id = $.epoAPI.dom.id( field.attr( 'data-tm-for-variation' ) );
36704					variationForm.find( "[data-attribute_name='attribute_" + id + "']" ).trigger( 'focusin' );
36705					variationForm.trigger( 'woocommerce_update_variation_values_tmlogic' );
36706				} );
36707
36708			variationForm.off( eventNamePrefix + 'found_variation.tmepo tm_fix_stock', '.single_variation_wrap' ).on( eventNamePrefix + 'found_variation.tmepo tm_fix_stock', '.single_variation_wrap', function() {
36709				fix_stock( $( this ), cartContainer );
36710			} );
36711
36712			// update prices when a variation is found
36713			variationForm
36714				.off( eventNamePrefix + 'found_variation.tmepo' )
36715				.on( eventNamePrefix + 'found_variation.tmepo', function( event, variation ) {
36716					var form = $( this );
36717
36718					totalsHolder.data( 'is-on-sale', variation.tc_is_on_sale );
36719					totalsHolder.data( 'regular-price', tm_set_backend_price( variation.display_regular_price, totalsHolder, variation ) );
36720
36721					jWindow.trigger( 'tm-epo-found-variation', {
36722						epo: epoObject,
36723						totalsHolder: totalsHolder,
36724						totalsHolderContainer: totalsHolderContainer,
36725						currentCart: currentCart,
36726						variationForm: form,
36727						variation: variation
36728					} );
36729
36730					found_variation_tmepo( {
36731						epoHolder: epoHolder,
36732						totalsHolder: totalsHolder,
36733						totalsHolderContainer: totalsHolderContainer,
36734						currentCart: currentCart,
36735						variationForm: form,
36736						variation: variation
36737					} );
36738
36739					fix_stock( form, cartContainer );
36740				} )
36741				.off( eventNamePrefix + 'hide_variation.tmepo' )
36742				.on( eventNamePrefix + 'hide_variation.tmepo', { _epoObject: epoObject }, function( event ) {
36743					var _nativeProductPriceSelector;
36744					if ( TMEPOJS.tm_epo_change_original_price === 'yes' ) {
36745						if ( ! alternativeCart || main_product.find( '.cpf-bto-price' ).length === 0 ) {
36746							if ( event.data._epoObject.associated_connect ) {
36747								_nativeProductPriceSelector = event.data._epoObject.main_product.find( tcAPI.associatedNativeProductPriceSelector );
36748							} else {
36749								_nativeProductPriceSelector = $( tcAPI.nativeProductPriceSelector );
36750							}
36751							if ( _nativeProductPriceSelector.data( 'tc-original-html' ) ) {
36752								_nativeProductPriceSelector.html( _nativeProductPriceSelector.data( 'tc-original-html' ) );
36753							}
36754						}
36755					}
36756					totalsHolder.data( 'price', false );
36757					// Fancy product Designer
36758					totalsHolder.removeData( 'tcprice' );
36759					currentCart.trigger( {
36760						type: 'tm-epo-update',
36761						norules: 2
36762					} );
36763				} )
36764				.off( eventNamePrefix + 'check_variations.tmepo' )
36765				.on( eventNamePrefix + 'check_variations.tmepo', function() {
36766					var data = {};
36767					var chosen = 0;
36768					var reset = epoVariationSection.find( '.reset_variations' );
36769
36770					variationForm.find( '.variations select, .tc-epo-variable-product-selector' ).toArray().forEach( function( sl ) {
36771						var $this = $( sl );
36772						var attribute_name = $this.data( 'attribute_name' ) || $this.attr( 'name' );
36773						var value = $this.val() || '';
36774
36775						if ( value.length > 0 ) {
36776							chosen++;
36777						}
36778
36779						data[ attribute_name ] = value;
36780					} );
36781
36782					if ( chosen > 0 ) {
36783						if ( reset.css( 'visibility' ) === 'hidden' ) {
36784							reset.css( 'visibility', 'visible' ).hide().fadeIn();
36785						}
36786					} else {
36787						reset.css( 'visibility', 'hidden' );
36788					}
36789				} )
36790				.trigger( eventNamePrefix + 'check_variations' );
36791
36792			tm_custom_variations( epoObject, cartContainer, itemId, main_product, epoHolder );
36793		}
36794
36795		selectSelector.trigger( 'tm-select-change-html' );
36796		tmQty.trigger( 'change.cpf', { init: 1 } );
36797		tmQuantity.trigger( 'showhide.cpfcustom' );
36798		epoFieldHasClearButton.filter( ':checked' ).trigger( 'cpfclearbutton' );
36799
36800		jWindow.on( 'tm-do-epo-update', function() {
36801			// This must be run every time to get correct results for percent price types
36802			// if set norules then discount will not auto work upon chosing a variation
36803			currentCart.trigger( {
36804				type: 'tm-epo-update'
36805				//"norules": 2
36806			} );
36807		} );
36808
36809		jWindow.trigger( 'tm-epo-init-events', {
36810			epo: {
36811				epo_id: epo_id,
36812				form: epoObject.form,
36813				currentCart: currentCart,
36814				cart_container: cartContainer,
36815				epo_holder: epoHolder,
36816				totals_holder_container: totalsHolderContainer,
36817				totals_holder: totalsHolder,
36818				main_cart: main_cart,
36819				main_epo_inside_form: main_epo_inside_form,
36820				product_id_selector: product_id_selector,
36821				epo_id_selector: epo_id_selector,
36822				product_id: product_id,
36823				this_epo_container: this_epo_container,
36824				this_totals_container: this_totals_container,
36825				this_epo_totals_container: this_epo_totals_container
36826			}
36827		} );
36828
36829		jWindow.trigger( 'epoEventHandlers', {
36830			epo: epoObject,
36831			currentCart: currentCart,
36832			cartContainer: cartContainer,
36833			qtyElement: qtyElement,
36834			epoHolder: epoHolder,
36835			totalsHolderContainer: totalsHolderContainer,
36836			totalsHolder: totalsHolder,
36837			variationForm: variationForm,
36838			variation_id_selector: variation_id_selector,
36839			main_epo_inside_form: main_epo_inside_form,
36840			this_product_type: this_product_type,
36841			get_price_excluding_tax: get_price_excluding_tax,
36842			get_price_including_tax: get_price_including_tax
36843		} );
36844
36845		// show final totals
36846		if ( finalTotalBoxMode !== 'disable' ) {
36847			totalsHolderContainer.addClass( 'tc-show' );
36848		}
36849
36850		// show extra options
36851		jWindow.trigger( 'epo_options_before_visible' );
36852
36853		if ( TMEPOJS.tm_epo_progressive_display === 'yes' ) {
36854			setTimeout( function() {
36855				epoHolder
36856					.css( 'opacity', 0 )
36857					.addClass( 'tc-show' )
36858					.animate(
36859						{
36860							opacity: 1
36861						},
36862						tcAPI.epoAnimationDelay,
36863						'easeOutExpo',
36864						function() {
36865							jWindow.trigger( 'epo_options_visible' );
36866							jWindow.trigger( 'tmlazy' );
36867						}
36868					);
36869			}, tcAPI.epoDelay );
36870		} else {
36871			epoHolder.addClass( 'tc-show' );
36872			jWindow.trigger( 'epo_options_visible' );
36873			jWindow.trigger( 'tmlazy' );
36874		}
36875
36876		main_product.addClass( 'tc-init' );
36877	}
36878
36879	function run_wc_variation_form_cpf( epoObject ) {
36880		var form = epoObject.variations_form;
36881		var cart = epoObject.main_cart;
36882		var this_epo_container = epoObject.this_epo_container;
36883		var eventName = epoObject.is_associated ? 'tc_variation_form.cpf' : 'wc_variation_form.cpf';
36884
36885		form.off( eventName ).on( eventName, function() {
36886			if ( form.data( 'epo_loaded' ) ) {
36887				return;
36888			}
36889
36890			// Start Condition Logic
36891			cpf_section_logic( epoObject );
36892			cpf_element_logic( epoObject );
36893
36894			jWindow.on( 'epoEventHandlers', function() {
36895				run_cpfdependson( this_epo_container );
36896				tm_lazyload();
36897			} );
36898
36899			// Init field price rules
36900			$.tcepo.lateFieldsPrices[ epoObject.epoEventId ] = [];
36901
36902			epoEventHandlers( epoObject );
36903			tm_set_upload_fields( epoObject );
36904			tm_product_image( epoObject );
36905
36906			jWindow.trigger( 'epo-after-init-in-timeout', { epo: epoObject } );
36907			epoObject.noEpoUpdate = false;
36908			cart.trigger( 'tm-epo-check-dpd' );
36909			cart.trigger( {
36910				type: 'tm-epo-update',
36911				rules: 'init'
36912			} );
36913
36914			form.data( 'epo_loaded', true );
36915		} );
36916
36917		if ( variationsFormIsLoaded ) {
36918			form.trigger( eventName );
36919		}
36920
36921		jWindow.trigger( 'epo-after-init', { epo: epoObject } );
36922	}
36923
36924	function detect_variation_swatches_interval( epoObject ) {
36925		var $id = requestAnimationFrame( function() {
36926			detect_variation_swatches_interval( epoObject );
36927		} );
36928		var obj = epoObject.variations_form;
36929		var bound = obj.data( 'bound' );
36930		var eventName = epoObject.is_associated ? 'tc_variation_form.cpf' : 'wc_variation_form.cpf';
36931
36932		if ( bound ) {
36933			cancelAnimationFrame( $id );
36934			run_wc_variation_form_cpf( epoObject );
36935			obj.trigger( eventName );
36936		}
36937	}
36938
36939	function manualInitEPO( epoObject, item, itemCart, itemEpoContainer, main_product ) {
36940		var epoObjectOriginal = $.extend( true, {}, epoObject );
36941		var product_id = itemEpoContainer.attr( 'data-product-id' );
36942		var epo_id = itemEpoContainer.attr( 'data-epo-id' );
36943		var product_id_selector = '.tm-product-id-' + product_id;
36944		var epo_id_selector = "[data-epo-id='" + epo_id + "']";
36945		var epoEventId = 'p' + product_id + 'e' + epo_id;
36946		var this_epo_container = $( tcAPI.epoSelector + product_id_selector + epo_id_selector );
36947		var this_totals_container = $( '.tc-totals-form' + product_id_selector + epo_id_selector );
36948		var this_epo_totals_container = $( '.tc-epo-totals' + product_id_selector + epo_id_selector );
36949
36950		epoObject.isManual = true;
36951
36952		$.tcepo.formSubmitEvents[ epoEventId ] = [];
36953		$.tcepo.errorObject[ epoEventId ] = false;
36954		$.tcepo.initialActivation[ epoEventId ] = false;
36955
36956		epoObject.product_id = product_id;
36957		epoObject.product_id_selector = product_id_selector;
36958		epoObject.epo_id = epo_id;
36959		epoObject.epo_id_selector = epo_id_selector;
36960		epoObject.epoEventId = epoEventId;
36961		epoObject.noEpoUpdate = true;
36962		epoObject.thisForm = item;
36963		epoObject.this_epo_container = this_epo_container;
36964		epoObject.this_totals_container = this_totals_container;
36965		epoObject.this_epo_totals_container = this_epo_totals_container;
36966
36967		tm_lazyload();
36968		main_product.find( '.tm-collapse' ).tmtoggle();
36969		main_product.find( '.tm-section-link' ).tmsectionpoplink();
36970
36971		tm_set_datepicker( item );
36972		setRangePickers( item );
36973		setRangePickersEvents();
36974		tm_set_repeaters( item, epoObject );
36975		tm_css_styles( item );
36976		tm_set_color_pickers( itemEpoContainer );
36977		tm_set_lightbox( itemEpoContainer.find( '.tc-lightbox-image' ).not( '.tm-extra-product-options-variations .radio-image' ) );
36978
36979		// Start Condition Logic
36980		cpf_section_logic( epoObject );
36981		cpf_element_logic( epoObject );
36982		run_cpfdependson( itemEpoContainer );
36983
36984		$.tcToolTip( item.find( '.tm-tooltip' ) );
36985		epoEventHandlers( epoObject, item, itemCart );
36986
36987		epoObject.noEpoUpdate = false;
36988
36989		itemCart.trigger( {
36990			type: 'tm-epo-update',
36991			norules: 2
36992		} );
36993		setTimeout( function() {
36994			epoObject.main_cart.trigger( {
36995				type: 'tm-epo-update',
36996				epoObject: epoObjectOriginal,
36997				norules: 1
36998			} );
36999		}, 200 );
37000		fix_stock( itemCart, item );
37001	}
37002
37003	function tm_init_epo( main_product, is_quickview, product_id, epo_id, associated_connect, mainEpoObject, reactivate ) {
37004		// Holds the main cart when using Composite Products
37005		var main_cart = false;
37006		var main_epo_inside_form = false;
37007		var main_totals_inside_form = false;
37008		var epoEventId;
37009		var has_epo = typeof product_id !== 'undefined';
37010		var not_has_epo = false;
37011		var add_to_cart_field;
37012		var product_id_selector;
37013		var epo_id_selector;
37014		var this_epo_container;
37015		var this_totals_container;
37016		var this_epo_totals_container;
37017		var epo_object;
37018		var variations_form;
37019		var detect_variation_swatches = $( '.variation_form_section .variations-table' ).length > 0;
37020		var is_associated = false;
37021
37022		main_product = $( main_product );
37023
37024		if ( main_product.is( '.tc-init' ) && ! reactivate ) {
37025			return true;
37026		}
37027
37028		if ( is_quickview ) {
37029			errorContainer = main_product;
37030		} else {
37031			errorContainer = $( window );
37032		}
37033
37034		jWindow.trigger( 'tm-epo-init-start' );
37035
37036		if ( ! has_epo ) {
37037			if ( main_product.is( '.product' ) ) {
37038				not_has_epo = true;
37039				has_epo = jBody.find( tcAPI.epoSelector ).length;
37040			}
37041		}
37042
37043		// return if product has no extra options and the totals box is not enabled for all products
37044		if ( ! has_epo && TMEPOJS.tm_epo_enable_final_total_box_all === 'no' && ! main_product.is( '.tm-no-options-composite' ) ) {
37045			jWindow.trigger( 'tm-epo-init-end-no-options' );
37046			return;
37047		}
37048
37049		// set the main_product variable again for products that have no extra options
37050		if ( not_has_epo ) {
37051			jWindow.trigger( 'tm-epo-init-no-options' );
37052			if ( main_product.is( '.product' ) && ! ( main_product.is( '.tm-no-options-pxq' ) || main_product.is( '.tm-no-options-composite' ) ) ) {
37053				main_product = jBody;
37054			}
37055		}
37056
37057		if ( ! product_id ) {
37058			add_to_cart_field = main_product.find( tcAPI.addToCartSelector ).last();
37059			if ( add_to_cart_field.length > 0 ) {
37060				product_id = add_to_cart_field.val();
37061			} else {
37062				add_to_cart_field = $( '.tc-totals-form.tm-totals-form-main' );
37063				product_id = add_to_cart_field.attr( 'data-product-id' );
37064			}
37065			if ( ! product_id ) {
37066				product_id = '';
37067			}
37068		}
37069
37070		if ( ! epo_id ) {
37071			epo_id = parseInt( main_product.find( 'input.tm-epo-counter' ).last().val(), 10 );
37072
37073			if ( ! Number.isFinite( epo_id ) ) {
37074				epo_id = '';
37075			}
37076		}
37077
37078		product_id_selector = '.tm-product-id-' + product_id;
37079		epo_id_selector = "[data-epo-id='" + epo_id + "']";
37080		this_epo_container = $( tcAPI.epoSelector + product_id_selector + epo_id_selector );
37081		this_totals_container = $( '.tc-totals-form' + product_id_selector + epo_id_selector );
37082		this_epo_totals_container = $( '.tc-epo-totals' + product_id_selector + epo_id_selector );
37083		variations_form = main_product.find( '.variations_form' ).not( '.composite_component .variations_form' ).first();
37084		epoEventId = 'p' + product_id + 'e' + epo_id;
37085
37086		if ( variations_form && variations_form.attr( 'data-product_id' ) ) {
37087			if ( variations_form.attr( 'data-product_id' ) !== product_id ) {
37088				variations_form = main_product.find( ".variations_form[data-product_id='" + product_id + "']" );
37089			}
37090		}
37091
37092		main_cart = get_main_cart( main_product, main_product, 'form', product_id );
37093		if ( main_cart.length === 0 ) {
37094			if ( main_product.is( '.tc-shortcode-wrap' ) ) {
37095				main_cart = get_main_cart( this_totals_container, this_totals_container, '.tc-totals-form', product_id );
37096			} else if ( main_product.is( '.tc-epo-element-product-container' ) ) {
37097				main_cart = main_product.find( tcAPI.associatedEpoCart );
37098				// should never be 0
37099				if ( main_cart.length === 0 ) {
37100					main_cart = this_epo_container.parent( tcAPI.associatedEpoSelector );
37101					if ( main_cart.length === 0 ) {
37102						main_cart = main_product.find( '.tc-epo-element-product-container-right' );
37103					}
37104				}
37105				is_associated = true;
37106			}
37107		}
37108
37109		if ( is_associated && variations_form.length === 0 && main_product.is( '.variations_form' ) ) {
37110			variations_form = main_product;
37111		}
37112
37113		$.tcepo.formSubmitEvents[ epoEventId ] = [];
37114		$.tcepo.errorObject[ epoEventId ] = false;
37115		$.tcepo.initialActivation[ epoEventId ] = false;
37116
37117		if ( main_cart.find( tcAPI.epoSelector ).length > 0 ) {
37118			main_epo_inside_form = true;
37119		}
37120		if ( main_cart.find( '.tc-totals-form' ).length > 0 ) {
37121			main_totals_inside_form = true;
37122		}
37123
37124		if ( ! main_totals_inside_form ) {
37125			$.tcepo.formSubmitEvents[ epoEventId ][ $.tcepo.formSubmitEvents[ epoEventId ].length ] = {
37126				trigger: function() {
37127					return true;
37128				},
37129				on_true: function() {
37130					// hidden fields see totals.php
37131					var epos_hidden = $( '.tc-totals-form.tm-product-id-' + product_id + "[data-epo-id='" + epo_id + "']" ).tcClone();
37132					var formepo = $( '<div class="tm-hidden tm-formepo-normal"></div>' );
37133
37134					main_cart.find( '.tm-formepo-normal' ).remove();
37135					formepo.append( epos_hidden );
37136					main_cart.append( formepo );
37137					return true;
37138				},
37139				on_false: function() {
37140					setTimeout( function() {
37141						$( '.tm-formepo-normal' ).remove();
37142					}, 100 );
37143				}
37144			};
37145		}
37146		if ( ! main_epo_inside_form ) {
37147			$.tcepo.formSubmitEvents[ epoEventId ][ $.tcepo.formSubmitEvents[ epoEventId ].length ] = {
37148				trigger: function() {
37149					return true;
37150				},
37151				on_true: function() {
37152					// visible fields
37153					var epos = $( tcAPI.epoSelector + '.tm-product-id-' + product_id + "[data-epo-id='" + epo_id + "']" )
37154						.tcClone()
37155						.addClass( 'formepo' );
37156					var formepo = $( '<div class="tm-hidden tm-formepo"></div>' );
37157
37158					main_cart.find( '.tm-formepo' ).remove();
37159					formepo.append( epos );
37160
37161					main_cart.append( formepo );
37162					return true;
37163				},
37164				on_false: function() {
37165					setTimeout( function() {
37166						$( '.tm-formepo' ).remove();
37167					}, 100 );
37168				}
37169			};
37170		}
37171
37172		epo_object = {
37173			main_product: main_product,
37174			main_cart: main_cart,
37175			epo_id: epo_id,
37176			form: get_main_form( main_product ),
37177			main_epo_inside_form: main_epo_inside_form,
37178			product_id_selector: product_id_selector,
37179			epo_id_selector: epo_id_selector,
37180			product_id: product_id,
37181			this_epo_container: this_epo_container,
37182			this_totals_container: this_totals_container,
37183			this_epo_totals_container: this_epo_totals_container,
37184			qtySelector: tcAPI.qtySelector,
37185			manualInitEPO: manualInitEPO,
37186			epoEventId: epoEventId,
37187			variations_form: variations_form,
37188			has_epo: has_epo,
37189			is_quickview: is_quickview,
37190			is_associated: is_associated,
37191			is_epo_shortcode: this_epo_container.is( '.tc-shortcode' ),
37192			mainEpoObject: mainEpoObject,
37193			associated_connect: associated_connect,
37194			noEpoUpdate: true
37195		};
37196
37197		$( epo_object.form ).data( 'epo_object', epo_object );
37198		this_epo_container.data( 'epo_object', epo_object );
37199
37200		main_cart.data( 'product_id', product_id ).data( 'epo_id', epo_id ).data( 'product_id_selector', product_id_selector ).data( 'epo_id_selector', epo_id_selector );
37201
37202		// tm_set_expanables();
37203		tm_set_checkboxes_rules( epo_object );
37204		tm_set_upload_rules( epo_object );
37205		tm_set_datepicker( this_epo_container );
37206		setRangePickers( this_epo_container );
37207		setRangePickersEvents();
37208		tm_set_repeaters( this_epo_container, epo_object );
37209		tm_set_url_fields();
37210
37211		$.tcToolTip( this_epo_container.find( '.tm-tooltip' ) );
37212
37213		this_epo_container.find( '.tm-collapse' ).tmtoggle();
37214		this_epo_container.find( '.tm-section-link' ).tmsectionpoplink();
37215
37216		if ( reactivate ) {
37217			this_epo_container.addClass( 'reactivate' );
37218		}
37219
37220		if ( variations_form.length > 0 ) {
37221			if ( reactivate ) {
37222				variations_form.data( 'epo_loaded', false );
37223			}
37224			this_epo_totals_container.data( 'price', false );
37225			if ( detect_variation_swatches ) {
37226				detect_variation_swatches_interval( epo_object );
37227			} else {
37228				run_wc_variation_form_cpf( epo_object );
37229			}
37230		} else {
37231			setTimeout( function() {
37232				// Start Condition Logic
37233				cpf_section_logic( epo_object );
37234				cpf_element_logic( epo_object );
37235
37236				jWindow.on( 'epoEventHandlers', function() {
37237					run_cpfdependson( this_epo_container );
37238					tm_lazyload();
37239				} );
37240
37241				// Init field price rules
37242				$.tcepo.lateFieldsPrices[ epoEventId ] = [];
37243				epoEventHandlers( epo_object );
37244				tm_set_upload_fields( epo_object );
37245				tm_product_image( epo_object );
37246
37247				jWindow.trigger( 'epo-after-init-in-timeout', { epo: epo_object } );
37248				main_cart.trigger( 'tm-epo-check-dpd' );
37249				epo_object.noEpoUpdate = false;
37250				main_cart.trigger( {
37251					type: 'tm-epo-update',
37252					rules: 'init'
37253				} );
37254			}, 20 );
37255			jWindow.trigger( 'epo-after-init', { epo: epo_object } );
37256		}
37257
37258		tm_css_styles( this_epo_container );
37259		tm_set_color_pickers( this_epo_container );
37260		tm_set_lightbox( this_epo_container.find( '.tc-lightbox-image' ).not( '.tm-extra-product-options-variations .radio-image' ) );
37261		tm_theme_specific_actions( epo_object );
37262		tc_compatibility( epo_object );
37263
37264		if ( ! is_associated ) {
37265			tm_floating_totals( this_epo_totals_container, is_quickview, main_cart );
37266			tm_form_submit_event( epo_object );
37267			epo_object = addShowHidetoEpoObject( epo_object, epoEventId, epo_object );
37268			tm_show_hide_add_to_cart_button( main_product, epo_object );
37269		}
37270
37271		jWindow.trigger( 'tm-epo-init-end', {
37272			epo: epo_object,
37273			replace_suffixes_in_html: replace_suffixes_in_html
37274		} );
37275
37276		return epo_object;
37277	}
37278
37279	function manual_init( container, reactivate ) {
37280		var $this = $( container );
37281		var product_id = $this.attr( 'data-product-id' );
37282		var epo_id = $this.attr( 'data-epo-id' );
37283		var quickview_floating = false;
37284		var testForm = $this.parent();
37285		if ( ! testForm.is( 'form' ) ) {
37286			testForm = $this.closest( 'form' );
37287			if ( ! testForm.is( 'form' ) ) {
37288				testForm = $this.parent();
37289			}
37290		}
37291		testForm = testForm.parent();
37292
37293		tm_init_epo( testForm, quickview_floating, product_id, epo_id, undefined, undefined, reactivate );
37294	}
37295
37296	function init_epo_plugin() {
37297		var epo_container;
37298		var epo_options_container;
37299
37300		if ( TMEPOJS.tm_epo_no_lazy_load === 'no' ) {
37301			$.extend( $.lazyLoadXT, {
37302				autoInit: false,
37303				selector: 'img.tmlazy',
37304				srcAttr: 'data-original',
37305				visibleOnly: false,
37306				updateEvent: $.lazyLoadXT.updateEvent + ' tmlazy'
37307			} );
37308		}
37309		/*
37310		 * tm-no-options-pxq = product has not options but the "Enable Final total box for all products" is on
37311		 * tm-no-options-composite = product is a composite product with no options but at least one of its bundles have options
37312		 */
37313		epo_container = $( '.tm-no-options-pxq, .tm-no-options-composite' );
37314
37315		if ( epo_container.length > 0 ) {
37316			// Special cases
37317			// -------------
37318			// Price x Quantity display (.tm-no-options-pxq) & composite
37319			// without option but a component has extra options
37320			// (.tm-no-options-composite)
37321
37322			epo_container.addClass( 'initializing' );
37323
37324			epo_container.toArray().forEach( function( product_wrap ) {
37325				var inputepoid;
37326				product_wrap = $( product_wrap );
37327				inputepoid = product_wrap.find( 'input.tm-epo-counter' );
37328				if ( inputepoid.length > 1 ) {
37329					inputepoid.toArray().forEach( function( ec ) {
37330						var currentepoid = $( ec );
37331						var currentcart = currentepoid.closest( '.cart' );
37332						var currentcartparent = currentcart.parent();
37333						if ( currentcartparent.is( 'form' ) ) {
37334							currentcartparent = currentcartparent.parent();
37335						}
37336
37337						if ( ! currentcartparent.is( '.initializing' ) ) {
37338							tm_init_epo( currentcartparent, false, undefined, parseInt( currentepoid.val(), 10 ) );
37339						}
37340					} );
37341				} else {
37342					tm_init_epo( product_wrap, false );
37343				}
37344				epo_container.removeClass( 'initializing' );
37345			} );
37346		}
37347
37348		// The setTimeout is used for compatibility with
37349		// skeleton screen mode that some themes have
37350
37351		setTimeout( function() {
37352			try {
37353				// new main way of calling tm_init_epo
37354				// -----------------------------------
37355				// Normal product pages
37356
37357				epo_options_container = $( tcAPI.epoSelector ).not( tcAPI.associatedEpoSelector + ' ' + tcAPI.epoSelector + ', .tm-no-options-pxq, .tm-no-options-composite, .wc-product-table ' + tcAPI.epoSelector );
37358
37359				if ( epo_options_container.length > 0 ) {
37360					epo_container.addClass( 'initializing' );
37361					epo_options_container.toArray().forEach( function( ec ) {
37362						var $this = $( ec );
37363						var product_id = $this.attr( 'data-product-id' );
37364						var epo_id = $this.attr( 'data-epo-id' );
37365						var quickview_floating = false;
37366						var jProductWrap;
37367						var addInputs = false;
37368						var inputepoid;
37369
37370						// First check if we are in a loop.
37371						jProductWrap = $this.closest( '.tc-after-shop-loop.tm-has-options' );
37372
37373						if ( jProductWrap.length === 0 ) {
37374							// Check based on plugin add to cart selector.
37375							jProductWrap = $( tcAPI.tcAddToCartSelector + "[data-epo-id='" + epo_id + "'][value='" + product_id + "']" )
37376								.closest( 'form,.cart' )
37377								.first()
37378								.parent();
37379							// Check based on native add to cart selector.
37380							if ( jProductWrap.length === 0 ) {
37381								jProductWrap = $( tcAPI.addToCartSelector + "[value='" + product_id + "']" )
37382									.closest( 'form,.cart' )
37383									.first()
37384									.parent();
37385								if ( jProductWrap.length === 0 ) {
37386									// Check if we are in a shortcode
37387									jProductWrap = $this.closest( 'form,.cart' ).first().parent( '.tm-has-options' );
37388									if ( jProductWrap.length === 0 ) {
37389										if ( $this.is( '.tc-shortcode' ) ) {
37390											jProductWrap = $this.wrap( '<div class="tc-shortcode-wrap tc-wrap-' + epo_id + '"></div>' );
37391											jProductWrap = $this.parent();
37392										}
37393										if ( jProductWrap.length > 0 ) {
37394											addInputs = true;
37395										}
37396									}
37397								}
37398							}
37399						} else {
37400							addInputs = true;
37401						}
37402
37403						if ( jProductWrap.length > 0 ) {
37404							if ( addInputs ) {
37405								// in shop (variation logic will not work here)
37406								quickview_floating = true;
37407								$this
37408									.closest( 'form,.cart' )
37409									.first()
37410									.append( $( '<input name="add-to-cart" value="' + product_id + '" type="hidden">' ) );
37411								$this.closest( 'form,.cart' ).first().append( $( '<input type="hidden" value="" class="variation_id" name="variation_id">' ) );
37412							}
37413
37414							if ( jProductWrap.is( 'form' ) ) {
37415								jProductWrap = jProductWrap.parent();
37416							}
37417
37418							inputepoid = jProductWrap.find( 'input.tm-epo-counter' );
37419							if ( inputepoid.length > 1 ) {
37420								inputepoid.toArray().forEach( function( c ) {
37421									var currentepoid = $( c );
37422									var currentcart = currentepoid.closest( '.cart' );
37423									var currentcartparent = currentcart.parent();
37424									if ( currentcartparent.is( 'form' ) ) {
37425										currentcartparent = currentcartparent.parent();
37426									}
37427
37428									if ( ! currentcartparent.is( '.initializing' ) ) {
37429										tm_init_epo( currentcartparent, quickview_floating, product_id, parseInt( currentepoid.val(), 10 ) );
37430									}
37431								} );
37432							} else {
37433								tm_init_epo( jProductWrap, quickview_floating, product_id, epo_id );
37434							}
37435						}
37436					} );
37437					epo_container.removeClass( 'initializing' );
37438				}
37439			} catch ( err ) {
37440				window.console.log( err );
37441				errorObject = err;
37442			}
37443		}, 1 );
37444	}
37445
37446	$.tcepo.tm_init_epo = function( main_product, is_quickview, product_id, epo_id ) {
37447		tm_init_epo( main_product, is_quickview, product_id, epo_id );
37448	};
37449
37450	$.tcepo.tmLazyloadContainer = function( container ) {
37451		tmLazyloadContainer = container;
37452	};
37453
37454	jWindow.on( 'tc_manual_init', function( evt, container ) {
37455		var reactivate;
37456		if ( 'container' in container && 'reactivate' in container ) {
37457			reactivate = container.reactivate;
37458			container = container.container;
37459		}
37460		manual_init( container, reactivate );
37461	} );
37462
37463	function tcAjaxAddToCart( $this ) {
37464		var _pid;
37465		var epos;
37466		var _cpf_product_price;
37467		var _dynamic_product_price;
37468		var _override_product_price;
37469		var form_prefix;
37470		var epoid;
37471		var obj;
37472		var data;
37473		var tcTotalsForm;
37474
37475		currentAjaxButton = $this;
37476		_pid = currentAjaxButton.attr( 'data-product_id' );
37477		if ( undefined === _pid ) {
37478			_pid = currentAjaxButton.val();
37479		}
37480
37481		if ( undefined !== _pid ) {
37482			epoid = currentAjaxButton.closest( '.cart' ).find( '.tm-epo-counter' ).val();
37483			if ( ! epoid ) {
37484				epoid = currentAjaxButton.attr( 'data-epo-id' );
37485				if ( ! epoid ) {
37486					return;
37487				}
37488			}
37489			if ( currentAjaxButton.closest( '.tm-has-options' ).length !== 1 && currentAjaxButton.attr( 'data-epo-id' ) === undefined ) {
37490				return;
37491			}
37492
37493			obj = {};
37494
37495			epos = $( tcAPI.epoSelector + '.tm-product-id-' + _pid + '[data-epo-id="' + epoid + '"]' );
37496
37497			if ( epos.length > 1 ) {
37498				if ( epos.filter( '.formepo' ) ) {
37499					epos = epos.filter( '.formepo' );
37500				} else {
37501					epos = epos.first();
37502				}
37503			}
37504
37505			if ( epos.length === 1 ) {
37506				tcTotalsForm = $( '.tc-totals-form.tm-product-id-' + _pid + '[data-epo-id="' + epoid + '"]' );
37507				_cpf_product_price = tcTotalsForm.find( '.cpf-product-price' ).val();
37508				_dynamic_product_price = tcTotalsForm.find( '.cpf-dynamic-product-price' ).val();
37509				_override_product_price = tcTotalsForm.find( '.cpf-override-product-price' ).val();
37510				form_prefix = tcTotalsForm.find( '.tc_form_prefix' ).val();
37511
37512				obj = $.extend( obj, {
37513					tcajax: 1,
37514					tcaddtocart: _pid,
37515					cpf_product_price: _cpf_product_price,
37516					dynamic_product_price: _dynamic_product_price,
37517					override_product_price: _override_product_price
37518				} );
37519				if ( form_prefix ) {
37520					obj.tc_form_prefix = form_prefix;
37521				}
37522			}
37523
37524			data = currentAjaxButton.data();
37525			TMEPOJS.ajaxbuttondata = $.epoAPI.util.deepCopyArray( data );
37526			TMEPOJS.ajaxbutton = currentAjaxButton;
37527
37528			currentAjaxButton.removeData();
37529			currentAjaxButton.data( $.extend( data, epos.tcSerializeObject(), obj ) );
37530
37531			TMEPOJS.ajaxdata = {
37532				epos: epos,
37533				_pid: _pid,
37534				data: data
37535			};
37536		}
37537	}
37538
37539	function ajaxAddToCartFunctions() {
37540		// Betheme ajax add to cart.
37541		$( document ).on( 'click.tcajax', 'body.mfn-ajax-add-to-cart .single_add_to_cart_button:not(.disabled)', function() {
37542			var formepo;
37543			var $this = $( this );
37544			tcAjaxAddToCart( $this );
37545			TMEPOJS.ajaxdata.cart = $this.closest( '.cart' );
37546			TMEPOJS.ajaxdata.cart.find( '.tm-formepo' ).remove();
37547			formepo = $( "<div class='tm-hidden tm-formepo'></div>" );
37548			formepo.append( TMEPOJS.ajaxdata.epos.tcClone().addClass( 'formepo' ) );
37549			$this.after( formepo );
37550		} );
37551
37552		jBody.on( 'click.tcajax', '.ajax_add_to_cart', function() {
37553			tcAjaxAddToCart( $( this ) );
37554			if ( undefined !== TMEPOJS.ajaxdata ) {
37555				delete TMEPOJS.ajaxdata;
37556			}
37557		} );
37558
37559		jBody.on( 'click.tcajax', '.single_add_to_cart_button', function() {
37560			currentAddToCartButton = $( this );
37561		} );
37562
37563		$( document.body ).on( 'added_to_cart', function() {
37564			if ( TMEPOJS.ajaxbuttondata && TMEPOJS.ajaxbutton instanceof $ ) {
37565				TMEPOJS.ajaxbutton.removeData();
37566				TMEPOJS.ajaxbutton.data( TMEPOJS.ajaxbuttondata );
37567			}
37568		} );
37569	}
37570
37571	// This needs to run here to register our event handlers before WooCommerce.
37572	ajaxAddToCartFunctions();
37573
37574	// document ready
37575	$( function() {
37576		tcAPI = $.epoAPI.applyFilter( 'tc_api', tcAPI );
37577
37578		jWindow.on( 'lazyLoadXToncomplete', function() {
37579			$( '.tm-owl-slider' ).toArray().forEach( function( slider ) {
37580				$( slider ).trigger( 'refresh.owl.carousel' );
37581			} );
37582		} );
37583
37584		jWindow.on( 'tc_init_epo_plugin', function( evt ) {
37585			init_epo_plugin( evt );
37586		} );
37587
37588		jWindow.on( 'tcShowLastError', function() {
37589			window.console.log( errorObject );
37590		} );
37591
37592		$.ajaxPrefilter( function( options, originalOptions ) {
37593			var found = false;
37594			var hashes;
37595			var hash;
37596			var i;
37597			var params;
37598			var $thisbutton;
37599			var _data;
37600			var _urldata;
37601			var _pid;
37602			var epos;
37603			var _cpf_product_price;
37604			var _dynamic_product_price;
37605			var _override_product_price;
37606			var form_prefix;
37607			var obj;
37608			var oldData;
37609			var formData;
37610			var can_be_added = false;
37611			var associativeArray = {};
37612			var tcTotalsForm;
37613
37614			if ( TMEPOJS.tm_epo_enable_in_shop === 'yes' ) {
37615				hashes = options.url.split( '?' );
37616
37617				if ( hashes && hashes.length >= 1 ) {
37618					hashes = hashes[ 1 ];
37619					if ( hashes ) {
37620						hash = hashes.split( '&' );
37621						for ( i = 0; i < hash.length; i += 1 ) {
37622							params = hash[ i ].split( '=' );
37623							if ( params.length >= 1 ) {
37624								if ( params[ 0 ] && params[ 1 ] && params[ 0 ] === 'wc-ajax' && params[ 1 ] === 'add_to_cart' ) {
37625									found = true;
37626								}
37627							}
37628						}
37629						if ( found ) {
37630							options.originalsuccess = options.success;
37631							options.success = function( response ) {
37632								if ( response && response.error && response.product_url ) {
37633									if ( currentAjaxButton && currentAjaxButton.length === 1 ) {
37634										$thisbutton = currentAjaxButton;
37635									}
37636									$thisbutton = $( ".ajax_add_to_cart[data-product_id='" + originalOptions.data.product_id + "']" );
37637									$thisbutton.removeClass( 'added' );
37638									$thisbutton.removeClass( 'loading' );
37639								} else {
37640									options.originalsuccess.call( null, response );
37641								}
37642							};
37643						}
37644					}
37645				}
37646			}
37647
37648			if ( FormData && originalOptions.data ) {
37649				_data = originalOptions.data;
37650				if ( typeof originalOptions.data === 'string' ) {
37651					_data = $.epoAPI.util.parseParams( originalOptions.data );
37652				}
37653				if ( typeof _data[ 0 ] === 'object' ) {
37654					_data.forEach( function( item ) {
37655						associativeArray[ item.name ] = item.value;
37656					} );
37657					_data = associativeArray;
37658				}
37659
37660				_urldata = [];
37661				if ( originalOptions.url && originalOptions.url.indexOf ) {
37662					_urldata = $.epoAPI.util.parseParams( originalOptions.url.slice( originalOptions.url.indexOf( '?' ) + 1 ) );
37663				}
37664
37665				if ( 'quantity' in _data && _data.tcaddtocart && ( _data.product_id || _data[ 'add-to-cart' ] || _urldata.product_id || _urldata[ 'add-to-cart' ] ) ) {
37666					can_be_added = true;
37667					_pid = _data.tcaddtocart;
37668				} else if ( Array.isArray( _data ) ) {
37669					can_be_added = _data.some( function( item ) {
37670						return item.name === 'quantity';
37671					} ) && _data.some( function( item ) {
37672						return item.name === 'add-to-cart';
37673					} ) && _data.some( function( item ) {
37674						return item.name === 'tcaddtocart';
37675					} );
37676					_pid = _data.find( function( item ) {
37677						return item.name === 'tcaddtocart';
37678					} );
37679					if ( _pid ) {
37680						_pid = _pid.value;
37681					} else {
37682						_pid = false;
37683					}
37684				}
37685
37686				if ( ! _pid ) {
37687					if ( 'qty' in _data && _data.tcaddtocart ) {
37688						can_be_added = true;
37689						_pid = _data.tcaddtocart;
37690					}
37691				}
37692
37693				if ( can_be_added ) {
37694					if ( currentAjaxButton && currentAjaxButton.length === 1 && currentAjaxButton.closest( '.tm-has-options' ).length === 1 ) {
37695						epos = currentAjaxButton.closest( '.tm-has-options' ).find( tcAPI.epoSelector + '.tm-product-id-' + _pid );
37696					} else {
37697						epos = $( tcAPI.epoSelector + '.tm-product-id-' + _pid );
37698					}
37699
37700					if ( epos.length > 1 ) {
37701						if ( epos.filter( '.formepo' ) ) {
37702							epos = epos.filter( '.formepo' );
37703						} else {
37704							epos = epos.first();
37705						}
37706					}
37707					if ( epos.length === 1 ) {
37708						tcTotalsForm = $( '.tc-totals-form.tm-product-id-' + _pid );
37709						_cpf_product_price = tcTotalsForm.find( '.cpf-product-price' ).val();
37710						_dynamic_product_price = tcTotalsForm.find( '.cpf-dynamic-product-price' ).val();
37711						_override_product_price = tcTotalsForm.find( '.cpf-override-product-price' ).val();
37712						form_prefix = tcTotalsForm.find( '.tc_form_prefix' ).val();
37713						obj = {
37714							tcajax: 1,
37715							tcaddtocart: _pid,
37716							cpf_product_price: _cpf_product_price,
37717							dynamic_product_price: _dynamic_product_price,
37718							override_product_price: _override_product_price
37719						};
37720						if ( form_prefix ) {
37721							obj.tc_form_prefix = form_prefix;
37722						}
37723
37724						oldData = $.epoAPI.util.parseParams( options.data, true );
37725						oldData = $.extend( oldData, epos.tcSerializeObject(), obj );
37726						formData = new FormData();
37727
37728						Object.keys( oldData ).forEach( function( key ) {
37729							if ( key ) {
37730								formData.append( key, oldData[ key ] );
37731							}
37732						} );
37733
37734						epos.find( ':file' )
37735							.toArray()
37736							.forEach( function( el ) {
37737								for ( i = 0; i < $( el )[ 0 ].files.length; i++ ) {
37738									if ( ! el.multiple ) {
37739										formData.delete( $( el ).attr( 'name' ) );
37740									}
37741									formData.append( $( el ).attr( 'name' ), $( el )[ 0 ].files[ i ] );
37742								}
37743							} );
37744
37745						options.data = formData;
37746						options.contentType = false;
37747						options.cache = false;
37748						options.processData = false;
37749					}
37750				}
37751			}
37752		} );
37753
37754		jDocument.ajaxSuccess( function( event, request, settings ) {
37755			// quickview plugins
37756			var qv_container = TMEPOJS.quickview_array || 'null';
37757			var fromaddons = TMEPOJS.quickview_container || 'null';
37758			var added = {};
37759			var selectors;
37760			var container;
37761			var product_id;
37762			var epo_id;
37763			var noProductCheck;
37764			var testContainer;
37765			var parsedUrl;
37766			var time = 1;
37767			var detectContainer;
37768			var requestContainer;
37769
37770			if ( undefined !== TMEPOJS.ajaxdata ) {
37771				TMEPOJS.ajaxdata.cart.find( '.tm-formepo' ).remove();
37772				delete TMEPOJS.ajaxdata;
37773			}
37774
37775			parsedUrl = $.epoAPI.util.parseParams( settings.data );
37776			if ( parsedUrl.action === 'wc_epo_get_associated_product_html' ) {
37777				return;
37778			}
37779
37780			$( '.tm-formepo-normal' ).remove();
37781			$( '.tm-formepo' ).remove();
37782
37783			//fix for menu cart pop up
37784			$( '.tm-cart-link' ).tmpoplink();
37785
37786			qv_container = $.epoAPI.util.parseJSON( qv_container );
37787
37788			fromaddons = $.epoAPI.util.parseJSON( fromaddons );
37789
37790			for ( selectors in fromaddons ) {
37791				if ( Object.prototype.hasOwnProperty.call( fromaddons, selectors ) ) {
37792					added[ fromaddons[ selectors ][ 0 ] ] = $( fromaddons[ selectors ][ 1 ] );
37793				}
37794			}
37795
37796			$.extend( qv_container, added );
37797
37798			detectContainer = function() {
37799				var keyfound = false;
37800				Object.keys( qv_container ).forEach( function( key ) {
37801					if ( $( qv_container[ key ] ).length ) {
37802						keyfound = key;
37803					}
37804				} );
37805				return keyfound;
37806			};
37807			requestContainer = function( times ) {
37808				var id = requestAnimationFrame( function() {
37809					setTimeout( function() {
37810						requestContainer( times - 1 );
37811					}, 200 );
37812				} );
37813				var key = detectContainer();
37814				var epoContainer;
37815				var initepo;
37816
37817				if ( key || 0 === times ) {
37818					cancelAnimationFrame( id );
37819					if ( key ) {
37820						noProductCheck = false;
37821						container = $( qv_container[ key ] );
37822						if ( container.length > 1 && currentAddToCartButton && currentAddToCartButton.length ) {
37823							container = currentAddToCartButton.closest( qv_container[ key ] );
37824						}
37825						if ( container.length > 1 && key !== 'quickview_pro' ) {
37826							return true;
37827						}
37828
37829						if ( key === 'woothemes_quick_view' && container.is( '.fusion-woocommerce-quick-view-container' ) ) {
37830							return true;
37831						}
37832
37833						if ( key === 'fusion_quick_view_load' && container.find( tcAPI.epoSelector ).length === 0 ) {
37834							return true;
37835						}
37836
37837						if ( key === 'woodmart_quick_shop' ) {
37838							parsedUrl = $.epoAPI.util.parseParams( settings.url );
37839							if ( parsedUrl.action === 'woodmart_quick_shop' ) {
37840								testContainer = $( $.epoAPI.util.escapeSelector( qv_container[ key ] + '.post-' + parsedUrl.id ) );
37841								if ( testContainer.length ) {
37842									container = testContainer;
37843									noProductCheck = true;
37844								}
37845							}
37846						}
37847						if ( key === 'woodmart_quick_view' ) {
37848							parsedUrl = $.epoAPI.util.parseParams( settings.url );
37849							if ( parsedUrl.action === 'woodmart_quick_view' ) {
37850								testContainer = $( $.epoAPI.util.escapeSelector( qv_container[ key ] + '.post-' + parsedUrl.id ) );
37851								if ( testContainer.length ) {
37852									container = testContainer;
37853									noProductCheck = true;
37854								}
37855							}
37856						}
37857
37858						if ( key === 'quickview_pro' ) {
37859							parsedUrl = settings.url.split( '/' );
37860							if ( parsedUrl.length ) {
37861								testContainer = $( $.epoAPI.util.escapeSelector( qv_container[ key ] + ' .post-' + parsedUrl[ parsedUrl.length - 1 ] ) );
37862								if ( testContainer.length ) {
37863									container = testContainer;
37864									noProductCheck = true;
37865								}
37866							}
37867						}
37868
37869						if ( key === 'exwoofood_booking_info' || key === 'wqv_popup_content' || key === 'wp_food' || key === 'jet_popup_get_content' ) {
37870							noProductCheck = true;
37871						}
37872
37873						if ( key === 'woofood' && settings.data ) {
37874							parsedUrl = $.epoAPI.util.parseParams( settings.data );
37875							if ( parsedUrl.action === 'woofood_quickview_ajax' ) {
37876								testContainer = testContainer = container.find( 'form' ).parent();
37877								if ( testContainer.length ) {
37878									container = testContainer;
37879									noProductCheck = true;
37880								}
37881							}
37882						}
37883
37884						if ( container.find( '.product' ).length === 0 && container.is( '.product' ) ) {
37885							noProductCheck = true;
37886						}
37887
37888						if ( ( container.find( '.product' ).length > 0 || noProductCheck ) ) {
37889							container.removeClass( 'tc-init' );
37890
37891							if (
37892								key === 'fusion_quick_view_load' ||
37893								key === 'jet_popup_get_content' ||
37894								key === 'wp_food' ||
37895								key === 'woodmart_quick_shop' ||
37896								key === 'woodmart_quick_view' ||
37897								key === 'lightboxpro' ||
37898								key === 'jckqv_quick_view' ||
37899								key === 'yith_quick_view' ||
37900								key === 'theme_flatsome_quick_view'
37901							) {
37902								variationsFormIsLoaded = true;
37903							}
37904							tmLazyloadContainer = container;
37905							if ( tmLazyloadContainer.length > 1 ) {
37906								while ( tmLazyloadContainer.length > 1 ) {
37907									tmLazyloadContainer = tmLazyloadContainer.filter( function() {
37908										return $( this ).find( tcAPI.epoSelector ).length > 0;
37909									} );
37910									if ( tmLazyloadContainer.length === 0 ) {
37911										break;
37912									}
37913								}
37914								if ( tmLazyloadContainer.length === 0 ) {
37915									setTimeout( function() {
37916										requestContainer( 10 );
37917									}, 1000 );
37918
37919									return true;
37920								}
37921							}
37922
37923							epoContainer = tmLazyloadContainer.find( tcAPI.epoSelector );
37924							if ( epoContainer.length === 0 ) {
37925								delete qv_container[ key ];
37926								requestContainer( 10 );
37927								return true;
37928							}
37929
37930							if ( key === 'fusion_quick_view_load' ) {
37931								time = 1400;
37932							}
37933
37934							initepo = function( redo ) {
37935								setTimeout( function() {
37936									product_id = epoContainer.attr( 'data-product-id' );
37937									epo_id = epoContainer.attr( 'data-epo-id' );
37938									if ( key === 'woodmart_quick_shop' ) {
37939										container.addClass( 'has-options' );
37940									}
37941
37942									// Reset element cache
37943									tcAPI.getElementFromFieldCache = [];
37944									tm_init_epo( tmLazyloadContainer, true, product_id, epo_id, undefined, undefined, redo );
37945									jWindow.trigger( 'tmlazy' );
37946									jWindow.trigger( 'tm_epo_loaded_quickview' );
37947									if ( $.jMaskGlobals ) {
37948										tmLazyloadContainer.find( $.jMaskGlobals.maskElements ).toArray().forEach( function( t ) {
37949											t = $( t );
37950
37951											if ( t.attr( 'data-mask' ) ) {
37952												t.mask( t.attr( 'data-mask' ) );
37953											}
37954										} );
37955									}
37956								}, time );
37957							};
37958
37959							initepo();
37960
37961							if ( key === 'minimog_product_quick_view' ) {
37962								$( '.minimog-product' ).on( 'click', '.quick-view-btn', function( evt ) {
37963									var $button = $( this );
37964									var productID = $button.data( 'pid' );
37965									var $template = jBody.children( '#' + 'modal-quick-view-product-' + productID );
37966									evt.preventDefault();
37967									evt.stopPropagation();
37968									if ( $button.hasClass( 'loading' ) ) {
37969										return;
37970									}
37971									if ( ! $.fn.MinimogModal ) {
37972										return;
37973									}
37974									if ( $template.length > 0 ) {
37975										tmLazyloadContainer = $( '.modal-quick-view-popup' );
37976										initepo( true );
37977									}
37978								} );
37979							}
37980						}
37981					}
37982				}
37983			};
37984			requestContainer( 10 );
37985		} );
37986
37987		init_epo_plugin();
37988
37989		$( '.tm-cart-link' ).tmpoplink();
37990		jBody.on( 'updated_checkout wc_fragments_loaded wc_fragments_refreshed wc_fragment_refresh', function() {
37991			$( '.tm-cart-link' ).tmpoplink();
37992		} );
37993
37994		jWindow.trigger( 'tmlazy' );
37995
37996		jWindow.trigger( 'tm_epo_loaded' );
37997	} );
37998
37999	function associated_table_item() {
38000		$( '.tc-associated-table-product td.product-name' ).toArray().forEach( function( el ) {
38001			el = $( el );
38002			if ( el.find( '.product-name' ).length === 0 ) {
38003				el.wrapInner( $( '<div class="tc-associated-table-product-indent"></div>' ) );
38004			}
38005		} );
38006	}
38007
38008	// Associated product cart indent
38009	jBody.on( 'updated_checkout updated_cart_totals', function() {
38010		associated_table_item();
38011	} );
38012
38013	associated_table_item();
38014
38015	// document ready
38016	$( function() {
38017		// Remove accept attribute from upload buttons when in Facebook or Instagram internral browser
38018		var ua = navigator.userAgent || navigator.vendor || window.opera;
38019		ua = ( ua.indexOf( 'FBAN' ) > -1 ) || ( ua.indexOf( 'FBAV' ) > -1 ) || ( ua.indexOf( 'Instagram' ) > -1 );
38020		if ( ua ) {
38021			$( '.tmcp-upload' ).removeAttr( 'accept' );
38022		}
38023
38024		// Fix several custom quantity buttons on themes
38025		jDocument.on( 'click', '.quantity .jckqv-qty-spinner, .quantity .ui-spinner-button', function() {
38026			$( this ).closest( '.quantity' ).find( tcAPI.qtySelector ).trigger( 'change' );
38027		} );
38028
38029		// Quantity selector buttons
38030		$( document ).on( 'click', '.tm-quantity .tc-qty-button, .tm-quantity-alt .tc-qty-button', function() {
38031			var qty = $( this ).closest( '.tm-quantity, .tm-quantity-alt' ).find( '.tm-qty, .tm-qty-alt' );
38032			var val = parseFloat( qty.val() );
38033			var max = parseFloat( qty.attr( 'max' ) );
38034			var min = parseFloat( qty.attr( 'min' ) );
38035			var step = parseFloat( qty.attr( 'step' ) );
38036			if ( isNaN( step ) ) {
38037				step = 1;
38038			}
38039
38040			// Change the value if plus or minus
38041			if ( $( this ).is( '.plus' ) ) {
38042				if ( ! isNaN( max ) && ( max <= val ) ) {
38043					qty.val( max );
38044				} else {
38045					qty.val( val + step );
38046				}
38047			} else if ( ! isNaN( min ) && ( min >= val ) ) {
38048				qty.val( min );
38049			} else if ( val > 0 ) {
38050				qty.val( val - step );
38051			}
38052
38053			$( this ).closest( '.tm-quantity, .tm-quantity-alt' ).find( 'input.tm-qty, input.tm-qty-alt' ).trigger( 'change' );
38054		} );
38055
38056		// bulk variations forms plugin
38057		$( '#wholesale_form' ).on( 'submit', function() {
38058			var _product_id = $( 'form.cart' ).find( tcAPI.addToCartSelector ).val();
38059			// visible fields
38060			var epos = $( tcAPI.epoSelector + ".tm-cart-main[data-product-id='" + _product_id + "']" ).tcClone();
38061			// hidden fields see totals.php
38062			var epos_hidden = $( ".tm-totals-form-main[data-product-id='" + _product_id + "']" ).tcClone();
38063			var formepo = $( "<div class='tm-hidden tm-formepo'></div>" );
38064
38065			formepo.append( epos );
38066			formepo.append( epos_hidden );
38067			$( this ).append( formepo );
38068			return true;
38069		} );
38070
38071		// Disable quote button if option validation fails
38072		jDocument.on( 'click', '#add_to_quote', function( e ) {
38073			var form;
38074			var epo_id;
38075			var epos;
38076
38077			if ( TMEPOJS && TMEPOJS.tm_epo_global_enable_validation === 'yes' ) {
38078				form = $( this ).parents( 'form' );
38079				epo_id = parseInt( form.find( '.tm-epo-counter' ).val(), 10 );
38080				epos = $( tcAPI.epoSelector + "[data-epo-id='" + epo_id + "']" );
38081
38082				// not validated
38083				if ( TMEPOJS.tm_epo_global_enable_validation === 'yes' && $.tc_validator && form.length > 0 && epos.length > 0 && ! form.tc_validate().form() ) {
38084					e.stopImmediatePropagation();
38085				}
38086			}
38087		} );
38088
38089		// PayPal for WooCommerce (PayPal Express Checkout button fix)
38090		$( '.single_add_to_cart_button.paypal_checkout_button' ).on( 'click', function( event ) {
38091			// this is the selector used by the paypal checkout plugin
38092			var form = $( '.cart' );
38093			var validator;
38094
38095			if ( form.data( 'tc_validator' ) ) {
38096				validator = form.data( 'tc_validator' );
38097				if ( validator.errorList ) {
38098					event.stopImmediatePropagation();
38099				}
38100			}
38101		} );
38102
38103		$( '.wc-product-table' ).on( 'init.wcpt', function( event, table ) {
38104			table.$table.find( tcAPI.epoSelector ).addClass( 'hidden' );
38105			table.$table.find( 'thead tr' ).append( '<th>&nbsp;</th>' );
38106			setTimeout( function() {
38107				table.$table.find( '.cart:not(.cart_group)' ).toArray().forEach( function( c ) {
38108					var epo = $( c ).find( tcAPI.epoSelector );
38109					var tr = epo.closest( 'tr' );
38110
38111					$( "<td class='wc-product-table-epo'></td>" ).appendTo( tr ).append( epo );
38112					$( window ).trigger( 'tc_manual_init', epo );
38113					epo.removeClass( 'hidden' );
38114				} );
38115			}, 500 );
38116		} );
38117
38118		// Generate product link.
38119		$( '.generate-product-link' ).on( 'click', function( e ) {
38120			var url = new URL( window.location.href );
38121			var params = url.searchParams;
38122			var newUrl;
38123			var processedRadios = {};
38124
38125			e.preventDefault();
38126
38127			/* Build the query-string from the fields inside #myFormDiv */
38128			$( tcAPI.epoSelector + ':visible' ).find( '.tm-epo-field:enabled' ).each( function() {
38129				var name = this.name;
38130				var value;
38131				var checkedRadio;
38132				if ( this.type === 'radio' ) {
38133					if ( processedRadios[ name ] ) {
38134						return;
38135					}
38136					processedRadios[ name ] = true;
38137
38138					// Find the checked radio in this group
38139					checkedRadio = $( tcAPI.epoSelector + ':visible' )
38140						.find( '.tm-epo-field[name="' + name + '"]:checked' );
38141
38142					value = checkedRadio.length ? checkedRadio.val() : '';
38143				} else if ( this.type === 'checkbox' ) {
38144					value = this.checked ? this.value : '';
38145				} else {
38146					value = $( this ).val();
38147				}
38148
38149				if ( name && value !== '' ) {
38150					params.set( name, value );
38151				}
38152				if ( value === '' ) {
38153					params.delete( name );
38154				}
38155			} );
38156
38157			/* Remove empty values we just stored above */
38158			for ( const [ key, val ] of params.entries() ) {
38159				if ( val === '' ) {
38160					params.delete( key );
38161				}
38162			}
38163
38164			/* Update the browser URL without reloading the page */
38165			newUrl = url.toString();
38166			history.replaceState( null, null, newUrl );
38167			navigator.clipboard.writeText( newUrl ).then( function() {
38168				window.alert( 'Link copied to clipboard:\n' + newUrl );
38169			} ).catch( function( err ) {
38170				window.console.error( 'Could not copy: ', err );
38171			} );
38172		} );
38173	} );
38174}( window, document, window.jQuery ) );
38175;
38176(()=>{"use strict";var t={d:(e,i)=>{for(var s in i)t.o(i,s)&&!t.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:i[s]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};function i(t){if(this.formData={},this.tree={},!(t instanceof FormData))return this;this.formData=t;const e=()=>{const t=new Map;return t.largestIndex=0,t.set=function(e,i){""===e?e=t.largestIndex++:/^[0-9]+$/.test(e)&&(e=parseInt(e),t.largestIndex<=e&&(t.largestIndex=e+1)),Map.prototype.set.call(t,e,i)},t};this.tree=e();const i=/^(?<name>[a-z][-a-z0-9_:]*)(?<array>(?:\[(?:[a-z][-a-z0-9_:]*|[0-9]*)\])*)/i;for(const[t,s]of this.formData){const o=t.match(i);if(o)if(""===o.groups.array)this.tree.set(o.groups.name,s);else{const t=[...o.groups.array.matchAll(/\[([a-z][-a-z0-9_:]*|[0-9]*)\]/gi)].map((([t,e])=>e));t.unshift(o.groups.name);const i=t.pop();t.reduce(((t,i)=>{if(/^[0-9]+$/.test(i)&&(i=parseInt(i)),t.get(i)instanceof Map)return t.get(i);const s=e();return t.set(i,s),s}),this.tree).set(i,s)}}}t.r(e),t.d(e,{all:()=>D,any:()=>M,date:()=>m,dayofweek:()=>u,email:()=>r,enum:()=>h,file:()=>d,maxdate:()=>z,maxfilesize:()=>j,maxitems:()=>v,maxlength:()=>x,maxnumber:()=>y,mindate:()=>A,minfilesize:()=>$,minitems:()=>w,minlength:()=>g,minnumber:()=>b,number:()=>c,required:()=>n,requiredfile:()=>a,stepnumber:()=>I,tel:()=>l,time:()=>f,url:()=>p}),i.prototype.entries=function(){return this.tree.entries()},i.prototype.get=function(t){return this.tree.get(t)},i.prototype.getAll=function(t){if(!this.has(t))return[];const e=t=>{const i=[];if(t instanceof Map)for(const[s,o]of t)i.push(...e(o));else""!==t&&i.push(t);return i};return e(this.get(t))},i.prototype.has=function(t){return this.tree.has(t)},i.prototype.keys=function(){return this.tree.keys()},i.prototype.values=function(){return this.tree.values()};const s=i;function o({rule:t,field:e,error:i,...s}){this.rule=t,this.field=e,this.error=i,this.properties=s}const n=function(t){if(0===t.getAll(this.field).map((t=>t.trim())).filter((t=>""!==t)).length)throw new o(this)},a=function(t){if(0===t.getAll(this.field).length)throw new o(this)},r=function(t){if(!t.getAll(this.field).map((t=>t.trim())).filter((t=>""!==t)).every((t=>{if(t.length<6)return!1;if(-1===t.indexOf("@",1))return!1;if(t.indexOf("@")!==t.lastIndexOf("@"))return!1;const[e,i]=t.split("@",2);if(!/^[a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]+$/.test(e))return!1;if(/\.{2,}/.test(i))return!1;if(/(?:^[ \t\n\r\0\x0B.]|[ \t\n\r\0\x0B.]$)/.test(i))return!1;const s=i.split(".");if(s.length<2)return!1;for(const t of s){if(/(?:^[ \t\n\r\0\x0B-]|[ \t\n\r\0\x0B-]$)/.test(t))return!1;if(!/^[a-z0-9-]+$/i.test(t))return!1}return!0})))throw new o(this)},p=function(t){const e=t.getAll(this.field).map((t=>t.trim())).filter((t=>""!==t));if(!e.every((t=>{try{return(t=>-1!==["http","https","ftp","ftps","mailto","news","irc","irc6","ircs","gopher","nntp","feed","telnet","mms","rtsp","sms","svn","tel","fax","xmpp","webcal","urn"].indexOf(t))(new URL(t).protocol.replace(/:$/,""))}catch{return!1}}
38176)))throw new o(this)},l=function(t){if(!t.getAll(this.field).map((t=>t.trim())).filter((t=>""!==t)).every((t=>(((t=(t=t.replace(/[#*].*$/,"")).replaceAll(/[()/.*#\s-]+/g,"")).startsWith("+")||t.startsWith("00"))&&(t=`+${t.replace(/^[+0]+/,"")}`),!!/^[+]?[0-9]+$/.test(t)&&5<t.length&&t.length<16))))throw new o(this)},c=function(t){if(!t.getAll(this.field).map((t=>t.trim())).filter((t=>""!==t)).every((t=>!!/^[-]?[0-9]+(?:[eE][+-]?[0-9]+)?$/.test(t)||!!/^[-]?(?:[0-9]+)?[.][0-9]+(?:[eE][+-]?[0-9]+)?$/.test(t))))throw new o(this)},m=function(t){if(!t.getAll(this.field).map((t=>t.trim())).filter((t=>""!==t)).every((t=>{if(!/^[0-9]{4,}-[0-9]{2}-[0-9]{2}$/.test(t))return!1;const e=new Date(t);return!Number.isNaN(e.valueOf())})))throw new o(this)},f=function(t){if(!t.getAll(this.field).map((t=>t.trim())).filter((t=>""!==t)).every((t=>{const e=t.match(/^([0-9]{2})\:([0-9]{2})(?:\:([0-9]{2}))?$/);if(!e)return!1;const i=parseInt(e[1]),s=parseInt(e[2]),o=e[3]?parseInt(e[3]):0;return 0<=i&&i<=23&&0<=s&&s<=59&&0<=o&&o<=59})))throw new o(this)},d=function(t){if(!t.getAll(this.field).every((t=>t instanceof File&&this.accept?.some((e=>/^\.[a-z0-9]+$/i.test(e)?t.name.toLowerCase().endsWith(e.toLowerCase()):(t=>{const e=[],i=t.match(/^(?<toplevel>[a-z]+)\/(?<sub>[*]|[a-z0-9.+-]+)$/i);if(i){const t=i.groups.toplevel.toLowerCase(),s=i.groups.sub.toLowerCase();for(const[o,n]of(()=>{const t=new Map;return t.set("jpg|jpeg|jpe","image/jpeg"),t.set("gif","image/gif"),t.set("png","image/png"),t.set("bmp","image/bmp"),t.set("tiff|tif","image/tiff"),t.set("webp","image/webp"),t.set("ico","image/x-icon"),t.set("heic","image/heic"),t.set("asf|asx","video/x-ms-asf"),t.set("wmv","video/x-ms-wmv"),t.set("wmx","video/x-ms-wmx"),t.set("wm","video/x-ms-wm"),t.set("avi","video/avi"),t.set("divx","video/divx"),t.set("flv","video/x-flv"),t.set("mov|qt","video/quicktime"),t.set("mpeg|mpg|mpe","video/mpeg"),t.set("mp4|m4v","video/mp4"),t.set("ogv","video/ogg"),t.set("webm","video/webm"),t.set("mkv","video/x-matroska"),t.set("3gp|3gpp","video/3gpp"),t.set("3g2|3gp2","video/3gpp2"),t.set("txt|asc|c|cc|h|srt","text/plain"),t.set("csv","text/csv"),t.set("tsv","text/tab-separated-values"),t.set("ics","text/calendar"),t.set("rtx","text/richtext"),t.set("css","text/css"),t.set("htm|html","text/html"),t.set("vtt","text/vtt"),t.set("dfxp","application/ttaf+xml"),t.set("mp3|m4a|m4b","audio/mpeg"),t.set("aac","audio/aac"),t.set("ra|ram","audio/x-realaudio"),t.set("wav","audio/wav"),t.set("ogg|oga","audio/ogg"),t.set("flac","audio/flac"),t.set("mid|midi","audio/midi"),t.set("wma","audio/x-ms-wma"),t.set("wax","audio/x-ms-wax"),t.set("mka","audio/x-matroska"),t.set("rtf","application/rtf"),t.set("js","application/javascript"),t.set("pdf","application/pdf"),t.set("swf","application/x-shockwave-flash"),t.set("class","application/java"),t.set("tar","application/x-tar"),t.set("zip","application/zip"),t.set("gz|gzip","application/x-gzip"),t.set("rar","application/rar"),t.set("7z","application/x-7z-compressed"),t.set("exe","application/x-msdownload"),t.set("psd","application/octet-stream"),t.set("xcf","application/octet-stream"),t.set("doc","application/msword"),t.set("pot|pps|ppt","application/vnd.ms-powerpoint"),t.set("wri","application/vnd.ms-write"),t.set("xla|xls|xlt|xlw","application/vnd.ms-excel"),t.set("mdb","application/vnd.ms-access"),t.set("mpp","application/vnd.ms-project"),t.set("docx","application/vnd.openxmlformats-officedocument.wordprocessingml.document"),t.set("docm","application/vnd.ms-word.document.macroEnabled.12"),t.set("dotx","application/vnd.openxmlformats-officedocument.wordprocessingml.template"),t.set("dotm","application/vnd.ms-word.template.macroEnabled.12"),t.set("xlsx","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"),t.set("xlsm","application/vnd.ms-excel.sheet.macroEnabled.12"),t.set("xlsb","application/vnd.ms-excel.sheet.binary.macroEnabled.12"),t.set("xltx","application/vnd.openxmlformats-officedocument.spreadsheetml.template"),t.set("xltm","application/vnd.ms-excel.template.macroEnabled.12"),t.set("xlam","application/vnd.ms-excel.addin.macroEnabled.12"),t.set("pptx","application/vnd.openxmlformats-officedocument.presentationml.presentation"),t.set("pptm","application/vnd.ms-powerpoint.presentation.macroEnabled.12"),t.set("ppsx","application/vnd.openxmlformats-officedocument.presentationml.slideshow"),t.set("ppsm","application/vnd.ms-powerpoint.slideshow.macroEnabled.12"),t.set("potx","application/vnd.openxmlformats-officedocument.presentationml.template"),t.set("potm","application/vnd.ms-powerpoint.template.macroEnabled.12"),t.set("ppam","application/vnd.ms-powerpoint.addin.macroEnabled.12"),t.set("sldx","application/vnd.openxmlformats-officedocument.presentationml.slide"),t.set("sldm","application/vnd.ms-powerpoint.slide.macroEnabled.12"),t.set("onetoc|onetoc2|onetmp|onepkg","application/onenote"),t.set("oxps","application/oxps"),t.set("xps","application/vnd.ms-xpsdocument"),t.set("odt","application/vnd.oasis.opendocument.text"),t.set("odp","application/vnd.oasis.opendocument.presentation"),t.set("ods","application/vnd.oasis.opendocument.spreadsheet"),t.set("odg","application/vnd.oasis.opendocument.graphics"),t.set("odc","application/vnd.oasis.opendocument.chart"),t.set("odb","application/vnd.oasis.opendocument.database"),t.set("odf","application/vnd.oasis.opendocument.formula"),t.set("wp|wpd","application/wordperfect"),t.set("key","application/vnd.apple.keynote"),t.set("numbers","application/vnd.apple.numbers"),t.set("pages","application/vnd.apple.pages"),t})())("*"===s&&n.startsWith(t+"/")||n===i[0])&&e.push(...o.split("|"))}return e})(e).some((e=>(e="."+e.trim(),t.name.toLowerCase().endsWith(e.toLowerCase())))))))))throw new o(this)},h=function(t){if(!t.getAll(this.field).map((t=>t.trim())).filter((t=>""!==t)).every((t=>this.accept?.some((e=>
38176t===String(e))))))throw new o(this)},u=function(t){if(!t.getAll(this.field).map((t=>t.trim())).filter((t=>""!==t)).every((t=>{const e=0===(i=new Date(t).getDay())?7:i;var i;return this.accept?.some((t=>e===parseInt(t)))})))throw new o(this)},w=function(t){if(t.getAll(this.field).map((t=>t.trim())).filter((t=>""!==t)).length<parseInt(this.threshold))throw new o(this)},v=function(t){const e=t.getAll(this.field).map((t=>t.trim())).filter((t=>""!==t));if(parseInt(this.threshold)<e.length)throw new o(this)},g=function(t){const e=t.getAll(this.field).map((t=>t.trim())).filter((t=>""!==t));let i=0;if(e.forEach((t=>{"string"==typeof t&&(i+=t.length)})),0!==i&&i<parseInt(this.threshold))throw new o(this)},x=function(t){const e=t.getAll(this.field).map((t=>t.trim())).filter((t=>""!==t));let i=0;if(e.forEach((t=>{"string"==typeof t&&(i+=t.length)})),parseInt(this.threshold)<i)throw new o(this)},b=function(t){if(!t.getAll(this.field).map((t=>t.trim())).filter((t=>""!==t)).every((t=>!(parseFloat(t)<parseFloat(this.threshold)))))throw new o(this)},y=function(t){if(!t.getAll(this.field).map((t=>t.trim())).filter((t=>""!==t)).every((t=>!(parseFloat(this.threshold)<parseFloat(t)))))throw new o(this)},A=function(t){if(!t.getAll(this.field).map((t=>t.trim())).filter((t=>""!==t)).every((t=>!(/^[0-9]{4,}-[0-9]{2}-[0-9]{2}$/.test(t)&&/^[0-9]{4,}-[0-9]{2}-[0-9]{2}$/.test(this.threshold)&&t<this.threshold))))throw new o(this)},z=function(t){if(!t.getAll(this.field).map((t=>t.trim())).filter((t=>""!==t)).every((t=>!(/^[0-9]{4,}-[0-9]{2}-[0-9]{2}$/.test(t)&&/^[0-9]{4,}-[0-9]{2}-[0-9]{2}$/.test(this.threshold)&&this.threshold<t))))throw new o(this)},$=function(t){const e=t.getAll(this.field);let i=0;if(e.forEach((t=>{t instanceof File&&(i+=t.size)})),i<parseInt(this.threshold))throw new o(this)},j=function(t){const e=t.getAll(this.field);let i=0;if(e.forEach((t=>{t instanceof File&&(i+=t.size)})),parseInt(this.threshold)<i)throw new o(this)},I=function(t){const e=t.getAll(this.field).map((t=>t.trim())).filter((t=>""!==t)),i=parseFloat(this.base),s=parseFloat(this.interval);if(!(0<s))return!0;if(!e.every((t=>{const e=(parseFloat(t)-i)%s;return"0.000000"===Math.abs(e).toFixed(6)||"0.000000"===Math.abs(e-s).toFixed(6)})))throw new o(this)},O=({ruleObj:t,options:i})=>{const{rule:s,...o}=t;return"function"==typeof e[s]&&("function"!=typeof e[s].matches||e[s].matches(o,i))},E=({ruleObj:t,formDataTree:i,options:s})=>{const{rule:o}=t;e[o].call(t,i,s)},k=[],F=t=>[...k].reduce(((t,e)=>i=>e(i,t)),t),D=function(t,e={}){const i=(this.rules??[]).filter((t=>O({ruleObj:t,options:e}))),s=F(E);if(!i.every((i=>{try{s({ruleObj:i,formDataTree:t,options:e})}catch(t){if(!(t instanceof o))throw t;if(void 0!==t.error)throw t;return!1}return!0})))throw new o(this)},M=function(t,e={}){const i=(this.rules??[]).filter((t=>O({ruleObj:t,options:e}))),s=F(E);if(!i.some((i=>{try{s({ruleObj:i,formDataTree:t,options:e})}catch(t){if(!(t instanceof o))throw t;return!1}return!0})))throw new o(this)};var L;window.swv={validators:e,validate:(t,e,i={})=>{const n=(t.rules??[]).filter((t=>O({ruleObj:t,options:i})));if(!n.length)return new Map;const a=F(E),r=new s(e),p=n.reduce(((t,e)=>{try{a({ruleObj:e,formDataTree:r,options:i})}catch(e){if(!(e instanceof o))throw e;if(void 0!==e.field&&!t.has(e.field)&&void 0!==e.error)return t.set(e.field,e)}return t}),new Map);for(const t of r.keys())p.has(t)||p.set(t,{validInputs:r.getAll(t)});return p},use:t=>{k.push(t)},...null!==(L=window.swv)&&void 0!==L?L:{}}})();;

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.