PageSourceSearch

https://chmielewski-malarstwo.pl/wp-content/themes/wright/assets/j…?id=33f0a9bbdd112ddba1b6

js chmielewski-malarstwo.pl collected 2026-10-10 00:07:55 UTC 44,256 bytes, 1,216 lines download raw bytes

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

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