1 2;(function($) { 3 4 /** 5 * Plugin 6 */ 7 8 $.fn.imagesGrid = function(options) { 9 10 var args = arguments; 11 12 return this.each(function() { 13 14 // If options is plain object - destroy previous instance and create new 15 if ($.isPlainObject(options)) { 16 17 if (this._imgGrid instanceof ImagesGrid) { 18 this._imgGrid.destroy(); 19 delete this._imgGrid; 20 } 21 22 var opts = $.extend({}, $.fn.imagesGrid.defaults, options); 23 opts.element = $(this); 24 this._imgGrid = new ImagesGrid(opts); 25 26 return; 27 } 28 29 // If options is string - execute method 30 if (typeof options === 'string' && this._imgGrid instanceof ImagesGrid) { 31 switch (options) { 32 case 'modal.open': 33 this._imgGrid.modal.open(args[1]); 34 break; 35 case 'modal.close': 36 this._imgGrid.modal.close(); 37 break; 38 case 'destroy': 39 this._imgGrid.destroy(); 40 delete this._imgGrid; 41 break; 42 } 43 } 44 45 }); 46 47 }; 48 49 /** 50 * Plugin default options 51 */ 52 53 $.fn.imagesGrid.defaults = { 54 images: [], 55 cells: 5, 56 align: false, 57 nextOnClick: true, 58 showViewAll: 'more', 59 viewAllStartIndex: 'auto', 60 loading: 'loading...', 61 getViewAllText: function(imagesCount) { 62 return '+' + imagesCount + ''; 63 }, 64 onGridRendered: $.noop, 65 onGridItemRendered: $.noop, 66 onGridLoaded: $.noop, 67 onGridImageLoaded: $.noop, 68 onModalOpen: $.noop, 69 onModalClose: $.noop, 70 onModalImageClick: $.noop, 71 onModalImageUpdate: $.noop 72 }; 73 74 /** 75 * ImagesGrid 76 * opts - Grid options 77 * opts.element - Element where to render images grid 78 * opts.images - Array of images. Array item can be string or object { src, alt, title, caption, thumbnail } 79 * opts.align - Align images with different height 80 * opts.cells - Maximum number of cells (from 1 to 6) 81 * opts.showViewAll - Show view all text: 82 * 'more' - show if number of images greater than number of cells 83 * 'always' - always show 84 * false - never show 85 * opts.viewAllStartIndex - Start image index when view all link clicked 86 * opts.getViewAllText - Callback function returns text for "view all images" link 87 * opts.onGridRendered - Callback function fired when grid items added to the DOM 88 * opts.onGridItemRendered - Callback function fired when grid item added to the DOM 89 * opts.onGridLoaded - Callback function fired when grid images loaded 90 * opts.onGridImageLoaded - Callback function fired when grid image loaded 91 */ 92 93 function ImagesGrid(opts) { 94 95 this.opts = opts || {}; 96 97 this.$window = $(window); 98 this.$element = this.opts.element; 99 this.$gridItems = []; 100 101 this.modal = null; 102 this.imageLoadCount = 0; 103 104 var cells = this.opts.cells; 105 this.opts.cells = (cells < 1)? 1: (cells > 6)? 6: cells; 106 107 this.onWindowResize = this.onWindowResize.bind(this); 108 this.onImageClick = this.onImageClick.bind(this); 109 110 this.init(); 111 } 112 113 ImagesGrid.prototype.init = function() { 114 115 this.setGridClass(); 116 this.renderGridItems(); 117 this.createModal(); 118 119 this.$window.on('resize', this.onWindowResize); 120 } 121 122 ImagesGrid.prototype.createModal = function() { 123 124 var opts = this.opts; 125 126 this.modal = new ImagesGridModal({ 127 loading: opts.loading, 128 images: opts.images, 129 nextOnClick: opts.nextOnClick, 130 onModalOpen: opts.onModalOpen, 131 onModalClose: opts.onModalClose, 132 onModalImageClick: opts.onModalImageClick, 133 onModalImageUpdate: opts.onModalImageUpdate 134 }); 135 } 136
137 ImagesGrid.prototype.setGridClass = function() { 138 139 var opts = this.opts, 140 imgsLen = opts.images.length, 141 cellsCount = (imgsLen < opts.cells)? imgsLen: opts.cells; 142 143 this.$element.addClass('imgs-grid imgs-grid-' + cellsCount); 144 } 145 146 ImagesGrid.prototype.renderGridItems = function() { 147 148 var opts = this.opts, 149 imgs = opts.images, 150 imgsLen = imgs.length; 151 152 if (!imgs) { 153 return; 154 } 155 156 this.$element.empty(); 157 this.$gridItems = []; 158 159 for (var i = 0; i < imgsLen; ++i) { 160 if (i === opts.cells) { 161 break; 162 } 163 this.renderGridItem(imgs[i], i); 164 } 165 166 if (opts.showViewAll === 'always' || 167 (opts.showViewAll === 'more' && imgsLen > opts.cells) 168 ) { 169 this.renderViewAll(); 170 } 171 172 opts.onGridRendered(this.$element); 173 } 174 175 ImagesGrid.prototype.renderGridItem = function(image, index) { 176 177 var src = image, 178 alt = '', 179 title = '', 180 opts = this.opts, 181 _this = this; 182 183 if ($.isPlainObject(image)) { 184 src = image.thumbnail || image.src; 185 alt = image.alt || ''; 186 title = image.title || ''; 187 } 188 189 var item = $('<div>', { 190 class: 'imgs-grid-image', 191 click: this.onImageClick, 192 data: { index: index } 193 }); 194 195 item.append( 196 $('<div>', { 197 class: 'image-wrap' 198 }).append( 199 $('<img>', { 200 src: src, 201 alt: alt, 202 title: title, 203 on: { 204 load: function(event) { 205 _this.onImageLoaded(event, $(this), image); 206 } 207 } 208 }) 209 ) 210 ); 211 212 this.$gridItems.push(item); 213 this.$element.append(item); 214 215 opts.onGridItemRendered(item, image); 216 } 217 218 ImagesGrid.prototype.renderViewAll = function() { 219 220 var opts = this.opts; 221 222 this.$element.find('.imgs-grid-image:last .image-wrap').append( 223 $('<div>', { 224 class: 'view-all' 225 }).append( 226 $('<span>', { 227 class: 'view-all-cover', 228 }), 229 $('<span>', { 230 class: 'view-all-text', 231 text: opts.getViewAllText(opts.images.length) 232 }) 233 ) 234 ); 235 } 236 237 ImagesGrid.prototype.onWindowResize = function(event) { 238 if (this.opts.align) { 239 this.align(); 240 } 241 } 242 243 ImagesGrid.prototype.onImageClick = function(event) { 244 245 var opts = this.opts, 246 img = $(event.currentTarget), 247 imageIndex; 248 249 if (img.find('.view-all').length > 0 && 250 typeof opts.viewAllStartIndex === 'number' ) { 251 imageIndex = opts.viewAllStartIndex; 252 } else { 253 imageIndex = img.data('index'); 254 } 255 256 this.modal.open(imageIndex); 257 } 258 259 ImagesGrid.prototype.onImageLoaded = function(event, imageEl, image) { 260 261 var opts = this.opts; 262 263 ++this.imageLoadCount; 264 265 opts.onGridImageLoaded(event, imageEl, image); 266 267 if (this.imageLoadCount === this.$gridItems.length) { 268 this.imageLoadCount = 0; 269 this.onAllImagesLoaded() 270 } 271 } 272 273 ImagesGrid.prototype.onAllImagesLoaded = function() { 274 275 var opts = this.opts; 276 277 if (opts.align) { 278 this.align(); 279 } 280 281 opts.onGridLoaded(this.$element); 282 } 283 284 ImagesGrid.prototype.align = function() { 285 286 var itemsLen = this.$gridItems.length; 287 288 switch (itemsLen) { 289 case 2: 290 case 3: 291 this.alignItems(this.$gridItems); 292 break; 293 case 4: 294 this.alignItems(this.$gridItems.slice(0, 2)); 295 this.alignItems(this.$gridItems.slice(2)); 296 break; 297 case 5: 298 case 6: 299 this.alignItems(this.$gridItems.slice(0, 3)); 300 this.alignItems(this.$gridItems.slice(3)); 301 break; 302 } 303 } 304
305 ImagesGrid.prototype.alignItems = function(items) { 306 307 var itemsHeight = items.map(function(item) { 308 return item.find('img').height(); 309 }); 310 311 var normalizedHeight = Math.min.apply(null, itemsHeight); 312 313 $(items).each(function() { 314 315 var item = $(this), 316 imgWrap = item.find('.image-wrap'), 317 img = item.find('img'), 318 imgHeight = img.height(); 319 320 imgWrap.height(normalizedHeight); 321 322 if (imgHeight > normalizedHeight) { 323 var top = Math.floor((imgHeight - normalizedHeight) / 2); 324 img.css({ top: -top }); 325 } 326 }); 327 } 328 329 ImagesGrid.prototype.destroy = function() { 330 331 this.$window.off('resize',this.onWindowResize); 332 333 this.$element.empty() 334 .removeClass('imgs-grid imgs-grid-' + this.$gridItems.length); 335 336 this.modal.destroy(); 337 } 338 339 /** 340 * ImagesGridModal 341 * opts - Modal options 342 * opts.images - Array of images 343 * opts.nextOnClick - Show next image when click on modal image 344 * opts.loading - Image loading text 345 * opts.onModalOpen - Callback function called when modal opened 346 * opts.onModalClose - Callback function called when modal closed 347 * opts.onModalImageClick - Callback function called on modal image click 348 */ 349 350 function ImagesGridModal(opts) { 351 352 this.opts = opts || {}; 353 354 this.imageIndex = null; 355 356 this.$document = $(document); 357 this.$modal = null; 358 this.$indicator = null; 359 360 this.close = this.close.bind(this); 361 this.prev = this.prev.bind(this); 362 this.next = this.next.bind(this); 363 this.onIndicatorClick = this.onIndicatorClick.bind(this); 364 this.onImageLoaded = this.onImageLoaded.bind(this); 365 this.onKeyUp = this.onKeyUp.bind(this); 366 367 this.$document.on('keyup', this.onKeyUp); 368 } 369 370 ImagesGridModal.prototype.open = function(imageIndex) { 371 372 if (this.isOpened()) { 373 return; 374 } 375 376 this.imageIndex = parseInt(imageIndex) || 0; 377 this.render(); 378 } 379 380 ImagesGridModal.prototype.close = function(event) { 381 382 if (!this.$modal) { 383 return; 384 } 385 386 var opts = this.opts; 387 388 this.$modal.animate({ 389 opacity: 0 390 }, { 391 duration: 100, 392 complete: function() { 393 this.$modal.remove(); 394 this.$modal = null; 395 this.$indicator = null; 396 this.imageIndex = null; 397 opts.onModalClose(); 398 }.bind(this) 399 }); 400 } 401 402 ImagesGridModal.prototype.isOpened = function() { 403 return (this.$modal && this.$modal.is(':visible')); 404 } 405 406 ImagesGridModal.prototype.render = function() { 407 408 var opts = this.opts; 409 410 this.renderModal(); 411 this.renderCaption(); 412 this.renderCloseButton(); 413 this.renderInnerContainer(); 414 this.renderIndicatorContainer(); 415 416 this.$modal.animate({ 417 opacity: 1 418 }, { 419 duration: 100, 420 complete: function() { 421 opts.onModalOpen(this.$modal, opts.images[this.imageIndex]); 422 }.bind(this) 423 }); 424 } 425 426 ImagesGridModal.prototype.renderModal = function() { 427 this.$modal = $('<div>', { 428 class: 'imgs-grid-modal' 429 }).appendTo('body'); 430 } 431 432 ImagesGridModal.prototype.renderCaption = function() { 433 this.$caption = $('<div>', { 434 class: 'modal-caption', 435 text: this.getImageCaption(this.imageIndex) 436 }).appendTo(this.$modal); 437 } 438 439 ImagesGridModal.prototype.renderCloseButton = function() { 440 this.$modal.append($('<div>', { 441 class: 'modal-close', 442 click: this.close 443 })); 444 } 445 446 ImagesGridModal.prototype.renderInnerContainer = function() { 447 448 var opts = this.opts, 449 image = this.getImage(this.imageIndex); 450 451 this.$modal.append( 452 $('<div>', {
453 class: 'modal-inner' 454 }).append( 455 $('<div>', { 456 class: 'modal-image' 457 }).append( 458 $('<img>', { 459 src: image.src, 460 alt: image.alt, 461 title: image.title, 462 on: { 463 load: this.onImageLoaded, 464 click: function(event) { 465 this.onImageClick(event, $(this), image); 466 }.bind(this) 467 } 468 }), 469 $('<div>', { 470 class: 'modal-loader', 471 html: opts.loading 472 }) 473 ), 474 $('<div>', { 475 class: 'modal-control left', 476 click: this.prev 477 }).append( 478 $('<div>', { 479 class: 'arrow left' 480 }) 481 ), 482 $('<div>', { 483 class: 'modal-control right', 484 click: this.next 485 }).append( 486 $('<div>', { 487 class: 'arrow right' 488 }) 489 ) 490 ) 491 ); 492 493 if (opts.images.length <= 1) { 494 this.$modal.find('.modal-control').hide(); 495 } 496 } 497 498 ImagesGridModal.prototype.renderIndicatorContainer = function() { 499 500 var opts = this.opts, 501 imgsLen = opts.images.length; 502 503 if (imgsLen == 1) { 504 return; 505 } 506 507 this.$indicator = $('<div>', { 508 class: 'modal-indicator' 509 }); 510 511 var list = $('<ul>'), i; 512 for (i = 0; i < imgsLen; ++i) { 513 list.append($('<li>', { 514 class: this.imageIndex == i? 'selected': '', 515 click: this.onIndicatorClick, 516 data: { index: i } 517 })); 518 } 519 520 this.$indicator.append(list); 521 this.$modal.append(this.$indicator); 522 } 523 524 ImagesGridModal.prototype.prev = function() { 525 526 var imgsLen = this.opts.images.length; 527 528 if (this.imageIndex > 0) { 529 --this.imageIndex; 530 } else { 531 this.imageIndex = imgsLen - 1; 532 } 533 534 this.updateImage(); 535 } 536 537 ImagesGridModal.prototype.next = function() { 538 539 var imgsLen = this.opts.images.length; 540 541 if (this.imageIndex < imgsLen - 1) { 542 ++this.imageIndex; 543 } else { 544 this.imageIndex = 0; 545 } 546 547 this.updateImage(); 548 } 549 550 ImagesGridModal.prototype.updateImage = function() { 551 552 var opts = this.opts, 553 image = this.getImage(this.imageIndex), 554 imageEl = this.$modal.find('.modal-image img'); 555 556 imageEl.attr({ 557 src: image.src, 558 alt: image.alt, 559 title: image.title 560 }); 561 562 this.$modal.find('.modal-caption').text( 563 this.getImageCaption(this.imageIndex) ); 564 565 if (this.$indicator) { 566 var indicatorList = this.$indicator.find('ul'); 567 indicatorList.children().removeClass('selected'); 568 indicatorList.children().eq(this.imageIndex).addClass('selected'); 569 } 570 571 this.showLoader(); 572 573 opts.onModalImageUpdate(imageEl, image); 574 } 575 576 ImagesGridModal.prototype.onImageClick = function(event, imageEl, image) { 577 578 var opts = this.opts; 579 580 if (opts.nextOnClick) { 581 this.next(); 582 } 583 584 opts.onModalImageClick(event, imageEl, image); 585 } 586 587 ImagesGridModal.prototype.onImageLoaded = function() { 588 this.hideLoader(); 589 } 590 591 ImagesGridModal.prototype.onIndicatorClick = function(event) { 592 var index = $(event.target).data('index'); 593 this.imageIndex = index; 594 this.updateImage(); 595 } 596 597 ImagesGridModal.prototype.onKeyUp = function(event) { 598 599 if (!this.$modal) { 600 return; 601 } 602 603 switch (event.keyCode) { 604 case 27: // Esc 605 this.close(); 606 break; 607 case 37: // Left arrow 608 this.prev(); 609 break; 610 case 39: // Right arrow 611 this.next(); 612 break; 613 } 614 } 615
616 ImagesGridModal.prototype.getImage = function(index) { 617 618 var opts = this.opts, 619 image = opts.images[index]; 620 621 if ($.isPlainObject(image)) { 622 return image; 623 } else { 624 return { src: image, alt: '', title: '' } 625 } 626 } 627 628 ImagesGridModal.prototype.getImageCaption = function(imgIndex) { 629 var img = this.getImage(imgIndex); 630 return img.caption || ''; 631 } 632 633 ImagesGridModal.prototype.showLoader = function() { 634 if (this.$modal) { 635 this.$modal.find('.modal-image img').hide(); 636 this.$modal.find('.modal-loader').show(); 637 } 638 } 639 640 ImagesGridModal.prototype.hideLoader = function() { 641 if (this.$modal) { 642 this.$modal.find('.modal-image img').show(); 643 this.$modal.find('.modal-loader').hide(); 644 } 645 } 646 647 ImagesGridModal.prototype.destroy = function() { 648 this.$document.off('keyup', this.onKeyUp); 649 this.close(); 650 } 651 652})(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.