├── README.md ├── package.json └── src ├── index.html ├── css └── style.css └── js └── main.js /README.md: -------------------------------------------------------------------------------- 1 | # [box-img](http://tscanlin.github.io/box-img/src/index.html) 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "box-img", 3 | "version": "0.0.1", 4 | "description": "lightbox-like script for image galleries", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/tscanlin/box-img" 12 | }, 13 | "keywords": [ 14 | "lightbox", 15 | "script", 16 | "image", 17 | "gallery", 18 | "preview", 19 | "full", 20 | "screen" 21 | ], 22 | "author": "Tim Scanlin", 23 | "license": "MIT" 24 | } 25 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Box Img 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 26 | 27 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/css/style.css: -------------------------------------------------------------------------------- 1 | /* BASE */ 2 | * { 3 | box-sizing: border-box; 4 | } 5 | html { 6 | height: 100%; 7 | } 8 | body { 9 | min-height: 100%; 10 | margin: 0; 11 | line-height: 1.4; 12 | font-family: sans-serif; 13 | position: relative; 14 | } 15 | 16 | .block { 17 | display: block; 18 | } 19 | .inline { 20 | display: inline; 21 | } 22 | .inline-block { 23 | display: inline-block; 24 | } 25 | .hidden { 26 | display: none; 27 | } 28 | 29 | 30 | /* CLEARFIX (from: https://css-tricks.com/snippets/css/clear-fix/) */ 31 | .clearfix::after { 32 | visibility: hidden; 33 | display: block; 34 | font-size: 0; 35 | content: " "; 36 | clear: both; 37 | height: 0; 38 | } 39 | .clearfix { display: inline-block; } 40 | /* start commented backslash hack \*/ 41 | * html .clearfix { height: 1%; } 42 | .clearfix { display: block; } 43 | /* close commented backslash hack */ 44 | 45 | /* HELPERS */ 46 | .max-height--100 { 47 | max-height: 100%; 48 | } 49 | 50 | .font-size--2 { 51 | font-size: 2em; 52 | } 53 | 54 | .max--100 { 55 | max-width: 100%; 56 | max-height: 100%; 57 | } 58 | 59 | .center { 60 | text-align: center; 61 | } 62 | 63 | .margin--auto { 64 | margin: auto; 65 | } 66 | 67 | .cursor--pointer { 68 | cursor: pointer; 69 | } 70 | 71 | .left { 72 | float: left; 73 | } 74 | .right { 75 | float: right; 76 | } 77 | 78 | .bg--dark { 79 | background-color: rgba(10, 10, 10, 0.5); 80 | } 81 | .bg--darker { 82 | background-color: rgba(10, 10, 10, 0.9); 83 | } 84 | .transition { 85 | transition: all 300ms ease-in-out; 86 | } 87 | 88 | 89 | /* GALLERY COMPONENTS */ 90 | .gallery { 91 | padding: 20px; 92 | } 93 | 94 | .image-container { 95 | margin-right: 5px; 96 | margin-bottom: 5px; 97 | position: relative; 98 | overflow: hidden; 99 | height: 240px; 100 | } 101 | 102 | .image-meta { 103 | color: white; 104 | position: relative; 105 | top: -1.5em; 106 | padding: 2px 4px; 107 | width: 100%; 108 | } 109 | 110 | 111 | /* PREVIEW COMPONENTS */ 112 | .preview-container { 113 | position: absolute; 114 | min-height: inherit; 115 | top: 0; 116 | left: 0; 117 | right: 0; 118 | } 119 | 120 | .preview-image-container { 121 | display: block; 122 | max-width: 90%; 123 | max-height: 90%; 124 | margin: 5% auto; 125 | overflow: hidden; 126 | } 127 | 128 | .preview-controls { 129 | padding: 0 20px; 130 | text-align: center; 131 | width: auto; 132 | } 133 | 134 | .preview-arrow { 135 | position: relative; 136 | top: -2px; 137 | } 138 | 139 | .preview-title { 140 | margin: 0 40px; 141 | } 142 | 143 | .btn--close { 144 | color: white; 145 | position: absolute; 146 | top: 0; 147 | right: 0; 148 | padding-top: 0.1em; 149 | padding-right: 0.4em; 150 | font-size: 3em; 151 | } 152 | 153 | 154 | /* CSS TRANSITIONS */ 155 | .preview-container { 156 | opacity: 1; 157 | z-index: 300; 158 | } 159 | 160 | .preview-image-container { 161 | transform: scale(1); 162 | } 163 | 164 | .preview-container.hidden { 165 | display: block; 166 | opacity: 0; 167 | z-index: -1; 168 | } 169 | 170 | .preview-container.hidden > .preview-image-container { 171 | transform: scale(0.9); 172 | } 173 | 174 | 175 | /* GRID VIEW TOGGLE */ 176 | #is-grid-view:checked ~ .gallery .image-container { 177 | text-align: center; 178 | width: 200px; 179 | } 180 | #is-grid-view:checked ~ .gallery .image-meta { 181 | width: auto; 182 | top: -1.4em; 183 | } 184 | -------------------------------------------------------------------------------- /src/js/main.js: -------------------------------------------------------------------------------- 1 | (function(window) { 2 | 3 | var each = [].forEach; 4 | var indexOf = [].indexOf; 5 | 6 | function initializePreview(document, photos) { 7 | var previewContainer = document.querySelector('[data-preview-container]'); 8 | var previewHides = document.querySelectorAll('[data-preview-hide]'); 9 | var previewImage = document.querySelector('[data-preview-image]'); 10 | var previewTitle = document.querySelector('[data-preview-title]'); 11 | var previewControls = document.querySelectorAll('[data-preview-control]'); 12 | var activators = document.querySelectorAll('[data-preview-show]'); 13 | var hiddenClass = 'hidden'; 14 | 15 | function contains(arr, item) { 16 | return indexOf.call(arr, item) !== -1; 17 | } 18 | 19 | function findPhoto(photos, url) { 20 | return photos.filter(function(photo) { 21 | return photo.largeImg === url; 22 | })[0] || {}; 23 | } 24 | 25 | function showPhoto(photo, event) { 26 | if (!photo) { 27 | return; 28 | } 29 | previewImage.src = photo.largeImg; 30 | previewTitle.textContent = photo.title; 31 | 32 | // Hide or show the arrows. 33 | var index = indexOf.call(photos, findPhoto(photos, previewImage.src)); 34 | if (index === 0) { 35 | previewControls.item(0).classList.add(hiddenClass); 36 | } else { 37 | previewControls.item(0).classList.remove(hiddenClass); 38 | } 39 | 40 | if (index === photos.length - 1) { 41 | previewControls.item(1).classList.add(hiddenClass); 42 | } else { 43 | previewControls.item(1).classList.remove(hiddenClass); 44 | } 45 | 46 | previewImage.onload = function() { 47 | previewContainer.classList.remove(hiddenClass); 48 | } 49 | 50 | if (event) { 51 | event.preventDefault(); 52 | } 53 | } 54 | 55 | function clickListener(event) { 56 | var el = event.target; 57 | 58 | // Clicking thumbnail images should actually trigger the parent link. 59 | if (el.getAttribute('data-click-parent')) { 60 | el = el.parentNode; 61 | } 62 | 63 | if (contains(previewHides, el)) { 64 | previewContainer.classList.add(hiddenClass) 65 | } 66 | 67 | if (contains(activators, el)) { 68 | var url = el.getAttribute('data-preview-show'); 69 | var photo = findPhoto(photos, url); 70 | showPhoto(photo, event); 71 | } 72 | 73 | // Listen for clicks on previous button. 74 | if (contains(previewControls, el)) { 75 | var val = el.getAttribute('data-preview-control'); 76 | 77 | var index = indexOf.call(photos, findPhoto(photos, previewImage.src)); 78 | var photo = photos[index + 1]; // Default to next. 79 | if (val === 'prev') { 80 | photo = photos[index - 1]; 81 | } 82 | showPhoto(photo); 83 | } 84 | } 85 | 86 | function keyupListener(event) { 87 | // Escape. 88 | if (event.keyCode === 27) { 89 | previewContainer.classList.add(hiddenClass); 90 | return; 91 | } 92 | 93 | // Left / Right arrows. 94 | var index = indexOf.call(photos, findPhoto(photos, previewImage.src)); 95 | var photo = false; // Default to false. 96 | if (event.keyCode === 37) { // Left. 97 | photo = photos[index - 1]; 98 | } else if (event.keyCode === 39) { // Right. 99 | photo = photos[index + 1]; 100 | } 101 | showPhoto(photo); 102 | } 103 | 104 | document.body.addEventListener('click', clickListener); 105 | document.body.addEventListener('keyup', keyupListener); 106 | 107 | return function() { 108 | document.body.removeEventListener('click', clickListener); 109 | document.body.removeEventListener('keyup', keyupListener); 110 | } 111 | } 112 | 113 | // Tranform Data. 114 | function buildURL(photoObj, size) { 115 | var ext = '_n.jpg'; 116 | if (size === 'large') { 117 | ext = '_b.jpg'; 118 | } 119 | return 'https://c' + photoObj.farm + '.staticflickr.com/' + photoObj.farm 120 | + '/' + photoObj.server + '/' + photoObj.id + '_' + photoObj.secret + ext; // "+ _h" 121 | } 122 | 123 | // Build HTML. 124 | function buildPhoto(photo, templateHtml) { 125 | var container = document.createElement('DIV'); 126 | container.className = 'left hidden'; 127 | container.innerHTML = templateHtml; 128 | for (var prop in photo) { 129 | var el = container.querySelector('[data-prop*="' + prop + '"]') 130 | if (!el) { 131 | continue; 132 | } 133 | var keyVal = el.getAttribute('data-prop').split(':'); 134 | var key = keyVal[0].trim(); 135 | var val = keyVal[1].trim(); 136 | if (key.split('-')[0] === 'data') { 137 | el.setAttribute(key, photo[val]); 138 | } else { 139 | el[key] = photo[val]; 140 | } 141 | 142 | // Show el once image loads. 143 | if (key === 'src') { 144 | el.onload = function() { 145 | el.parentNode.parentNode.classList.remove('hidden'); 146 | } 147 | } 148 | } 149 | 150 | return container; 151 | } 152 | 153 | function buildHTML(photos) { 154 | var gallery = document.querySelector('.js-gallery'); 155 | var templateHtml = document.querySelector('#image-template').innerHTML; 156 | 157 | var arr = []; 158 | each.call(photos, function(photo, i) { 159 | var data = { 160 | thumbnailImg: buildURL(photo), 161 | largeImg: buildURL(photo, 'large'), 162 | title: photo.title 163 | }; 164 | arr.push(data); 165 | var el = buildPhoto(data, templateHtml); 166 | 167 | gallery.appendChild(el); 168 | 169 | // At the end. 170 | if (i === photos.length - 1) { 171 | initializePreview(document, arr); 172 | } 173 | }); 174 | } 175 | 176 | // Get Data (Ajax). 177 | var xhr = new XMLHttpRequest(); 178 | var userId = '141551706@N07'; 179 | xhr.open('GET', encodeURI('https://api.flickr.com/services/rest/?&method=flickr.people.getPublicPhotos&format=json&nojsoncallback=1&api_key=5c1f1af33b907a0e7e1bb79b5b0a5fee&user_id=' + userId)); 180 | xhr.onload = function() { 181 | var response = {}; 182 | 183 | if (xhr.status !== 200) { 184 | console.error('Failed with status of ' + xhr.status); 185 | } 186 | 187 | try { 188 | response = JSON.parse(xhr.responseText); 189 | } catch (e) { 190 | console.error(e); 191 | } 192 | 193 | if (response.photos && response.photos.photo.length) { 194 | buildHTML(response.photos.photo); 195 | } else { 196 | console.error('You don\'t have any public photos'); 197 | } 198 | }; 199 | xhr.send(); 200 | 201 | }(window)); 202 | --------------------------------------------------------------------------------