]> BookStack Code Mirror - bookstack/blob - resources/assets/js/components/image-manager.vue
c47fee184dac95ae79696dbe19e823a997d10528
[bookstack] / resources / assets / js / components / image-manager.vue
1 <template>
2     <div id="image-manager">
3         <div class="overlay" v-el:overlay @click="overlayClick">
4             <div class="image-manager-body">
5                 <div class="image-manager-content">
6                     <div class="image-manager-list">
7                         <div v-for="image in images">
8                             <img class="anim fadeIn"
9                                  :class="{selected: (image==selectedImage)}"
10                                  :src="image.thumbs.gallery" :alt="image.title" :title="image.name"
11                                  @click="imageClick(image)"
12                                  :style="{animationDelay: ($index > 26) ? '160ms' : ($index * 25) + 'ms'}">
13                         </div>
14                         <div class="load-more" v-show="hasMore" @click="fetchData">Load More</div>
15                     </div>
16                 </div>
17                 <button class="neg button image-manager-close" @click="hide">x</button>
18                 <div class="image-manager-sidebar">
19                     <h2 v-el:image-title>Images</h2>
20                     <hr class="even">
21                     <div class="dropzone-container" v-el:drop-zone>
22                         <div class="dz-message">Drop files or click here to upload</div>
23                     </div>
24                     <div class="image-manager-details anim fadeIn" v-show="selectedImage">
25                         <hr class="even">
26                         <form @submit="saveImageDetails" v-el:image-form>
27                             <div class="form-group">
28                                 <label for="name">Image Name</label>
29                                 <input type="text" id="name" name="name" v-model="selectedImage.name">
30                             </div>
31                         </form>
32                         <hr class="even">
33                         <div v-show="dependantPages">
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
36                                 this image.
37                             </p>
38                             <ul class="text-neg">
39                                 <li v-for="page in dependantPages">
40                                     <a :href="page.url" target="_blank" class="text-neg">{{ page.name }}</a>
41                                 </li>
42                             </ul>
43                         </div>
44
45                         <form @submit="deleteImage" v-el:image-delete-form>
46                             <button class="button neg"><i class="zmdi zmdi-delete"></i>Delete Image</button>
47                         </form>
48                     </div>
49                     <div class="image-manager-bottom">
50                         <button class="button pos anim fadeIn" v-show="selectedImage" @click="selectButtonClick"><i
51                                 class="zmdi zmdi-square-right"></i>Select Image
52                         </button>
53                     </div>
54                 </div>
55             </div>
56         </div>
57     </div>
58 </template>
59
60 <script>
61
62     var Dropzone = require('dropzone');
63
64     module.exports = {
65         data: function () {
66             return {
67                 images: [],
68                 hasMore: false,
69                 page: 0,
70                 cClickTime: 0,
71                 selectedImage: false,
72                 dependantPages: false,
73                 deleteForm: {},
74                 token: document.querySelector('meta[name=token]').getAttribute('content'),
75                 dataLoaded: false
76             }
77         },
78
79         props: {
80             imageType: {
81                 type: String,
82                 required: true
83             },
84             resizeWidth: {
85                 type: String
86             },
87             resizeHeight: {
88                 type: String
89             },
90             resizeCrop: {
91                 type: Boolean
92             }
93         },
94
95         created: function () {
96             window.ImageManager = this;
97         },
98
99         ready: function () {
100             // Create dropzone
101             this.setupDropZone();
102         },
103
104         methods: {
105             fetchData: function () {
106                 var _this = this;
107                 this.$http.get('/images/' + _this.imageType + '/all/' + _this.page, function (data) {
108                     _this.images = _this.images.concat(data.images);
109                     _this.hasMore = data.hasMore;
110                     _this.page++;
111                 });
112             },
113
114             setupDropZone: function () {
115                 var _this = this;
116                 var dropZone = new Dropzone(_this.$els.dropZone, {
117                     url: '/images/' + _this.imageType + '/upload',
118                     init: function () {
119                         var dz = this;
120                         this.on("sending", function (file, xhr, data) {
121                             data.append("_token", _this.token);
122                         });
123                         this.on("success", function (file, data) {
124                             _this.images.unshift(data);
125                             $(file.previewElement).fadeOut(400, function () {
126                                 dz.removeFile(file);
127                             });
128                         });
129                         this.on('error', function (file, errorMessage, xhr) {
130                             if (errorMessage.file) {
131                                 $(file.previewElement).find('[data-dz-errormessage]').text(errorMessage.file[0]);
132                             }
133                             console.log(errorMessage);
134                         });
135                     }
136                 });
137             },
138
139             returnCallback: function (image) {
140                 var _this = this;
141                 var isResized = _this.resizeWidth && _this.resizeHeight;
142
143                 if (!isResized) {
144                     _this.callback(image);
145                     return;
146                 }
147
148                 var cropped = _this.resizeCrop ? 'true' : 'false';
149                 var requestString = '/images/thumb/' + image.id + '/' + _this.resizeWidth + '/' + _this.resizeHeight + '/' + cropped;
150                 _this.$http.get(requestString, function(data) {
151                     image.thumbs.custom = data.url;
152                     _this.callback(image);
153                 });
154
155             },
156
157             imageClick: function (image) {
158                 var dblClickTime = 380;
159                 var cTime = (new Date()).getTime();
160                 var timeDiff = cTime - this.cClickTime;
161                 if (this.cClickTime !== 0 && timeDiff < dblClickTime && this.selectedImage === image) {
162                     // DoubleClick
163                     if (this.callback) {
164                         this.returnCallback(image);
165                     }
166                     this.hide();
167                 } else {
168                     this.selectedImage = (this.selectedImage === image) ? false : image;
169                     this.dependantPages = false;
170                 }
171                 this.cClickTime = cTime;
172             },
173
174             selectButtonClick: function () {
175                 if (this.callback) {
176                     this.returnCallback(this.selectedImage);
177                 }
178                 this.hide();
179             },
180
181             show: function (callback) {
182                 this.callback = callback;
183                 this.$els.overlay.style.display = 'block';
184                 // Get initial images if they have not yet been loaded in.
185                 if (!this.dataLoaded) {
186                     this.fetchData(this.page);
187                     this.dataLoaded = true;
188                 }
189             },
190
191             overlayClick: function (e) {
192                 if (e.target.className === 'overlay') {
193                     this.hide();
194                 }
195             },
196
197             hide: function () {
198                 this.$els.overlay.style.display = 'none';
199             },
200
201             saveImageDetails: function (e) {
202                 e.preventDefault();
203                 var _this = this;
204                 _this.selectedImage._token = _this.token;
205                 var form = $(_this.$els.imageForm);
206                 $.ajax('/images/update/' + _this.selectedImage.id, {
207                     method: 'PUT',
208                     data: _this.selectedImage
209                 }).done(function () {
210                     form.showSuccess('Image name updated');
211                 }).fail(function (jqXHR) {
212                     form.showFailure(jqXHR.responseJSON);
213                 })
214             },
215
216             deleteImage: function (e) {
217                 e.preventDefault();
218                 var _this = this;
219                 _this.deleteForm.force = _this.dependantPages !== false;
220                 _this.deleteForm._token = _this.token;
221                 $.ajax('/images/' + _this.selectedImage.id, {
222                     method: 'DELETE',
223                     data: _this.deleteForm
224                 }).done(function () {
225                     _this.images.splice(_this.images.indexOf(_this.selectedImage), 1);
226                     _this.selectedImage = false;
227                     $(_this.$els.imageTitle).showSuccess('Image Deleted');
228                 }).fail(function (jqXHR, textStatus) {
229                     // Pages failure
230                     if (jqXHR.status === 400) {
231                         _this.dependantPages = jqXHR.responseJSON;
232                     }
233                 });
234             }
235
236         }
237
238     };
239 </script>