Dan Brown

Improved image serving and uploading. Fixes #7 and #8.

...@@ -71,13 +71,18 @@ class ImageController extends Controller ...@@ -71,13 +71,18 @@ class ImageController extends Controller
71 */ 71 */
72 public function getAll($page = 0) 72 public function getAll($page = 0)
73 { 73 {
74 - $pageSize = 25; 74 + $pageSize = 13;
75 $images = DB::table('images')->orderBy('created_at', 'desc') 75 $images = DB::table('images')->orderBy('created_at', 'desc')
76 ->skip($page*$pageSize)->take($pageSize)->get(); 76 ->skip($page*$pageSize)->take($pageSize)->get();
77 foreach($images as $image) { 77 foreach($images as $image) {
78 $image->thumbnail = $this->getThumbnail($image, 150, 150); 78 $image->thumbnail = $this->getThumbnail($image, 150, 150);
79 } 79 }
80 - return response()->json($images); 80 + $hasMore = count(DB::table('images')->orderBy('created_at', 'desc')
81 + ->skip(($page+1)*$pageSize)->take($pageSize)->get()) > 0;
82 + return response()->json([
83 + 'images' => $images,
84 + 'hasMore' => $hasMore
85 + ]);
81 } 86 }
82 87
83 /** 88 /**
...@@ -93,18 +98,24 @@ class ImageController extends Controller ...@@ -93,18 +98,24 @@ class ImageController extends Controller
93 array_splice($explodedPath, 3, 0, ['thumbs-' . $width . '-' . $height]); 98 array_splice($explodedPath, 3, 0, ['thumbs-' . $width . '-' . $height]);
94 $thumbPath = implode('/', $explodedPath); 99 $thumbPath = implode('/', $explodedPath);
95 $thumbFilePath = storage_path() . $thumbPath; 100 $thumbFilePath = storage_path() . $thumbPath;
101 +
102 + // Return the thumbnail url path if already exists
96 if(file_exists($thumbFilePath)) { 103 if(file_exists($thumbFilePath)) {
97 return $thumbPath; 104 return $thumbPath;
98 } 105 }
99 106
100 - //dd($thumbFilePath); 107 + // Otherwise create the thumbnail
101 $thumb = ImageTool::make(storage_path() . $image->url); 108 $thumb = ImageTool::make(storage_path() . $image->url);
102 $thumb->fit($width, $height); 109 $thumb->fit($width, $height);
110 +
111 + // Create thumbnail folder if it does not exist
103 if(!file_exists(dirname($thumbFilePath))) { 112 if(!file_exists(dirname($thumbFilePath))) {
104 mkdir(dirname($thumbFilePath), 0775, true); 113 mkdir(dirname($thumbFilePath), 0775, true);
105 } 114 }
115 +
116 + //Save Thumbnail
106 $thumb->save($thumbFilePath); 117 $thumb->save($thumbFilePath);
107 - return $thumbFilePath; 118 + return $thumbPath;
108 } 119 }
109 120
110 /** 121 /**
...@@ -130,6 +141,7 @@ class ImageController extends Controller ...@@ -130,6 +141,7 @@ class ImageController extends Controller
130 $this->image->created_by = Auth::user()->id; 141 $this->image->created_by = Auth::user()->id;
131 $this->image->updated_by = Auth::user()->id; 142 $this->image->updated_by = Auth::user()->id;
132 $this->image->save(); 143 $this->image->save();
144 + $this->image->thumbnail = $this->getThumbnail($this->image, 150, 150);
133 return response()->json($this->image); 145 return response()->json($this->image);
134 } 146 }
135 147
......
1 1
2 +// Dropzone config
3 +Dropzone.options.imageUploadDropzone = {
4 + uploadMultiple: false,
5 + previewsContainer: '.image-manager-display .uploads',
6 + init: function() {
7 + this.on('success', function(event, image) {
8 + $('.image-manager-display .uploads').empty();
9 + var newImage = $('<img />').attr('data-image-id', image.id);
10 + newImage.attr('title', image.name).attr('src', image.thumbnail);
11 + newImage.data('imageData', image);
12 + $('.image-manager-display .uploads').after(newImage);
13 + });
14 + }
15 +};
16 +
2 (function() { 17 (function() {
3 18
4 var isInit = false; 19 var isInit = false;
...@@ -6,6 +21,9 @@ ...@@ -6,6 +21,9 @@
6 var overlay; 21 var overlay;
7 var display; 22 var display;
8 var imageIndexUrl = '/images/all'; 23 var imageIndexUrl = '/images/all';
24 + var pageIndex = 0;
25 + var hasMore = true;
26 + var isGettingImages = true;
9 27
10 var ImageManager = {}; 28 var ImageManager = {};
11 var action = false; 29 var action = false;
...@@ -22,23 +40,48 @@ ...@@ -22,23 +40,48 @@
22 }; 40 };
23 41
24 ImageManager.init = function(selector) { 42 ImageManager.init = function(selector) {
25 - console.log('cat');
26 elem = $(selector); 43 elem = $(selector);
27 overlay = elem.closest('.overlay'); 44 overlay = elem.closest('.overlay');
28 - display = elem.find('.image-manager-display').first() 45 + display = elem.find('.image-manager-display').first();
29 - 46 + var uploads = display.find('.uploads');
47 + var images = display.find('images');
48 + var loadMore = display.find('.load-more');
30 // Get recent images and show 49 // Get recent images and show
31 $.getJSON(imageIndexUrl, showImages); 50 $.getJSON(imageIndexUrl, showImages);
32 - function showImages(images) { 51 + function showImages(data) {
52 + var images = data.images;
53 + hasMore = data.hasMore;
54 + pageIndex++;
55 + isGettingImages = false;
33 for(var i = 0; i < images.length; i++) { 56 for(var i = 0; i < images.length; i++) {
34 var image = images[i]; 57 var image = images[i];
35 var newImage = $('<img />').attr('data-image-id', image.id); 58 var newImage = $('<img />').attr('data-image-id', image.id);
36 newImage.attr('title', image.name).attr('src', image.thumbnail); 59 newImage.attr('title', image.name).attr('src', image.thumbnail);
37 - display.append(newImage); 60 + loadMore.before(newImage);
38 newImage.data('imageData', image); 61 newImage.data('imageData', image);
39 } 62 }
63 + if(hasMore) loadMore.show();
40 } 64 }
41 65
66 + loadMore.click(function() {
67 + loadMore.hide();
68 + if(isGettingImages === false) {
69 + isGettingImages = true;
70 + $.getJSON(imageIndexUrl + '/' + pageIndex, showImages);
71 + }
72 + });
73 +
74 + // Image grabbing on scroll
75 + display.on('scroll', function() {
76 + var displayBottom = display.scrollTop() + display.height();
77 + var elemTop = loadMore.offset().top;
78 + if(elemTop < displayBottom && hasMore && isGettingImages === false) {
79 + isGettingImages = true;
80 + loadMore.hide();
81 + $.getJSON(imageIndexUrl + '/' + pageIndex, showImages);
82 + }
83 + });
84 +
42 elem.on('dblclick', '.image-manager-display img', function() { 85 elem.on('dblclick', '.image-manager-display img', function() {
43 var imageElem = $(this); 86 var imageElem = $(this);
44 var imageData = imageElem.data('imageData'); 87 var imageData = imageElem.data('imageData');
...@@ -55,7 +98,7 @@ ...@@ -55,7 +98,7 @@
55 // Set up dropzone 98 // Set up dropzone
56 elem.find('.image-manager-dropzone').first().dropzone({ 99 elem.find('.image-manager-dropzone').first().dropzone({
57 uploadMultiple: false 100 uploadMultiple: false
58 - }) 101 + });
59 102
60 isInit = true; 103 isInit = true;
61 }; 104 };
......
1 +#image-manager {
2 + background-color: #EEE;
3 + max-width: 90%;
4 + max-height: 90%;
5 + width: 90%;
6 + height: 90%;
7 + margin: 2% 5%;
8 + //border: 2px solid $primary;
9 + border-radius: 4px;
10 + box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.3);
11 + overflow: hidden;
12 + .image-manager-display img {
13 + border-radius: 0;
14 + float: left;
15 + margin: 1px;
16 + cursor: pointer;
17 + }
18 +}
19 +#image-manager .dropzone {
20 + display: table;
21 + position: absolute;
22 + top: 10px;
23 + left: 300px;
24 + width: 480px;
25 + height: 60px;
26 + border: 4px dashed $primary;
27 + text-align: center;
28 + z-index: 900;
29 + .dz-message {
30 + display: table-cell;
31 + vertical-align: middle;
32 + color: $primary;
33 + font-size: 1.2em;
34 + }
35 + * {
36 + pointer-events: none;
37 + }
38 +}
39 +.image-manager-left {
40 + background-color: #FFF;
41 + height: 100%;
42 + width: 100%;
43 + text-align: left;
44 + position: relative;
45 + .image-manager-display-wrap {
46 + height: 100%;
47 + padding-top: 87px;
48 + position: absolute;
49 + top: 0;width: 100%;
50 + }
51 + .image-manager-display {
52 + height: 100%;
53 + width: 100%;
54 + text-align: left;
55 + overflow-y: scroll;
56 + }
57 + .image-manager-header {
58 + z-index: 50;
59 + position: relative;
60 + }
61 +}
62 +
63 +#image-manager .load-more {
64 + width: 150px;
65 + height: 150px;
66 + display: none;
67 + float: left;
68 + text-align: center;
69 + background-color: #888;
70 + margin: 1px;
71 + color: #FFF;
72 + line-height: 140px;
73 + font-size: 20px;
74 + cursor: pointer;
75 +}
76 +.image-manager-title {
77 + font-size: 2em;
78 + text-align: left;
79 + margin: 0 $-m;
80 + padding: $-xl $-m;
81 + color: #666;
82 + border-bottom: 1px solid #DDD;
83 +}
84 +
85 +.image-manager-dropzone {
86 + background-color: lighten($primary, 40%);
87 + height: 25%;
88 + text-align: center;
89 + font-size: 2em;
90 + line-height: 2em;
91 + padding-top: $-xl*1.2;
92 + color: #666;
93 + border-top: 2px solid $primary;
94 +}
95 +
96 +// Dropzone
97 +/*
98 + * The MIT License
99 + * Copyright (c) 2012 Matias Meno <m@tias.me>
100 + */
101 +
102 +@keyframes passing-through {
103 + 0% {
104 + opacity: 0;
105 + transform: translateY(40px); }
106 + 30%, 70% {
107 + opacity: 1;
108 + transform: translateY(0px); }
109 + 100% {
110 + opacity: 0;
111 + transform: translateY(-40px); } }
112 +
113 +@keyframes slide-in {
114 + 0% {
115 + opacity: 0;
116 + transform: translateY(40px); }
117 + 30% {
118 + opacity: 1;
119 + transform: translateY(0px); } }
120 +@keyframes pulse {
121 + 0% {
122 + -webkit-transform: scale(1);
123 + -moz-transform: scale(1);
124 + -ms-transform: scale(1);
125 + -o-transform: scale(1);
126 + transform: scale(1); }
127 + 10% {
128 + -webkit-transform: scale(1.1);
129 + -moz-transform: scale(1.1);
130 + -ms-transform: scale(1.1);
131 + -o-transform: scale(1.1);
132 + transform: scale(1.1); }
133 + 20% {
134 + -webkit-transform: scale(1);
135 + -moz-transform: scale(1);
136 + -ms-transform: scale(1);
137 + -o-transform: scale(1);
138 + transform: scale(1); } }
139 +.dropzone, .dropzone * {
140 + box-sizing: border-box; }
141 +
142 +.dropzone {
143 + background: white;
144 + padding: 20px 20px; }
145 +.dropzone.dz-clickable {
146 + cursor: pointer; }
147 +.dropzone.dz-clickable * {
148 + cursor: default; }
149 +.dropzone.dz-clickable .dz-message, .dropzone.dz-clickable .dz-message * {
150 + cursor: pointer; }
151 +.dropzone.dz-started .dz-message {
152 + display: none; }
153 +.dropzone.dz-drag-hover {
154 + border-style: solid; }
155 +.dropzone.dz-drag-hover .dz-message {
156 + opacity: 0.5; }
157 +.dropzone .dz-message {
158 + text-align: center;
159 + margin: 2em 0; }
160 +.dz-preview {
161 + position: relative;
162 + display: inline-block;
163 + vertical-align: top;
164 + margin: 16px;
165 + min-height: 100px; }
166 +.dz-preview:hover {
167 + z-index: 1000; }
168 +.dz-preview:hover .dz-details {
169 + opacity: 1; }
170 +.dz-preview.dz-file-preview .dz-image {
171 + border-radius: 4px;
172 + background: #999;
173 + background: linear-gradient(to bottom, #eee, #ddd); }
174 +.dz-preview.dz-file-preview .dz-details {
175 + opacity: 1; }
176 +.dz-preview.dz-image-preview {
177 + background: white; }
178 +.dz-preview.dz-image-preview .dz-details {
179 + -webkit-transition: opacity 0.2s linear;
180 + -moz-transition: opacity 0.2s linear;
181 + -ms-transition: opacity 0.2s linear;
182 + -o-transition: opacity 0.2s linear;
183 + transition: opacity 0.2s linear; }
184 +.dz-preview .dz-remove {
185 + font-size: 14px;
186 + text-align: center;
187 + display: block;
188 + cursor: pointer;
189 + border: none; }
190 +.dz-preview .dz-remove:hover {
191 + text-decoration: underline; }
192 +.dz-preview:hover .dz-details {
193 + opacity: 1; }
194 +.dz-preview .dz-details {
195 + z-index: 20;
196 + position: absolute;
197 + top: 0;
198 + left: 0;
199 + opacity: 0;
200 + font-size: 13px;
201 + min-width: 100%;
202 + max-width: 100%;
203 + padding: 2em 1em;
204 + text-align: center;
205 + color: rgba(0, 0, 0, 0.9);
206 + line-height: 150%; }
207 +.dz-preview .dz-details .dz-size {
208 + margin-bottom: 1em;
209 + font-size: 16px; }
210 +.dz-preview .dz-details .dz-filename {
211 + white-space: nowrap; }
212 +.dz-preview .dz-details .dz-filename:hover span {
213 + border: 1px solid rgba(200, 200, 200, 0.8);
214 + background-color: rgba(255, 255, 255, 0.8); }
215 +.dz-preview .dz-details .dz-filename:not(:hover) {
216 + overflow: hidden;
217 + text-overflow: ellipsis; }
218 +.dz-preview .dz-details .dz-filename:not(:hover) span {
219 + border: 1px solid transparent; }
220 +.dz-preview .dz-details .dz-filename span, .dz-preview .dz-details .dz-size span {
221 + background-color: rgba(255, 255, 255, 0.4);
222 + padding: 0 0.4em;
223 + border-radius: 3px; }
224 +.dz-preview:hover .dz-image img {
225 + -webkit-transform: scale(1.05, 1.05);
226 + -moz-transform: scale(1.05, 1.05);
227 + -ms-transform: scale(1.05, 1.05);
228 + -o-transform: scale(1.05, 1.05);
229 + transform: scale(1.05, 1.05);
230 + -webkit-filter: blur(8px);
231 + filter: blur(8px); }
232 +.dz-preview .dz-image {
233 + border-radius: 4px;
234 + overflow: hidden;
235 + width: 120px;
236 + height: 120px;
237 + position: relative;
238 + display: block;
239 + z-index: 10; }
240 +.dz-preview .dz-image img {
241 + display: block; }
242 +.dz-preview.dz-success .dz-success-mark {
243 + animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); }
244 +.dz-preview.dz-error .dz-error-mark {
245 + opacity: 1;
246 + animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); }
247 +.dz-preview .dz-success-mark, .dz-preview .dz-error-mark {
248 + pointer-events: none;
249 + opacity: 0;
250 + z-index: 500;
251 + position: absolute;
252 + display: block;
253 + top: 50%;
254 + left: 50%;
255 + margin-left: -27px;
256 + margin-top: -27px; }
257 +.dz-preview .dz-success-mark svg, .dz-preview .dz-error-mark svg {
258 + display: block;
259 + width: 54px;
260 + height: 54px; }
261 +.dz-preview.dz-processing .dz-progress {
262 + opacity: 1;
263 + transition: all 0.2s linear; }
264 +.dz-preview.dz-complete .dz-progress {
265 + opacity: 0;
266 + transition: opacity 0.4s ease-in; }
267 +.dz-preview:not(.dz-processing) .dz-progress {
268 + animation: pulse 6s ease infinite; }
269 +.dz-preview .dz-progress {
270 + opacity: 1;
271 + z-index: 1000;
272 + pointer-events: none;
273 + position: absolute;
274 + height: 16px;
275 + left: 50%;
276 + top: 50%;
277 + margin-top: -8px;
278 + width: 80px;
279 + margin-left: -40px;
280 + background: rgba(255, 255, 255, 0.9);
281 + -webkit-transform: scale(1);
282 + border-radius: 8px;
283 + overflow: hidden; }
284 +.dz-preview .dz-progress .dz-upload {
285 + background: #333;
286 + background: linear-gradient(to bottom, #666, #444);
287 + position: absolute;
288 + top: 0;
289 + left: 0;
290 + bottom: 0;
291 + width: 0;
292 + transition: width 300ms ease-in-out; }
293 +.dz-preview.dz-error .dz-error-message {
294 + display: block; }
295 +.dz-preview.dz-error:hover .dz-error-message {
296 + opacity: 1;
297 + pointer-events: auto; }
298 +.dz-preview .dz-error-message {
299 + pointer-events: none;
300 + z-index: 1000;
301 + position: absolute;
302 + display: block;
303 + display: none;
304 + opacity: 0;
305 + transition: opacity 0.3s ease;
306 + border-radius: 8px;
307 + font-size: 13px;
308 + top: 130px;
309 + left: -10px;
310 + width: 140px;
311 + background: #be2626;
312 + background: linear-gradient(to bottom, #be2626, #a92222);
313 + padding: 0.5em 1.2em;
314 + color: white; }
315 +.dz-preview .dz-error-message:after {
316 + content: '';
317 + position: absolute;
318 + top: -6px;
319 + left: 64px;
320 + width: 0;
321 + height: 0;
322 + border-left: 6px solid transparent;
323 + border-right: 6px solid transparent;
324 + border-bottom: 6px solid #be2626; }
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
9 @import "forms"; 9 @import "forms";
10 @import "tables"; 10 @import "tables";
11 @import "tinymce"; 11 @import "tinymce";
12 +@import "image-manager";
12 13
13 header { 14 header {
14 display: block; 15 display: block;
...@@ -192,57 +193,6 @@ ul.menu { ...@@ -192,57 +193,6 @@ ul.menu {
192 right: 0; 193 right: 0;
193 bottom: 0; 194 bottom: 0;
194 } 195 }
195 -#image-manager {
196 - background-color: #EEE;
197 - max-width: 90%;
198 - max-height: 90%;
199 - width: 90%;
200 - height: 90%;
201 - margin: 2% 5%;
202 - //border: 2px solid $primary;
203 - border-radius: 4px;
204 - box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.3);
205 - overflow: hidden;
206 - .image-manager-display img {
207 - width: 150px;
208 - height: 150px;
209 - display: inline-block;
210 - margin: $-s 0 0 $-s;
211 - cursor: pointer;
212 - }
213 -}
214 -.image-manager-left {
215 - background-color: #FFF;
216 - height: 100%;
217 - width: 100%;
218 - text-align: left;
219 - .image-manager-display {
220 - height: 75%;
221 - width: 100%;
222 - text-align: left;
223 - overflow-y: scroll;
224 - }
225 -}
226 -
227 -.image-manager-title {
228 - font-size: 2em;
229 - text-align: left;
230 - margin: 0 $-m;
231 - padding: $-xl $-m;
232 - color: #666;
233 - border-bottom: 1px solid #DDD;
234 -}
235 -
236 -.image-manager-dropzone {
237 - background-color: lighten($primary, 40%);
238 - height: 25%;
239 - text-align: center;
240 - font-size: 2em;
241 - line-height: 2em;
242 - padding-top: $-xl*1.2;
243 - color: #666;
244 - border-top: 2px solid $primary;
245 -}
246 196
247 // Link hooks & popovers 197 // Link hooks & popovers
248 a.link-hook { 198 a.link-hook {
......
1 <section class="overlay" style="display:none;"> 1 <section class="overlay" style="display:none;">
2 +{{--<section class="overlay">--}}
2 <div id="image-manager"> 3 <div id="image-manager">
3 <div class="image-manager-left"> 4 <div class="image-manager-left">
4 <div class="image-manager-header"> 5 <div class="image-manager-header">
5 <button type="button" class="button neg float right" data-action="close">Close</button> 6 <button type="button" class="button neg float right" data-action="close">Close</button>
6 <div class="image-manager-title">Image Library</div> 7 <div class="image-manager-title">Image Library</div>
7 </div> 8 </div>
8 - <div class="image-manager-display"> 9 + <div class="image-manager-display-wrap">
10 + <div class="image-manager-display">
11 + <div class="uploads"></div>
12 + <div class="images">
13 + <div class="load-more">Load More</div>
14 + </div>
15 + </div>
9 </div> 16 </div>
10 - <form action="/upload/image" class="image-manager-dropzone"> 17 + <form action="/upload/image"
11 - {{ csrf_field() }} 18 + class="dropzone"
12 - Drag images or click here to upload 19 + id="image-upload-dropzone">
20 + {!! csrf_field() !!}
13 </form> 21 </form>
14 </div> 22 </div>
15 {{--<div class="sidebar">--}} 23 {{--<div class="sidebar">--}}
......