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.