├── imgs ├── 1.jpg ├── 2.jpg ├── 3.jpg ├── 4.jpg ├── 5.jpg ├── 6.jpg ├── 7.jpg └── 1-thumbnail.jpg ├── src ├── img │ └── imgs-grid-icons.png ├── images-grid.css ├── images-grid.less └── images-grid.js ├── LICENSE.txt ├── index.html ├── test.html └── README.md /imgs/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taras-d/images-grid/HEAD/imgs/1.jpg -------------------------------------------------------------------------------- /imgs/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taras-d/images-grid/HEAD/imgs/2.jpg -------------------------------------------------------------------------------- /imgs/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taras-d/images-grid/HEAD/imgs/3.jpg -------------------------------------------------------------------------------- /imgs/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taras-d/images-grid/HEAD/imgs/4.jpg -------------------------------------------------------------------------------- /imgs/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taras-d/images-grid/HEAD/imgs/5.jpg -------------------------------------------------------------------------------- /imgs/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taras-d/images-grid/HEAD/imgs/6.jpg -------------------------------------------------------------------------------- /imgs/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taras-d/images-grid/HEAD/imgs/7.jpg -------------------------------------------------------------------------------- /imgs/1-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taras-d/images-grid/HEAD/imgs/1-thumbnail.jpg -------------------------------------------------------------------------------- /src/img/imgs-grid-icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taras-d/images-grid/HEAD/src/img/imgs-grid-icons.png -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright 2016-2017 taras-d, https://taras-d.github.io/images-grid/ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 16 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 17 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 18 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 19 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 20 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Images grid 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 29 | 30 | 31 | 32 | 33 |

34 | Images grid jQuery plugin
35 | More info on GitHub 36 |

37 | 38 | 39 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Images grid test 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 31 | 32 | 33 | 34 | 35 |

1 image

36 | 37 | 38 |

2 images

39 | 40 | 41 |

3 images

42 | 43 | 44 |

4 images

45 | 46 | 47 |

5 images

48 | 49 | 50 |

6 images

51 | 52 | 53 |

More than 6 images

54 | 55 | 56 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # images-grid 2 | 3 | Images grid jQuery plugin 4 | 5 | [Demo](https://taras-d.github.io/demo/images-grid) 6 | 7 | ## Usage 8 | Add Images grid plugin to html page (or use [CDN links](#cdn-links)) 9 | ```html 10 | 11 | 12 | ``` 13 | Init Images grid 14 | ```html 15 |
16 | 21 | ``` 22 | 23 | ## Options 24 | 25 | ##### **`images {array}`** 26 | Array of images. Array element can be string or object 27 | ```javascript 28 | images: [ 29 | 'hello.png', 30 | 'preview.jpg', 31 | { 32 | src: 'car.png', // url 33 | alt: 'Car', // alternative text 34 | title: 'Car', // title 35 | caption: 'Supercar', // modal caption 36 | thumbnail: 'cap-preview.png' // thumbnail image url 37 | } 38 | ] 39 | ``` 40 | 41 | ##### **`cells {number}`** 42 | Maximum number of cells (min: 1, max: 6, default: 5) 43 | ```javascript 44 | cells: 5 45 | ``` 46 | 47 | ##### **`align {boolean}`** 48 | Align images with different height (default: false) 49 | ```javascript 50 | align: false 51 | ``` 52 | 53 | ##### **`nextOnClick {boolean}`** 54 | Show next image when click on modal image (default: true) 55 | ```javascript 56 | nextOnClick: true 57 | ``` 58 | 59 | ##### **`loading {string}`** 60 | Image loading text (default: 'loading...') 61 | ```javascript 62 | loading: 'loading...' 63 | ``` 64 | 65 | ##### **`showViewAll {string|boolean}`** 66 | Show view all text (default: 'more') 67 | ```javascript 68 | // Possible values: 69 | showViewAll: 'more' // show if number of images greater than number of cells 70 | 'always' // always show 71 | false // never show 72 | ``` 73 | 74 | ##### **`viewAllStartIndex {number}`** 75 | Start image index when view all link clicked (default: 'auto') 76 | ```javascript 77 | viewAllStartIndex: 'auto' 78 | ``` 79 | 80 | ##### **`getViewAllText {function}`** 81 | Callback function returns text for "view all images" link 82 | ```javascript 83 | getViewAllText: function(imagesCount) { 84 | return 'View all ' + imagesCount + ' images'; 85 | } 86 | ``` 87 | 88 | #### Grid Events: 89 | 90 | ##### **`onGridRendered {function}`** 91 | Callback function fired when grid items added to the DOM 92 | ```javascript 93 | onGridRendered: function($grid) { } 94 | ``` 95 | 96 | ##### **`onGridItemRendered {function}`** 97 | Callback function fired when grid item added to the DOM 98 | ```javascript 99 | onGridItemRendered: function($item, image) { } 100 | ``` 101 | 102 | ##### **`onGridLoaded {function}`** 103 | Callback function fired when grid images loaded 104 | ```javascript 105 | onGridLoaded: function($grid) { } 106 | ``` 107 | 108 | ##### **`onGridImageLoaded {function}`** 109 | Callback function fired when grid image loaded 110 | ```javascript 111 | onGridImageLoaded: function(event, $img, image) { } 112 | 113 | ``` 114 | 115 | #### Modal Events: 116 | 117 | ##### **`onModalOpen {function}`** 118 | Callback function called when modal opened 119 | ```javascript 120 | onModalOpen: function($modal, image) { } 121 | ``` 122 | 123 | ##### **`onModalClose {function}`** 124 | Callback function called when modal closed 125 | ```javascript 126 | onModalClose: function() { } 127 | ``` 128 | 129 | ##### **`onModalImageClick {function}`** 130 | Callback function called on modal image click 131 | ```javascript 132 | onModalImageClick: function(event, $img, image) { } 133 | ``` 134 | 135 | ##### **`onModalImageUpdate {function}`** 136 | Callback function called when modal image updated 137 | ```javascript 138 | onModalImageUpdate: function($img, image) { } 139 | ``` 140 | 141 | ## Methods: 142 | 143 | ##### **modal.open** 144 | Open modal window (optional second parameter is image index) 145 | ```javascript 146 | $('#imgs').imagesGrid('modal.open', 0) 147 | ``` 148 | 149 | ##### **modal.close** 150 | Close modal window 151 | ```javascript 152 | $('#imgs').imagesGrid('modal.close') 153 | ``` 154 | 155 | ##### **destroy** 156 | Destroy images grid (remove DOM nodes and event listeners) 157 | ```javascript 158 | $('#imgs').imagesGrid('destroy') 159 | ``` 160 | 161 | ## Default options 162 | Default options can be found [here](https://github.com/taras-d/images-grid/blob/master/src/images-grid.js#L49-L72) 163 | 164 | ## CDN links 165 | ```html 166 | 167 | 168 | ``` 169 | -------------------------------------------------------------------------------- /src/images-grid.css: -------------------------------------------------------------------------------- 1 | .imgs-grid { 2 | max-width: 800px; 3 | margin: 0 auto; 4 | font-size: 0; 5 | } 6 | .imgs-grid.imgs-grid-1 .imgs-grid-image { 7 | width: 100%; 8 | text-align: center; 9 | } 10 | .imgs-grid.imgs-grid-2 .imgs-grid-image, 11 | .imgs-grid.imgs-grid-4 .imgs-grid-image { 12 | width: 50%; 13 | } 14 | .imgs-grid.imgs-grid-3 .imgs-grid-image, 15 | .imgs-grid.imgs-grid-6 .imgs-grid-image { 16 | width: 33.333333333333336%; 17 | } 18 | .imgs-grid.imgs-grid-5 .imgs-grid-image:nth-child(1), 19 | .imgs-grid.imgs-grid-5 .imgs-grid-image:nth-child(2), 20 | .imgs-grid.imgs-grid-5 .imgs-grid-image:nth-child(3) { 21 | width: 33.333333333333336%; 22 | } 23 | .imgs-grid.imgs-grid-5 .imgs-grid-image:nth-child(4), 24 | .imgs-grid.imgs-grid-5 .imgs-grid-image:nth-child(5) { 25 | width: 50%; 26 | } 27 | .imgs-grid .imgs-grid-image { 28 | position: relative; 29 | display: inline-block; 30 | padding: 1px; 31 | box-sizing: border-box; 32 | text-align: center; 33 | } 34 | .imgs-grid .imgs-grid-image:before { 35 | content: ""; 36 | display: block; 37 | position: absolute; 38 | top: 1px; 39 | left: 1px; 40 | right: 1px; 41 | bottom: 1px; 42 | background-color: #f0f0f0; 43 | } 44 | .imgs-grid .imgs-grid-image:hover { 45 | cursor: pointer; 46 | } 47 | .imgs-grid .imgs-grid-image .image-wrap { 48 | position: relative; 49 | display: inline-block; 50 | overflow: hidden; 51 | vertical-align: middle; 52 | } 53 | .imgs-grid .imgs-grid-image .image-wrap img { 54 | position: relative; 55 | width: 100%; 56 | height: auto; 57 | margin: 0; 58 | } 59 | .imgs-grid .imgs-grid-image .view-all { 60 | position: absolute; 61 | top: 0; 62 | left: 0; 63 | right: 0; 64 | bottom: 0; 65 | text-align: center; 66 | } 67 | .imgs-grid .imgs-grid-image .view-all:before { 68 | display: inline-block; 69 | content: ""; 70 | vertical-align: middle; 71 | height: 100%; 72 | } 73 | .imgs-grid .imgs-grid-image .view-all:hover { 74 | cursor: pointer; 75 | } 76 | .imgs-grid .imgs-grid-image .view-all:hover .view-all-text { 77 | text-decoration: underline; 78 | } 79 | .imgs-grid .imgs-grid-image .view-all .view-all-cover { 80 | position: absolute; 81 | top: 0; 82 | left: 0; 83 | width: 100%; 84 | height: 100%; 85 | background-color: black; 86 | opacity: 0.4; 87 | } 88 | .imgs-grid .imgs-grid-image .view-all .view-all-text { 89 | position: relative; 90 | font-size: 16px; 91 | font-family: sans-serif; 92 | color: white; 93 | } 94 | @media (max-width: 350px) { 95 | .imgs-grid .imgs-grid-image .view-all .view-all-text { 96 | font-size: 10px; 97 | } 98 | } 99 | .imgs-grid-modal { 100 | position: fixed; 101 | left: 0; 102 | right: 0; 103 | top: 0; 104 | bottom: 0; 105 | background-color: black; 106 | opacity: 0; 107 | z-index: 100; 108 | -webkit-user-select: none; 109 | -moz-user-select: -moz-none; 110 | -khtml-user-select: none; 111 | -o-user-select: none; 112 | user-select: none; 113 | } 114 | .imgs-grid-modal .modal-caption { 115 | padding: 30px 50px; 116 | text-align: center; 117 | color: white; 118 | } 119 | .imgs-grid-modal .modal-close { 120 | position: absolute; 121 | right: 10px; 122 | top: 10px; 123 | width: 35px; 124 | height: 35px; 125 | background-image: url(img/imgs-grid-icons.png); 126 | background-repeat: no-repeat; 127 | background-position: -100px; 128 | } 129 | .imgs-grid-modal .modal-close:hover { 130 | cursor: pointer; 131 | } 132 | .imgs-grid-modal .modal-inner { 133 | position: absolute; 134 | top: 60px; 135 | bottom: 60px; 136 | left: 0; 137 | right: 0; 138 | } 139 | .imgs-grid-modal .modal-inner .modal-control { 140 | position: absolute; 141 | top: 0; 142 | bottom: 0; 143 | width: 70px; 144 | } 145 | .imgs-grid-modal .modal-inner .modal-control:hover { 146 | cursor: pointer; 147 | } 148 | .imgs-grid-modal .modal-inner .modal-control.left { 149 | left: 0; 150 | } 151 | .imgs-grid-modal .modal-inner .modal-control.right { 152 | right: 0; 153 | } 154 | .imgs-grid-modal .modal-inner .modal-control .arrow { 155 | margin: 0 auto; 156 | height: 100%; 157 | width: 40px; 158 | background-repeat: no-repeat; 159 | background-image: url(img/imgs-grid-icons.png); 160 | } 161 | .imgs-grid-modal .modal-inner .modal-control .arrow.left { 162 | background-position: 2px center; 163 | } 164 | .imgs-grid-modal .modal-inner .modal-control .arrow.right { 165 | background-position: -42px center; 166 | } 167 | .imgs-grid-modal .modal-inner .modal-image { 168 | position: absolute; 169 | top: 0; 170 | left: 70px; 171 | right: 70px; 172 | bottom: 0; 173 | text-align: center; 174 | } 175 | .imgs-grid-modal .modal-inner .modal-image:before { 176 | display: inline-block; 177 | content: ""; 178 | vertical-align: middle; 179 | height: 100%; 180 | } 181 | .imgs-grid-modal .modal-inner .modal-image img { 182 | max-width: 100%; 183 | max-height: 100%; 184 | vertical-align: middle; 185 | } 186 | .imgs-grid-modal .modal-inner .modal-image img:hover { 187 | cursor: pointer; 188 | } 189 | .imgs-grid-modal .modal-inner .modal-loader { 190 | display: inline-block; 191 | vertical-align: middle; 192 | color: silver; 193 | font-size: 14px; 194 | } 195 | @media (max-width: 800px) { 196 | .imgs-grid-modal .modal-inner .modal-control { 197 | width: 40px; 198 | } 199 | .imgs-grid-modal .modal-inner .modal-control .arrow { 200 | -webkit-transform: scale(0.7); 201 | -moz-transform: scale(0.7); 202 | -o-transform: scale(0.7); 203 | -ms-transform: scale(0.7); 204 | transform: scale(0.7); 205 | } 206 | .imgs-grid-modal .modal-inner .modal-image { 207 | left: 0; 208 | right: 0; 209 | } 210 | } 211 | .imgs-grid-modal .modal-indicator { 212 | position: absolute; 213 | bottom: 0; 214 | height: 60px; 215 | width: 100%; 216 | text-align: center; 217 | } 218 | .imgs-grid-modal .modal-indicator ul { 219 | margin: 0; 220 | padding: 0; 221 | } 222 | .imgs-grid-modal .modal-indicator ul li { 223 | display: inline-block; 224 | width: 12px; 225 | height: 12px; 226 | border: 1px solid white; 227 | box-sizing: border-box; 228 | border-radius: 100%; 229 | margin: 0 1px; 230 | vertical-align: middle; 231 | } 232 | .imgs-grid-modal .modal-indicator ul li:hover { 233 | cursor: pointer; 234 | } 235 | .imgs-grid-modal .modal-indicator ul li.selected { 236 | background-color: white; 237 | width: 14px; 238 | height: 14px; 239 | margin: 0; 240 | } 241 | -------------------------------------------------------------------------------- /src/images-grid.less: -------------------------------------------------------------------------------- 1 | 2 | .imgs-grid { 3 | max-width: 800px; 4 | margin: 0 auto; 5 | font-size: 0; 6 | &.imgs-grid-1 .imgs-grid-image { 7 | width: 100%; 8 | text-align: center; 9 | } 10 | &.imgs-grid-2 .imgs-grid-image, 11 | &.imgs-grid-4 .imgs-grid-image { 12 | width: 50%; 13 | } 14 | &.imgs-grid-3 .imgs-grid-image, 15 | &.imgs-grid-6 .imgs-grid-image { 16 | width: 33.333333333333336%; 17 | } 18 | &.imgs-grid-5 { 19 | .imgs-grid-image:nth-child(1), 20 | .imgs-grid-image:nth-child(2), 21 | .imgs-grid-image:nth-child(3) { 22 | width: 33.333333333333336%; 23 | } 24 | .imgs-grid-image:nth-child(4), 25 | .imgs-grid-image:nth-child(5) { 26 | width: 50%; 27 | } 28 | } 29 | .imgs-grid-image { 30 | position: relative; 31 | display: inline-block; 32 | padding: 1px; 33 | box-sizing: border-box; 34 | text-align: center; 35 | &:before { 36 | content: ""; 37 | display: block; 38 | position: absolute; 39 | top: 1px; 40 | left: 1px; 41 | right: 1px; 42 | bottom: 1px; 43 | background-color: #f0f0f0; 44 | } 45 | &:hover { 46 | cursor: pointer; 47 | } 48 | .image-wrap { 49 | position: relative; 50 | display: inline-block; 51 | overflow: hidden; 52 | vertical-align: middle; 53 | img { 54 | position: relative; 55 | width: 100%; 56 | height: auto; 57 | margin: 0; 58 | } 59 | } 60 | .view-all { 61 | position: absolute; 62 | top: 0; 63 | left: 0; 64 | right: 0; 65 | bottom: 0; 66 | text-align: center; 67 | &:before { 68 | display: inline-block; 69 | content: ""; 70 | vertical-align: middle; 71 | height: 100%; 72 | } 73 | &:hover { 74 | cursor: pointer; 75 | .view-all-text { 76 | text-decoration: underline; 77 | } 78 | } 79 | .view-all-cover { 80 | position: absolute; 81 | top: 0; 82 | left: 0; 83 | width: 100%; 84 | height: 100%; 85 | background-color: black; 86 | opacity: 0.4; 87 | } 88 | .view-all-text { 89 | position: relative; 90 | font-size: 16px; 91 | font-family: sans-serif; 92 | color: white; 93 | @media(max-width: 350px) { 94 | font-size: 10px; 95 | } 96 | } 97 | } 98 | } 99 | } 100 | 101 | .imgs-grid-modal { 102 | position: fixed; 103 | left: 0; 104 | right: 0; 105 | top: 0; 106 | bottom: 0; 107 | background-color: black; 108 | opacity: 0; 109 | z-index: 100; 110 | -webkit-user-select: none; 111 | -moz-user-select: -moz-none; 112 | -khtml-user-select: none; 113 | -o-user-select: none; 114 | user-select: none; 115 | .modal-caption { 116 | padding: 30px 50px; 117 | text-align: center; 118 | color: white; 119 | } 120 | .modal-close { 121 | position: absolute; 122 | right: 10px; 123 | top: 10px; 124 | width: 35px; 125 | height: 35px; 126 | background-image: url(img/imgs-grid-icons.png); 127 | background-repeat: no-repeat; 128 | background-position: -100px; 129 | &:hover { 130 | cursor: pointer; 131 | } 132 | } 133 | .modal-inner { 134 | position: absolute; 135 | top: 60px; 136 | bottom: 60px; 137 | left: 0; 138 | right: 0; 139 | .modal-control { 140 | position: absolute; 141 | top: 0; 142 | bottom: 0; 143 | width: 70px; 144 | &:hover { 145 | cursor: pointer; 146 | } 147 | &.left { 148 | left: 0; 149 | } 150 | &.right { 151 | right: 0; 152 | } 153 | .arrow { 154 | margin: 0 auto; 155 | height: 100%; 156 | width: 40px; 157 | background-repeat: no-repeat; 158 | background-image: url(img/imgs-grid-icons.png); 159 | &.left { 160 | background-position: 2px center; 161 | } 162 | &.right { 163 | background-position: -42px center; 164 | } 165 | } 166 | } 167 | .modal-image { 168 | position: absolute; 169 | top: 0; 170 | left: 70px; 171 | right: 70px; 172 | bottom: 0; 173 | text-align: center; 174 | &:before { 175 | display: inline-block; 176 | content: ""; 177 | vertical-align: middle; 178 | height: 100%; 179 | } 180 | img { 181 | max-width: 100%; 182 | max-height: 100%; 183 | vertical-align: middle; 184 | &:hover { 185 | cursor: pointer; 186 | } 187 | } 188 | } 189 | .modal-loader { 190 | display: inline-block; 191 | vertical-align: middle; 192 | color: silver; 193 | font-size: 14px; 194 | } 195 | @media (max-width: 800px) { 196 | .modal-control { 197 | width: 40px; 198 | .arrow { 199 | -webkit-transform: scale(0.7); 200 | -moz-transform: scale(0.7); 201 | -o-transform: scale(0.7); 202 | -ms-transform: scale(0.7); 203 | transform: scale(0.7); 204 | } 205 | } 206 | .modal-image { 207 | left: 0; 208 | right: 0; 209 | } 210 | } 211 | } 212 | .modal-indicator { 213 | position: absolute; 214 | bottom: 0; 215 | height: 60px; 216 | width: 100%; 217 | text-align: center; 218 | ul { 219 | margin: 0; 220 | padding: 0; 221 | li { 222 | display: inline-block; 223 | width: 12px; 224 | height: 12px; 225 | border: 1px solid white; 226 | box-sizing: border-box; 227 | border-radius: 100%; 228 | margin: 0 1px; 229 | vertical-align: middle; 230 | &:hover { 231 | cursor: pointer; 232 | } 233 | &.selected { 234 | background-color: white; 235 | width: 14px; 236 | height: 14px; 237 | margin: 0; 238 | } 239 | } 240 | } 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /src/images-grid.js: -------------------------------------------------------------------------------- 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 'View all ' + imagesCount + ' images'; 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 = $('
', { 190 | class: 'imgs-grid-image', 191 | click: this.onImageClick, 192 | data: { index: index } 193 | }); 194 | 195 | item.append( 196 | $('
', { 197 | class: 'image-wrap' 198 | }).append( 199 | $('', { 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 | $('
', { 224 | class: 'view-all' 225 | }).append( 226 | $('', { 227 | class: 'view-all-cover', 228 | }), 229 | $('', { 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 = $('
', { 428 | class: 'imgs-grid-modal' 429 | }).appendTo('body'); 430 | } 431 | 432 | ImagesGridModal.prototype.renderCaption = function() { 433 | this.$caption = $('
', { 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($('
', { 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 | $('
', { 453 | class: 'modal-inner' 454 | }).append( 455 | $('
', { 456 | class: 'modal-image' 457 | }).append( 458 | $('', { 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 | $('
', { 470 | class: 'modal-loader', 471 | html: opts.loading 472 | }) 473 | ), 474 | $('
', { 475 | class: 'modal-control left', 476 | click: this.prev 477 | }).append( 478 | $('
', { 479 | class: 'arrow left' 480 | }) 481 | ), 482 | $('
', { 483 | class: 'modal-control right', 484 | click: this.next 485 | }).append( 486 | $('
', { 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 = $('
', { 508 | class: 'modal-indicator' 509 | }); 510 | 511 | var list = $('
    '), i; 512 | for (i = 0; i < imgsLen; ++i) { 513 | list.append($('
  • ', { 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); 653 | --------------------------------------------------------------------------------