jquery-justified-gallery-rails 3.6.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2ad74823b9e6d98ca3984adb1ce658a1dcd8a67e
4
+ data.tar.gz: a59b1fbae8d6d5b12825cbb82fe923f4f34864c6
5
+ SHA512:
6
+ metadata.gz: b1ebf90884a4df2cef7463742980bc7fbe486e8eda60a53b5152e9508aa35b061b1c1edc48cd7111fb45a13524e81c7cc2f1a44569e65f3d8230145a74d6ea67
7
+ data.tar.gz: ab923d61faaf69e8c1a180cb982467eb70123c5a2defb5dad0cbacea9372ac30bfe1618851da7b5a6bcc95bfe6bbc7a5b964b6d9d056f161735971a45b61deef
@@ -0,0 +1 @@
1
+ require 'jquery/justified/gallery/rails'
@@ -0,0 +1,11 @@
1
+ require 'jquery/justified/gallery/rails/engine'
2
+ require 'jquery/justified/gallery/rails/version'
3
+
4
+ module Jquery
5
+ module Justified
6
+ module Gallery
7
+ module Rails
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ module Jquery
2
+ module Justified
3
+ module Gallery
4
+ module Rails
5
+ class Engine < ::Rails::Engine
6
+ end
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ module Jquery
2
+ module Justified
3
+ module Gallery
4
+ module Rails
5
+ VERSION = '3.6.1.1'
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,1129 @@
1
+ /*!
2
+ * Justified Gallery - v3.6.1
3
+ * http://miromannino.github.io/Justified-Gallery/
4
+ * Copyright (c) 2015 Miro Mannino
5
+ * Licensed under the MIT license.
6
+ */
7
+ (function($) {
8
+
9
+ /**
10
+ * Justified Gallery controller constructor
11
+ *
12
+ * @param $gallery the gallery to build
13
+ * @param settings the settings (the defaults are in $.fn.justifiedGallery.defaults)
14
+ * @constructor
15
+ */
16
+ var JustifiedGallery = function ($gallery, settings) {
17
+
18
+ this.settings = settings;
19
+ this.checkSettings();
20
+
21
+ this.imgAnalyzerTimeout = null;
22
+ this.entries = null;
23
+ this.buildingRow = {
24
+ entriesBuff : [],
25
+ width : 0,
26
+ height : 0,
27
+ aspectRatio : 0
28
+ };
29
+ this.lastAnalyzedIndex = -1;
30
+ this.yield = {
31
+ every : 2, // do a flush every n flushes (must be greater than 1)
32
+ flushed : 0 // flushed rows without a yield
33
+ };
34
+ this.border = settings.border >= 0 ? settings.border : settings.margins;
35
+ this.maxRowHeight = this.retrieveMaxRowHeight();
36
+ this.suffixRanges = this.retrieveSuffixRanges();
37
+ this.offY = this.border;
38
+ this.spinner = {
39
+ phase : 0,
40
+ timeSlot : 150,
41
+ $el : $('<div class="spinner"><span></span><span></span><span></span></div>'),
42
+ intervalId : null
43
+ };
44
+ this.checkWidthIntervalId = null;
45
+ this.galleryWidth = $gallery.width();
46
+ this.$gallery = $gallery;
47
+
48
+ };
49
+
50
+ /** @returns {String} the best suffix given the width and the height */
51
+ JustifiedGallery.prototype.getSuffix = function (width, height) {
52
+ var longestSide, i;
53
+ longestSide = (width > height) ? width : height;
54
+ for (i = 0; i < this.suffixRanges.length; i++) {
55
+ if (longestSide <= this.suffixRanges[i]) {
56
+ return this.settings.sizeRangeSuffixes[this.suffixRanges[i]];
57
+ }
58
+ }
59
+ return this.settings.sizeRangeSuffixes[this.suffixRanges[i - 1]];
60
+ };
61
+
62
+ /**
63
+ * Remove the suffix from the string
64
+ *
65
+ * @returns {string} a new string without the suffix
66
+ */
67
+ JustifiedGallery.prototype.removeSuffix = function (str, suffix) {
68
+ return str.substring(0, str.length - suffix.length);
69
+ };
70
+
71
+ /**
72
+ * @returns {boolean} a boolean to say if the suffix is contained in the str or not
73
+ */
74
+ JustifiedGallery.prototype.endsWith = function (str, suffix) {
75
+ return str.indexOf(suffix, str.length - suffix.length) !== -1;
76
+ };
77
+
78
+ /**
79
+ * Get the used suffix of a particular url
80
+ *
81
+ * @param str
82
+ * @returns {String} return the used suffix
83
+ */
84
+ JustifiedGallery.prototype.getUsedSuffix = function (str) {
85
+ for (var si in this.settings.sizeRangeSuffixes) {
86
+ if (this.settings.sizeRangeSuffixes.hasOwnProperty(si)) {
87
+ if (this.settings.sizeRangeSuffixes[si].length === 0) continue;
88
+ if (this.endsWith(str, this.settings.sizeRangeSuffixes[si])) return this.settings.sizeRangeSuffixes[si];
89
+ }
90
+ }
91
+ return '';
92
+ };
93
+
94
+ /**
95
+ * Given an image src, with the width and the height, returns the new image src with the
96
+ * best suffix to show the best quality thumbnail.
97
+ *
98
+ * @returns {String} the suffix to use
99
+ */
100
+ JustifiedGallery.prototype.newSrc = function (imageSrc, imgWidth, imgHeight) {
101
+ var newImageSrc;
102
+
103
+ if (this.settings.thumbnailPath) {
104
+ newImageSrc = this.settings.thumbnailPath(imageSrc, imgWidth, imgHeight);
105
+ } else {
106
+ var matchRes = imageSrc.match(this.settings.extension);
107
+ var ext = (matchRes !== null) ? matchRes[0] : '';
108
+ newImageSrc = imageSrc.replace(this.settings.extension, '');
109
+ newImageSrc = this.removeSuffix(newImageSrc, this.getUsedSuffix(newImageSrc));
110
+ newImageSrc += this.getSuffix(imgWidth, imgHeight) + ext;
111
+ }
112
+
113
+ return newImageSrc;
114
+ };
115
+
116
+ /**
117
+ * Shows the images that is in the given entry
118
+ *
119
+ * @param $entry the entry
120
+ * @param callback the callback that is called when the show animation is finished
121
+ */
122
+ JustifiedGallery.prototype.showImg = function ($entry, callback) {
123
+ if (this.settings.cssAnimation) {
124
+ $entry.addClass('entry-visible');
125
+ if (callback) callback();
126
+ } else {
127
+ $entry.stop().fadeTo(this.settings.imagesAnimationDuration, 1.0, callback);
128
+ }
129
+ };
130
+
131
+ /**
132
+ * Extract the image src form the image, looking from the 'safe-src', and if it can't be found, from the
133
+ * 'src' attribute. It saves in the image data the 'jg.originalSrc' field, with the extracted src.
134
+ *
135
+ * @param $image the image to analyze
136
+ * @returns {String} the extracted src
137
+ */
138
+ JustifiedGallery.prototype.extractImgSrcFromImage = function ($image) {
139
+ var imageSrc = (typeof $image.data('safe-src') !== 'undefined') ? $image.data('safe-src') : $image.attr('src');
140
+ $image.data('jg.originalSrc', imageSrc);
141
+ return imageSrc;
142
+ };
143
+
144
+ /** @returns {jQuery} the image in the given entry */
145
+ JustifiedGallery.prototype.imgFromEntry = function ($entry) {
146
+ var $img = $entry.find('> img');
147
+ if ($img.length === 0) $img = $entry.find('> a > img');
148
+ return $img.length === 0 ? null : $img;
149
+ };
150
+
151
+ /** @returns {jQuery} the caption in the given entry */
152
+ JustifiedGallery.prototype.captionFromEntry = function ($entry) {
153
+ var $caption = $entry.find('> .caption');
154
+ return $caption.length === 0 ? null : $caption;
155
+ };
156
+
157
+ /**
158
+ * Display the entry
159
+ *
160
+ * @param {jQuery} $entry the entry to display
161
+ * @param {int} x the x position where the entry must be positioned
162
+ * @param y the y position where the entry must be positioned
163
+ * @param imgWidth the image width
164
+ * @param imgHeight the image height
165
+ * @param rowHeight the row height of the row that owns the entry
166
+ */
167
+ JustifiedGallery.prototype.displayEntry = function ($entry, x, y, imgWidth, imgHeight, rowHeight) {
168
+ $entry.width(imgWidth);
169
+ $entry.height(rowHeight);
170
+ $entry.css('top', y);
171
+ $entry.css('left', x);
172
+
173
+ var $image = this.imgFromEntry($entry);
174
+ if ($image !== null) {
175
+ $image.css('width', imgWidth);
176
+ $image.css('height', imgHeight);
177
+ $image.css('margin-left', - imgWidth / 2);
178
+ $image.css('margin-top', - imgHeight / 2);
179
+
180
+ // Image reloading for an high quality of thumbnails
181
+ var imageSrc = $image.attr('src');
182
+ var newImageSrc = this.newSrc(imageSrc, imgWidth, imgHeight);
183
+
184
+ $image.one('error', function () {
185
+ $image.attr('src', $image.data('jg.originalSrc')); //revert to the original thumbnail, we got it.
186
+ });
187
+
188
+ var loadNewImage = function () {
189
+ if (imageSrc !== newImageSrc) { //load the new image after the fadeIn
190
+ $image.attr('src', newImageSrc);
191
+ }
192
+ };
193
+
194
+ if ($entry.data('jg.loaded') === 'skipped') {
195
+ this.onImageEvent(imageSrc, $.proxy(function() {
196
+ this.showImg($entry, loadNewImage);
197
+ $entry.data('jg.loaded', true);
198
+ }, this));
199
+ } else {
200
+ this.showImg($entry, loadNewImage);
201
+ }
202
+
203
+ } else {
204
+ this.showImg($entry);
205
+ }
206
+
207
+ this.displayEntryCaption($entry);
208
+ };
209
+
210
+ /**
211
+ * Display the entry caption. If the caption element doesn't exists, it creates the caption using the 'alt'
212
+ * or the 'title' attributes.
213
+ *
214
+ * @param {jQuery} $entry the entry to process
215
+ */
216
+ JustifiedGallery.prototype.displayEntryCaption = function ($entry) {
217
+ var $image = this.imgFromEntry($entry);
218
+ if ($image !== null && this.settings.captions) {
219
+ var $imgCaption = this.captionFromEntry($entry);
220
+
221
+ // Create it if it doesn't exists
222
+ if ($imgCaption === null) {
223
+ var caption = $image.attr('alt');
224
+ if (!this.isValidCaption(caption)) caption = $entry.attr('title');
225
+ if (this.isValidCaption(caption)) { // Create only we found something
226
+ $imgCaption = $('<div class="caption">' + caption + '</div>');
227
+ $entry.append($imgCaption);
228
+ $entry.data('jg.createdCaption', true);
229
+ }
230
+ }
231
+
232
+ // Create events (we check again the $imgCaption because it can be still inexistent)
233
+ if ($imgCaption !== null) {
234
+ if (!this.settings.cssAnimation) $imgCaption.stop().fadeTo(0, this.settings.captionSettings.nonVisibleOpacity);
235
+ this.addCaptionEventsHandlers($entry);
236
+ }
237
+ } else {
238
+ this.removeCaptionEventsHandlers($entry);
239
+ }
240
+ };
241
+
242
+ /**
243
+ * Validates the caption
244
+ *
245
+ * @param caption The caption that should be validated
246
+ * @return {boolean} Validation result
247
+ */
248
+ JustifiedGallery.prototype.isValidCaption = function (caption) {
249
+ return (typeof caption !== 'undefined' && caption.length > 0);
250
+ };
251
+
252
+ /**
253
+ * The callback for the event 'mouseenter'. It assumes that the event currentTarget is an entry.
254
+ * It shows the caption using jQuery (or using CSS if it is configured so)
255
+ *
256
+ * @param {Event} eventObject the event object
257
+ */
258
+ JustifiedGallery.prototype.onEntryMouseEnterForCaption = function (eventObject) {
259
+ var $caption = this.captionFromEntry($(eventObject.currentTarget));
260
+ if (this.settings.cssAnimation) {
261
+ $caption.addClass('caption-visible').removeClass('caption-hidden');
262
+ } else {
263
+ $caption.stop().fadeTo(this.settings.captionSettings.animationDuration,
264
+ this.settings.captionSettings.visibleOpacity);
265
+ }
266
+ };
267
+
268
+ /**
269
+ * The callback for the event 'mouseleave'. It assumes that the event currentTarget is an entry.
270
+ * It hides the caption using jQuery (or using CSS if it is configured so)
271
+ *
272
+ * @param {Event} eventObject the event object
273
+ */
274
+ JustifiedGallery.prototype.onEntryMouseLeaveForCaption = function (eventObject) {
275
+ var $caption = this.captionFromEntry($(eventObject.currentTarget));
276
+ if (this.settings.cssAnimation) {
277
+ $caption.removeClass('caption-visible').removeClass('caption-hidden');
278
+ } else {
279
+ $caption.stop().fadeTo(this.settings.captionSettings.animationDuration,
280
+ this.settings.captionSettings.nonVisibleOpacity);
281
+ }
282
+ };
283
+
284
+ /**
285
+ * Add the handlers of the entry for the caption
286
+ *
287
+ * @param $entry the entry to modify
288
+ */
289
+ JustifiedGallery.prototype.addCaptionEventsHandlers = function ($entry) {
290
+ var captionMouseEvents = $entry.data('jg.captionMouseEvents');
291
+ if (typeof captionMouseEvents === 'undefined') {
292
+ captionMouseEvents = {
293
+ mouseenter: $.proxy(this.onEntryMouseEnterForCaption, this),
294
+ mouseleave: $.proxy(this.onEntryMouseLeaveForCaption, this)
295
+ };
296
+ $entry.on('mouseenter', undefined, undefined, captionMouseEvents.mouseenter);
297
+ $entry.on('mouseleave', undefined, undefined, captionMouseEvents.mouseleave);
298
+ $entry.data('jg.captionMouseEvents', captionMouseEvents);
299
+ }
300
+ };
301
+
302
+ /**
303
+ * Remove the handlers of the entry for the caption
304
+ *
305
+ * @param $entry the entry to modify
306
+ */
307
+ JustifiedGallery.prototype.removeCaptionEventsHandlers = function ($entry) {
308
+ var captionMouseEvents = $entry.data('jg.captionMouseEvents');
309
+ if (typeof captionMouseEvents !== 'undefined') {
310
+ $entry.off('mouseenter', undefined, captionMouseEvents.mouseenter);
311
+ $entry.off('mouseleave', undefined, captionMouseEvents.mouseleave);
312
+ $entry.removeData('jg.captionMouseEvents');
313
+ }
314
+ };
315
+
316
+ /**
317
+ * Justify the building row, preparing it to
318
+ *
319
+ * @param isLastRow
320
+ * @returns a boolean to know if the row has been justified or not
321
+ */
322
+ JustifiedGallery.prototype.prepareBuildingRow = function (isLastRow) {
323
+ var i, $entry, imgAspectRatio, newImgW, newImgH, justify = true;
324
+ var minHeight = 0;
325
+ var availableWidth = this.galleryWidth - 2 * this.border - (
326
+ (this.buildingRow.entriesBuff.length - 1) * this.settings.margins);
327
+ var rowHeight = availableWidth / this.buildingRow.aspectRatio;
328
+ var justifiable = this.buildingRow.width / availableWidth > this.settings.justifyThreshold;
329
+
330
+ //Skip the last row if we can't justify it and the lastRow == 'hide'
331
+ if (isLastRow && this.settings.lastRow === 'hide' && !justifiable) {
332
+ for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
333
+ $entry = this.buildingRow.entriesBuff[i];
334
+ if (this.settings.cssAnimation)
335
+ $entry.removeClass('entry-visible');
336
+ else
337
+ $entry.stop().fadeTo(0, 0);
338
+ }
339
+ return -1;
340
+ }
341
+
342
+ // With lastRow = nojustify, justify if is justificable (the images will not become too big)
343
+ if (isLastRow && !justifiable && this.settings.lastRow !== 'justify' && this.settings.lastRow !== 'hide') justify = false;
344
+
345
+ for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
346
+ $entry = this.buildingRow.entriesBuff[i];
347
+ imgAspectRatio = $entry.data('jg.width') / $entry.data('jg.height');
348
+
349
+ if (justify) {
350
+ newImgW = (i === this.buildingRow.entriesBuff.length - 1) ? availableWidth : rowHeight * imgAspectRatio;
351
+ newImgH = rowHeight;
352
+
353
+ /* With fixedHeight the newImgH must be greater than rowHeight.
354
+ In some cases here this is not satisfied (due to the justification).
355
+ But we comment it, because is better to have a shorter but justified row instead
356
+ to have a cropped image at the end. */
357
+ /*if (this.settings.fixedHeight && newImgH < this.settings.rowHeight) {
358
+ newImgW = this.settings.rowHeight * imgAspectRatio;
359
+ newImgH = this.settings.rowHeight;
360
+ }*/
361
+
362
+ } else {
363
+ newImgW = this.settings.rowHeight * imgAspectRatio;
364
+ newImgH = this.settings.rowHeight;
365
+ }
366
+
367
+ availableWidth -= Math.round(newImgW);
368
+ $entry.data('jg.jwidth', Math.round(newImgW));
369
+ $entry.data('jg.jheight', Math.ceil(newImgH));
370
+ if (i === 0 || minHeight > newImgH) minHeight = newImgH;
371
+ }
372
+
373
+ if (this.settings.fixedHeight && minHeight > this.settings.rowHeight)
374
+ minHeight = this.settings.rowHeight;
375
+
376
+ this.buildingRow.height = minHeight;
377
+ return justify;
378
+ };
379
+
380
+ /**
381
+ * Clear the building row data to be used for a new row
382
+ */
383
+ JustifiedGallery.prototype.clearBuildingRow = function () {
384
+ this.buildingRow.entriesBuff = [];
385
+ this.buildingRow.aspectRatio = 0;
386
+ this.buildingRow.width = 0;
387
+ };
388
+
389
+ /**
390
+ * Flush a row: justify it, modify the gallery height accordingly to the row height
391
+ *
392
+ * @param isLastRow
393
+ */
394
+ JustifiedGallery.prototype.flushRow = function (isLastRow) {
395
+ var settings = this.settings;
396
+ var $entry, buildingRowRes, offX = this.border, i;
397
+
398
+ buildingRowRes = this.prepareBuildingRow(isLastRow);
399
+ if (isLastRow && settings.lastRow === 'hide' && this.buildingRow.height === -1) {
400
+ this.clearBuildingRow();
401
+ return;
402
+ }
403
+
404
+ if (this.maxRowHeight.isPercentage) {
405
+ if (this.maxRowHeight.value * settings.rowHeight < this.buildingRow.height) {
406
+ this.buildingRow.height = this.maxRowHeight.value * settings.rowHeight;
407
+ }
408
+ } else {
409
+ if (this.maxRowHeight.value > 0 && this.maxRowHeight.value < this.buildingRow.height) {
410
+ this.buildingRow.height = this.maxRowHeight.value;
411
+ }
412
+ }
413
+
414
+ //Align last (unjustified) row
415
+ if (settings.lastRow === 'center' || settings.lastRow === 'right') {
416
+ var availableWidth = this.galleryWidth - 2 * this.border - (this.buildingRow.entriesBuff.length - 1) * settings.margins;
417
+
418
+ for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
419
+ $entry = this.buildingRow.entriesBuff[i];
420
+ availableWidth -= $entry.data('jg.jwidth');
421
+ }
422
+
423
+ if (settings.lastRow === 'center')
424
+ offX += availableWidth / 2;
425
+ else if (settings.lastRow === 'right')
426
+ offX += availableWidth;
427
+ }
428
+
429
+
430
+ for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
431
+ $entry = this.buildingRow.entriesBuff[i];
432
+ this.displayEntry($entry, offX, this.offY, $entry.data('jg.jwidth'), $entry.data('jg.jheight'), this.buildingRow.height);
433
+ offX += $entry.data('jg.jwidth') + settings.margins;
434
+ }
435
+
436
+ //Gallery Height
437
+ this.$gallery.height(this.offY + this.buildingRow.height +
438
+ this.border + (this.isSpinnerActive() ? this.getSpinnerHeight() : 0));
439
+
440
+ if (!isLastRow || (this.buildingRow.height <= settings.rowHeight && buildingRowRes)) {
441
+ //Ready for a new row
442
+ this.offY += this.buildingRow.height + settings.margins;
443
+ this.clearBuildingRow();
444
+ this.$gallery.trigger('jg.rowflush');
445
+ }
446
+ };
447
+
448
+ /**
449
+ * Checks the width of the gallery container, to know if a new justification is needed
450
+ */
451
+ JustifiedGallery.prototype.checkWidth = function () {
452
+ this.checkWidthIntervalId = setInterval($.proxy(function () {
453
+ var galleryWidth = parseFloat(this.$gallery.width());
454
+ if (Math.abs(galleryWidth - this.galleryWidth) > this.settings.refreshSensitivity) {
455
+ this.galleryWidth = galleryWidth;
456
+ this.rewind();
457
+
458
+ // Restart to analyze
459
+ this.startImgAnalyzer(true);
460
+ }
461
+ }, this), this.settings.refreshTime);
462
+ };
463
+
464
+ /**
465
+ * @returns {boolean} a boolean saying if the spinner is active or not
466
+ */
467
+ JustifiedGallery.prototype.isSpinnerActive = function () {
468
+ return this.spinner.intervalId !== null;
469
+ };
470
+
471
+ /**
472
+ * @returns {int} the spinner height
473
+ */
474
+ JustifiedGallery.prototype.getSpinnerHeight = function () {
475
+ return this.spinner.$el.innerHeight();
476
+ };
477
+
478
+ /**
479
+ * Stops the spinner animation and modify the gallery height to exclude the spinner
480
+ */
481
+ JustifiedGallery.prototype.stopLoadingSpinnerAnimation = function () {
482
+ clearInterval(this.spinner.intervalId);
483
+ this.spinner.intervalId = null;
484
+ this.$gallery.height(this.$gallery.height() - this.getSpinnerHeight());
485
+ this.spinner.$el.detach();
486
+ };
487
+
488
+ /**
489
+ * Starts the spinner animation
490
+ */
491
+ JustifiedGallery.prototype.startLoadingSpinnerAnimation = function () {
492
+ var spinnerContext = this.spinner;
493
+ var $spinnerPoints = spinnerContext.$el.find('span');
494
+ clearInterval(spinnerContext.intervalId);
495
+ this.$gallery.append(spinnerContext.$el);
496
+ this.$gallery.height(this.offY + this.buildingRow.height + this.getSpinnerHeight());
497
+ spinnerContext.intervalId = setInterval(function () {
498
+ if (spinnerContext.phase < $spinnerPoints.length) {
499
+ $spinnerPoints.eq(spinnerContext.phase).fadeTo(spinnerContext.timeSlot, 1);
500
+ } else {
501
+ $spinnerPoints.eq(spinnerContext.phase - $spinnerPoints.length).fadeTo(spinnerContext.timeSlot, 0);
502
+ }
503
+ spinnerContext.phase = (spinnerContext.phase + 1) % ($spinnerPoints.length * 2);
504
+ }, spinnerContext.timeSlot);
505
+ };
506
+
507
+ /**
508
+ * Rewind the image analysis to start from the first entry.
509
+ */
510
+ JustifiedGallery.prototype.rewind = function () {
511
+ this.lastAnalyzedIndex = -1;
512
+ this.offY = this.border;
513
+ this.clearBuildingRow();
514
+ };
515
+
516
+ /**
517
+ * Update the entries searching it from the justified gallery HTML element
518
+ *
519
+ * @param norewind if norewind only the new entries will be changed (i.e. randomized, sorted or filtered)
520
+ * @returns {boolean} true if some entries has been founded
521
+ */
522
+ JustifiedGallery.prototype.updateEntries = function (norewind) {
523
+ this.entries = this.$gallery.find(this.settings.selector).toArray();
524
+ if (this.entries.length === 0) return false;
525
+
526
+ // Filter
527
+ if (this.settings.filter) {
528
+ this.modifyEntries(this.filterArray, norewind);
529
+ } else {
530
+ this.modifyEntries(this.resetFilters, norewind);
531
+ }
532
+
533
+ // Sort or randomize
534
+ if ($.isFunction(this.settings.sort)) {
535
+ this.modifyEntries(this.sortArray, norewind);
536
+ } else if (this.settings.randomize) {
537
+ this.modifyEntries(this.shuffleArray, norewind);
538
+ }
539
+
540
+ return true;
541
+ };
542
+
543
+ /**
544
+ * Apply the entries order to the DOM, iterating the entries and appending the images
545
+ *
546
+ * @param entries the entries that has been modified and that must be re-ordered in the DOM
547
+ */
548
+ JustifiedGallery.prototype.insertToGallery = function (entries) {
549
+ var that = this;
550
+ $.each(entries, function () {
551
+ $(this).appendTo(that.$gallery);
552
+ });
553
+ };
554
+
555
+ /**
556
+ * Shuffle the array using the Fisher-Yates shuffle algorithm
557
+ *
558
+ * @param a the array to shuffle
559
+ * @return the shuffled array
560
+ */
561
+ JustifiedGallery.prototype.shuffleArray = function (a) {
562
+ var i, j, temp;
563
+ for (i = a.length - 1; i > 0; i--) {
564
+ j = Math.floor(Math.random() * (i + 1));
565
+ temp = a[i];
566
+ a[i] = a[j];
567
+ a[j] = temp;
568
+ }
569
+ this.insertToGallery(a);
570
+ return a;
571
+ };
572
+
573
+ /**
574
+ * Sort the array using settings.comparator as comparator
575
+ *
576
+ * @param a the array to sort (it is sorted)
577
+ * @return the sorted array
578
+ */
579
+ JustifiedGallery.prototype.sortArray = function (a) {
580
+ a.sort(this.settings.sort);
581
+ this.insertToGallery(a);
582
+ return a;
583
+ };
584
+
585
+ /**
586
+ * Reset the filters removing the 'jg-filtered' class from all the entries
587
+ *
588
+ * @param a the array to reset
589
+ */
590
+ JustifiedGallery.prototype.resetFilters = function (a) {
591
+ for (var i = 0; i < a.length; i++) $(a[i]).removeClass('jg-filtered');
592
+ return a;
593
+ };
594
+
595
+ /**
596
+ * Filter the entries considering theirs classes (if a string has been passed) or using a function for filtering.
597
+ *
598
+ * @param a the array to filter
599
+ * @return the filtered array
600
+ */
601
+ JustifiedGallery.prototype.filterArray = function (a) {
602
+ var settings = this.settings;
603
+ if ($.type(settings.filter) === 'string') {
604
+ // Filter only keeping the entries passed in the string
605
+ return a.filter(function (el) {
606
+ var $el = $(el);
607
+ if ($el.is(settings.filter)) {
608
+ $el.removeClass('jg-filtered');
609
+ return true;
610
+ } else {
611
+ $el.addClass('jg-filtered');
612
+ return false;
613
+ }
614
+ });
615
+ } else if ($.isFunction(settings.filter)) {
616
+ // Filter using the passed function
617
+ return a.filter(settings.filter);
618
+ }
619
+ };
620
+
621
+ /**
622
+ * Modify the entries. With norewind only the new inserted images will be modified (the ones after lastAnalyzedIndex)
623
+ *
624
+ * @param functionToApply the function to call to modify the entries (e.g. sorting, randomization, filtering)
625
+ * @param norewind specify if the norewind has been called or not
626
+ */
627
+ JustifiedGallery.prototype.modifyEntries = function (functionToApply, norewind) {
628
+ var lastEntries = norewind ?
629
+ this.entries.splice(this.lastAnalyzedIndex + 1, this.entries.length - this.lastAnalyzedIndex - 1)
630
+ : this.entries;
631
+ lastEntries = functionToApply.call(this, lastEntries);
632
+ this.entries = norewind ? this.entries.concat(lastEntries) : lastEntries;
633
+ };
634
+
635
+ /**
636
+ * Destroy the Justified Gallery instance.
637
+ *
638
+ * It clears all the css properties added in the style attributes. We doesn't backup the original
639
+ * values for those css attributes, because it costs (performance) and because in general one
640
+ * shouldn't use the style attribute for an uniform set of images (where we suppose the use of
641
+ * classes). Creating a backup is also difficult because JG could be called multiple times and
642
+ * with different style attributes.
643
+ */
644
+ JustifiedGallery.prototype.destroy = function () {
645
+ clearInterval(this.checkWidthIntervalId);
646
+
647
+ $.each(this.entries, $.proxy(function(_, entry) {
648
+ var $entry = $(entry);
649
+
650
+ // Reset entry style
651
+ $entry.css('width', '');
652
+ $entry.css('height', '');
653
+ $entry.css('top', '');
654
+ $entry.css('left', '');
655
+ $entry.data('jg.loaded', undefined);
656
+ $entry.removeClass('jg-entry');
657
+
658
+ // Reset image style
659
+ var $img = this.imgFromEntry($entry);
660
+ $img.css('width', '');
661
+ $img.css('height', '');
662
+ $img.css('margin-left', '');
663
+ $img.css('margin-top', '');
664
+ $img.attr('src', $img.data('jg.originalSrc'));
665
+ $img.data('jg.originalSrc', undefined);
666
+
667
+ // Remove caption
668
+ this.removeCaptionEventsHandlers($entry);
669
+ var $caption = this.captionFromEntry($entry);
670
+ if ($entry.data('jg.createdCaption')) {
671
+ // remove also the caption element (if created by jg)
672
+ $entry.data('jg.createdCaption', undefined);
673
+ if ($caption !== null) $caption.remove();
674
+ } else {
675
+ if ($caption !== null) $caption.fadeTo(0, 1);
676
+ }
677
+
678
+ }, this));
679
+
680
+ this.$gallery.css('height', '');
681
+ this.$gallery.removeClass('justified-gallery');
682
+ this.$gallery.data('jg.controller', undefined);
683
+ };
684
+
685
+ /**
686
+ * Analyze the images and builds the rows. It returns if it found an image that is not loaded.
687
+ *
688
+ * @param isForResize if the image analyzer is called for resizing or not, to call a different callback at the end
689
+ */
690
+ JustifiedGallery.prototype.analyzeImages = function (isForResize) {
691
+ for (var i = this.lastAnalyzedIndex + 1; i < this.entries.length; i++) {
692
+ var $entry = $(this.entries[i]);
693
+ if ($entry.data('jg.loaded') === true || $entry.data('jg.loaded') === 'skipped') {
694
+ var availableWidth = this.galleryWidth - 2 * this.border - (
695
+ (this.buildingRow.entriesBuff.length - 1) * this.settings.margins);
696
+ var imgAspectRatio = $entry.data('jg.width') / $entry.data('jg.height');
697
+ if (availableWidth / (this.buildingRow.aspectRatio + imgAspectRatio) < this.settings.rowHeight) {
698
+ this.flushRow(false);
699
+ if(++this.yield.flushed >= this.yield.every) {
700
+ this.startImgAnalyzer(isForResize);
701
+ return;
702
+ }
703
+ }
704
+
705
+ this.buildingRow.entriesBuff.push($entry);
706
+ this.buildingRow.aspectRatio += imgAspectRatio;
707
+ this.buildingRow.width += imgAspectRatio * this.settings.rowHeight;
708
+ this.lastAnalyzedIndex = i;
709
+
710
+ } else if ($entry.data('jg.loaded') !== 'error') {
711
+ return;
712
+ }
713
+ }
714
+
715
+ // Last row flush (the row is not full)
716
+ if (this.buildingRow.entriesBuff.length > 0) this.flushRow(true);
717
+
718
+ if (this.isSpinnerActive()) {
719
+ this.stopLoadingSpinnerAnimation();
720
+ }
721
+
722
+ /* Stop, if there is, the timeout to start the analyzeImages.
723
+ This is because an image can be set loaded, and the timeout can be set,
724
+ but this image can be analyzed yet.
725
+ */
726
+ this.stopImgAnalyzerStarter();
727
+
728
+ //On complete callback
729
+ this.$gallery.trigger(isForResize ? 'jg.resize' : 'jg.complete');
730
+ };
731
+
732
+ /**
733
+ * Stops any ImgAnalyzer starter (that has an assigned timeout)
734
+ */
735
+ JustifiedGallery.prototype.stopImgAnalyzerStarter = function () {
736
+ this.yield.flushed = 0;
737
+ if (this.imgAnalyzerTimeout !== null) clearTimeout(this.imgAnalyzerTimeout);
738
+ };
739
+
740
+ /**
741
+ * Starts the image analyzer. It is not immediately called to let the browser to update the view
742
+ *
743
+ * @param isForResize specifies if the image analyzer must be called for resizing or not
744
+ */
745
+ JustifiedGallery.prototype.startImgAnalyzer = function (isForResize) {
746
+ var that = this;
747
+ this.stopImgAnalyzerStarter();
748
+ this.imgAnalyzerTimeout = setTimeout(function () {
749
+ that.analyzeImages(isForResize);
750
+ }, 0.001); // we can't start it immediately due to a IE different behaviour
751
+ };
752
+
753
+ /**
754
+ * Checks if the image is loaded or not using another image object. We cannot use the 'complete' image property,
755
+ * because some browsers, with a 404 set complete = true.
756
+ *
757
+ * @param imageSrc the image src to load
758
+ * @param onLoad callback that is called when the image has been loaded
759
+ * @param onError callback that is called in case of an error
760
+ */
761
+ JustifiedGallery.prototype.onImageEvent = function (imageSrc, onLoad, onError) {
762
+ if (!onLoad && !onError) return;
763
+
764
+ var memImage = new Image();
765
+ var $memImage = $(memImage);
766
+ if (onLoad) {
767
+ $memImage.one('load', function () {
768
+ $memImage.off('load error');
769
+ onLoad(memImage);
770
+ });
771
+ }
772
+ if (onError) {
773
+ $memImage.one('error', function() {
774
+ $memImage.off('load error');
775
+ onError(memImage);
776
+ });
777
+ }
778
+ memImage.src = imageSrc;
779
+ };
780
+
781
+ /**
782
+ * Init of Justified Gallery controlled
783
+ * It analyzes all the entries starting theirs loading and calling the image analyzer (that works with loaded images)
784
+ */
785
+ JustifiedGallery.prototype.init = function () {
786
+ var imagesToLoad = false, skippedImages = false, that = this;
787
+ $.each(this.entries, function (index, entry) {
788
+ var $entry = $(entry);
789
+ var $image = that.imgFromEntry($entry);
790
+
791
+ $entry.addClass('jg-entry');
792
+
793
+ if ($entry.data('jg.loaded') !== true && $entry.data('jg.loaded') !== 'skipped') {
794
+
795
+ // Link Rel global overwrite
796
+ if (that.settings.rel !== null) $entry.attr('rel', that.settings.rel);
797
+
798
+ // Link Target global overwrite
799
+ if (that.settings.target !== null) $entry.attr('target', that.settings.target);
800
+
801
+ if ($image !== null) {
802
+
803
+ // Image src
804
+ var imageSrc = that.extractImgSrcFromImage($image);
805
+ $image.attr('src', imageSrc);
806
+
807
+ /* If we have the height and the width, we don't wait that the image is loaded, but we start directly
808
+ * with the justification */
809
+ if (that.settings.waitThumbnailsLoad === false) {
810
+ var width = parseFloat($image.attr('width'));
811
+ var height = parseFloat($image.attr('height'));
812
+ if (!isNaN(width) && !isNaN(height)) {
813
+ $entry.data('jg.width', width);
814
+ $entry.data('jg.height', height);
815
+ $entry.data('jg.loaded', 'skipped');
816
+ skippedImages = true;
817
+ that.startImgAnalyzer(false);
818
+ return true; // continue
819
+ }
820
+ }
821
+
822
+ $entry.data('jg.loaded', false);
823
+ imagesToLoad = true;
824
+
825
+ // Spinner start
826
+ if (!that.isSpinnerActive()) that.startLoadingSpinnerAnimation();
827
+
828
+ that.onImageEvent(imageSrc, function (loadImg) { // image loaded
829
+ $entry.data('jg.width', loadImg.width);
830
+ $entry.data('jg.height', loadImg.height);
831
+ $entry.data('jg.loaded', true);
832
+ that.startImgAnalyzer(false);
833
+ }, function () { // image load error
834
+ $entry.data('jg.loaded', 'error');
835
+ that.startImgAnalyzer(false);
836
+ });
837
+
838
+ } else {
839
+ $entry.data('jg.loaded', true);
840
+ $entry.data('jg.width', $entry.width() | parseFloat($entry.css('width')) | 1);
841
+ $entry.data('jg.height', $entry.height() | parseFloat($entry.css('height')) | 1);
842
+ }
843
+
844
+ }
845
+
846
+ });
847
+
848
+ if (!imagesToLoad && !skippedImages) this.startImgAnalyzer(false);
849
+ this.checkWidth();
850
+ };
851
+
852
+ /**
853
+ * Checks that it is a valid number. If a string is passed it is converted to a number
854
+ *
855
+ * @param settingContainer the object that contains the setting (to allow the conversion)
856
+ * @param settingName the setting name
857
+ */
858
+ JustifiedGallery.prototype.checkOrConvertNumber = function (settingContainer, settingName) {
859
+ if ($.type(settingContainer[settingName]) === 'string') {
860
+ settingContainer[settingName] = parseFloat(settingContainer[settingName]);
861
+ }
862
+
863
+ if ($.type(settingContainer[settingName]) === 'number') {
864
+ if (isNaN(settingContainer[settingName])) throw 'invalid number for ' + settingName;
865
+ } else {
866
+ throw settingName + ' must be a number';
867
+ }
868
+ };
869
+
870
+ /**
871
+ * Checks the sizeRangeSuffixes and, if necessary, converts
872
+ * its keys from string (e.g. old settings with 'lt100') to int.
873
+ */
874
+ JustifiedGallery.prototype.checkSizeRangesSuffixes = function () {
875
+ if ($.type(this.settings.sizeRangeSuffixes) !== 'object') {
876
+ throw 'sizeRangeSuffixes must be defined and must be an object';
877
+ }
878
+
879
+ var suffixRanges = [];
880
+ for (var rangeIdx in this.settings.sizeRangeSuffixes) {
881
+ if (this.settings.sizeRangeSuffixes.hasOwnProperty(rangeIdx)) suffixRanges.push(rangeIdx);
882
+ }
883
+
884
+ var newSizeRngSuffixes = {0: ''};
885
+ for (var i = 0; i < suffixRanges.length; i++) {
886
+ if ($.type(suffixRanges[i]) === 'string') {
887
+ try {
888
+ var numIdx = parseInt(suffixRanges[i].replace(/^[a-z]+/, ''), 10);
889
+ newSizeRngSuffixes[numIdx] = this.settings.sizeRangeSuffixes[suffixRanges[i]];
890
+ } catch (e) {
891
+ throw 'sizeRangeSuffixes keys must contains correct numbers (' + e + ')';
892
+ }
893
+ } else {
894
+ newSizeRngSuffixes[suffixRanges[i]] = this.settings.sizeRangeSuffixes[suffixRanges[i]];
895
+ }
896
+ }
897
+
898
+ this.settings.sizeRangeSuffixes = newSizeRngSuffixes;
899
+ };
900
+
901
+ /**
902
+ * check and convert the maxRowHeight setting
903
+ */
904
+ JustifiedGallery.prototype.retrieveMaxRowHeight = function () {
905
+ var newMaxRowHeight = { };
906
+
907
+ if ($.type(this.settings.maxRowHeight) === 'string') {
908
+ if (this.settings.maxRowHeight.match(/^[0-9]+%$/)) {
909
+ newMaxRowHeight.value = parseFloat(this.settings.maxRowHeight.match(/^([0-9]+)%$/)[1]) / 100;
910
+ newMaxRowHeight.isPercentage = false;
911
+ } else {
912
+ newMaxRowHeight.value = parseFloat(this.settings.maxRowHeight);
913
+ newMaxRowHeight.isPercentage = true;
914
+ }
915
+ } else if ($.type(this.settings.maxRowHeight) === 'number') {
916
+ newMaxRowHeight.value = this.settings.maxRowHeight;
917
+ newMaxRowHeight.isPercentage = false;
918
+ } else {
919
+ throw 'maxRowHeight must be a number or a percentage';
920
+ }
921
+
922
+ // check if the converted value is not a number
923
+ if (isNaN(newMaxRowHeight.value)) throw 'invalid number for maxRowHeight';
924
+
925
+ // check values
926
+ if (newMaxRowHeight.isPercentage) {
927
+ if (newMaxRowHeight.value < 100) newMaxRowHeight.value = 100;
928
+ } else {
929
+ if (newMaxRowHeight.value > 0 && newMaxRowHeight.value < this.settings.rowHeight) {
930
+ newMaxRowHeight.value = this.settings.rowHeight;
931
+ }
932
+ }
933
+
934
+ return newMaxRowHeight;
935
+
936
+ };
937
+
938
+ /**
939
+ * Checks the settings
940
+ */
941
+ JustifiedGallery.prototype.checkSettings = function () {
942
+ this.checkSizeRangesSuffixes();
943
+
944
+ this.checkOrConvertNumber(this.settings, 'rowHeight');
945
+ this.checkOrConvertNumber(this.settings, 'margins');
946
+ this.checkOrConvertNumber(this.settings, 'border');
947
+
948
+ if (this.settings.lastRow !== 'justify' &&
949
+ this.settings.lastRow !== 'nojustify' && this.settings.lastRow !== 'left' &&
950
+ this.settings.lastRow !== 'center' &&
951
+ this.settings.lastRow !== 'right' &&
952
+ this.settings.lastRow !== 'hide') {
953
+ throw 'lastRow must be "justify", "nojustify", "left", "center", "right" or "hide"';
954
+ }
955
+
956
+ this.checkOrConvertNumber(this.settings, 'justifyThreshold');
957
+ if (this.settings.justifyThreshold < 0 || this.settings.justifyThreshold > 1) {
958
+ throw 'justifyThreshold must be in the interval [0,1]';
959
+ }
960
+ if ($.type(this.settings.cssAnimation) !== 'boolean') {
961
+ throw 'cssAnimation must be a boolean';
962
+ }
963
+
964
+ if ($.type(this.settings.captions) !== 'boolean') throw 'captions must be a boolean';
965
+ this.checkOrConvertNumber(this.settings.captionSettings, 'animationDuration');
966
+
967
+ this.checkOrConvertNumber(this.settings.captionSettings, 'visibleOpacity');
968
+ if (this.settings.captionSettings.visibleOpacity < 0 ||
969
+ this.settings.captionSettings.visibleOpacity > 1) {
970
+ throw 'captionSettings.visibleOpacity must be in the interval [0, 1]';
971
+ }
972
+
973
+ this.checkOrConvertNumber(this.settings.captionSettings, 'nonVisibleOpacity');
974
+ if (this.settings.captionSettings.nonVisibleOpacity < 0 ||
975
+ this.settings.captionSettings.nonVisibleOpacity > 1) {
976
+ throw 'captionSettings.nonVisibleOpacity must be in the interval [0, 1]';
977
+ }
978
+
979
+ if ($.type(this.settings.fixedHeight) !== 'boolean') throw 'fixedHeight must be a boolean';
980
+ this.checkOrConvertNumber(this.settings, 'imagesAnimationDuration');
981
+ this.checkOrConvertNumber(this.settings, 'refreshTime');
982
+ this.checkOrConvertNumber(this.settings, 'refreshSensitivity');
983
+ if ($.type(this.settings.randomize) !== 'boolean') throw 'randomize must be a boolean';
984
+ if ($.type(this.settings.selector) !== 'string') throw 'selector must be a string';
985
+
986
+ if (this.settings.sort !== false && !$.isFunction(this.settings.sort)) {
987
+ throw 'sort must be false or a comparison function';
988
+ }
989
+
990
+ if (this.settings.filter !== false && !$.isFunction(this.settings.filter) &&
991
+ $.type(this.settings.filter) !== 'string') {
992
+ throw 'filter must be false, a string or a filter function';
993
+ }
994
+ };
995
+
996
+ /**
997
+ * It brings all the indexes from the sizeRangeSuffixes and it orders them. They are then sorted and returned.
998
+ * @returns {Array} sorted suffix ranges
999
+ */
1000
+ JustifiedGallery.prototype.retrieveSuffixRanges = function () {
1001
+ var suffixRanges = [];
1002
+ for (var rangeIdx in this.settings.sizeRangeSuffixes) {
1003
+ if (this.settings.sizeRangeSuffixes.hasOwnProperty(rangeIdx)) suffixRanges.push(parseInt(rangeIdx, 10));
1004
+ }
1005
+ suffixRanges.sort(function (a, b) { return a > b ? 1 : a < b ? -1 : 0; });
1006
+ return suffixRanges;
1007
+ };
1008
+
1009
+ /**
1010
+ * Update the existing settings only changing some of them
1011
+ *
1012
+ * @param newSettings the new settings (or a subgroup of them)
1013
+ */
1014
+ JustifiedGallery.prototype.updateSettings = function (newSettings) {
1015
+ // In this case Justified Gallery has been called again changing only some options
1016
+ this.settings = $.extend({}, this.settings, newSettings);
1017
+ this.checkSettings();
1018
+
1019
+ // As reported in the settings: negative value = same as margins, 0 = disabled
1020
+ this.border = this.settings.border >= 0 ? this.settings.border : this.settings.margins;
1021
+
1022
+ this.maxRowHeight = this.retrieveMaxRowHeight();
1023
+ this.suffixRanges = this.retrieveSuffixRanges();
1024
+ };
1025
+
1026
+ /**
1027
+ * Justified Gallery plugin for jQuery
1028
+ *
1029
+ * Events
1030
+ * - jg.complete : called when all the gallery has been created
1031
+ * - jg.resize : called when the gallery has been resized
1032
+ * - jg.rowflush : when a new row appears
1033
+ *
1034
+ * @param arg the action (or the settings) passed when the plugin is called
1035
+ * @returns {*} the object itself
1036
+ */
1037
+ $.fn.justifiedGallery = function (arg) {
1038
+ return this.each(function (index, gallery) {
1039
+
1040
+ var $gallery = $(gallery);
1041
+ $gallery.addClass('justified-gallery');
1042
+
1043
+ var controller = $gallery.data('jg.controller');
1044
+ if (typeof controller === 'undefined') {
1045
+ // Create controller and assign it to the object data
1046
+ if (typeof arg !== 'undefined' && arg !== null && $.type(arg) !== 'object') {
1047
+ if (arg === 'destroy') return; // Just a call to an unexisting object
1048
+ throw 'The argument must be an object';
1049
+ }
1050
+ controller = new JustifiedGallery($gallery, $.extend({}, $.fn.justifiedGallery.defaults, arg));
1051
+ $gallery.data('jg.controller', controller);
1052
+ } else if (arg === 'norewind') {
1053
+ // In this case we don't rewind: we analyze only the latest images (e.g. to complete the last unfinished row
1054
+ // ... left to be more readable
1055
+ } else if (arg === 'destroy') {
1056
+ controller.destroy();
1057
+ return;
1058
+ } else {
1059
+ // In this case Justified Gallery has been called again changing only some options
1060
+ controller.updateSettings(arg);
1061
+ controller.rewind();
1062
+ }
1063
+
1064
+ // Update the entries list
1065
+ if (!controller.updateEntries(arg === 'norewind')) return;
1066
+
1067
+ // Init justified gallery
1068
+ controller.init();
1069
+
1070
+ });
1071
+ };
1072
+
1073
+ // Default options
1074
+ $.fn.justifiedGallery.defaults = {
1075
+ sizeRangeSuffixes: { }, /* e.g. Flickr configuration
1076
+ {
1077
+ 100: '_t', // used when longest is less than 100px
1078
+ 240: '_m', // used when longest is between 101px and 240px
1079
+ 320: '_n', // ...
1080
+ 500: '',
1081
+ 640: '_z',
1082
+ 1024: '_b' // used as else case because it is the last
1083
+ }
1084
+ */
1085
+ thumbnailPath: undefined, /* If defined, sizeRangeSuffixes is not used, and this function is used to determine the
1086
+ path relative to a specific thumbnail size. The function should accept respectively three arguments:
1087
+ current path, width and height */
1088
+ rowHeight: 120,
1089
+ maxRowHeight: -1, // negative value = no limits, number to express the value in pixels,
1090
+ // '[0-9]+%' to express in percentage (e.g. 300% means that the row height
1091
+ // can't exceed 3 * rowHeight)
1092
+ margins: 1,
1093
+ border: -1, // negative value = same as margins, 0 = disabled, any other value to set the border
1094
+
1095
+ lastRow: 'nojustify', // … which is the same as 'left', or can be 'justify', 'center', 'right' or 'hide'
1096
+
1097
+ justifyThreshold: 0.75, /* if row width / available space > 0.75 it will be always justified
1098
+ * (i.e. lastRow setting is not considered) */
1099
+ fixedHeight: false,
1100
+ waitThumbnailsLoad: true,
1101
+ captions: true,
1102
+ cssAnimation: false,
1103
+ imagesAnimationDuration: 500, // ignored with css animations
1104
+ captionSettings: { // ignored with css animations
1105
+ animationDuration: 500,
1106
+ visibleOpacity: 0.7,
1107
+ nonVisibleOpacity: 0.0
1108
+ },
1109
+ rel: null, // rewrite the rel of each analyzed links
1110
+ target: null, // rewrite the target of all links
1111
+ extension: /\.[^.\\/]+$/, // regexp to capture the extension of an image
1112
+ refreshTime: 200, // time interval (in ms) to check if the page changes its width
1113
+ refreshSensitivity: 0, // change in width allowed (in px) without re-building the gallery
1114
+ randomize: false,
1115
+ sort: false, /*
1116
+ - false: to do not sort
1117
+ - function: to sort them using the function as comparator (see Array.prototype.sort())
1118
+ */
1119
+ filter: false, /*
1120
+ - false: for a disabled filter
1121
+ - a string: an entry is kept if entry.is(filter string) returns true
1122
+ see jQuery's .is() function for further information
1123
+ - a function: invoked with arguments (entry, index, array). Return true to keep the entry, false otherwise.
1124
+ see Array.prototype.filter for further information.
1125
+ */
1126
+ selector: '> a, > div:not(.spinner)' // The selector that is used to know what are the entries of the gallery
1127
+ };
1128
+
1129
+ }(jQuery));