PageSourceSearch

https://iphotochannel.com.br/wp-content/plugins/perfect-survey/res…/assets/js/featherlight.js?ver=1.7.9

js iphotochannel.com.br collected 2026-10-02 07:18:38 UTC 22,973 bytes, 628 lines download raw bytes

1/**
2 * Featherlight - ultra slim jQuery lightbox
3 * Version 1.7.9 - http://noelboss.github.io/featherlight/
4 *
5 * Copyright 2017, Noël Raoul Bossart (http://www.noelboss.com)
6 * MIT Licensed.
7**/
8(function($) {
9	"use strict";
10
11	if('undefined' === typeof $) {
12		if('console' in window){ window.console.info('Too much lightness, Featherlight needs jQuery.'); }
13		return;
14	}
15
16	/* Featherlight is exported as $.featherlight.
17	   It is a function used to open a featherlight lightbox.
18
19	   [tech]
20	   Featherlight uses prototype inheritance.
21	   Each opened lightbox will have a corresponding object.
22	   That object may have some attributes that override the
23	   prototype's.
24	   Extensions created with Featherlight.extend will have their
25	   own prototype that inherits from Featherlight's prototype,
26	   thus attributes can be overriden either at the object level,
27	   or at the extension level.
28	   To create callbacks that chain themselves instead of overriding,
29	   use chainCallbacks.
30	   For those familiar with CoffeeScript, this correspond to
31	   Featherlight being a class and the Gallery being a class
32	   extending Featherlight.
33	   The chainCallbacks is used since we don't have access to
34	   CoffeeScript's `super`.
35	*/
36
37	function Featherlight($content, config) {
38		if(this instanceof Featherlight) {  /* called with new */
39			this.id = Featherlight.id++;
40			this.setup($content, config);
41			this.chainCallbacks(Featherlight._callbackChain);
42		} else {
43			var fl = new Featherlight($content, config);
44			fl.open();
45			return fl;
46		}
47	}
48
49	var opened = [],
50		pruneOpened = function(remove) {
51			opened = $.grep(opened, function(fl) {
52				return fl !== remove && fl.$instance.closest('body').length > 0;
53			} );
54			return opened;
55		};
56
57	// Removes keys of `set` from `obj` and returns the removed key/values.
58	function slice(obj, set) {
59		var r = {};
60		for (var key in obj) {
61			if (key in set) {
62				r[key] = obj[key];
63				delete obj[key];
64			}
65		}
66		return r;
67	}
68
69	// NOTE: List of available [iframe attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe).
70	var iFrameAttributeSet = {
71		allowfullscreen: 1, frameborder: 1, height: 1, longdesc: 1, marginheight: 1, marginwidth: 1,
72		name: 1, referrerpolicy: 1, scrolling: 1, sandbox: 1, src: 1, srcdoc: 1, width: 1
73	};
74
75	// Converts camelCased attributes to dasherized versions for given prefix:
76	//   parseAttrs({hello: 1, hellFrozeOver: 2}, 'hell') => {froze-over: 2}
77	function parseAttrs(obj, prefix) {
78		var attrs = {},
79			regex = new RegExp('^' + prefix + '([A-Z])(.*)');
80		for (var key in obj) {
81			var match = key.match(regex);
82			if (match) {
83				var dasherized = (match[1] + match[2].replace(/([A-Z])/g, '-$1')).toLowerCase();
84				attrs[dasherized] = obj[key];
85			}
86		}
87		return attrs;
88	}
89
90	/* document wide key handler */
91	var eventMap = { keyup: 'onKeyUp', resize: 'onResize' };
92
93	var globalEventHandler = function(event) {
94		$.each(Featherlight.opened().reverse(), function() {
95			if (!event.isDefaultPrevented()) {
96				if (false === this[eventMap[event.type]](event)) {
97					event.preventDefault(); event.stopPropagation(); return false;
98			  }
99			}
100		});
101	};
102
103	var toggleGlobalEvents = function(set) {
104			if(set !== Featherlight._globalHandlerInstalled) {
105				Featherlight._globalHandlerInstalled = set;
106				var events = $.map(eventMap, function(_, name) { return name+'.'+Featherlight.prototype.namespace; } ).join(' ');
107				$(window)[set ? 'on' : 'off'](events, globalEventHandler);
108			}
109		};
110
111	Featherlight.prototype = {
112		constructor: Featherlight,
113		/*** defaults ***/
114		/* extend featherlight with defaults and methods */
115		namespace:      'featherlight',        /* Name of the events and css class prefix */
116		targetAttr:     'data-featherlight',   /* Attribute of the triggered element that contains the selector to the lightbox content */
117		variant:        null,                  /* Class that will be added to change look of the lightbox */
118		resetCss:       false,                 /* Reset all css */
119		background:     null,                  /* Custom DOM for the background, wrapper and the closebutton */
120		openTrigger:    'click',               /* Event that triggers the lightbox */
121		closeTrigger:   'click',               /* Event that triggers the closing of the lightbox */
122		filter:         null,                  /* Selector to filter events. Think $(...).on('click', filter, eventHandler) */
123		root:           'body',                /* Where to append featherlights */
124		openSpeed:      250,                   /* Duration of opening animation */
125		closeSpeed:     250,                   /* Duration of closing animation */
126		closeOnClick:   'background',          /* Close lightbox on click ('background', 'anywhere' or false) */
127		closeOnEsc:     true,                  /* Close lightbox when pressing esc */
128		closeIcon:      '✕',            /* Close icon */
129		loading:        '',                    /* Content to show while initial content is loading */
130		persist:        false,                 /* If set, the content will persist and will be shown again when opened again. 'shared' is a special value when binding multiple elements for them to share the same content */
131		otherClose:     null,                  /* Selector for alternate close buttons (e.g. "a.close") */
132		beforeOpen:     $.noop,                /* Called before open. can return false to prevent opening of lightbox. Gets event as parameter, this contains all data */
133		beforeContent:  $.noop,                /* Called when content is loaded. Gets event as parameter, this contains all data */
134		beforeClose:    $.noop,                /* Called before close. can return false to prevent opening of lightbox. Gets event as parameter, this contains all data */
135		afterOpen:      $.noop,                /* Called after open. Gets event as parameter, this contains all data */
136		afterContent:   $.noop,                /* Called after content is ready and has been set. Gets event as parameter, this contains all data */
137		afterClose:     $.noop,                /* Called after close. Gets event as parameter, this contains all data */
138		onKeyUp:        $.noop,                /* Called on key up for the frontmost featherlight */
139		onResize:       $.noop,                /* Called after new content and when a window is resized */
140		type:           null,                  /* Specify type of lightbox. If unset, it will check for the targetAttrs value. */
141		contentFilters: ['jquery', 'image', 'html', 'ajax', 'iframe', 'text'], /* List of content filters to use to determine the content */
142
143		/*** methods ***/
144		/* setup iterates over a single instance of featherlight and prepares the background and binds the events */
145		setup: function(target, config){
146			/* all arguments are optional */
147			if (typeof target === 'object' && target instanceof $ === false && !config) {
148				config = target;
149				target = undefined;
150			}
151
152			var self = $.extend(this, config, {target: target}),
153				css = !self.resetCss ? self.namespace : self.namespace+'-reset', /* by adding -reset to the classname, we reset all the default css */
154				$background = $(self.background || [
155					'<div class="'+css+'-loading '+css+'">',
156						'<div class="'+css+'-content">',
157							'<button class="'+css+'-close-icon '+ self.namespace + '-close" aria-label="Close">',
158								self.closeIcon,
159							'</button>',
160							'<div class="'+self.namespace+'-inner">' + self.loading + '</div>',
161						'</div>',
162					'</div>'].join('')),
163				closeButtonSelector = '.'+self.namespace+'-close' + (self.otherClose ? ',' + self.otherClose : '');
164
165			self.$instance = $background.clone().addClass(self.variant); /* clone DOM for the background, wrapper and the close button */
166
167			/* close when click on background/anywhere/null or closebox */
168			self.$instance.on(self.closeTrigger+'.'+self.namespace, function(event) {
169				var $target = $(event.target);
170				if( ('background' === self.closeOnClick  && $target.is('.'+self.namespace))
171					|| 'anywhere' === self.closeOnClick
172					|| $target.closest(closeButtonSelector).length ){
173					self.close(event);
174					event.preventDefault();
175				}
176			});
177
178			return this;
179		},
180
181		/* this method prepares the content and converts it into a jQuery object or a promise */
182		getContent: function(){
183			if(this.persist !== false && this.$content) {
184				return this.$content;
185			}
186			var self = this,
187				filters = this.constructor.contentFilters,
188				readTargetAttr = function(name){ return self.$currentTarget && self.$currentTarget.attr(name); },
189				targetValue = readTargetAttr(self.targetAttr),
190				data = self.target || targetValue || '';
191
192			/* Find which filter applies */
193			var filter = filters[self.type]; /* check explicit type like {type: 'image'} */
194
195			/* check explicit type like data-featherlight="image" */
196			if(!filter && data in filters) {
197				filter = filters[data];
198				data = self.target && targetValue;
199			}
200			data = data || readTargetAttr('href') || '';
201
202			/* check explicity type & content like {image: 'photo.jpg'} */
203			if(!filter) {
204				for(var filterName in filters) {
205					if(self[filterName]) {
206						filter = filters[filterName];
207						data = self[filterName];
208					}
209				}
210			}
211
212			/* otherwise it's implicit, run checks */
213			if(!filter) {
214				var target = data;
215				data = null;
216				$.each(self.contentFilters, function() {
217					filter = filters[this];
218					if(filter.test)  {
219						data = filter.test(target);
220					}
221					if(!data && filter.regex && target.match && target.match(filter.regex)) {
222						data = target;
223					}
224					return !data;
225				});
226				if(!data) {
227					if('console' in window){ window.console.error('Featherlight: no content filter found ' + (target ? ' for "' + target + '"' : ' (no target specified)')); }
228					return false;
229				}
230			}
231			/* Process it */
232			return filter.process.call(self, data);
233		},
234
235		/* sets the content of $instance to $content */
236		setContent: function($content){
237			var self = this;
238			/* we need a special class for the iframe */
239			if($content.is('iframe')) {
240				self.$instance.addClass(self.namespace+'-iframe');
241			}
242
243			self.$instance.removeClass(self.namespace+'-loading');
244
245			/* replace content by appending to existing one before it is removed
246			   this insures that featherlight-inner remain at the same relative
247				 position to any other items added to featherlight-content */
248			self.$instance.find('.'+self.namespace+'-inner')
249				.not($content)                /* excluded new content, important if persisted */
250				.slice(1).remove().end()      /* In the unexpected event where there are many inner elements, remove all but the first one */
251				.replaceWith($.contains(self.$instance[0], $content[0]) ? '' : $content);
252
253			self.$content = $content.addClass(self.namespace+'-inner');
254
255			return self;
256		},
257
258		/* opens the lightbox. "this" contains $instance with the lightbox, and with the config.
259			Returns a promise that is resolved after is successfully opened. */
260		open: function(event){
261			var self = this;
262			self.$instance.hide().appendTo(self.root);
263			if((!event || !event.isDefaultPrevented())
264				&& self.beforeOpen(event) !== false) {
265
266				if(event){
267					event.preventDefault();
268				}
269				var $content = self.getContent();
270
271				if($content) {
272					opened.push(self);
273
274					toggleGlobalEvents(true);
275
276					self.$instance.fadeIn(self.openSpeed);
277					self.beforeContent(event);
278
279					/* Set content and show */
280					return $.when($content)
281						.always(function($content){
282							self.setContent($content);
283							self.afterContent(event);
284						})
285						.then(self.$instance.promise())
286						/* Call afterOpen after fadeIn is done */
287						.done(function(){ self.afterOpen(event); });
288				}
289			}
290			self.$instance.detach();
291			return $.Deferred().reject().promise();
292		},
293
294		/* closes the lightbox. "this" contains $instance with the lightbox, and with the config
295			returns a promise, resolved after the lightbox is successfully closed. */
296		close: function(event){
297			var self = this,
298				deferred = $.Deferred();
299
300			if(self.beforeClose(event) === false) {
301				deferred.reject();
302			} else {
303
304				if (0 === pruneOpened(self).length) {
305					toggleGlobalEvents(false);
306				}
307
308				self.$instance.fadeOut(self.closeSpeed,function(){
309					self.$instance.detach();
310					self.afterClose(event);
311					deferred.resolve();
312				});
313			}
314			return deferred.promise();
315		},
316
317		/* resizes the content so it fits in visible area and keeps the same aspect ratio.
318				Does nothing if either the width or the height is not specified.
319				Called automatically on window resize.
320				Override if you want different behavior. */
321		resize: function(w, h) {
322			if (w && h) {
323				/* Reset apparent image size first so container grows */
324				this.$content.css('width', '').css('height', '');
325				/* Calculate the worst ratio so that dimensions fit */
326				 /* Note: -1 to avoid rounding errors */
327				var ratio = Math.max(
328					w  / (this.$content.parent().width()-1),
329					h / (this.$content.parent().height()-1));
330				/* Resize content */
331				if (ratio > 1) {
332					ratio = h / Math.floor(h / ratio); /* Round ratio down so height calc works */
333					this.$content.css('width', '' + w / ratio + 'px').css('height', '' + h / ratio + 'px');
334				}
335			}
336		},
337
338		/* Utility function to chain callbacks
339		   [Warning: guru-level]
340		   Used be extensions that want to let users specify callbacks but
341		   also need themselves to use the callbacks.
342		   The argument 'chain' has callback names as keys and function(super, event)
343		   as values. That function is meant to call `super` at some point.
344		*/
345		chainCallbacks: function(chain) {
346			for (var name in chain) {
347				this[name] = $.proxy(chain[name], this, $.proxy(this[name], this));
348			}
349		}
350	};
351
352	$.extend(Featherlight, {
353		id: 0,                                    /* Used to id single featherlight instances */
354		autoBind:       '[data-featherlight]',    /* Will automatically bind elements matching this selector. Clear or set before onReady */
355		defaults:       Featherlight.prototype,   /* You can access and override all defaults using $.featherlight.defaults, which is just a synonym for $.featherlight.prototype */
356		/* Contains the logic to determine content */
357		contentFilters: {
358			jquery: {
359				regex: /^[#.]\w/,         /* Anything that starts with a class name or identifiers */
360				test: function(elem)    { return elem instanceof $ && elem; },
361				process: function(elem) { return this.persist !== false ? $(elem) : $(elem).clone(true); }
362			},
363			image: {
364				regex: /\.(png|jpg|jpeg|gif|tiff|bmp|svg)(\?\S*)?$/i,
365				process: function(url)  {
366					var self = this,
367						deferred = $.Deferred(),
368						img = new Image(),
369						$img = $('<img src="'+url+'" alt="" class="'+self.namespace+'-image" />');
370					img.onload  = function() {
371						/* Store naturalWidth & height for IE8 */
372						$img.naturalWidth = img.width; $img.naturalHeight = img.height;
373						deferred.resolve( $img );
374					};
375					img.onerror = function() { deferred.reject($img); };
376					img.src = url;
377					return deferred.promise();
378				}
379			},
380			html: {
381				regex: /^\s*<[\w!][^<]*>/, /* Anything that starts with some kind of valid tag */
382				process: function(html) { return $(html); }
383			},
384			ajax: {
385				regex: /./,            /* At this point, any content is assumed to be an URL */
386				process: function(url)  {
387					var self = this,
388						deferred = $.Deferred();
389					/* we are using load so one can specify a target with: url.html #targetelement */
390					var $container = $('<div></div>').load(url, function(response, status){
391						if ( status !== "error" ) {
392							deferred.resolve($container.contents());
393						}
394						deferred.fail();
395					});
396					return deferred.promise();
397				}
398			},
399			iframe: {
400				process: function(url) {
401					var deferred = new $.Deferred();
402					var $content = $('<iframe/>');
403					var css = parseAttrs(this, 'iframe');
404					var attrs = slice(css, iFrameAttributeSet);
405					$content.hide()
406						.attr('src', url)
407						.attr(attrs)
408						.css(css)
409						.on('load', function() { deferred.resolve($content.show()); })
410						// We can't move an <iframe> and avoid reloading it,
411						// so let's put it in place ourselves right now:
412						.appendTo(this.$instance.find('.' + this.namespace + '-content'));
413					return deferred.promise();
414				}
415			},
416			text: {
417				process: function(text) { return $('<div>', {text: text}); }
418			}
419		},
420
421		functionAttributes: ['beforeOpen', 'afterOpen', 'beforeContent', 'afterContent', 'beforeClose', 'afterClose'],
422
423		/*** class methods ***/
424		/* read element's attributes starting with data-featherlight- */
425		readElementConfig: function(element, namespace) {
426			var Klass = this,
427				regexp = new RegExp('^data-' + namespace + '-(.*)'),
428				config = {};
429			if (element && element.attributes) {
430				$.each(element.attributes, function(){
431					var match = this.name.match(regexp);
432					if (match) {
433						var val = this.value,
434							name = $.camelCase(match[1]);
435						if ($.inArray(name, Klass.functionAttributes) >= 0) {  /* jshint -W054 */
436							val = new Function(val);                           /* jshint +W054 */
437						} else {
438							try { val = JSON.parse(val); }
439							catch(e) {}
440						}
441						config[name] = val;
442					}
443				});
444			}
445			return config;
446		},
447
448		/* Used to create a Featherlight extension
449		   [Warning: guru-level]
450		   Creates the extension's prototype that in turn
451		   inherits Featherlight's prototype.
452		   Could be used to extend an extension too...
453		   This is pretty high level wizardy, it comes pretty much straight
454		   from CoffeeScript and won't teach you anything about Featherlight
455		   as it's not really specific to this library.
456		   My suggestion: move along and keep your sanity.
457		*/
458		extend: function(child, defaults) {
459			/* Setup class hierarchy, adapted from CoffeeScript */
460			var Ctor = function(){ this.constructor = child; };
461			Ctor.prototype = this.prototype;
462			child.prototype = new Ctor();
463			child.__super__ = this.prototype;
464			/* Copy class methods & attributes */
465			$.extend(child, this, defaults);
466			child.defaults = child.prototype;
467			return child;
468		},
469
470		attach: function($source, $content, config) {
471			var Klass = this;
472			if (typeof $content === 'object' && $content instanceof $ === false && !config) {
473				config = $content;
474				$content = undefined;
475			}
476			/* make a copy */
477			config = $.extend({}, config);
478
479			/* Only for openTrigger and namespace... */
480			var namespace = config.namespace || Klass.defaults.namespace,
481				tempConfig = $.extend({}, Klass.defaults, Klass.readElementConfig($source[0], namespace), config),
482				sharedPersist;
483			var handler = function(event) {
484				var $target = $(event.currentTarget);
485				/* ... since we might as well compute the config on the actual target */
486				var elemConfig = $.extend(
487					{$source: $source, $currentTarget: $target},
488					Klass.readElementConfig($source[0], tempConfig.namespace),
489					Klass.readElementConfig(event.currentTarget, tempConfig.namespace),
490					config);
491				var fl = sharedPersist || $target.data('featherlight-persisted') || new Klass($content, elemConfig);
492				if(fl.persist === 'shared') {
493					sharedPersist = fl;
494				} else if(fl.persist !== false) {
495					$target.data('featherlight-persisted', fl);
496				}
497				if (elemConfig.$currentTarget.blur) {
498					elemConfig.$currentTarget.blur(); // Otherwise 'enter' key might trigger the dialog again
499				}
500				fl.open(event);
501			};
502
503			$source.on(tempConfig.openTrigger+'.'+tempConfig.namespace, tempConfig.filter, handler);
504
505			return handler;
506		},
507
508		current: function() {
509			var all = this.opened();
510			return all[all.length - 1] || null;
511		},
512
513		opened: function() {
514			var klass = this;
515			pruneOpened();
516			return $.grep(opened, function(fl) { return fl instanceof klass; } );
517		},
518
519		close: function(event) {
520			var cur = this.current();
521			if(cur) { return cur.close(event); }
522		},
523
524		/* Does the auto binding on startup.
525		   Meant only to be used by Featherlight and its extensions
526		*/
527		_onReady: function() {
528			var Klass = this;
529			if(Klass.autoBind){
530				/* Bind existing elements */
531				$(Klass.autoBind).each(function(){
532					Klass.attach($(this));
533				});
534				/* If a click propagates to the document level, then we have an item that was added later on */
535				$(document).on('click', Klass.autoBind, function(evt) {
536					if (evt.isDefaultPrevented()) {
537						return;
538					}
539					/* Bind featherlight */
540					var handler = Klass.attach($(evt.currentTarget));
541					/* Dispatch event directly */
542					handler(evt);
543				});
544			}
545		},
546
547		/* Featherlight uses the onKeyUp callback to intercept the escape key.
548		   Private to Featherlight.
549		*/
550		_callbackChain: {
551			onKeyUp: function(_super, event){
552				if(27 === event.keyCode) {
553					if (this.closeOnEsc) {
554						$.featherlight.close(event);
555					}
556					return false;
557				} else {
558					return _super(event);
559				}
560			},
561
562			beforeOpen: function(_super, event) {
563				// Used to disable scrolling
564				$(document.documentElement).addClass('with-featherlight');
565
566				// Remember focus:
567				this._previouslyActive = document.activeElement;
568
569				// Disable tabbing:
570				// See http://stackoverflow.com/questions/1599660/which-html-elements-can-receive-focus
571				this._$previouslyTabbable = $("a, input, select, textarea, iframe, button, iframe, [contentEditable=true]")
572					.not('[tabindex]')
573					.not(this.$instance.find('button'));
574
575				this._$previouslyWithTabIndex = $('[tabindex]').not('[tabindex="-1"]');
576				this._previousWithTabIndices = this._$previouslyWithTabIndex.map(function(_i, elem) {
577					return $(elem).attr('tabindex');
578				});
579
580				this._$previouslyWithTabIndex.add(this._$previouslyTabbable).attr('tabindex', -1);
581
582				if (document.activeElement.blur) {
583					document.activeElement.blur();
584				}
585				return _super(event);
586			},
587
588			afterClose: function(_super, event) {
589				var r = _super(event);
590				// Restore focus
591				var self = this;
592				this._$previouslyTabbable.removeAttr('tabindex');
593				this._$previouslyWithTabIndex.each(function(i, elem) {
594					$(elem).attr('tabindex', self._previousWithTabIndices[i]);
595				});
596				this._previouslyActive.focus();
597				// Restore scroll
598				if(Featherlight.opened().length === 0) {
599					$(document.documentElement).removeClass('with-featherlight');
600				}
601				return r;
602			},
603
604			onResize: function(_super, event){
605				this.resize(this.$content.naturalWidth, this.$content.naturalHeight);
606				return _super(event);
607			},
608
609			afterContent: function(_super, event){
610				var r = _super(event);
611				this.$instance.find('[autofocus]:not([disabled])').focus();
612				this.onResize(event);
613				return r;
614			}
615		}
616	});
617
618	$.featherlight = Featherlight;
619
620	/* bind jQuery elements to trigger featherlight */
621	$.fn.featherlight = function($content, config) {
622		Featherlight.attach(this, $content, config);
623		return this;
624	};
625
626	/* bind featherlight on ready if config autoBind is set */
627	$(document).ready(function(){ Featherlight._onReady(); });
628}(jQuery));

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