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