Dan Brown

Image uploads now quicker, and image sized reduced with links to originals

...@@ -39,7 +39,7 @@ class ImageController extends Controller ...@@ -39,7 +39,7 @@ class ImageController extends Controller
39 $images = $this->image->orderBy('created_at', 'desc') 39 $images = $this->image->orderBy('created_at', 'desc')
40 ->skip($page * $pageSize)->take($pageSize)->get(); 40 ->skip($page * $pageSize)->take($pageSize)->get();
41 foreach ($images as $image) { 41 foreach ($images as $image) {
42 - $image->thumbnail = $this->getThumbnail($image, 150, 150); 42 + $this->loadSizes($image);
43 } 43 }
44 $hasMore = $this->image->orderBy('created_at', 'desc') 44 $hasMore = $this->image->orderBy('created_at', 'desc')
45 ->skip(($page + 1) * $pageSize)->take($pageSize)->count() > 0; 45 ->skip(($page + 1) * $pageSize)->take($pageSize)->count() > 0;
...@@ -50,16 +50,29 @@ class ImageController extends Controller ...@@ -50,16 +50,29 @@ class ImageController extends Controller
50 } 50 }
51 51
52 /** 52 /**
53 + * Loads the standard thumbnail sizes for an image.
54 + * @param Image $image
55 + */
56 + private function loadSizes(Image $image)
57 + {
58 + $image->thumbnail = $this->getThumbnail($image, 150, 150);
59 + $image->display = $this->getThumbnail($image, 840, 0, true);
60 + }
61 +
62 + /**
53 * Get the thumbnail for an image. 63 * Get the thumbnail for an image.
64 + * If $keepRatio is true only the width will be used.
54 * @param $image 65 * @param $image
55 * @param int $width 66 * @param int $width
56 * @param int $height 67 * @param int $height
68 + * @param bool $keepRatio
57 * @return string 69 * @return string
58 */ 70 */
59 - public function getThumbnail($image, $width = 220, $height = 220) 71 + public function getThumbnail($image, $width = 220, $height = 220, $keepRatio = false)
60 { 72 {
61 $explodedPath = explode('/', $image->url); 73 $explodedPath = explode('/', $image->url);
62 - array_splice($explodedPath, 4, 0, ['thumbs-' . $width . '-' . $height]); 74 + $dirPrefix = $keepRatio ? 'scaled-' : 'thumbs-';
75 + array_splice($explodedPath, 4, 0, [$dirPrefix . $width . '-' . $height]);
63 $thumbPath = implode('/', $explodedPath); 76 $thumbPath = implode('/', $explodedPath);
64 $thumbFilePath = public_path() . $thumbPath; 77 $thumbFilePath = public_path() . $thumbPath;
65 78
...@@ -70,7 +83,14 @@ class ImageController extends Controller ...@@ -70,7 +83,14 @@ class ImageController extends Controller
70 83
71 // Otherwise create the thumbnail 84 // Otherwise create the thumbnail
72 $thumb = ImageTool::make(public_path() . $image->url); 85 $thumb = ImageTool::make(public_path() . $image->url);
86 + if($keepRatio) {
87 + $thumb->resize($width, null, function ($constraint) {
88 + $constraint->aspectRatio();
89 + $constraint->upsize();
90 + });
91 + } else {
73 $thumb->fit($width, $height); 92 $thumb->fit($width, $height);
93 + }
74 94
75 // Create thumbnail folder if it does not exist 95 // Create thumbnail folder if it does not exist
76 if (!file_exists(dirname($thumbFilePath))) { 96 if (!file_exists(dirname($thumbFilePath))) {
...@@ -107,7 +127,7 @@ class ImageController extends Controller ...@@ -107,7 +127,7 @@ class ImageController extends Controller
107 $this->image->created_by = auth()->user()->id; 127 $this->image->created_by = auth()->user()->id;
108 $this->image->updated_by = auth()->user()->id; 128 $this->image->updated_by = auth()->user()->id;
109 $this->image->save(); 129 $this->image->save();
110 - $this->image->thumbnail = $this->getThumbnail($this->image, 150, 150); 130 + $this->loadSizes($this->image);
111 return response()->json($this->image); 131 return response()->json($this->image);
112 } 132 }
113 133
...@@ -126,6 +146,7 @@ class ImageController extends Controller ...@@ -126,6 +146,7 @@ class ImageController extends Controller
126 $image = $this->image->findOrFail($imageId); 146 $image = $this->image->findOrFail($imageId);
127 $image->fill($request->all()); 147 $image->fill($request->all());
128 $image->save(); 148 $image->save();
149 + $this->loadSizes($image);
129 return response()->json($this->image); 150 return response()->json($this->image);
130 } 151 }
131 152
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
14 "tests" 14 "tests"
15 ], 15 ],
16 "dependencies": { 16 "dependencies": {
17 - "tinymce-dist": "~4.2.1", 17 + "tinymce-dist": "~4.2.6",
18 "bootstrap": "~3.3.5", 18 "bootstrap": "~3.3.5",
19 "jquery-sortable": "~0.9.13", 19 "jquery-sortable": "~0.9.13",
20 "material-design-iconic-font": "~2.1.1" 20 "material-design-iconic-font": "~2.1.1"
......
1 <template> 1 <template>
2 <div id="image-manager"> 2 <div id="image-manager">
3 - <div class="overlay" v-el="overlay" v-on="click: overlayClick" > 3 + <div class="overlay" v-el="overlay" v-on="click: overlayClick">
4 <div class="image-manager-body"> 4 <div class="image-manager-body">
5 <div class="image-manager-content"> 5 <div class="image-manager-content">
6 <div class="image-manager-list"> 6 <div class="image-manager-list">
...@@ -32,7 +32,8 @@ ...@@ -32,7 +32,8 @@
32 <hr class="even"> 32 <hr class="even">
33 <div v-show="dependantPages"> 33 <div v-show="dependantPages">
34 <p class="text-neg text-small"> 34 <p class="text-neg text-small">
35 - This image is used in the pages below, Click delete again to confirm you want to delete this image. 35 + This image is used in the pages below, Click delete again to confirm you want to delete
36 + this image.
36 </p> 37 </p>
37 <ul class="text-neg"> 38 <ul class="text-neg">
38 <li v-repeat="page: dependantPages"> 39 <li v-repeat="page: dependantPages">
...@@ -46,7 +47,9 @@ ...@@ -46,7 +47,9 @@
46 </form> 47 </form>
47 </div> 48 </div>
48 <div class="image-manager-bottom"> 49 <div class="image-manager-bottom">
49 - <button class="button pos anim fadeIn" v-show="selectedImage" v-on="click:selectButtonClick"><i class="zmdi zmdi-square-right"></i>Select Image</button> 50 + <button class="button pos anim fadeIn" v-show="selectedImage" v-on="click:selectButtonClick"><i
51 + class="zmdi zmdi-square-right"></i>Select Image
52 + </button>
50 </div> 53 </div>
51 </div> 54 </div>
52 </div> 55 </div>
...@@ -59,7 +62,7 @@ ...@@ -59,7 +62,7 @@
59 var Dropzone = require('dropzone'); 62 var Dropzone = require('dropzone');
60 63
61 module.exports = { 64 module.exports = {
62 - data: function(){ 65 + data: function () {
63 return { 66 return {
64 images: [], 67 images: [],
65 hasMore: false, 68 hasMore: false,
...@@ -68,13 +71,12 @@ ...@@ -68,13 +71,12 @@
68 selectedImage: false, 71 selectedImage: false,
69 dependantPages: false, 72 dependantPages: false,
70 deleteForm: {}, 73 deleteForm: {},
71 - token: document.querySelector('meta[name=token]').getAttribute('content') 74 + token: document.querySelector('meta[name=token]').getAttribute('content'),
75 + dataLoaded: false
72 } 76 }
73 }, 77 },
74 78
75 created: function () { 79 created: function () {
76 - // Get initial images
77 - this.fetchData(this.page);
78 window.ImageManager = this; 80 window.ImageManager = this;
79 }, 81 },
80 82
...@@ -139,6 +141,11 @@ ...@@ -139,6 +141,11 @@
139 show: function (callback) { 141 show: function (callback) {
140 this.callback = callback; 142 this.callback = callback;
141 this.$$.overlay.style.display = 'block'; 143 this.$$.overlay.style.display = 'block';
144 + // Get initial images if they have not yet been loaded in.
145 + if (!this.dataLoaded) {
146 + this.fetchData(this.page);
147 + this.dataLoaded = true;
148 + }
142 }, 149 },
143 150
144 overlayClick: function (e) { 151 overlayClick: function (e) {
...@@ -178,9 +185,9 @@ ...@@ -178,9 +185,9 @@
178 _this.images.splice(_this.images.indexOf(_this.selectedImage), 1); 185 _this.images.splice(_this.images.indexOf(_this.selectedImage), 1);
179 _this.selectedImage = false; 186 _this.selectedImage = false;
180 $(_this.$$.imageTitle).showSuccess('Image Deleted'); 187 $(_this.$$.imageTitle).showSuccess('Image Deleted');
181 - }).fail(function(jqXHR, textStatus) { 188 + }).fail(function (jqXHR, textStatus) {
182 // Pages failure 189 // Pages failure
183 - if(jqXHR.status === 400) { 190 + if (jqXHR.status === 400) {
184 _this.dependantPages = jqXHR.responseJSON; 191 _this.dependantPages = jqXHR.responseJSON;
185 } 192 }
186 }); 193 });
......
...@@ -15,7 +15,7 @@ module.exports = { ...@@ -15,7 +15,7 @@ module.exports = {
15 automatic_uploads: false, 15 automatic_uploads: false,
16 valid_children: "-div[p|pre|h1|h2|h3|h4|h5|h6|blockquote]", 16 valid_children: "-div[p|pre|h1|h2|h3|h4|h5|h6|blockquote]",
17 plugins: "image table textcolor paste link imagetools fullscreen code hr", 17 plugins: "image table textcolor paste link imagetools fullscreen code hr",
18 - toolbar: "undo redo | styleselect | bold italic underline strikethrough superscript subscript | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | table image link hr | removeformat code fullscreen", 18 + toolbar: "undo redo | styleselect | bold italic underline strikethrough superscript subscript | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | table image-insert link hr | removeformat code fullscreen",
19 content_style: "body {padding-left: 15px !important; padding-right: 15px !important; margin:0!important; margin-left:auto!important;margin-right:auto!important;}", 19 content_style: "body {padding-left: 15px !important; padding-right: 15px !important; margin:0!important; margin-left:auto!important;margin-right:auto!important;}",
20 style_formats: [ 20 style_formats: [
21 {title: "Header 1", format: "h1"}, 21 {title: "Header 1", format: "h1"},
...@@ -51,6 +51,62 @@ module.exports = { ...@@ -51,6 +51,62 @@ module.exports = {
51 } 51 }
52 }, 52 },
53 setup: function(editor) { 53 setup: function(editor) {
54 +
55 + ( function() {
56 + var wrap;
57 +
58 + function hasTextContent( node ) {
59 + return node && !! ( node.textContent || node.innerText );
60 + }
61 +
62 + editor.on( 'dragstart', function() {
63 + var node = editor.selection.getNode();
64 +
65 + if ( node.nodeName === 'IMG' ) {
66 + wrap = editor.dom.getParent( node, '.mceTemp' );
67 +
68 + if ( ! wrap && node.parentNode.nodeName === 'A' && ! hasTextContent( node.parentNode ) ) {
69 + wrap = node.parentNode;
70 + }
71 + }
72 + } );
73 +
74 + editor.on( 'drop', function( event ) {
75 + var dom = editor.dom,
76 + rng = tinymce.dom.RangeUtils.getCaretRangeFromPoint( event.clientX, event.clientY, editor.getDoc() );
77 +
78 + // Don't allow anything to be dropped in a captioned image.
79 + if ( dom.getParent( rng.startContainer, '.mceTemp' ) ) {
80 + event.preventDefault();
81 + } else if ( wrap ) {
82 + event.preventDefault();
83 +
84 + editor.undoManager.transact( function() {
85 + editor.selection.setRng( rng );
86 + editor.selection.setNode( wrap );
87 + dom.remove( wrap );
88 + } );
89 + }
90 +
91 + wrap = null;
92 + } );
93 + } )();
94 +
95 + // Image picker button
96 + editor.addButton('image-insert', {
97 + title: 'My title',
98 + icon: 'image',
99 + tooltip: 'Insert an image',
100 + onclick: function() {
101 + ImageManager.show(function(image) {
102 + var html = '<p><a href="'+image.url+'" target="_blank">';
103 + html += '<img src="'+image.display+'" alt="'+image.name+'">';
104 + html += '</a></p>';
105 + console.log(image);
106 + editor.execCommand('mceInsertContent', false, html);
107 + });
108 + }
109 + });
54 // Paste image-uploads 110 // Paste image-uploads
55 editor.on('paste', function(e) { 111 editor.on('paste', function(e) {
56 if(e.clipboardData) { 112 if(e.clipboardData) {
......