PageSourceSearch

https://busansinbo.or.kr/common/js/opensources/jquery/jquery.masonry.js

js busansinbo.or.kr collected 2026-10-05 06:05:49 UTC 14,778 bytes, 499 lines download raw bytes

1/**
2 * jQuery Masonry v2.1.05
3 * A dynamic layout plugin for jQuery
4 * The flip-side of CSS Floats
5 * http://masonry.desandro.com
6 *
7 * Licensed under the MIT license.
8 * Copyright 2012 David DeSandro
9 */
10
11/*jshint browser: true, curly: true, eqeqeq: true, forin: false, immed: false, newcap: true, noempty: true, strict: true, undef: true */
12/*global jQuery: false */
13
14(function( window, $, undefined ){
15
16  'use strict';
17
18  /*
19   * smartresize: debounced resize event for jQuery
20   *
21   * latest version and complete README available on Github:
22   * https://github.com/louisremi/jquery.smartresize.js
23   *
24   * Copyright 2011 @louis_remi
25   * Licensed under the MIT license.
26   */
27
28  var $event = $.event,
29      resizeTimeout;
30
31  $event.special.smartresize = {
32    setup: function() {
33      $(this).bind( "resize", $event.special.smartresize.handler );
34    },
35    teardown: function() {
36      $(this).unbind( "resize", $event.special.smartresize.handler );
37    },
38    handler: function( event, execAsap ) {
39      // Save the context
40      var context = this,
41          args = arguments;
42
43      // set correct event type
44      event.type = "smartresize";
45
46      if ( resizeTimeout ) { clearTimeout( resizeTimeout ); }
47      resizeTimeout = setTimeout(function() {
48        $.event.handle.apply( context, args );
49      }, execAsap === "execAsap"? 0 : 100 );
50    }
51  };
52
53  $.fn.smartresize = function( fn ) {
54    return fn ? this.bind( "smartresize", fn ) : this.trigger( "smartresize", ["execAsap"] );
55  };
56
57
58
59// ========================= Masonry ===============================
60
61
62  // our "Widget" object constructor
63  $.Mason = function( options, element ){
64    this.element = $( element );
65
66    this._create( options );
67    this._init();
68  };
69
70  $.Mason.settings = {
71    isResizable: true,
72    isAnimated: false,
73    animationOptions: {
74      queue: false,
75      duration: 500
76    },
77    gutterWidth: 0,
78    isRTL: false,
79    isFitWidth: false,
80    containerStyle: {
81      position: 'relative'
82    }
83  };
84
85  $.Mason.prototype = {
86
87    _filterFindBricks: function( $elems ) {
88      var selector = this.options.itemSelector;
89      // if there is a selector
90      // filter/find appropriate item elements
91      return !selector ? $elems : $elems.filter( selector ).add( $elems.find( selector ) );
92    },
93
94    _getBricks: function( $elems ) {
95      var $bricks = this._filterFindBricks( $elems )
96        .css({ position: 'absolute' })
97        .addClass('masonry-brick');
98      return $bricks;
99    },
100    
101    // sets up widget
102    _create : function( options ) {
103      
104      this.options = $.extend( true, {}, $.Mason.settings, options );
105      this.styleQueue = [];
106
107      // get original styles in case we re-apply them in .destroy()
108      var elemStyle = this.element[0].style;
109      this.originalStyle = {
110        // get height
111        height: elemStyle.height || ''
112      };
113      // get other styles that will be overwritten
114      var containerStyle = this.options.containerStyle;
115      for ( var prop in containerStyle ) {
116        this.originalStyle[ prop ] = elemStyle[ prop ] || '';
117      }
118
119      this.element.css( containerStyle );
120
121      this.horizontalDirection = this.options.isRTL ? 'right' : 'left';
122
123      this.offset = {
124        x: parseInt( this.element.css( 'padding-' + this.horizontalDirection ), 10 ),
125        y: parseInt( this.element.css( 'padding-top' ), 10 )
126      };
127      
128      this.isFluid = this.options.columnWidth && typeof this.options.columnWidth === 'function';
129
130      // add masonry class first time around
131      var instance = this;
132      setTimeout( function() {
133        instance.element.addClass('masonry');
134      }, 0 );
135      
136      // bind resize method
137      if ( this.options.isResizable ) {
138        $(window).bind( 'smartresize.masonry', function() { 
139          instance.resize();
140        });
141      }
142
143
144      // need to get bricks
145      this.reloadItems();
146
147    },
148  
149    // _init fires when instance is first created
150    // and when instance is triggered again -> $el.masonry();
151    _init : function( callback ) {
152      this._getColumns();
153      this._reLayout( callback );
154    },
155
156    option: function( key, value ){
157      // set options AFTER initialization:
158      // signature: $('#foo').bar({ cool:false });
159      if ( $.isPlainObject( key ) ){
160        this.options = $.extend(true, this.options, key);
161      } 
162    },
163    
164    // ====================== General Layout ======================
165
166    // used on collection of atoms (should be filtered, and sorted before )
167    // accepts atoms-to-be-laid-out to start with
168    layout : function( $bricks, callback ) {
169
170      // place each brick
171      for (var i=0, len = $bricks.length; i < len; i++) {
172        this._placeBrick( $bricks[i] );
173      }
174      
175      // set the size of the container
176      var containerSize = {};
177      containerSize.height = Math.max.apply( Math, this.colYs );
178      if ( this.options.isFitWidth ) {
179        var unusedCols = 0;
180        i = this.cols;
181        // count unused columns
182        while ( --i ) {
183          if ( this.colYs[i] !== 0 ) {
184            break;
185          }
186          unusedCols++;
187        }
188        // fit container to columns that have been used;
189        containerSize.width = (this.cols - unusedCols) * this.columnWidth - this.options.gutterWidth;
190      }
191      this.styleQueue.push({ $el: this.element, style: containerSize });
192
193      // are we animating the layout arrangement?
194      // use plugin-ish syntax for css or animate
195      var styleFn = !this.isLaidOut ? 'css' : (
196            this.options.isAnimated ? 'animate' : 'css'
197          ),
198          animOpts = this.options.animationOptions;
199
200      // process styleQueue
201      var obj;
202      for (i=0, len = this.styleQueue.length; i < len; i++) {
203        obj = this.styleQueue[i];
204        obj.$el[ styleFn ]( obj.style, animOpts );
205      }
206
207      // clear out queue for next time
208      this.styleQueue = [];
209
210      // provide $elems as context for the callback
211      if ( callback ) {
212        callback.call( $bricks );
213      }
214      
215      this.isLaidOut = true;
216    },
217    
218    // calculates number of columns
219    // i.e. this.columnWidth = 200
220    _getColumns : function() {
221      var container = this.options.isFitWidth ? this.element.parent() : this.element,
222          containerWidth = container.width();
223
224                         // use fluid columnWidth function if there
225      this.columnWidth = this.isFluid ? this.options.columnWidth( containerWidth ) 
225:
226                    // if not, how about the explicitly set option?
227                    this.options.columnWidth ||
228                    // or use the size of the first item
229                    this.$bricks.outerWidth(true) ||
230                    // if there's no items, use size of container
231                    containerWidth;
232
233      this.columnWidth += this.options.gutterWidth;
234
235      this.cols = Math.floor( ( containerWidth + this.options.gutterWidth ) / this.columnWidth );
236      this.cols = Math.max( this.cols, 1 );
237
238    },
239
240    // layout logic
241    _placeBrick: function( brick ) {
242      var $brick = $(brick),
243          colSpan, groupCount, groupY, groupColY, j;
244
245      //how many columns does this brick span
246      colSpan = Math.ceil( $brick.outerWidth(true) / this.columnWidth );
247      colSpan = Math.min( colSpan, this.cols );
248
249      if ( colSpan === 1 ) {
250        // if brick spans only one column, just like singleMode
251        groupY = this.colYs;
252      } else {
253        // brick spans more than one column
254        // how many different places could this brick fit horizontally
255        groupCount = this.cols + 1 - colSpan;
256        groupY = [];
257
258        // for each group potential horizontal position
259        for ( j=0; j < groupCount; j++ ) {
260          // make an array of colY values for that one group
261          groupColY = this.colYs.slice( j, j+colSpan );
262          // and get the max value of the array
263          groupY[j] = Math.max.apply( Math, groupColY );
264        }
265
266      }
267
268      // get the minimum Y value from the columns
269      var minimumY = Math.min.apply( Math, groupY ),
270          shortCol = 0;
271      
272      // Find index of short column, the first from the left
273      for (var i=0, len = groupY.length; i < len; i++) {
274        if ( groupY[i] === minimumY ) {
275          shortCol = i;
276          break;
277        }
278      }
279
280      // position the brick
281      var position = {
282        top: minimumY + this.offset.y
283      };
284      // position.left or position.right
285      position[ this.horizontalDirection ] = this.columnWidth * shortCol + this.offset.x;
286      this.styleQueue.push({ $el: $brick, style: position });
287
288      // apply setHeight to necessary columns
289      var setHeight = minimumY + $brick.outerHeight(true),
290          setSpan = this.cols + 1 - len;
291      for ( i=0; i < setSpan; i++ ) {
292        this.colYs[ shortCol + i ] = setHeight;
293      }
294
295    },
296    
297    
298    resize: function() {
299      var prevColCount = this.cols;
300      // get updated colCount
301      this._getColumns();
302      if ( this.isFluid || this.cols !== prevColCount ) {
303        // if column count has changed, trigger new layout
304        this._reLayout();
305      }
306    },
307    
308    
309    _reLayout : function( callback ) {
310      // reset columns
311      var i = this.cols;
312      this.colYs = [];
313      while (i--) {
314        this.colYs.push( 0 );
315      }
316      // apply layout logic to all bricks
317      this.layout( this.$bricks, callback );
318    },
319    
320    // ====================== Convenience methods ======================
321    
322    // goes through all children again and gets bricks in proper order
323    reloadItems : function() {
324      this.$bricks = this._getBricks( this.element.children() );
325    },
326    
327    
328    reload : function( callback ) {
329      this.reloadItems();
330      this._init( callback );
331    },
332    
333
334    // convienence method for working with Infinite Scroll
335    appended : function( $content, isAnimatedFromBottom, callback ) {
336      if ( isAnimatedFromBottom ) {
337        // set new stuff to the bottom
338        this._filterFindBricks( $content ).css({ top: this.element.height() });
339        var instance = this;
340        setTimeout( function(){
341          instance._appended( $content, callback );
342        }, 1 );
343      } else {
344        this._appended( $content, callback );
345      }
346    },
347    
348    _appended : function( $content, callback ) {
349      var $newBricks = this._getBricks( $content );
350      // add new bricks to brick pool
351      this.$bricks = this.$bricks.add( $newBricks );
352      this.layout( $newBricks, callback );
353    },
354    
355    // removes elements from Masonry widget
356    remove : function( $content ) {
357      this.$bricks = this.$bricks.not( $content );
358      $content.remove();
359    },
360    
361    // destroys widget, returns elements and container back (close) to original style
362    destroy : function() {
363
364      this.$bricks
365        .removeClass('masonry-brick')
366        .each(function(){
367          this.style.position = '';
368          this.style.top = '';
369          this.style.left = '';
370        });
371      
372      // re-apply saved container styles
373      var elemStyle = this.element[0].style;
374      for ( var prop in this.originalStyle ) {
375        elemStyle[ prop ] = this.originalStyle[ prop ];
376      }
377
378      this.element
379        .unbind('.masonry')
380        .removeClass('masonry')
381        .removeData('masonry');
382      
383      $(window).unbind('.masonry');
384
385    }
386    
387  };
388  
389  
390  // ======================= imagesLoaded Plugin ===============================
391  /*!
392   * jQuery imagesLoaded plugin v1.1.0
393   * http://github.com/desandro/imagesloaded
394   *
395   * MIT License. by Paul Irish et al.
396   */
397
398
399  // $('#my-container').imagesLoaded(myFunction)
400  // or
401  // $('img').imagesLoaded(myFunction)
402
403  // execute a callback when all images have loaded.
404  // needed because .load() doesn't work on cached images
405
406  // callback function gets image collection as argument
407  //  `this` is the container
408
409  $.fn.imagesLoaded = function( callback ) {
410    var $this = this,
411        $images = $this.find('img').add( $this.filter('img') ),
412        len = $images.length,
413        blank = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==',
414        loaded = [];
415
416    function triggerCallback() {
417      callback.call( $this, $images );
418    }
419
420    function imgLoaded( event ) {
421      var img = event.target;
422      if ( img.src !== blank && $.inArray( img, loaded ) === -1 ){
423        loaded.push( img );
424        if ( --len <= 0 ){
425          setTimeout( triggerCallback );
426          $images.unbind( '.imagesLoaded', imgLoaded );
427        }
428      }
429    }
430
431    // if no images, trigger immediately
432    if ( !len ) {
433      triggerCallback();
434    }
435
436    $images.bind( 'load.imagesLoaded error.imagesLoaded',  imgLoaded ).each( function() {
437      // cached images don't fire load sometimes, so we reset src.
438      var src = this.src;
439      // webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f
440      // data uri bypasses webkit log warning (thx doug jones)
441      this.src = blank;
442      this.src = src;
443    });
444
445    return $this;
446  };
447
448
449  // helper function for logging errors
450  // $.error breaks jQuery chaining
451  var logError = function( message ) {
452    if ( window.console ) {
453      window.console.error( message );
454    }
455  };
456  
457  // =======================  Plugin bridge  ===============================
458  // leverages data method to either create or return $.Mason constructor
459  // A bit from jQuery UI
460  //   https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js
461  // A bit from jcarousel 
462  //   https://github.com/jsor/jcarousel/blob/master/lib/jquery.jcarou
462sel.js
463
464  $.fn.masonry = function( options ) {
465    if ( typeof options === 'string' ) {
466      // call method
467      var args = Array.prototype.slice.call( arguments, 1 );
468
469      this.each(function(){
470        var instance = $.data( this, 'masonry' );
471        if ( !instance ) {
472          logError( "cannot call methods on masonry prior to initialization; " +
473            "attempted to call method '" + options + "'" );
474          return;
475        }
476        if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) {
477          logError( "no such method '" + options + "' for masonry instance" );
478          return;
479        }
480        // apply method
481        instance[ options ].apply( instance, args );
482      });
483    } else {
484      this.each(function() {
485        var instance = $.data( this, 'masonry' );
486        if ( instance ) {
487          // apply options & init
488          instance.option( options || {} );
489          instance._init();
490        } else {
491          // initialize new instance
492          $.data( this, 'masonry', new $.Mason( options, this ) );
493        }
494      });
495    }
496    return this;
497  };
498
499})( window, jQuery );

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