· 8 years ago · Mar 16, 2018, 08:56 AM
1<!DOCTYPE html>
2<html xmlns="http://www.w3.org/1999/xhtml"
3 xmlns:th="http://www.thymeleaf.org"
4 xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
5 layout:decorator="layout/mainLayout">
6
7<head><title>Property Photos</title></head>
8
9<body>
10
11 <th:block layout:fragment="pageCss">
12 <!-- Dropzone -->
13 <link rel="stylesheet" th:href="@{/assets/plugins/dropzone/dropzone.css}" />
14 </th:block>
15
16 <th:block layout:fragment="pageHeader">
17 <h1 th:inline="text">
18 <span class="text-nowrap">[[${property.code}]]
19 <span class="link-muted"> - [[${property.displayAddress} + ',']] [[#{label.countryCode.+${property.countryCode}}]]</span>
20 </span>
21 <span class="my-br"><br/></span>
22 <span class="text-nowrap my-text-nowrap" style="padding-left: 100px;">Property Status:
23 <span class="text-info"> [[#{label.propStatus.+${property.displayStatus}}]]</span>
24 <span class="text-warning" th:remove="${property.photosUploadedDate == null and property.photosEditedDate == null} ? none : all"> (photos not ready yet)</span>
25 <span class="text-success" th:remove="${property.photosUploadedDate != null and property.photosEditedDate == null} ? none : all"> (photos uploaded since [[${#dates.format(property.photosUploadedDate, 'dd/MM/yyyy')}]])</span>
26 <span class="text-success" th:remove="${property.photosEditedDate != null} ? none : all"> (photos edited since [[${#dates.format(property.photosEditedDate, 'dd/MM/yyyy')}]])</span>
27 </span>
28 </h1>
29 <h4 th:inline="text">
30 <span class="text"><span class="text-info">Floor:</span><span class="link-muted"> [[${property.address.displayFloor} + ', ']]</span><span class="text-info">Size:</span><span class="link-muted"> [[${#strings.defaultString(property.propSize, '')} + ' sq.m., ']]</span><span class="text-info">Bedrooms:</span><span class="link-muted"> [[${#strings.defaultString(property.bedroomsNum, '')}]]</span>
31 </span>
32 <br/>
33 <span class="text"><span class="text-info">Directions:</span>
34 <span class="link-muted"> [[${#strings.defaultString(property.address.directions, '-')}]]</span>
35 </span>
36 <span class="text" th:remove="${property.mngInfo.lockbox != null and property.mngInfo.lockbox == 1} ? none : all"><br/><span class="text-info">Lockbox instructions:</span>
37 <span class="link-muted"> [[${#strings.defaultString(property.mngInfo.lockboxInstructions, '-')}]]</span>
38 </span>
39 <span class="text" th:remove="${property.countryCode == 'USA' and property.house.minMonths != null and property.house.minMonths > 0} ? none : all"><br/><span class="text-info">Minimum stay:</span>
40 <span class="link-muted"> [[${#numbers.formatDecimal(property.house.minMonths,0,0)} + ' month(s)']]</span>
41 </span>
42 <br/>
43 <span class="text"><span class="text-info">Photo sequence instructions:</span>
44 <button type="button" class="btn btn-sm btn-info" data-toggle="modal" data-target="#photos-info"><i class="fa fa-info-circle"></i></button>
45 </span>
46 </h4>
47
48 <ol class="breadcrumb">
49 <li><a th:href="@{/admin/home}"><i class="fa fa-th"></i> Home</a></li>
50 <li><a th:href="@{${parentPath}+'/dashboard'}"><i class="fa fa-sitemap"></i> Dashboard</a></li>
51 <li><a><i class="fa fa-camera"></i> Property Photos</a></li>
52 </ol>
53 </th:block>
54
55 <th:block layout:fragment="pageContent">
56
57 <h3>
58 <span th:remove="${property.photosEditedDate == null and photoType == 1 and #lists.size(dbPhotos) lt 8} ? none : all" class="label label-danger"><i class="fa fa-info-circle"></i> Upload at least 8 photos and click button 'Done!' when finished</span>
59 </h3>
60 <div class="panel panel-default">
61 <div class="panel-body">
62
63 <div class="row">
64 <div class="form-group col-md-2">
65 <select id="photoType" name="photoType" class="form-control" th:disabled="${#authentication.principal.details.external}">
66 <option value="1">Furnished photos</option>
67 <option value="2">Unfurnished photos</option>
68 <option value="5">Area photos</option>
69 <option value="3">Entrance photos</option>
70 <option value="4">Keys/Lockbox photos</option>
71 <option value="6">Floor plan</option>
72 </select>
73 </div>
74 <div class="col-md-10 btn-toolbar">
75 <button id="cancelBtn" class="btn btn-default" disabled="disabled"><i class="fa fa-undo"></i> Cancel</button>
76 <button id="updateBtn" class="btn btn-primary" disabled="disabled"><i class="fa fa-upload"></i> Update</button>
77 <button id="downloadBtn" class="btn btn-primary" disabled="disabled"><i class="fa fa-download"></i> Download</button>
78 <div id="editedDiv">
79 <div th:fragment="edited-btn-div">
80 <!-- 'Done' button available to all users -->
81 <button id="editedBtn" class="btn btn-success pull-right" th:remove="${property.photosEditedDate == null and photoType == 1 and #lists.size(dbPhotos) ge 8} ? none : all"><i class="fa fa-check"></i> Done!</button>
82
83 <!-- 'Done' button available only to photo editor in case photosEditedBy is another user than the photoEditor or there is a new upload after edited date -->
84 <th:block th:if="${#authentication.principal.hasRole('ROLE_PHOTO_EDITOR')}">
85 <button id="editedBtn" class="btn btn-success pull-right" th:remove="${property.photosEditedDate != null and photoType == 1 and #lists.size(dbPhotos) ge 8 and ((editedByAnotherUser == true) or (property.photosUploadedDate != null and property.photosUploadedDate gt property.photosEditedDate))} ? none : all"><i class="fa fa-check"></i> Done!</button>
86 </th:block>
87
88 </div>
89 </div>
90
91 <a id="reloadPageUrl" th:href="@{${basePath}+'/{propId}/photos/view/{photoType}'(propId=${property.id},photoType=${photoType})}" class="btn loader" style="display:none;"></a>
92 <div id="total-progress" class="progress progress-striped active" style="opacity:0;padding-left:10px;" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
93 <div class="progress-bar progress-bar-success" style="width:0%;" data-dz-uploadprogress=""></div>
94 </div>
95 </div>
96
97 </div>
98 <div class="row">
99 <div id="photoUrlDiv" class="form-group col-lg-6" th:classappend="${zipUrl == null} ? ' hidden'">
100 <div class="input-group">
101 <span class="input-group-btn">
102 <button class="btn copy-clipboard-btn" type="button" data-clipboard-target="#photoUrl" data-toggle="tooltip" data-placement="bottom" title="Copy to clipboard">
103 <i class="fa fa-clipboard"></i>
104 </button>
105 </span>
106 <input id="photoUrl" type="text" th:value="${zipUrl}" class="form-control" />
107 </div>
108 </div>
109 </div>
110 <form id="dropzone-form" class="dropzone" enctype="multipart/form-data">
111
112 <div class="dz-default dz-message file-dropzone text-center well col-sm-12">
113 <h4 th:inline="text"><i class="fa fa-cloud-upload"></i> Drop photos here to upload [[${acceptedFiles != null} ? '(' + ${acceptedFiles} + ')' : '']]</h4>
114 </div>
115
116 <!-- this is were the previews should be displayed -->
117 <div class="dropzone-previews"></div>
118
119 </form>
120
121 <div class="modal fade bs-modal-lg" id="carouselModal" tabindex="-1" role="dialog">
122 <div class="modal-dialog modal-lg">
123 <div class="modal-content">
124 <div id="carousel-generic" class="carousel slide" data-ride="carousel" data-interval="false">
125
126 <!-- iterate on photos -->
127 <div class="carousel-inner">
128 <th:block th:each="iterator,counter : ${dbPhotos}">
129 <div class="item" th:classappend="${counter.count == 1} ? ' active'">
130 <img class="img-responsive" th:src="${iterator.baseURL} + ${iterator.thumbnail}" />
131 <div class="carousel-caption" th:text="${iterator.caption}"></div>
132 </div>
133 </th:block>
134 </div>
135
136 <!-- Controls -->
137 <a class="left carousel-control" href="#carousel-generic" role="button" data-slide="prev"><span class="glyphicon glyphicon-chevron-left"></span></a>
138 <a class="right carousel-control" href="#carousel-generic" role="button" data-slide="next"><span class="glyphicon glyphicon-chevron-right"></span></a>
139 </div>
140 </div>
141 </div>
142 </div>
143
144 <div class="modal fade modal-info" id="photos-info" tabindex="-1" role="dialog">
145 <div class="modal-dialog modal-lg">
146 <div class="modal-content">
147 <div class="modal-header">
148 <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
149 <h4 class="modal-title"><i class="fa fa-info-circle"></i> Photo sequence instructions</h4>
150 </div>
151 <div class="modal-body">
152 <ul>
153 <li>Preferably, minimum 10 photos</li>
154 <li>If bedrooms are less please fill with next photos. If bedrooms are more please fill before photo #11</li>
155 <li>In bathroom photos, preferably avoid including the toilet in the photo</li>
156 <li>In living room Photo, TV should be on</li>
157 <li>Photos can be re-arranged based on quality; e.g. if Bedroom Photos are really good, they can go higher in the sequence</li>
158 </ul>
159 <table class="table table-bordered">
160 <tbody>
161 <tr>
162 <th style="width: 10px">Sequence</th>
163 <th>Photo</th>
164 <th>Comments</th>
165 </tr>
166 <tr>
167 <td>1.</td>
168 <td>Living Room</td>
169 <td>ALWAYS best photo here. Preferably shares some of the view too</td>
170 </tr>
171 <tr>
172 <td>2.</td>
173 <td>Living Room</td>
174 <td>From a different angle, if applicable</td>
175 </tr>
176 <tr>
177 <td>3.</td>
178 <td>Living Room</td>
179 <td>if applicable</td>
180 </tr>
181 <tr>
182 <td>4.</td>
183 <td>Balcony with View</td>
184 <td>Optional, if view is really good</td>
185 </tr>
186 <tr>
187 <td>5.</td>
188 <td>Dining Room</td>
189 <td> </td>
190 </tr>
191 <tr>
192 <td>6.</td>
193 <td>Kitchen</td>
194 <td> </td>
195 </tr>
196 <tr>
197 <td>7.</td>
198 <td>Kitchen</td>
199 <td> </td>
200 </tr>
201 <tr>
202 <td>8.</td>
203 <td>Master BD</td>
204 <td> </td>
205 </tr>
206 <tr>
207 <td>9.</td>
208 <td>Master BD</td>
209 <td>From a different angle</td>
210 </tr>
211 <tr>
212 <td>10.</td>
213 <td>Bathroom</td>
214 <td>the en-suite of Master Bedroom</td>
215 </tr>
216 <tr>
217 <td>11.</td>
218 <td>2nd Bedroom</td>
219 <td> </td>
220 </tr>
221 <tr>
222 <td>12.</td>
223 <td>2nd Bedroom</td>
224 <td> </td>
225 </tr>
226 <tr>
227 <td>13.</td>
228 <td>Bathroom</td>
229 <td>the one from the second bedroom</td>
230 </tr>
231 <tr>
232 <td>14.</td>
233 <td>WC</td>
234 <td>if applicable</td>
235 </tr>
236 <tr>
237 <td>15.</td>
238 <td>Balcony with View</td>
239 <td>If not on picture #4, or we have multiple shots of the balcony</td>
240 </tr>
241 </tbody>
242 </table>
243 </div>
244 <div class="modal-footer">
245 <button type="button" class="btn btn-outline pull-right" data-dismiss="modal">OK</button>
246 </div>
247 </div>
248 </div>
249 </div>
250
251 </div>
252 </div>
253
254 </th:block>
255
256 <th:block layout:fragment="pageJavascriptFiles">
257 <!-- Dropzone.js -->
258 <script th:src="@{/assets/plugins/dropzone/dropzone.js}"></script>
259 <!-- Clipboard.js -->
260 <script th:src="@{/assets/plugins/clipboard/clipboard.min.js}"></script>
261 </th:block>
262
263 <th:block th:inline="javascript" layout:fragment="pageJavascript">
264 <script>
265 /*<![CDATA[*/
266 var basePath = /*[[ ${basePath} ]]*/;
267 var propId = /*[[ ${property.id} ]]*/;
268 var photoType = /*[[ ${photoType} ]]*/;
269 var photoVersion = /*[[ ${photoVersion} ]]*/;
270 var acceptedFiles = /*[[ ${acceptedFiles} ]]*/;
271 var successMsg = /*[[ #{label.notifyMsg.success} ]]*/;
272 var errorMsg = /*[[ #{label.notifyMsg.error} ]]*/;
273
274 var existingDbPhotos = /*[[ ${existingDbPhotos} ]]*/; //2 minor fields used for better ux in order to display warn message to click update button when all files are removed
275 var uploadMsgDisplayed = false;
276 var reloadPage = false; //minor field used for better ux in order not to always the reload page, (e.g reload only when files are uploaded from the queue), now always reload due to version control
277
278 $(document).ready(function() {
279
280 $('.file-dropzone').on('dragover', handleDragEnter);
281 $('.file-dropzone').on('dragleave', handleDragLeave);
282 $('.file-dropzone').on('drop', handleDragLeave);
283
284 function handleDragEnter(e) {
285 this.classList.add('drag-over');
286 }
287
288 function handleDragLeave(e) {
289 this.classList.remove('drag-over');
290 }
291
292 $('#photoType').val(photoType);
293 $('#photoType').on('change',function() {
294 //add a hidden href button just for the loader animation
295 photoType = $('#photoType').val();
296 $("#reloadPageUrl").attr("href", basePath + '/' + propId + '/photos/view/' + photoType);
297 $('#reloadPageUrl')[0].click();
298 })
299
300 function addFilesFromServer(myDropzone) {
301 $.get(basePath + '/' + propId + '/photos/get/' + photoType, function(data) {
302 var index = 0;
303 $.each(data, function (key, file) {
304 //console.log(file.url);
305
306 myDropzone.emit('addedfile', file); //event 'addedfile' must be invoked
307 myDropzone.emit('thumbnail', file, file.url); //to display the original photo from server
308 //myDropzone.createThumbnailFromUrl(file, file.url, null, true);//to create a thumbnail of the orginal photo. We get Cross-Origin error, show do not use it
309 myDropzone.files.push(file); //push files, otherwise there are errors in case of empty array in myDropzone.uploadFiles(), also in cancel all files (including server files) will be removed
310
311 //set caption and the fileProgressBar to 100% for already uploaded files
312 file.previewElement.getElementsByClassName('caption')[0].value = file.caption;
313 file.previewElement.getElementsByClassName('published')[0].value = file.published;
314 file.previewElement.getElementsByClassName('fileIndex')[0].value = index;
315 file.previewElement.getElementsByClassName('download-single-photo')[0].href =file.originalUrl;
316 var fileProgressBar = file.previewElement.getElementsByClassName('dz-upload')[0];
317 fileProgressBar.innerHTML = '100%';
318 fileProgressBar.style.width = '100%';
319 index++;
320 });
321 if(index > 0) {
322 myDropzone.options.maxFiles = myDropzone.options.maxFiles - index; //adjust the correct maxFiles to allow no more than maxFiles in the dropzone
323 }
324 });
325 }
326
327 // 'dropzoneForm' is the camel-case version of the form id 'dropzone-form'
328 Dropzone.options.dropzoneForm = {
329
330 url: basePath + '/' + propId + '/photos/update/' + photoType + '/' + photoVersion,
331 autoProcessQueue: false, //No auto upload. Method processQueue() is called on upload click
332 uploadMultiple: true, //multiple files can be uploaded
333 maxFilesize: 20, //maxFilesize in MB
334 maxThumbnailFilesize: 20, //When the filename exceeds this limit, the thumbnail will not be generated (so must be equal to maxFilesize)
335 parallelUploads: 100,
336 maxFiles: 50,
337 addRemoveLinks: true,
338 acceptedFiles: acceptedFiles, //these files only
339 previewsContainer: '.dropzone-previews',
340 previewTemplate: "<div class=\"dz-preview dz-file-preview\">\n <div class=\"dz-image\"><img data-dz-thumbnail height=\"120\" width=\"120\" /></div>\n <div class=\"dz-details\">\n <div class=\"dz-size\"><span data-dz-size></span></div>\n <div class=\"dz-filename\"><a href=\"#\"><span data-dz-name></span></a></div>\n </div>\n <div class=\"dz-progress\"><span class=\"dz-upload\" data-dz-uploadprogress=\"\" style=\"width: 0%;\"></span></div>\n <div class=\"dz-error-message\"><span data-dz-errormessage></span></div><input type=\"text\" name=\"caption\" data-dz-caption=\"\" class=\"caption\" placeholder=\"add caption\" style=\"width:100%;\" onblur=\"upperFirst(this)\"><select name=\"published\" data-dz-published=\"\" class=\"published\" style=\"width:100%;\"><option value=\"true\">Visible to client</option><option value=\"false\">Hidden from client</option></select><a style='width: 100%' class=\"btn btn-primary download-single-photo\" download=\"\" >Download Picture</a>\n<input type=\"hidden\" name=\"filename\" class=\"filename\"><input type=\"hidden\" name=\"fileIndex\" class=\"fileIndex\">\n <div class=\"dz-success-mark\">\n <svg width=\"54px\" height=\"54px\" viewBox=\"0 0 54 54\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:sketch=\"http://www.bohemiancoding.com/sketch/ns\">\n <title>Check</title>\n <defs></defs>\n <g id=\"Page-1\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" sketch:type=\"MSPage\">\n <path d=\"M23.5,31.8431458 L17.5852419,25.9283877 C16.0248253,24.3679711 13.4910294,24.366835 11.9289322,25.9289322 C10.3700136,27.4878508 10.3665912,30.0234455 11.9283877,31.5852419 L20.4147581,40.0716123 C20.5133999,40.1702541 20.6159315,40.2626649 20.7218615,40.3488435 C22.2835669,41.8725651 24.794234,41.8626202 26.3461564,40.3106978 L43.3106978,23.3461564 C44.8771021,21.7797521 44.8758057,19.2483887 43.3137085,17.6862915 C41.7547899,16.1273729 39.2176035,16.1255422 37.6538436,17.6893022 L23.5,31.8431458 Z M27,53 C41.3594035,53 53,41.3594035 53,27 C53,12.6405965 41.3594035,1 27,1 C12.6405965,1 1,12.6405965 1,27 C1,41.3594035 12.6405965,53 27,53 Z\" id=\"Oval-2\" stroke-opacity=\"0.198794158\" stroke=\"#747474\" fill-opacity=\"0.816519475\" fill=\"#FFFFFF\" sketch:type=\"MSShapeGroup\"></path>\n </g>\n </svg>\n </div>\n <div class=\"dz-error-mark\">\n <svg width=\"54px\" height=\"54px\" viewBox=\"0 0 54 54\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:sketch=\"http://www.bohemiancoding.com/sketch/ns\">\n <title>Error</title>\n <defs></defs>\n <g id=\"Page-1\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" sketch:type=\"MSPage\">\n <g id=\"Check-+-Oval-2\" sketch:type=\"MSLayerGroup\" stroke=\"#747474\" stroke-opacity=\"0.198794158\" fill=\"#FFFFFF\" fill-opacity=\"0.816519475\">\n <path d=\"M32.6568542,29 L38.3106978,23.3461564 C39.8771021,21.7797521 39.8758057,19.2483887 38.3137085,17.6862915 C36.7547899,16.1273729 34.2176035,16.1255422 32.6538436,17.6893022 L27,23.3431458 L21.3461564,17.6893022 C19.7823965,16.1255422 17.2452101,16.1273729 15.6862915,17.6862915 C14.1241943,19.2483887 14.1228979,21.7797521 15.6893022,23.3461564 L21.3431458,29 L15.6893022,34.6538436 C14.1228979,36.2202479 14.1241943,38.7516113 15.6862915,40.3137085 C17.2452101,41.8726271 19.7823965,41.8744578 21.3461564,40.3106978 L27,34.6568542 L32.6538436,40.3106978 C34.2176035,41.8744578 36.7547899,41.8726271 38.3137085,40.3137085 C39.8758057,38.7516113 39.8771021,36.2202479 38.3106978,34.6538436 L32.6568542,29 Z M27,53 C41.3594035,53 53,41.3594035 53,27 C53,12.6405965 41.3594035,1 27,1 C12.6405965,1 1,12.6405965 1,27 C1,41.3594035 12.6405965,53 27,53 Z\" id=\"Oval-2\" sketch:type=\"MSShapeGroup\"></path>\n </g>\n </g>\n </svg>\n </div>\n</div>",
341
342 // The setting up of the dropzone
343 init: function() {
344 var myDropzone = this;
345
346 addFilesFromServer(myDropzone);
347
348 $('#updateBtn').on('click', function(e) {
349 for(var i=0; i<myDropzone.files.length; i++) {
350 if(myDropzone.files[i].previewElement.getElementsByClassName('caption')[0].value == '') {
351 notify('error', 'Please enter caption to all photos');
352 return false;
353 }
354 }
355 //order files before processing, not needed after all, files are processed server-side with the order that are sent
356 /*
357 var orderedQueue = [];
358 var unorderedQueue = myDropzone.files;
359 $('#dropzone-form .dz-preview .dz-filename [data-dz-name]').each(function(count, el) {
360 var name = el.innerHTML;
361 unorderedQueue.forEach(function(file) {
362 if (file.name === name) {
363 orderedQueue.push(file);
364 }
365 });
366 });
367 myDropzone.files = orderedQueue;
368 */
369
370 if(myDropzone.getQueuedFiles().length > 0) { //check if there are files in the queue
371 console.log('processQueue');
372 myDropzone.processQueue(); //first set autoProcessQueue = false
373 existingDbPhotos = true;
374 uploadMsgDisplayed = false;
375 reloadPage = true;
376 } else if(myDropzone.files.length > 0) { //if nothing in the queue then just call uploadFiles([]) (with empty array) to submit the form data (a fix is added in dropzone.js for this to work)
377 console.log('uploadFiles');
378 myDropzone.uploadFiles(myDropzone.files);
379 reloadPage = true;
380 } else {
381 console.log('uploadDummy');
382 var file = {name: 'dummyFilename'}; //nothing to upload, add a dummy file in order for the server to remove all previous files
383 myDropzone.emit('addedfile', file); //event 'addedfile' will be invoked
384 myDropzone.files.push(file);
385 myDropzone.uploadFiles(myDropzone.files);
386 myDropzone.removeFile(file); //remove file here in order not to display the dummy file in the dropzone
387 existingDbPhotos = false;
388 reloadPage = true;
389 }
390 });
391
392 // remove files from dropzone
393 $('#cancelBtn').on('click', function(e) {
394 uploadMsgDisplayed = true; //set it to true in order not to display the message on cancel
395 myDropzone.removeAllFiles();
396 addFilesFromServer(myDropzone);
397 uploadMsgDisplayed = false;
398 });
399
400 // enable upload/cancel buttons when a file is added
401 this.on('addedfile', function(file) {
402 if(myDropzone.files.length) { //do not allow duplicate files with the same name
403 var _i, _len;
404 for(_i = 0, _len = myDropzone.files.length; _i < _len - 1; _i++) { // -1 to exclude current file
405 if(myDropzone.files[_i].name === file.name) {
406 myDropzone.removeFile(file);
407 notify('warn', 'File ' + file.name + ' already exists');
408 }
409 }
410 }
411 $('#updateBtn').prop('disabled', false);
412 $('#cancelBtn').prop('disabled', false);
413 if(existingDbPhotos) {
414 $('#downloadBtn').prop('disabled', false);
415 }
416 file.previewElement.getElementsByClassName('filename')[0].value = file.name; //one of the most important lines: set the hidden filename parameter.
417 });
418
419 // disable upload/cancel buttons while uploading (relevant event: processingmultiple)
420 this.on('sendingmultiple', function() {
421 $('#total-progress').css({opacity:1}); //show bar background
422 $('#updateBtn').prop('disabled', true);
423 $('#cancelBtn').prop('disabled', true);
424 $('#editedBtn').prop('disabled', true);
425 //TODO disable remove links
426 });
427
428 // disable upload/cancel buttons when all files are removed
429 this.on('reset', function() {
430 if(existingDbPhotos) {
431 //in case of existing photos in db the update button must be enabled when dropzone is empty in order to remove all files in server ('dummyFilename' case)
432 if(!uploadMsgDisplayed) {
433 uploadMsgDisplayed = true;
434 notify('warn', 'Click "Update" button to remove files from database');
435 }
436 } else {
437 $('#updateBtn').prop('disabled', true); //disable update button when dropzone is empty and there are no existing photos in db
438 $('#cancelBtn').prop('disabled', true);
439 }
440 $('#downloadBtn').prop('disabled', true);
441 $('#photoUrlDiv').addClass('hidden');
442 });
443
444 // customizing the default progress bar for each file
445 this.on('uploadprogress', function(file, progress) {
446 progress = parseFloat(progress).toFixed(0);
447 if(file.previewElement != undefined) {
448 //in the case of adding files programmatically this element is undefined. Moreover the progress bar for already uploaded files is already set to 100%
449 var progressBar = file.previewElement.getElementsByClassName('dz-upload')[0];
450 progressBar.innerHTML = progress + '%';
451 }
452 });
453
454 // customizing the total progress bar for all files
455 this.on('totaluploadprogress', function(progress) {
456 if(progress > 90) {
457 progress = 90; //max progress is 90%, 100% will be set on successmultiple after server has responded
458 }
459 $('#total-progress .progress-bar').width(progress + '%');
460 });
461
462 // when all dropzone files are uploaded successfully in server, serverResponse is the json response object (relevant events: queuecomplete, completemultiple)
463 this.on('successmultiple', function(files, serverResponse) {
464 $('#total-progress .progress-bar').width('100%');
465 notify('success', successMsg);
466 if(files[0].name != 'dummyFilename') { //very minor check in order not to enable buttons if case of 'dummyFilename' which is always the last file
467 $('#updateBtn').prop('disabled', false);
468 $('#cancelBtn').prop('disabled', false);
469 $('#downloadBtn').prop('disabled', false);
470 }
471 if(reloadPage) {
472 setTimeout(function() {
473 $('#reloadPageUrl')[0].click(); //reload page, otherwise it seems that in case of update immediately after upload the request contains again the mutlipart files
474 }, 1500);
475 }
476 });
477
478 // when all dropzone files in the queue finish uploading, add 0.5 second of delay before reset the progress bar in order to show the animation smoothly
479 // the buttons are still enabled in case for new upload
480 this.on('queuecomplete', function() {
481 setTimeout(function() {
482 $('#total-progress').css({opacity:0});
483 $('#total-progress .progress-bar').width('0%');
484 }, 500);
485 //location.reload(); //reload the page, otherwise it seems that in case of update immediately after upload the request contains again the mutlipart files (done in successmultiple)
486 });
487
488 // when all dropzone files are uploaded successfully in server, serverResponse is the json response object
489 this.on('error', function(file, errorMessage) {
490 if($.type(errorMessage) === 'string') {
491 myDropzone.removeFile(file); //remove file in case of dropzone error on specific file
492 notify('warn', errorMessage);
493 } else {
494 notify('error', errorMsg);
495 }
496 });
497 }
498 }
499
500 $('#dropzone-form').sortable({
501 items:'.dz-preview',
502 cursor: 'move',
503 opacity: 0.5,
504 containment: '#dropzone-form',
505 distance: 20,
506 tolerance: 'pointer'
507 });
508
509 $('#downloadBtn').on('click',function() {
510 $('#updateBtn').prop('disabled', true);
511 $('#cancelBtn').prop('disabled', true);
512 $('#downloadBtn').prop('disabled', true);
513 $('#editedBtn').prop('disabled', true);
514
515 $('#total-progress').css({opacity:1});
516 $('#total-progress .progress-bar').width('40%');
517 $.ajax ({
518 url: basePath + '/' + propId + '/photos/download/' + photoType,
519 type: 'GET',
520 dataType : 'text',
521 success: function(data) {
522 window.location.href = data; //data contains the url of the downloaded file
523
524 $('#photoUrlDiv').removeClass('hidden');
525 $('#photoUrl').val(data);
526
527 var msg = 'Download completed successfully. The file also can be accessed via the below URL';
528 notify('success', msg);
529 $('#updateBtn').prop('disabled', false);
530 $('#cancelBtn').prop('disabled', false);
531 $('#downloadBtn').prop('disabled', false);
532 $('#editedBtn').prop('disabled', false);
533 $('#total-progress .progress-bar').width('100%');
534 setTimeout(function() {
535 $('#total-progress').css({opacity:0});
536 $('#total-progress .progress-bar').width('0%');
537 }, 1000);
538 },
539 error: function(response) {
540 notify('error', errorMsg);
541 $('#updateBtn').prop('disabled', false);
542 $('#cancelBtn').prop('disabled', false);
543 $('#downloadBtn').prop('disabled', false);
544 $('#editedBtn').prop('disabled', false);
545 $('#total-progress .progress-bar').width('0%');
546 setTimeout(function() {
547 $('#total-progress').css({opacity:0});
548 }, 1000);
549 }
550 });
551 });
552
553 $('body').on('click','.dz-image,.dz-details', function() {
554 var fileIndex = 0;
555 $(this).closest('.dz-preview').find('.fileIndex').each(function() {
556 fileIndex = $(this).val();
557 });
558 if(fileIndex) { //for new photos added in dropzone the fileIndex will be undefined, these photos are not part of the carousel
559 $('#carouselModal').modal('show');
560 $('.carousel').carousel(parseInt(fileIndex));
561 }
562 });
563
564 $('#editedBtn').on('click',function() {
565 addBtnInLoadingState($(this), 'fa fa-spin fa-spinner');
566 $.ajax ({
567 url: basePath + '/' + propId + '/photos/edited',
568 type: 'POST',
569 dataType: 'html',
570 success: function(response) {
571 $('#editedDiv').html(response);
572 notify('success', successMsg);
573 },
574 error: function(response) {
575 removeBtnFromLoadingState($(this), 'fa fa-check');
576 notify('error', errorMsg);
577 }
578 });
579 })
580
581 });
582
583 /*]]>*/
584 </script>
585 </th:block>
586
587</body>
588</html>