/** * This model is used by admin/analyse_album.blade.php, to analyse all images. * @constructor */ function AnalyseAlbumViewModel() { this.el = '#analyse-album'; this.data = { imagesFailed: [], imagesToAnalyse: [], imagesInProgress: [], imagesRecentlyCompleted: [], numberSuccessful: 0, numberFailed: 0 }; this.computed = { failedPercentage: function () { var result = 0; if (this.numberTotal > 0) { result = (this.numberFailed / this.numberTotal) * 100; } return result.toFixed(2) + '%'; }, isCompleted: function () { return this.numberTotal > 0 && (this.numberSuccessful + this.numberFailed >= this.numberTotal); }, latestCompletedImages: function() { var startIndex = this.imagesRecentlyCompleted.length - 3 < 0 ? 0 : this.imagesRecentlyCompleted.length - 3; var endIndex = startIndex + 3; return this.imagesRecentlyCompleted.slice(startIndex, endIndex); }, numberTotal: function () { return this.imagesToAnalyse.length; }, successfulPercentage: function () { var result = 0; if (this.numberTotal > 0) { result = (this.numberSuccessful / this.numberTotal) * 100; } return result.toFixed(2) + '%'; } }; this.methods = { // This method is called when an image is added to the array, automatically issue it for analysis // item is an instance of AnalyseImageViewModel analyseImage: function (item) { var self = this; this.imagesToAnalyse.push(item); $.ajax( item.url, { beforeSend: function() { self.imagesInProgress.push(item); }, dataType: 'json', error: function (xhr, textStatus, errorThrown) { self.numberFailed++; self.imagesFailed.push({ 'name': item.name, 'reason': textStatus }); item.isSuccessful = false; item.isPending = false; }, method: 'POST', success: function (data) { if (data.is_successful) { self.numberSuccessful++; item.isSuccessful = true; item.isPending = false; // Push into our "recently completed" array self.imagesRecentlyCompleted.push(item); var indexToRemove = self.imagesInProgress.indexOf(item); if (indexToRemove > -1) { self.imagesInProgress.splice(indexToRemove, 1); } // Remove it again after a few seconds /*window.setTimeout(function() { var indexToRemove = self.imagesRecentlyCompleted.indexOf(item); if (indexToRemove > -1) { self.imagesRecentlyCompleted.splice(indexToRemove, 1); } }, 2000);*/ } else { self.numberFailed++; self.imagesFailed.push({ 'name': item.name, 'reason': data.message }); item.isSuccessful = false; item.isPending = false; } } } ); } }; } /** * This model is used by admin/analyse_album.blade.php, as a sub-model of AnalyseAlbumViewModel. * @param image_info Array of information about the image * @constructor */ function AnalyseImageViewModel(image_info) { this.isPending = true; this.isSuccessful = false; this.name = image_info.name; this.photoID = image_info.photo_id; this.url = image_info.url; } /** * This model is used by admin/show_album.blade.php to handle photo changes. * @param album_id ID of the album the photos are in * @param language Array containing language strings * @param urls Array containing URLs * @constructor */ function EditPhotosViewModel(album_id, language, urls) { this.el = '#photos-tab'; this.data = { albums: [], bulkModifyMethod: '', isSubmitting: false, photoIDs: [], photoIDsAvailable: [], selectAllInAlbum: 0 }; // When a photo is un-selected, remove the "select all in album" flag as the user has overridden the selection /*self.photoIDs.subscribe(function (changes) { if (changes[0].status !== 'deleted') { return; } self.selectAllInAlbum(0); }, null, 'arrayChange');*/ this.methods = { // Called when the Apply button on the "bulk apply selected actions" form is clicked bulkModifySelected: function (e) { if (this.isSubmitting) { return true; } var self = this; var bulk_form = $(e.target).closest('form'); if (this.bulkModifyMethod === 'change_album') { // Prompt for the new album to move to this.promptForNewAlbum(function (dialog) { var album_id = $('select', dialog).val(); $('input[name="new-album-id"]', bulk_form).val(album_id); self.isSubmitting = true; $('button[name="bulk-apply"]', bulk_form).click(); _bt_showLoadingModal(); }); e.preventDefault(); return false; } else if (this.bulkModifyMethod === 'delete') { // Prompt for a confirmation - are you sure?! bootbox.dialog({ message: language.delete_bulk_confirm_message, title: language.delete_bulk_confirm_title, buttons: { cancel: { label: language.action_cancel, className: "btn-secondary" }, confirm: { label: language.action_delete, className: "btn-danger", callback: function () { self.isSubmitting = true; $('button[name="bulk-apply"]', bulk_form).click(); _bt_showLoadingModal(); } } } }); e.preventDefault(); return false; } // All other methods submit the form as normal return true; }, changeAlbum: function (e) { this.selectPhotoSingle(e.target); var photo_id = this.photoIDs[0]; this.photoIDs = []; this.promptForNewAlbum(function (dialog) { var album_id = $('select', dialog).val(); $.post(urls.move_photo.replace(/\/0$/, '/' + photo_id), {'new_album_id': album_id}, function () { window.location.reload(); }); //_bt_showLoadingModal(); }); e.preventDefault(); return false; }, deletePhoto: function (e) { var self = this; this.selectPhotoSingle(e.target); var photo_id = self.photoIDs[0]; this.photoIDs = []; bootbox.dialog({ message: language.delete_confirm_message, title: language.delete_confirm_title, buttons: { cancel: { label: language.action_cancel, className: "btn-secondary" }, confirm: { label: language.action_delete, className: "btn-danger", callback: function () { var url = urls.delete_photo; url = url.replace(/\/0$/, '/' + photo_id); $('.loading', parent).show(); $.post(url, {'_method': 'DELETE'}, function (data) { window.location.reload(); }); } } } }); e.preventDefault(); return false; }, flip: function (horizontal, vertical, parent) { var url = urls.flip_photo; url = url.replace('/0/', '/' + this.photoIDs[0] + '/'); if (horizontal) { url = url.replace(/\/-1\//, '/1/'); } else { url = url.replace(/\/-1\//, '/0/'); } if (vertical) { url = url.replace(/\/-2$/, '/1'); } else { url = url.replace(/\/-2$/, '/0'); } $('.loading', parent).show(); $.post(url, function () { var image = $('img.photo-thumbnail', parent); var originalUrl = image.data('original-src'); image.attr('src', originalUrl + "&_=" + new Date().getTime()); $('.loading', parent).hide(); }); this.photoIDs = []; }, flipBoth: function (e) { this.selectPhotoSingle(e.target); this.flip(true, true, $(e.target).closest('.photo')); e.preventDefault(); return false; }, flipHorizontal: function (e) { this.selectPhotoSingle(e.target); this.flip(true, false, $(e.target).closest('.photo')); e.preventDefault(); return false; }, flipVertical: function (e) { this.selectPhotoSingle(e.target); this.flip(false, true, $(e.target).closest('.photo')); e.preventDefault(); return false; }, isPhotoSelected: function(photoID) { if (this.photoIDs.indexOf(photoID) > -1) { return 'checked'; } return ''; }, promptForNewAlbum: function (callback_on_selected) { var albums = this.albums; var select = $('') .attr('name', 'album_id') .addClass('form-control'); for (var i = 0; i < albums.length; i++) { var option = $('') .attr('value', albums[i].id) .html(albums[i].name) .appendTo(select); // Pre-select the current album if (album_id === albums[i].id) { option.attr('selected', 'selected'); } } bootbox.dialog({ message: $('
').html(language.change_album_message).prop('outerHTML') + select.prop('outerHTML'), title: language.change_album_title, buttons: { cancel: { label: language.action_cancel, className: 'btn-secondary' }, confirm: { label: language.action_continue, className: 'btn-success', callback: function () { callback_on_selected(this); } } } }); }, regenerateThumbnails: function (e) { this.selectPhotoSingle(e.target); var parent = $(e.target).closest('.photo'); var url = urls.regenerate_thumbnails; url = url.replace(/\/0$/, '/' + this.photoIDs[0]); $('.loading', parent).show(); $.post(url, function () { var image = $('img.photo-thumbnail', parent); var originalUrl = image.data('original-src'); image.attr('src', originalUrl + "&_=" + new Date().getTime()); $('.loading', parent).hide(); }); this.photoIDs = []; e.preventDefault(); return false; }, replacePhoto: function (e) { var self = this; this.selectPhotoSingle(e.target); var parent = $(e.target).closest('.photo'); bootbox.dialog({ message: $('').html(language.replace_image_message).prop('outerHTML') + $('#replace-image-form').clone().removeAttr('style').attr('id', 'replace-image-form-visible').prop('outerHTML'), title: language.replace_image_title, buttons: { cancel: { label: language.action_cancel, className: 'btn-secondary' }, confirm: { label: language.action_continue, className: 'btn-success', callback: function () { $('input[name="photo_id"]', '#replace-image-form-visible').val(self.photoIDs[0]); $('#replace-image-form-visible').submit(); } } } }); e.preventDefault(); return false; }, rotate: function (angle, parent) { var url = urls.rotate_photo; url = url.replace('/0/', '/' + this.photoIDs[0] + '/'); url = url.replace(/\/1$/, '/' + angle); $('.loading', parent).show(); $.post(url, function () { var image = $('img.photo-thumbnail', parent); var originalUrl = image.data('original-src'); image.attr('src', originalUrl + "&_=" + new Date().getTime()); $('.loading', parent).hide(); }); this.photoIDs = []; }, rotateLeft: function (e) { this.selectPhotoSingle(e.target); this.rotate(90, $(e.target).closest('.photo')); e.preventDefault(); return false; }, rotateRight: function(e) { this.selectPhotoSingle(e.target); this.rotate(270, $(e.target).closest('.photo')); e.preventDefault(); return false; }, selectAll: function() { var self = this; bootbox.dialog({ title: language.select_all_choice_title, message: language.select_all_choice_message, buttons: { select_all: { label: language.select_all_choice_all_action, className: 'btn-secondary', callback: function() { self.selectAllInAlbum = 1; for (i = 0; i < self.photoIDsAvailable.length; i++) { self.photoIDs.push(self.photoIDsAvailable[i]); } } }, select_visible: { label: language.select_all_choice_visible_action, className: 'btn-primary', callback: function() { self.selectAllInAlbum = 0; for (i = 0; i < self.photoIDsAvailable.length; i++) { self.photoIDs.push(self.photoIDsAvailable[i]); } } } } }); return false; }, selectNone: function() { this.photoIDs = []; this.selectAllInAlbum = 0; return false; }, selectPhotoSingle: function (link_item) { // Get the photo ID from the clicked link var parent = $(link_item).closest('.photo'); var photo_id = $(parent).data('photo-id'); // Save the photo ID this.photoIDs = []; this.photoIDs.push(photo_id); }, switchToUploadTab: function (e) { $('.nav-tabs a[href="#upload-tab"]').tab('show'); e.preventDefault(); return false; } } } function SlideShowViewModel(required_timeout_ms) { this.el = '#slideshow-container'; this.data = { current: null, currentIndex: 0, images: [], interval: null, isPaused: false, isRunning: false }; this.methods = { changeCurrentImage: function(photo_id) { for (var i = 0; i < this.images.length; i++) { var this_image = this.images[i]; if (this_image.id === photo_id) { this.current = this_image; this.currentIndex = i; window.clearInterval(this.interval); this.interval = window.setInterval(this.rotateNextImage, required_timeout_ms); return; } } }, continueSlideshow: function() { this.isPaused = false; this.interval = window.setInterval(this.rotateNextImage, required_timeout_ms); }, pauseSlideshow: function(e) { this.isPaused = true; window.clearInterval(this.interval); }, rotateNextImage: function() { var next_index = this.currentIndex + 1; if (next_index >= this.images.length) { next_index = 0; } this.current = this.images[next_index]; this.currentIndex = next_index; }, startSlideshow: function() { if (this.images.length <= 0) { return; } this.interval = window.setInterval(this.rotateNextImage, required_timeout_ms); this.current = this.images[0]; this.isRunning = true; } }; } /** * This model is used by admin/show_album.blade.php to handle photo uploads. * @param album_id ID of the album the photos are being uploaded to * @param queue_token Unique token of the upload queue to save the photos to * @param language Array containing language strings * @param urls Array containing URLs * @constructor */ function UploadPhotosViewModel(album_id, queue_token, language, urls) { this.el = '#upload-tab'; this.data = { currentStatus: '', imagesFailed: 0, imagesTotal: 0, imagesUploaded: 0, isBulkUploadInProgress: false, isUploadInProgress: false, statusMessages: [] }; this.computed = { failedPercentage: function () { var result = 0; if (this.imagesTotal > 0) { result = (this.imagesFailed / this.imagesTotal) * 100; } return result.toFixed(2) + '%'; }, successfulPercentage: function () { var result = 0; if (this.imagesTotal > 0) { result = (this.imagesUploaded / this.imagesTotal) * 100; } return result.toFixed(2) + '%'; } }; this.methods = { // This method is called when an image is uploaded - regardless if it fails or not onUploadCompleted: function () { this.currentStatus = language.upload_status .replace(':current', (this.imagesUploaded + this.imagesFailed)) .replace(':total', this.imagesTotal); if ((this.imagesFailed + this.imagesUploaded) >= this.imagesTotal) { this.isUploadInProgress = false; if (this.imagesFailed === 0 && this.imagesUploaded > 0) { window.location = urls.analyse; } } }, // This method is called when an uploaded image fails onUploadFailed: function (data, file_name) { this.imagesFailed++; this.statusMessages.push({ 'message_class': 'text-danger', 'message_text': language.image_failed.replace(':file_name', file_name) }); this.onUploadCompleted(); }, // This method is called when an uploaded image succeeds onUploadSuccessful: function (data, file_name) { if (data.is_successful) { this.imagesUploaded++; // Don't add to statusMessages() array so user only sees errors /*self.statusMessages.push({ 'message_class': 'text-success', 'message_text': language.image_uploaded.replace(':file_name', file_name) });*/ this.onUploadCompleted(); } else { this.onUploadFailed(data, file_name); } }, uploadFile: function uploadImageFile(formObject, imageFile) { var self = this; var formData = new FormData(); formData.append('album_id', album_id); formData.append('queue_token', queue_token); formData.append('photo[]', imageFile, imageFile.name); $.ajax( { contentType: false, data: formData, error: function (data) { self.onUploadFailed(data, imageFile.name); }, method: $(formObject).attr('method'), processData: false, success: function (data) { self.onUploadSuccessful(data, imageFile.name); }, url: $(formObject).attr('action') } ); this.isUploadInProgress = true; this.currentStatus = language.upload_status .replace(':current', 0) .replace(':total', this.imagesTotal); }, uploadBulkFiles: function(event) { this.isBulkUploadInProgress = true; return true; }, uploadIndividualFiles: function(event) { var fileSelect = $('input[type=file]', event.target); // Get the selected files var files = fileSelect[0].files; if (files.length === 0) { alert(language.no_file_selected); event.preventDefault(); return false; } // Reset statistics this.currentStatus = ''; this.statusMessages = []; this.imagesUploaded = 0; this.imagesFailed = 0; this.imagesTotal = files.length; this.isUploadInProgress = true; // Loop through each of the selected files and upload them individually for (var i = 0; i < files.length; i++) { var file = files[i]; // We're only interested in image files if (!file.type.match('image.*')) { alert(language.not_an_image_file.replace(':file_name', file.name)); this.onUploadFailed(null, file.name); continue; } // Upload the file this.uploadFile(event.target, file); } // Prevent standard form upload event.preventDefault(); return false; } }; }