PageSourceSearch

https://www.lifesafer.com/wp-content/themes/lifesafer-2022/assets/js/remodal.js?ver=1.1.1

js lifesafer.com collected 2026-09-24 20:13:26 UTC 18,038 bytes, 785 lines download raw bytes

vendor: 18,038 bytes, lines 1-785
1/*
2 *  Remodal - v1.1.1
3 *  Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin with declarative configuration and hash tracking.
4 *  http://vodkabears.github.io/remodal/
5 *
6 *  Made by Ilya Makarov
7 *  Under MIT License
8 */
9
10!(function(root, factory) {
11	if (typeof define === 'function' && define.amd) {
12		define(['jquery'], function($) {
13			return factory(root, $);
14		});
15	} else if (typeof exports === 'object') {
16		factory(root, require('jquery'));
17	} else {
18		factory(root, root.jQuery || root.Zepto);
19	}
20})(this, function(global, $) {
21
22	'use strict';
23
24	/**
25	 * Name of the plugin
26	 * @private
27	 * @const
28	 * @type {String}
29	 */
30	var PLUGIN_NAME = 'remodal';
31
32	/**
33	 * Namespace for CSS and events
34	 * @private
35	 * @const
36	 * @type {String}
37	 */
38	var NAMESPACE = global.REMODAL_GLOBALS && global.REMODAL_GLOBALS.NAMESPACE || PLUGIN_NAME;
39
40	/**
41	 * Animationstart event with vendor prefixes
42	 * @private
43	 * @const
44	 * @type {String}
45	 */
46	var ANIMATIONSTART_EVENTS = $.map(
47		['animationstart', 'webkitAnimationStart', 'MSAnimationStart', 'oAnimationStart'],
48
49		function(eventName) {
50			return eventName + '.' + NAMESPACE;
51		}
52
53	).join(' ');
54
55	/**
56	 * Animationend event with vendor prefixes
57	 * @private
58	 * @const
59	 * @type {String}
60	 */
61	var ANIMATIONEND_EVENTS = $.map(
62		['animationend', 'webkitAnimationEnd', 'MSAnimationEnd', 'oAnimationEnd'],
63
64		function(eventName) {
65			return eventName + '.' + NAMESPACE;
66		}
67
68	).join(' ');
69
70	/**
71	 * Default settings
72	 * @private
73	 * @const
74	 * @type {Object}
75	 */
76	var DEFAULTS = $.extend({
77		hashTracking: true,
78		closeOnConfirm: true,
79		closeOnCancel: true,
80		closeOnEscape: true,
81		closeOnOutsideClick: true,
82		modifier: '',
83		appendTo: null
84	}, global.REMODAL_GLOBALS && global.REMODAL_GLOBALS.DEFAULTS);
85
86	/**
87	 * States of the Remodal
88	 * @private
89	 * @const
90	 * @enum {String}
91	 */
92	var STATES = {
93		CLOSING: 'closing',
94		CLOSED: 'closed',
95		OPENING: 'opening',
96		OPENED: 'opened'
97	};
98
99	/**
100	 * Reasons of the state change.
101	 * @private
102	 * @const
103	 * @enum {String}
104	 */
105	var STATE_CHANGE_REASONS = {
106		CONFIRMATION: 'confirmation',
107		CANCELLATION: 'cancellation'
108	};
109
110	/**
111	 * Is animation supported?
112	 * @private
113	 * @const
114	 * @type {Boolean}
115	 */
116	var IS_ANIMATION = (function() {
117		var style = document.createElement('div').style;
118
119		return style.animationName !== undefined ||
120			style.WebkitAnimationName !== undefined ||
121			style.MozAnimationName !== undefined ||
122			style.msAnimationName !== undefined ||
123			style.OAnimationName !== undefined;
124	})();
125
126	/**
127	 * Is iOS?
128	 * @private
129	 * @const
130	 * @type {Boolean}
131	 */
132	var IS_IOS = /iPad|iPhone|iPod/.test(navigator.platform);
133
134	/**
135	 * Current modal
136	 * @private
137	 * @type {Remodal}
138	 */
139	var current;
140
141	/**
142	 * Scrollbar position
143	 * @private
144	 * @type {Number}
145	 */
146	var scrollTop;
147
148	/**
149	 * Returns an animation duration
150	 * @private
151	 * @param {jQuery} $elem
152	 * @returns {Number}
153	 */
154	function getAnimationDuration($elem) {
155		if (
156			IS_ANIMATION &&
157			$elem.css('animation-name') === 'none' &&
158			$elem.css('-webkit-animation-name') === 'none' &&
159			$elem.css('-moz-animation-name') === 'none' &&
160			$elem.css('-o-animation-name') === 'none' &&
161			$elem.css('-ms-animation-name') === 'none'
162		) {
163			return 0;
164		}
165
166		var duration = $elem.css('animation-duration') ||
167			$elem.css('-webkit-animation-duration') ||
168			$elem.css('-moz-animation-duration') ||
169			$elem.css('-o-animation-duration') ||
170			$elem.css('-ms-animation-duration') ||
171			'0s';
172
173		var delay = $elem.css('animation-delay') ||
174			$elem.css('-webkit-animation-delay') ||
175			$elem.css('-moz-animation-delay') ||
176			$elem.css('-o-animation-delay') ||
177			$elem.css('-ms-animation-delay') ||
178			'0s';
179
180		var iterationCount = $elem.css('animation-iteration-count') ||
181			$elem.css('-webkit-animation-iteration-count') ||
182			$elem.css('-moz-animation-iteration-count') ||
183			$elem.css('-o-animation-iteration-count') ||
184			$elem.css('-ms-animation-iteration-count') ||
185			'1';
186
187		var max;
188		var len;
189		var num;
190		var i;
191
192		duration = duration.split(', ');
193		delay = delay.split(', ');
194		iterationCount = iterationCount.split(', ');
195
196		// The 'duration' size is the same as the 'delay' size
197		for (i = 0, len = duration.length, max = Number.NEGATIVE_INFINITY; i < len; i++) {
198			num = parseFloat(duration[i]) * parseInt(iterationCount[i], 10) + parseFloat(delay[i]);
199
200			if (num > max) {
201				max = num;
202			}
203		}
204
205		return max;
206	}
207
208	/**
209	 * Returns a scrollbar width
210	 * @private
211	 * @returns {Number}
212	 */
213	function getScrollbarWidth() {
214		if ($(document).height() <= $(window).height()) {
215			return 0;
216		}
217
218		var outer = document.createElement('div');
219		var inner = document.createElement('div');
220		var widthNoScroll;
221		var widthWithScroll;
222
223		outer.style.visibility = 'hidden';
224		outer.style.width = '100px';
225		document.body.appendChild(outer);
226
227		widthNoScroll = outer.offsetWidth;
228
229		// Force scrollbars
230		outer.style.overflow = 'scroll';
231
232		// Add inner div
233		inner.style.width = '100%';
234		outer.appendChild(inner);
235
236		widthWithScroll = inner.offsetWidth;
237
238		// Remove divs
239		outer.parentNode.removeChild(outer);
240
241		return widthNoScroll - widthWithScroll;
242	}
243
244	/**
245	 * Locks the screen
246	 * @private
247	 */
248	function lockScreen() {
249		if (IS_IOS) {
250			return;
251		}
252
253		var $html = $('html');
254		var lockedClass = namespacify('is-locked');
255		var paddingRight;
256		var $body;
257
258		if (!$html.hasClass(lockedClass)) {
259			$body = $(document.body);
260
261			// Zepto does not support '-=', '+=' in the `css` method
262			paddingRight = parseInt($body.css('padding-right'), 10) + getScrollbarWidth();
263
264			$body.css('padding-right', paddingRight + 'px');
265			$html.addClass(lockedClass);
266		}
267	}
268
269	/**
270	 * Unlocks the screen
271	 * @private
272	 */
273	function unlockScreen() {
274		if (IS_IOS) {
275			return;
276		}
277
278		var $html = $('html');
279		var lockedClass = namespacify('is-locked');
280		var paddingRight;
281		var $body;
282
283		if ($html.hasClass(lockedClass)) {
284			$body = $(document.body);
285
286			// Zepto does not support '-=', '+=' in the `css` method
287			paddingRight = parseInt($body.css('padding-right'), 10) - getScrollbarWidth();
288
289			$body.css('padding-right', paddingRight + 'px');
290			$html.removeClass(lockedClass);
291		}
292	}
293
294	/**
295	 * Sets a state for an instance
296	 * @private
297	 * @param {Remodal} instance
298	 * @param {STATES} state
299	 * @param {Boolean} isSilent If true, Remodal does not trigger events
300	 * @param {String} Reason of a state change.
301	 */
302	function setState(instance, state, isSilent, reason) {
303
304		var newState = namespacify('is', state);
305		var allStates = [namespacify('is', STATES.CLOSING),
306			namespacify('is', STATES.OPENING),
307			namespacify('is', STATES.CLOSED),
308			namespacify('is', STATES.OPENED)].join(' ');
309
310		instance.$bg
311			.removeClass(allStates)
312			.addClass(newState);
313
314		instance.$overlay
315			.removeClass(allStates)
316			.addClass(newState);
317
318		instance.$wrapper
319			.removeClass(allStates)
320			.addClass(newState);
321
322		instance.$modal
323			.removeClass(allStates)
324			.addClass(newState);
325
326		instance.state = state;
327		!isSilent && instance.$modal.trigger({
328			type: state,
329			reason: reason
330		}, [{ reason: reason }]);
331	}
332
333	/**
334	 * Synchronizes with the animation
335	 * @param {Function} doBeforeAnimation
336	 * @param {Function} doAfterAnimation
337	 * @param {Remodal} instance
338	 */
339	function syncWithAnimation(doBeforeAnimation, doAfterAnimation, instance) {
340		var runningAnimationsCount = 0;
341
342		var handleAnimationStart = function(e) {
343			if (e.target !== this) {
344				return;
345			}
346
347			runningAnimationsCount++;
348		};
349
350		var handleAnimationEnd = function(e) {
351			if (e.target !== this) {
352				return;
353			}
354
355			if (--runningAnimationsCount === 0) {
356
357				// Remove event listeners
358				$.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) {
359					instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS);
360				});
361
362				doAfterAnimation();
363			}
364		};
365
366		$.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) {
367			instance[elemName]
368				.on(ANIMATIONSTART_EVENTS, handleAnimationStart)
369				.on(ANIMATIONEND_EVENTS, handleAnimationEnd);
370		});
371
372		doBeforeAnimation();
373
374		// If the animation is not supported by a browser or its duration is 0
375		if (
376			getAnimationDuration(instance.$bg) === 0 &&
377			getAnimationDuration(instance.$overlay) === 0 &&
378			getAnimationDuration(instance.$wrapper) === 0 &&
379			getAnimationDuration(instance.$modal) === 0
380		) {
381
382			// Remove event listeners
383			$.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) {
384				instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS);
385			});
386
387			doAfterAnimation();
388		}
389	}
390
391	/**
392	 * Closes immediately
393	 * @private
394	 * @param {Remodal} instance
395	 */
396	function halt(instance) {
397		if (instance.state === STATES.CLOSED) {
398			return;
399		}
400
401		$.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) {
402			instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS);
403		});
404
405		instance.$bg.removeClass(instance.settings.modifier);
406		instance.$overlay.removeClass(instance.settings.modifier).hide();
407		instance.$wrapper.hide();
408		unlockScreen();
409		setState(instance, STATES.CLOSED, true);
410	}
411
412	/**
413	 * Parses a string with options
414	 * @private
415	 * @param str
416	 * @returns {Object}
417	 */
418	function parseOptions(str) {
419		var obj = {};
420		var arr;
421		var len;
422		var val;
423		var i;
424
425		// Remove spaces before and after delimiters
426		str = str.replace(/\s*:\s*/g, ':').replace(/\s*,\s*/g, ',');
427
428		// Parse a string
429		arr = str.split(',');
430		for (i = 0, len = arr.length; i < len; i++) {
431			arr[i] = arr[i].split(':');
432			val = arr[i][1];
433
434			// Convert a string value if it is like a boolean
435			if (typeof val === 'string' || val instanceof String) {
436				val = val === 'true' || (val === 'false' ? false : val);
437			}
438
439			// Convert a string value if it is like a number
440			if (typeof val === 'string' || val instanceof String) {
441				val = !isNaN(val) ? +val : val;
442			}
443
444			obj[arr[i][0]] = val;
445		}
446
447		return obj;
448	}
449
450	/**
451	 * Generates a string separated by dashes and prefixed with NAMESPACE
452	 * @private
453	 * @param {...String}
454	 * @returns {String}
455	 */
456	function namespacify() {
457		var result = NAMESPACE;
458
459		for (var i = 0; i < arguments.length; ++i) {
460			result += '-' + arguments[i];
461		}
462
463		return result;
464	}
465
466	/**
467	 * Handles the hashchange event
468	 * @private
469	 * @listens hashchange
470	 */
471	function handleHashChangeEvent() {
472		var id = location.hash.replace('#', '');
473		var instance;
474		var $elem;
475
476		if (!id) {
477
478			// Check if we have currently opened modal and animation was completed
479			if (current && current.state === STATES.OPENED && current.settings.hashTracking) {
480				current.close();
481			}
482		} else {
483
484			// Catch syntax error if your hash is bad
485			try {
486				$elem = $(
487					'[data-' + PLUGIN_NAME + '-id="' + id + '"]'
488				);
489			} catch (err) {}
490
491			if ($elem && $elem.length) {
492				instance = $[PLUGIN_NAME].lookup[$elem.data(PLUGIN_NAME)];
493
494				if (instance && instance.settings.hashTracking) {
495					instance.open();
496				}
497			}
498
499		}
500	}
501
502	/**
503	 * Remodal constructor
504	 * @constructor
505	 * @param {jQuery} $modal
506	 * @param {Object} options
507	 */
508	function Remodal($modal, options) {
509		var $body = $(document.body);
510		var $appendTo = $body;
511		var remodal = this;
512
513		remodal.settings = $.extend({}, DEFAULTS, options);
514		remodal.index = $[PLUGIN_NAME].lookup.push(remodal) - 1;
515		remodal.state = STATES.CLOSED;
516
517		remodal.$overlay = $('.' + namespacify('overlay'));
518
519		if (remodal.settings.appendTo !== null && remodal.settings.appendTo.length) {
520			$appendTo = $(remodal.settings.appendTo);
521		}
522
523		if (!remodal.$overlay.length) {
524			remodal.$overlay = $('<div>').addClass(namespacify('overlay') + ' ' + namespacify('is', STATES.CLOSED)).hide();
525			$appendTo.append(remodal.$overlay);
526		}
527
528		remodal.$bg = $('.' + namespacify('bg')).addClass(namespacify('is', STATES.CLOSED));
529
530		remodal.$modal = $modal
531			.addClass(
532				NAMESPACE + ' ' +
533				namespacify('is-initialized') + ' ' +
534				remodal.settings.modifier + ' ' +
535				namespacify('is', STATES.CLOSED))
536			.attr('tabindex', '-1');
537
538		remodal.$wrapper = $('<div>')
539			.addClass(
540				namespacify('wrapper') + ' ' +
541				remodal.settings.modifier + ' ' +
542				namespacify('is', STATES.CLOSED))
543			.hide()
544			.append(remodal.$modal);
545		$appendTo.append(remodal.$wrapper);
546
547		// Add the event listener for the close button
548		remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + PLUGIN_NAME + '-action="close"]', function(e) {
549			e.preventDefault();
550
551			remodal.close();
552		});
553
554		// Add the event listener for the cancel button
555		remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + PLUGIN_NAME + '-action="cancel"]', function(e) {
556			e.preventDefault();
557
558			remodal.$modal.trigger(STATE_CHANGE_REASONS.CANCELLATION);
559
560			if (remodal.settings.closeOnCancel) {
561				remodal.close(STATE_CHANGE_REASONS.CANCELLATION);
562			}
563		});
564
565		// Add the event listener for the confirm button
566		remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + PLUGIN_NAME + '-action="confirm"]', function(e) {
567			e.preventDefault();
568
569			remodal.$modal.trigger(STATE_CHANGE_REASONS.CONFIRMATION);
570
571			if (remodal.settings.closeOnConfirm) {
572				remodal.close(STATE_CHANGE_REASONS.CONFIRMATION);
573			}
574		});
575
576		// Add the event listener for the overlay
577		remodal.$wrapper.on('click.' + NAMESPACE, function(e) {
578			var $target = $(e.target);
579
580			if (!$target.hasClass(namespacify('wrapper'))) {
581				return;
582			}
583
584			if (remodal.settings.closeOnOutsideClick) {
585				remodal.close();
586			}
587		});
588	}
589
590	/**
591	 * Opens a modal window
592	 * @public
593	 */
594	Remodal.prototype.open = function() {
595		var remodal = this;
596		var id;
597
598		// Check if the animation was completed
599		if (remodal.state === STATES.OPENING || remodal.state === STATES.CLOSING) {
600			return;
601		}
602
603		id = remodal.$modal.attr('data-' + PLUGIN_NAME + '-id');
604
605		if (id && remodal.settings.hashTracking) {
606			scrollTop = $(window).scrollTop();
607			location.hash = id;
608		}
609
610		if (current && current !== remodal) {
611			halt(current);
612		}
613
614		current = remodal;
615		lockScreen();
616		remodal.$bg.addClass(remodal.settings.modifier);
617		remodal.$overlay.addClass(remodal.settings.modifier).show();
618		remodal.$wrapper.show().scrollTop(0);
619		remodal.$modal.focus();
620
621		syncWithAnimation(
622			function() {
623				setState(remodal, STATES.OPENING);
624			},
625
626			function() {
627				setState(remodal, STATES.OPENED);
628			},
629
630			remodal);
631	};
632
633	/**
634	 * Closes a modal window
635	 * @public
636	 * @param {String} reason
637	 */
638	Remodal.prototype.close = function(reason) {
639		var remodal = this;
640
641		// Check if the animation was completed
642		if (remodal.state === STATES.OPENING || remodal.state === STATES.CLOSING || remodal.state === STATES.CLOSED) {
643			return;
644		}
645
646		if (
647			remodal.settings.hashTracking &&
648			remodal.$modal.attr('data-' + PLUGIN_NAME + '-id') === location.hash.substr(1)
649		) {
650			location.hash = '';
651			$(window).scrollTop(scrollTop);
652		}
653
654		syncWithAnimation(
655			function() {
656				setState(remodal, STATES.CLOSING, false, reason);
657			},
658
659			function() {
660				remodal.$bg.removeClass(remodal.settings.modifier);
661				remodal.$overlay.removeClass(remodal.settings.modifier).hide();
662				remodal.$wrapper.hide();
663				unlockScreen();
664
665				setState(remodal, STATES.CLOSED, false, reason);
666			},
667
668			remodal);
669	};
670
671	/**
672	 * Returns a current state of a modal
673	 * @public
674	 * @returns {STATES}
675	 */
676	Remodal.prototype.getState = function() {
677		return this.state;
678	};
679
680	/**
681	 * Destroys a modal
682	 * @public
683	 */
684	Remodal.prototype.destroy = function() {
685		var lookup = $[PLUGIN_NAME].lookup;
686		var instanceCount;
687
688		halt(this);
689		this.$wrapper.remove();
690
691		delete lookup[this.index];
692		instanceCount = $.grep(lookup, function(instance) {
693			return !!instance;
694		}).length;
695
696		if (instanceCount === 0) {
697			this.$overlay.remove();
698			this.$bg.removeClass(
699				namespacify('is', STATES.CLOSING) + ' ' +
700				namespacify('is', STATES.OPENING) + ' ' +
701				namespacify('is', STATES.CLOSED) + ' ' +
702				namespacify('is', STATES.OPENED));
703		}
704	};
705
706	/**
707	 * Special plugin object for instances
708	 * @public
709	 * @type {Object}
710	 */
711	$[PLUGIN_NAME] = {
712		lookup: []
713	};
714
715	/**
716	 * Plugin constructor
717	 * @constructor
718	 * @param {Object} options
719	 * @returns {JQuery}
720	 */
721	$.fn[PLUGIN_NAME] = function(opts) {
722		var instance;
723		var $elem;
724
725		this.each(function(index, elem) {
726			$elem = $(elem);
727
728			if ($elem.data(PLUGIN_NAME) == null) {
729				instance = new Remodal($elem, opts);
730				$elem.data(PLUGIN_NAME, instance.index);
731
732				if (
733					instance.settings.hashTracking &&
734					$elem.attr('data-' + PLUGIN_NAME + '-id') === location.hash.substr(1)
735				) {
736					instance.open();
737				}
738			} else {
739				instance = $[PLUGIN_NAME].lookup[$elem.data(PLUGIN_NAME)];
740			}
741		});
742
743		return instance;
744	};
745
746	$(document).ready(function() {
747
748		// data-remodal-target opens a modal window with the special Id
749		$(document).on('click', '[data-' + PLUGIN_NAME + '-target]', function(e) {
750			e.preventDefault();
751
752			var elem = e.currentTarget;
753			var id = elem.getAttribute('data-' + PLUGIN_NAME + '-target');
754			var $target = $('[data-' + PLUGIN_NAME + '-id="' + id + '"]');
755
756			$[PLUGIN_NAME].lookup[$target.data(PLUGIN_NAME)].open();
757		});
758
759		// Auto initialization of modal windows
760		// They should have the 'remodal' class attribute
761		// Also you can write the `data-remodal-options` attribute to pass params into the modal
762		$(document).find('.' + NAMESPACE).each(function(i, container) {
763			var $container = $(container);
764			var options = $container.data(PLUGIN_NAME + '-options');
765
766			if (!options) {
767				options = {};
768			} else if (typeof options === 'string' || options instanceof String) {
769				options = parseOptions(options);
770			}
771
772			$container[PLUGIN_NAME](options);
773		});
774
775		// Handles the keydown event
776		$(document).on('keydown.' + NAMESPACE, function(e) {
777			if (current && current.settings.closeOnEscape && current.state === STATES.OPENED && e.keyCode === 27) {
778				current.close();
779			}
780		});
781
782		// Handles the hashchange event
783		$(window).on('hashchange.' + NAMESPACE, handleHashChangeEvent);
784	});
785});

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.