716 lines
24 KiB
JavaScript
716 lines
24 KiB
JavaScript
/**
|
|
* 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 = $('<select/>')
|
|
.attr('name', 'album_id')
|
|
.addClass('form-control');
|
|
|
|
for (var i = 0; i < albums.length; i++) {
|
|
var option = $('<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: $('<p/>').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: $('<p/>').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;
|
|
}
|
|
};
|
|
} |