jumbo-jekyll-theme 2.5.0 → 3.0.0

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