PageSourceSearch

https://fvsu.edu/admin/plugins/photogallery/pkg/js/photo-gallery.js

js fvsu.edu collected 2026-10-01 11:38:29 UTC 43,704 bytes, 1,000 lines download raw bytes

1/* ==========================================================================
2   Project:     TWD CMS 7 - Photo Gallery
3   Date:        04/01/20 - File created
4   Created by:  Third Wave Digital (www.thirdwavedigital.com)
5   ========================================================================== */
6   
7   var cmsPhotoGallery = function() {
8     var $albumList = $(".cms-album-list"),
9         $albumLocations = $(".cms-album-locations"),
10         $albumMgmt = $(".cms-album-mgmt"),
11         $photoGallery = $(".cms-photogallery"),
12         $photoGallerySettings = $("[class*='cms-photogallery-settings']");
13     
14     window.awaitingUploadCount = 0;
15 
16     /* ==========================================================================
17          Album List
18     ========================================================================== */
19 
20     if ($albumList.length) {
21         
22         // Edit
23         $albumList.find("table .btn-edit").on("click", function(e) {
24             $albumList.find("table .flex-row.d-none").removeClass("d-none").addClass("d-flex").end()
25                 .find("table .input-group").addClass("d-none");
26 
27             $(this).parent().removeClass("d-flex").addClass("d-none").end()
28                 .parent().next(".input-group").removeClass("d-none");
29         })
30 
31         $albumList.find(".btn-delete").on("click",function(e) {
32             e.preventDefault();
33             var href = $(this).attr('href');
34             bootbox.confirm({
35                  message: "Are you sure you want to delete this album?",
36                  buttons: {
37                       confirm: {
38                            label: "Yes"
39                       },
40                       cancel: {
41                            label: "No",
42                            className: "btn-secondary"
43                       }
44                  },
45                  callback: function (result) {
46                       if( result ) {
47                            window.location.assign( href );
48                       }
49                  }
50             })
51        })
52
53        // Datatable
54        $albumList.find("table").dataTable({
55            "aaSorting": [
56                [0, "asc"]
57            ],
58            "aoColumns": [null, {
59                "bSortable": false
60            }],
61            "aLengthMenu": [[20, 100, -1], [20, 100, "All"]],
62            "iDisplayLength" : 20
63        })
64     }
65 
66     /* ==========================================================================
67          Album Locations
68     ========================================================================== */
69 
70     if ($albumLocations.length) {
71         // Datatable
72         $albumLocations.find("table").dataTable({
73             "aaSorting": [
74                 [0, "asc"]
75             ],
76             "aoColumns": [null, {
77                 "bSortable": false
78             }],
79             "aLengthMenu": [[20, 100, -1], [20, 100, "All"]],
80             "iDisplayLength" : 20
81         })
82     }
83 
84     /* ==========================================================================
85          Album Mgmt.
86     ========================================================================== */
87 
88     if ($albumMgmt.length) {
89         var $photoupload = $(".photo-upload"),
90             $photomenu = $(".photo-menu"),
91             $photogrid = $(".photo-grid"),
92             $photodetails = $(".photo-details");
93 
94         setupDragDrop( $photoupload[0] );
95 
96         // Photo Upload
97         $photoupload.find(".btn-browse").on("click", function(e) {
98             $photoupload.find("input[type=file]").trigger("click");
99         });        
100 
101         var stateChange = function() {
102             // show the CKFinder and regenerate buttons if images exist
103             if( $(".photo-grid").find("li:not('.awaiting-upload')").length ) {
104                 $('button.btn-ckfinder').removeClass('d-none');
105                 $('button.btn-regenerate').removeClass('d-none');
106             } else {
107                 $('button.btn-ckfinder').addClass('d-none');
108                 $('button.btn-regenerate').addClass('d-none');
109             }
110 
111             // hide/show the Select All button
112             if( $(".photo-grid").find('li').length ) {
113                 $('button.btn-select').removeClass('d-none');
114             } else {
115                 $('button.btn-select').addClass('d-none');
116             }
117 
118             // need something for btn-delete 
119             if( $(".photo-grid").find('li').length == 0 ) {
120                 $('button.btn-delete').parent().addClass('d-none');
121             }
122         }
123 
124         stateChange();
125 
126 
127         $photoupload.find("input[type=file]").on("change", function(e) {
128             let files = $(this).prop("files");
129 
130             for (var i = 0; i < files.length; i++) {
131 
132                 (function(imageFile) {
133                     var $template = getAwaitingUploadTemplate();
134                     $template.find('img')
135                         .attr('alt', '')
136                         .attr('title', '')
137                         .data('description', '')
138                         .data('filename', imageFile.name)
139                         .data('filesize', readableBytes(imageFile.size))
140                         .data('size_bytes', imageFile.size)
141                         .data('type', imageFile.type)
142                         .data('fileobj', imageFile)
143                         .data('photo_is_album_cover', 'No');
144 
145                     var imgFileReader = new FileReader();
146 
147                     imgFileReader.onload = (fileReaderEvent) => {
148                         $template.find('img').attr('src', fileReaderEvent.target.result)
149                     }
150 
151                     $template.prependTo($photogrid);
152 
153                     imgFileReader.readAsDataURL(imageFile);
154 
155                     stateChange();
156 
157                 })(files[i]);
158 
159             }
160         })
161 
162 
163         // Photo Menu
164         var allSelected = false;
165 
166         var deselectAll = function() {
167             allSelected = true;
168             $photomenu.find(".btn-select").trigger("click");
169         }
170 
171         $photomenu.find(".custom-select").on("change", function(e) {
172 
173         })
174         /*
175         $photomenu.find(".btn-upload").on("click",function(e) {
176              uploadPhotos( $(this).closest("li") );
177         })
178         $(".btn-upload").on("click",function(e) {
179              uploadPhoto( $(this).closest("li") );
180         })
181         $photomenu.find(".btn-delete").on("click",function(e) {
182              deletePhotos( $(this).closest("li") );
183         })
184         */
185         $photomenu.find(".btn-select").on("click", function(e) {
186             var numSelected = $photogrid.find("input[type=checkbox]:checked").length,
187                 numAwaitingUpload;
188 
189             if (allSelected == false) {
190                 $(this).html("Deselect All");
191                 $photogrid.find("input[type=checkbox]").prop("checked", true);
192                 numSelected = $photogrid.find("input[type=checkbox]:checked").length;
193                 numAwaitingUpload = $photogrid.find("input[type=checkbox]:checked").closest(".awaiting-upload").length;
194                 showPhotoDetails(numSelected, numAwaitingUpload);
195                 allSelected = true;
196             } else {
197                 $(this).html("Select All");
198                 $photogrid.find("input[type=checkbox]").prop("checked", false);
199                 showPhotoDetails(0);
200                 allSelected = false;
201             }
202         })
203 
204         // Photo Grid
205         $(document).on("change", "li input[type=checkbox]", function(e) {
206             var numSelected = $photogrid.find("input[type=checkbox]:checked").length,
207                 numAwaitingUpload = $photogrid.find("input[type=checkbox]:checked").closest(".awaiting-upload").length,
208                 $photo = $photogrid.find("input[type=checkbox]:checked").parent().find("img"),
209                 photoDetails = {
210                     filename: $photo.data("filename"),
211                     alt: $photo.attr("alt"),
212                     title: $photo.attr("title"),
213                     description: $photo.data("description"),
214                     filesize: $photo.data("filesize"),
215                     cover: $photo.data("photo_is_album_cover")
216                 };
217             
218             // handle showing/hiding 'Image Editor' and regenerate buttons
219             if( numSelected == 0 ) {
220                 $('button.btn-ckfinder').removeClass('d-none');
221                 $('button.btn-regenerate').removeClass('d-none');
222             } else {
223                 $('button.btn-ckfinder').addClass('d-none');
224                 $('button.btn-regenerate').addClass('d-none');
225             }
226 
227             showPhotoDetails(numSelected, numAwaitingUpload, photoDetails)
228         })
229         $(document).on("click", ".btn-upload-all", function(e) {
230             uploadPhotos();
231         })
232         $(document).on("click", ".btn-upload", function(e) {
233             uploadPhoto($(this).closest("li").find("img"));
234             $(this).tooltip("hide");
235         })
236         $(document).on("click", ".btn-edit", function(e) {
237             //editPhoto( $(this).closest("li").find("img") );
238         })
239         $(document).on("click", ".btn-delete-single", function(e) {
240             deletePhoto($(this).closest("li").find("img"));
241         })
242         $(document).on("click", ".btn-delete", function(e) {
243             deletePhotos();
244         })
245 
246         $(document).on("click", "form#form-details button.btn-submit", function(e) {
247             savePhotos();
248         })
249 
250         // update the photo data/attributes on change
251         $(document).on("change", "form#form-details input[name='title']", function(e) {
252             $("input[type=checkbox]:checked").parent().find("img").attr('title', $(this).val());
253         })
254         $(document).on("change", "form#form-details input[name='alt']", function(e) {
255             $("input[type=checkbox]:checked").parent().find("img").attr('alt', $(this).val());
256         })
257         $(document).on("change", "form#form-details textarea[name='description']", function(e) {
258             $("input[type=checkbox]:checked").parent().find("img").data('description', $(this).val());
259         })
260 
261         // Sortable
262         $photogrid.sortable({
263             animation: 150,
264             swapThreshold: 1,
265             forceFallback: (document.documentMode || /Edge/.test(navigator.userAgent) || /Edg/.test(navigator.userAgent)) ? true : false,
266             onStart: function(e) {
267                     
268             },
269             onEnd: function(e) {
270                 var ordered = [];
271                 
272                 $photogrid.find('li figure img').each(function() {
273                     ordered.push( $(this).data('photo_id') );
274                 });;
275 
276                 $.post( '/admin/plugins/photogallery/pkg/com/upload.cfc?method=updateSortOrder', { photo_ids: ordered.join() } );
277             },
278             onUpdate: function(e) {
279                     var oldPosition = (e.oldIndex - 1),
280                         newPosition = (e.newIndex - 1);
281             },
282             chosenClass: "dragging"
283         });
284 
285 
286         // Photo Details
287         function showPhotoDetails(numSelected, numAwaitingUpload, photoDetails) {
288             $("html").addClass("photomgmt-details-open");
289 
290             document.getElementById("cover").checked = false;
291 
292 
293             //console.log(...allImageData);
294             
295             /*
296             var detailsCopy = Object.assign({}
296, photoDetails);
297             delete detailsCopy.filesize;
298             delete detailsCopy.filename;
299             //console.log('detailsCopy ', detailsCopy);
300             */
301             
302 
303 
304             $photodetails
305                 .removeClass("d-none")
306                 .find(".filename, .filesize").html("")
307                 .find("#photo-title, #photo-description, #photo-alt").val("");
308             //.find("input[type=checkbox]").prop("checked",false);  
309 
310             if (numSelected == 0) {
311                 $("html").removeClass("photomgmt-details-open");
312                 $photodetails.addClass("d-none");
313                 $photomenu.find(".btn-delete").parent().addClass("d-none");
314             } else if (numSelected == 1 && photoDetails) {
315                 $photodetails
316                     .find(".custom-control, .list-unstyled").removeClass("d-none").end()
317                     .find(".filename").html(photoDetails.filename).end()
318                     .find(".filesize").html(photoDetails.filesize).end()
319                     .find("#photo-title").val(photoDetails.title).end()
320                     .find("#photo-description").val(photoDetails.description).end()
321                     .find("#photo-alt").val(photoDetails.alt);
322 
323                 if (photoDetails.cover.toLowerCase() == 'yes') {
324                     document.getElementById("cover").checked = true;
325                 }
326 
327                 $photomenu.find(".btn-delete").parent().removeClass("d-none");
328             } else {
329 
330                 var detailData = doSelectedImageDetailsMatch();
331                 if( detailData.match ) {
332                     try {
333                         $photodetails.find("#photo-title").val( detailData.details[0].title );
334                         $photodetails.find("#photo-description").val( detailData.details[0].description );
335                         $photodetails.find("#photo-alt").val( detailData.details[0].alt );
336                     } catch(e) {}
337                     $photodetails.find(".custom-control, .list-unstyled").addClass("d-none");
338                 } else {
339                     $photodetails
340                         .find("#photo-title, #photo-description,#photo-alt").val("").end()
341                         .find(".custom-control, .list-unstyled").addClass("d-none");
342                 }
343                 $photomenu.find(".btn-delete").parent().removeClass("d-none");
344             }
345 
346             if (numAwaitingUpload > 0) {
347                 $photomenu.find(".btn-upload-all").parent().removeClass("d-none");
348             } else {
349                 $photomenu.find(".btn-upload-all").parent().addClass("d-none");
350             }
351         }
352 
353 
354         function doSelectedImageDetailsMatch() {
355             // lets assume the data is the same
356             var sameData = true;
357             var allImageData = [];
358 
359             // find all the checked images
360             $('li:not(".awaiting-upload") input[type="checkbox"]:checked').each(function() {
361                 // keep going only if sameData is still true, break out otherwise
362                 var img = $(this).parent().find('img');
363                 var data = {
364                     description: img.data('description') + '',
365                     alt: img.attr('alt') + '',
366                     title: img.attr('title') + ''
367                 };
368                 allImageData.push(data);
369             });
370 
371             for (i = 0; i < allImageData.length; i++) {
372                 if( sameData ) {
373                     var first = allImageData[0];
374                     var curr = allImageData[i];
375                     if( JSON.stringify(first) !== JSON.stringify(curr) ) {
376                         sameData = false;
377                         break;
378                     }
379                 }
380             }
381 
382             return {
383                 details: allImageData,
384                 match: sameData
385             };
386         }
387 
388 
389         // Photo Operations
390         function uploadPhotos(finishedCallback) {
391             window.awaitingUploadCount = 0;
392             var lis = $('li.awaiting-upload input[type="checkbox"]:checked');
393             if( !finishedCallback ) {
394                 window.awaitingUploadCount = lis.length;
395                 finishedCallback = function() {
396                     window.awaitingUploadCount--;
397                     if( window.awaitingUploadCount == 0 ) {
398                         deselectAll();
399                     }
400                 }
401             }
402             lis.each(function(index) {
403                 var elm = $(this);
404                 var img = elm.next().find('img');
405 
406                 //console.log(img.data());
407                 uploadPhoto(img, finishedCallback);
408             });
409         }
410 
411         function uploadPhoto(img, finishedCallback) {
412             var $progress = img.closest("li").find("div.progress-bar");
413 
414             var oReq = new XMLHttpRequest();
415             oReq.overrideMimeType("application/json");
416             oReq.open("POST", "/admin/plugins/photogallery/pkg/com/upload.cfc?method=uploadImage", true);
417 
418             oReq.onload = function(oEvent) {
419                 $progress.attr('aria-valuenow', 100).css('width', '100%');
420                 $progress.parent().addClass("d-none");
421                 $('#save-errors').html('').addClass('d-none');
422 
423                 if (oReq.readyState === oReq.DONE) {
424                     if (oReq.status === 200) {
425                         var response = JSON.parse(oReq.response);
426                         if (response.status) {
427                             // result success
428                             uploadSuccess(img, response);
429                             if (finishedCallback) {
430                                 finishedCallback(true);
431                             }
432                             stateChange();
433                         } else {
434                             $('#save-errors').removeClass('d-none');
435                             $('#save-errors').append('Error: ' + response.errors.join('<br />'));
436                         }
437                     } else {
438                         $('#save-errors').removeClass('d-none');
439                         $('#save-errors').append('Error: ' + oReq.responseText);
440                     }
441                 }
442             };
443 
444             oReq.upload.addEventListener('loadstart', (evt) => {
445                 $progress.parent().removeClass("d-none");
446                 $progress.attr('aria-valuenow', 0).css('width', 0 + '%');
447             });
448             oReq.upload.addEventListener('progress', (evt) => {
449                 if (evt.lengthComputable) {
450                     var percent = Math.round(evt.loaded / evt.total * 100);
451                     $progress.attr('aria-valuenow', percent).css('width', percent + '%');
452                 }
453             });
454             // we may want to ignore this and listen to oReq.onload instead, that event includes waiting application logic and processing
455             //oReq.upload.addEventListener('loadend', (evt) => {});
456 
457             var formData = new FormData();
458 
459             formData.append("album_id", $('input#album_id').val());
460 
461             formData.append("fileField", "htmLFileAttachment"); // tell the server API the label/name of the file field that contains file data
462             formData.append("htmLFileAttachment", img.data('fileobj'), img.data('filename'));
463 
464             formData.append("filename", img.data('filename'));
465             formData.append("filesize", img.data('filesize'));
466             formData.append("size_bytes", img.data('size_bytes'));
467             formData.append("description", img.data('description'));
468             formData.append("alt", img.attr('alt'));
469             formData.append("title", img.attr('title'));
470             formData.append("photo_is_album_cover", img.data('photo_is_album_cover'));
471 
472             oReq.send(formData);
473         }
474 
475         function editPhoto() {
476 
477         }
478 
479         function uploadSuccess(img, response) {
480             var li = img.closest('li.awaiting-upload');
481             li.removeClass('awaiting-upload');
482             li.find('input[type="checkbox"]:checked').trigger('click');
483             li.find('div.progress').remove();
484 
485             li.find('button.btn-upload').remove();
486             //li.find('div.btns').prepend('<button type="button" class="btn btn-cms btn-edit" data-tooltip="tooltip" title="" aria-label="Edit" data-original-title="Edit"><span class="fas fa-edit" aria-hidden="true"></span></button>');
487 
488             img.data('album_id', response.data.photo.ALBUM_ID);
489             img.data('photo_id', response.data.photo.PHOTO_ID);
490         }
491 
492         function savePhoto(img) {
493             var postData = [];
494             var photoData = {
495                 photo_id: img.data('photo_id'),
496                 album_id: img.data('album_id'),
497                 photo_description: img.data('description'),
498                 photo_alt: img.attr('alt'),
499                 photo_title: img.attr('title')
500             }
501             postData.push(photoData);
502             // if only one is selected, we can let the cover parameter added
503             if ($('input#cover:checked').length > 0) {
504                 $('.fa-star').remove();
505                 postData[0].photo_is_album_cover = 'Yes';
506                 img.closest('li').append('<span class="fas fa-star text-primary" aria-hidden="true" data-tooltip="tooltip" title="Album Cover"></span>');
507             }
508 
509             sendPostData(postData);
510         }
511 
512         function savePhotos(uploaded) {
513             // get a reference to all the currently checked/selected checkboxes
514             $('#loading-indicator').removeClass('d-none');
515 
516             var imgs = $("input[type=checkbox]:checked").next().find('img');
517 
518             if (!uploaded) {
519                 // check if any of these need to be uploaded first
520                 if (imgs.closest('li.awaiting-upload').length) {
521                     imgs.each(function(i) {
522                         var img = $(this);
523                         uploadPhoto(img, function() {
524                             savePhoto(img);
525                         });
526                     });
527                     return;
528                 }
529             }
530 
531             var postData = [];
532             imgs.each(function(i) {
533                 var img = $(this);
534                 var photoData = {
535                     photo_id: img.data('photo_id'),
536                     album_id: img.data('album_id'),
537                     photo_description: img.data('description'),
538                     photo_alt: img.attr('alt'),
539                     photo_title: img.attr('title')
540                 }
541                 postData.push(photoData);
542             });
543 
544             // if only one is selected, we can let the cover parameter added
545             if (imgs.length == 1 && $('input#cover:checked').length > 0) {
546                 $('.fa-star').remove();
547                 postData[0].photo_is_album_cover = 'Yes';
548                 imgs.closest('li').append('<span class="fas fa-star text-primary" aria-hidden="true" data-tooltip="tooltip" title="Album Cover"></span>');
549             }
550 
551             sendPostData(postData);
552         }
553 
554 
555         function sendPostData(postData) {
556             var xhr = new XMLHttpRequest();
557             var url = "url";
558             xhr.open("POST", "/admin/plugins/photogallery/pkg/com/photocontroller.cfc?method=updatePhotosDetail", true);
559             xhr.setRequestHeader("Content-Type", "application/json");
560             xhr.onreadystatechange = function() {
561                 if (xhr.readyState === 4) {
562                     // its finished, remove loading
563                     $('#loading-indicator').addClass('d-none');
564                     if (xhr.status === 200) {
565                         $('#save-errors').html('').addClass('d-none');
566                         var json = JSON.parse(xhr.responseText);
567                         // this is an array of update results
568                         for (var i = 0; i < json.length; i++) {
569                             var res = json[i];
570                             if (!res.status) {
571                                 // an error occored
572                                 $('#save-errors').removeClass('d-none');
573                                 for (var x = 0; x < res.errors.length; x++) {
574                                     $('#save-errors').append('Error: ' + res.errors[x] + '<br />');
575                                 }
576                             } else {
577                                 $('.toast').toast('show');
578                             }
579                         }
580                         deselectAll();
581                     }
582                 }
583             };
584             xhr.send(JSON.stringify(postData));
585         }
586 
587         $(".toast-photodetails").on('shown.bs.toast', function () {
588             $(this).addClass("show");
589         })
590 
591         $(".toast-photodetails").on('hidden.bs.toast', function () {
592             $(this).removeClass("show");
593         })
594 
595 
596         function deletePhotos() {
597             //var numSelected = $photogrid.find("input[type=checkbox]:checked").length,
598             var numSelected = $("input[type=checkbox]:checked").closest("li").find("img").length,
599                 fileString = (numSelected > 1) ? "these files?" : "this file?";
600 
601             bootbox.confirm({
602                 message: "Are you sure you want to delete " + fileString + "",
603                 buttons: {
604                     confirm: {
605                         label: "Yes"
606                     },
607                     cancel: {
608                         label: "No",
609                         className: "btn-secondary"
610                     }
611                 },
612                 callback: function(result) {
613                     if (result) {
614                         $("input[type=checkbox]:checked").closest("li").find("img").each(function() {
615                             var img = $(this);
616                             deleteSinglePhoto(img);
617                         });
618                     }
619                 }
620             })
621         }
622 
623         function deletePhoto(img) {
624             bootbox.confirm({
625                 message: "Are you sure you want to delete this file?",
626                 buttons: {
627                     confirm: {
628                         label: "Yes"
629                     },
630                     cancel: {
631                         label: "No",
632                         className: "btn-secondary"
633                     }
634                 },
635                 callback: function(result) {
636                     if (result) {
637                         deleteSinglePhoto(img);
638                     }
639                 }
640             })
641         }
642 
643         function deleteSinglePhoto(img) {
644             var li = img.closest("li");
645             if (typeof img.data('photo_id') !== 'undefined' && img.data('photo_id').toString().length) {
646                 // the data photo_id attribute exists, that means it's a persistant image
647 
648                 var xhr = new XMLHttpRequest();
649                 var url = "url";
650                 xhr.open("GET", "/admin/plugins/photogallery/pkg/com/photocontroller.cfc?method=deletePhoto&photo_id=" + img.data('photo_id'), true);
651                 xhr.setRequestHeader("Content-Type", "application/json");
652                 xhr.onreadystatechange = function() {
653                     if (xhr.readyState === 4) {
654                         if (xhr.status === 200) {
655                             $('#save-errors').html('').addClass('d-none');
656                             var json = JSON.parse(xhr.responseText);
657                             // this is an array of update results
658                             if (!json.status) {
659                                 // an error occored
660                                 $('#save-errors').removeClass('d-none');
661                                 for (var x = 0; x < json.errors.length; x++) {
662                                     $('#save-errors').append('Error: ' + json.errors[x] + '<br />');
663                                 }
664                             } else {
665                                 li.remove();
666 
667                                 // if the user deletes All photos, refresh the page to fix the state
668                                 if( $('ul.photo-grid li').length == 0 ) {
669                                     // wait 1 second before refreshing
670                                     setTimeout(function(){
671                                         window.location.reload(false);
672                                     }, 1000);
673                                 } else {
674                                     showPhotoDetails(0);
675                                     stateChange();
676                                 }
677                             }
678                         }
679                     }
680                 };
681                 xhr.send(JSON.stringify([]));
682 
683             } else {
684                 // the user simply put this in the uploader but never submitted it, remove the li
685                 li.remove();
686             }
687         }
688
689          // Sanitize title & description fields
690          $("#photo-title,#photo-description,#photo-alt").on("keydown input",function(e) {
691               var val = $(this).val();
692               $(this).val(val.replace(/[(\n\r|\n|\r|)]/gm," "));
693               if (e.keyCode == 13) e.preventDefault();
694          })
695     
696     }
697 
698     /* ==========================================================================
699          Photogallery Settings
700     ========================================================================== */
701 
702     if ($photoGallerySettings.length) {
703         $photoGallerySettings.each(function(index, element) {
704             // Toggle
705             $(element).find("button[aria-controls]").on("click", function(e) {
706                 $(element)
707                     .find(".collapse").collapse("hide").end()
708                     .find("button[aria-controls]").not(this).removeClass("active");
709             })
710             $(element).find(".collapse").on("show.bs.collapse", function(e) {
711                 $(element).find(".cms-photogallery-submit").removeClass("d-none");
712                 $.each(e.target.classList, function(idx, target) {
713                     $(element).find("button[aria-controls=" + target + "]").addClass("active");
714                 })
715             })
716 
717             $(element).find(".collapse").on("hide.bs.collapse", function(e) {
718                 $(element).find(".cms-photogallery-submit").addClass("d-none");
719                 $.each(e.target.classList, function(idx, target) {
720                     $(element).find("button[aria-controls=" + target + "]").removeClass("active");
721                 })
722             })
723 
724             // Init Datatable
725            var dt = $(element).find("[class*='cms-picker-settings-'] table").dataTable({
726                 "aaSorting": [
727                     [0, "asc"]
728                 ],
729                 "aoColumns": [null, {
730                     "bSortable": false
731                 }],
732                 "columnDefs": [{
733                     targets: -1,
734                     className: "text-right"
735                 }],
736                 "aLengthMenu": [
737                     [5, 10, 50, -1],
738                     [5, 10, 50, "All"]
739                 ],
740                 "iDisplayLength": 5,
741                 "paging": false,
742                 "rowReorder": {
743                      selector: ".btn-reorder",
744                      update: false
745                 }
746                 });
747                 
748                 // Save reorder 
749                 dt.on("row-reorder.dt", function (e,details,edit) {
750                 var trs = $(this).find('tbody tr'),
751                    ordered = [];
752                 
753                 trs.each(function() {
754                    ordered.push( $(this).data('album_id') );
755                 });
756                
757                 // POST takes place here to update the sort order
758                 $.post( '/admin/plugins/photogallery/pkg/com/photoalbums.cfc?method=updateSortOrder', { album_ids: ordered.join() } );
759                 })
760
761             
762            // this is done because DataTables remove checkboxes from the DOM.  When submitting the form the previous page selections are missing
763            $(element).find('.submit-edit-page-gallery-button').on('click', function() {
764                var ids = [];
765                $(element).find("[class*='cms-picker-settings-'] table").DataTable().rows().nodes().to$().find('input[name="album_select_checkbox"][type="checkbox"]:checked').each(function(){
766                    ids.push( $(this).val() );
767                });
768                $(element).find('input[name="album_select"]').val( ids.join() );
769                $(element).find('form.gallery-edit-form').trigger('submit');
770            });
771             
772 
773             // Gallery Settings
774             $(element).find("[name='num_photos']").on("change", function(e) {
775                 $(element).next(".cms-photogallery").toggleClass("cms-gallery-badge-hidden");
776             })
777 
778             $(element).find("[name='gallery_template']").on("change", function(e) {
779                 var val = $(this).val();
780                 $(element).next(".cms-photogallery").removeClass("cms-gallery-card cms-gallery-masonry").addClass(val);
781             })
782 
783             $(element).find("[name='gallery_title_position']").on("change", function(e) {
784                 var val = $(this).val();
785                 $(element).next(".cms-photogallery").removeClass("cms-gallery-title-below cms-gallery-title-over cms-gallery-title-above cms-gallery-title-hidden").addClass(val);
786             })
787 
788             $(element).find("[name='gallery_thumbnail_size']").on("change", function(e) {
789                 var val = $(this).val();
790                 $(element).next(".cms-photogallery").removeClass("cms-gallery-thumbs-sm cms-gallery-thumbs-md  cms-gallery-thumbs-lg").addClass(val);
791             })
792 
793             $(element).find("[name='gallery_thumbnail_padding']").on("change", function(e) {
794                 var val = $(this).val();
795                 $(element).next(".cms-photogallery").removeClass("cms-gallery-padding-sm cms-gallery-padding-md cms-gallery-padding-lg cms-gallery-padding-none").addClass(val);
796             })
797 
798             // Album Setting Visibility
799             if ($(element).next(".cms-album").length) {
800                 $(element).find("[class*='cms-picker-settings-'] input[type='checkbox']").on("change", function(e) {
801                     var numSelected = $(element).find("[class*='cms-picker-settings-'] input[type='checkbox']:checked").length;
802                     if ($(element).next(".cms-album").length && numSelected > 1) {
803                         $(element).find("button[aria-controls*='cms-album-settings-'], button[aria-controls*='cms-modal-settings-']").addClass("disabled");
804                     } else {
805                         $(element).find("button[aria-controls*='cms-album-settings-'], button[aria-controls*='cms-modal-settings-']").removeClass("disabled");
806                     }
807                 })
808             }
809 
810             // Gallery Setting Visibility
811             if ($(element).next(".cms-gallery").length) {
812                 $(element).find("[class*='cms-picker-settings-'] input[type='checkbox']").on("change", function(e) {
813                     var numSelected = $(element).find("[class*='cms-picker-settings-'] input[type='checkbox']:checked").length;
814                     if ($(element).next(".cms-gallery").length && numSelected == 1) {
815                         $(element).find("button[aria-controls*='cms-gallery-settings-'], button[aria-controls*='cms-modal-settings-']").addClass("disabled");
816                     } else {
817                         $(element).find("button[aria-controls*='cms-gallery-settings-'], button[aria-controls*='cms-modal-settings-']").removeClass("disabled");
818                     }
819                 })
820             }
821 
822             // Album Settings
823             $(element).find("[name='album_template']").on("change", function(e) {
824                 var val = $(this).val();
825                 $(element).next(".cms-photogallery").removeClass("cms-album-grid-cropped cms-album-grid cms-album-masonry cms-album-polaroids").addClass(val);
826             })
827 
828             $(element).find("[name='album_title_position']").on("change", function(e) {
829                 var val = $(this).val();
830                 $(element).next(".cms-photogallery").removeClass("cms-album-title-below cms-album-title-over cms-album-title-above cms-album-title-hidden").addClass(val);
831             })
832 
833             $(element).find("[name='album_thumbnail_size']").on("change", function(e) {
834                 var val = $(this).val();
835                 $(element).next(".cms-photogallery").removeClass("cms-album-thumbs-sm cms-album-thumbs-md  cms-album-thumbs-lg").addClass(val);
836             })
837 
838             $(element).find("[name='album_thumbnail_padding']").on("change", function(e) {
839                 var val = $(this).val();
840                 $(element).next(".cms-photogallery").removeClass("cms-album-padding-sm cms-album-padding-md cms-album-padding-lg cms-album-padding-none").addClass(val);
841             })
842 
843         })
844     }
845 
846     /* ==========================================================================
847          Photogallery
848     ========================================================================== */
849 
850     if ($photoGallery.length) {
851         
852         $photoGallery.each(function(index, element) {
853             // Album
854             if ($(element).hasClass("cms-album")) {
855                 // lightGallery
856                 var thumbDisplay = $(element).hasClass("cms-album-modalthumbs") ? thumbDisplay = true : thumbDisplay = false;
857 
858                 $(element).lightGallery({
859                     selector: "a",
860                     thumbnail: thumbDisplay,
861                     getCaptionFromTitleOrAlt: false,
862                     exThumbImage: 'data-exthumbimage'
863                 })
864                 // Gallery
865             } else if ($(element).hasClass("cms-gallery") && $(".edit-gallery-form-container").length > 0) {
866                 // Sortable
867                 $(element).sortable({
868                     sort: true,
869                     swapThreshold: .5,
870                     animation: 500,
871                     ghostClass: "bg-light",
872                     dataIdAttr: "data-id",
873                     store: {
874                         get: function(sortable) {
875                             //console.log('get ', sortable);
876                         },
877                         set: function(sortable) {
878                            var idsInOrder = sortable.toArray().toString();
879                            //$('input[name="album_sort_order"]').val(idsInOrder); 
880                            // we don't allow sorting anymore?
881                         }
882                     },
883                     draggable: "li",
884                     onStart: function(e) {
885 
886                     },
887                     onEnd: function(e) {
888 
889                     },
890                     onSort: function(e) {
891 
892                     }
893                 })
894             }
895         })
896     }
897 
898 }
899
900
901 
902 function getAwaitingUploadTemplate() {
903     var $html = $(' \
904           <li class="awaiting-upload"> \
905                <div class="progress d-none"> \
906                     <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="75" aria-valuemin="0" aria-valuemax="100" style="width: 75%"></div> \
907                </div> \
908                <div class="d-flex justify-content-end align-items-center btns w-100"> \
909                     <button type="button" class="btn btn-cms btn-upload" data-tooltip="tooltip" title="Upload" aria-label="Upload"><span class="fas fa-cloud-upload-alt" aria-hidden="true"></span></button> \
910                     <button type="button" class="btn btn-cms btn-delete-single" data-tooltip="tooltip" title="Delete" aria-label="Delete">
910<span class="fas fa-trash-alt" aria-hidden="true"></span></button> \
911                </div> \
912                <div class="custom-control custom-checkbox checkbox-lg"> \
913                     <input type="checkbox" class="custom-control-input"> \
914                     <label class="custom-control-label"><span class="sr-only">Select</span> \
915                           <figure> \
916                                 <img src="" alt="" title=""> \
917                           </figure> \
918                     </label> \
919                </div> \
920           </li> \
921      ');
922 
923     var elementid = uuidv4();
924     $html.find('input[type="checkbox"]').attr('id', elementid);
925     $html.find('label.custom-control-label').attr('for', elementid);
926     $html.find('[data-tooltip="tooltip"]').tooltip({
927          boundary: "window",
928          template: '<div class="tooltip cms-tooltip cms-mainmenu-tooltip" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>'
929      })
930     return $html;
931 }
932 
933 
934 function getImageData(img) {
935     var canvas = document.createElement("canvas");
936     var ctx = canvas.getContext("2d");
937     ctx.drawImage(img, 0, 0);
938     imageData = ctx.getImageData(0, 0, img.width, img.height).data;
939     //console.log("image data size:", imageData.length);
940     canvas.remove();
941     return imageData;
942 }
943 
944 function readableBytes(bytes) {
945     var i = Math.floor(Math.log(bytes) / Math.log(1024)),
946         sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
947     return (bytes / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + sizes[i];
948 }
949 
950 function uuidv4() {
951     return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
952         var r = Math.random() * 16 | 0,
953             v = c == 'x' ? r : (r & 0x3 | 0x8);
954         return v.toString(16);
955     });
956 }
957 
958 function setupDragDrop( dropArea ) {
959     var preventDefaults = function(e) {
960         e.preventDefault()
961         e.stopPropagation()
962     };
963 
964     var highlight = function(e) {
965         dropArea.classList.add('bg-light')
966     };
967 
968     var unhighlight = function(e) {
969         dropArea.classList.remove('bg-light')
970     };
971 
972     ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
973         dropArea.addEventListener(eventName, preventDefaults, false);
974     });
975 
976     ['dragenter', 'dragover'].forEach(eventName => {
977         dropArea.addEventListener(eventName, highlight, false)
978     });
979 
980     ['dragleave', 'drop'].forEach(eventName => {
981         dropArea.addEventListener(eventName, unhighlight, false)
982     });
983 
984     dropArea.addEventListener('drop', function(e) {
985         var dt = e.dataTransfer;
986         var files = dt.files;
987         var $fileInput = $(dropArea).find("input[type=file]");
988         $fileInput[0].files = e.dataTransfer.files;
989         $fileInput.trigger('change');
990     }, false);
991 
992 }
993 
994 
995 $(document).ready(function() {
996 
997     cmsPhotoGallery();
998 
999 })
1000 

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.