jam-cloud/web/app/assets/javascripts/accounts_profile_avatar.js

438 lines
18 KiB
JavaScript

(function(context,$) {
"use strict";
context.JK = context.JK || {};
context.JK.AccountProfileAvatarScreen = function(app) {
var self = this;
var logger = context.JK.logger;
var rest = context.JK.Rest();
var userId;
var user = {};
var tmpUploadPath = null;
var userDetail = null;
var avatar;
var selection = null;
var targetCropSize = 88;
var updatingAvatar = false;
var userDropdown;
function beforeShow(data) {
logger.debug("data.id=" + data.id);
userId = data.id;
}
function afterShow(data) {
resetForm();
renderAvatarScreen()
}
function resetForm() {
// remove all display errors
$('#account-profile-avatar-content-scroller form .error-text').remove()
$('#account-profile-avatar-content-scroller form .error').removeClass("error")
}
function populateAvatar(userDetail) {
self.userDetail = userDetail;
rest.getFilepickerPolicy()
.done(function(filepicker_policy) {
var template= context.JK.fillTemplate($('#template-account-profile-avatar').html(), {
"fp_apikey" : gon.fp_apikey,
"data-fp-store-path" : createStorePath(userDetail) + createOriginalFilename(userDetail),
"fp_policy" : filepicker_policy.policy,
"fp_signature" : filepicker_policy.signature
});
$('#account-profile-avatar-content-scroller').html(template);
var currentFpfile = determineCurrentFpfile();
var currentCropSelection = determineCurrentSelection(userDetail);
renderAvatar(currentFpfile, currentCropSelection ? JSON.parse(currentCropSelection) : null);
})
.error(app.ajaxError);
}
// events for main screen
function events() {
// wire up main panel clicks
$('#account-profile-avatar-content-scroller').on('click', '#account-edit-avatar-upload', function(evt) { evt.stopPropagation(); handleFilePick(); return false; } );
$('#account-profile-avatar-content-scroller').on('click', '#account-edit-avatar-delete', function(evt) { evt.stopPropagation(); handleDeleteAvatar(); return false; } );
$('#account-profile-avatar-content-scroller').on('click', '#account-edit-avatar-cancel', function(evt) { evt.stopPropagation(); navToEditProfile(); return false; } );
$('#account-profile-avatar-content-scroller').on('click', '#account-edit-avatar-submit', function(evt) { evt.stopPropagation(); handleUpdateAvatar(); return false; } );
//$('#account-profile-avatar-content-scroller').on('change', 'input[type=filepicker-dragdrop]', function(evt) { evt.stopPropagation(); afterImageUpload(evt.originalEvent.fpfile); return false; } );
}
function handleDeleteAvatar() {
if(self.updatingAvatar) {
// protect against concurrent update attempts
return;
}
self.updatingAvatar = true;
renderAvatarSpinner();
rest.deleteAvatar()
.done(function() {
removeAvatarSpinner({ delete:true });
deleteAvatarSuccess(arguments);
selection = null;
})
.fail(function() {
app.ajaxError(arguments);
$.cookie('original_fpfile', null);
self.updatingAvatar = false;
})
.always(function() {
})
}
function deleteAvatarSuccess(response) {
renderAvatar(null, null);
userDropdown.loadMe();
rest.getUserDetail()
.done(function(userDetail) {
self.userDetail = userDetail;
})
.error(app.ajaxError)
.always(function() {
self.updatingAvatar = false;
})
}
function handleFilePick() {
rest.getFilepickerPolicy()
.done(function(filepickerPolicy) {
renderAvatarSpinner();
filepicker.setKey(gon.fp_apikey);
filepicker.pickAndStore({
mimetype: 'image/*',
maxSize: 10000*1024,
policy: filepickerPolicy.policy,
signature: filepickerPolicy.signature
}, { path: createStorePath(self.userDetail), access: 'public' },
function(fpfiles) {
removeAvatarSpinner();
afterImageUpload(fpfiles[0]);
}, function(fperror) {
removeAvatarSpinner();
if(fperror.code != 101) { // 101 just means the user closed the dialog
alert("unable to upload file: " + JSON.stringify(fperror))
}
})
})
.fail(app.ajaxError);
}
function renderAvatarScreen() {
rest.getUserDetail()
.done(populateAvatar)
.error(app.ajaxError)
}
function navToEditProfile() {
resetForm();
window.location = '/client#/account/profile'
}
function renderAvatarSpinner() {
var avatarSpace = $('#account-profile-avatar-content-scroller .account-profile-avatar .avatar-space');
// if there is already an image tag, we only obscure it.
var avatar = $('img.preview_profile_avatar', avatarSpace);
var spinner = $('<div class="spinner spinner-large"></div>')
if(avatar.length === 0) {
avatarSpace.prepend(spinner);
}
else {
// in this case, just style the spinner to obscure using opacity, and center it
var jcropHolder = $('.jcrop-holder', avatarSpace);
spinner.width(jcropHolder.width());
spinner.height(jcropHolder.height());
spinner.addClass('op50');
var jcrop = avatar.data('Jcrop');
if(jcrop) {
jcrop.disable();
}
avatarSpace.append(spinner);
}
}
function removeAvatarSpinner(options) {
var avatarSpace = $('#account-profile-avatar-content-scroller .account-profile-avatar .avatar-space');
if(options && options.delete) {
avatarSpace.children().remove();
}
var spinner = $('.spinner-large', avatarSpace);
spinner.remove();
var avatar = $('img.preview_profile_avatar', avatarSpace);
var jcrop = avatar.data('Jcrop')
if(jcrop) {
jcrop.enable();
}
}
function renderAvatar(fpfile, storedSelection) {
// clear out
var avatarSpace = $('#account-profile-avatar-content-scroller .account-profile-avatar .avatar-space');
if(!fpfile) {
renderNoAvatar(avatarSpace);
}
else {
rest.getFilepickerPolicy({handle: fpfile.url})
.done(function(filepickerPolicy) {
avatarSpace.children().remove();
renderAvatarSpinner();
var photo_url = fpfile.url + '?signature=' + filepickerPolicy.signature + '&policy=' + filepickerPolicy.policy;
avatar = new Image();
$(avatar)
.load(function(e) {
removeAvatarSpinner();
avatar = $(this);
avatarSpace.append(avatar);
var width = avatar.naturalWidth();
var height = avatar.naturalHeight();
if(storedSelection) {
var left = storedSelection.x;
var right = storedSelection.x2;
var top = storedSelection.y;
var bottom = storedSelection.y2;
}
else {
if(width < height) {
var left = width * .25;
var right = width * .75;
var top = (height / 2) - (width / 4);
var bottom = (height / 2) + (width / 4);
}
else {
var top = height * .25;
var bottom = height * .75;
var left = (width / 2) - (height / 4);
var right = (width / 2) + (height / 4);
}
}
// jcrop only works well with px values (not percentages)
// so we get container, and work out a decent % ourselves
var container = $('#account-profile-avatar-content-scroller');
avatar.Jcrop({
aspectRatio: 1,
boxWidth: container.width() * .75,
boxHeight: container.height() * .75,
// minSelect: [targetCropSize, targetCropSize], unnecessary with scaling involved
setSelect: [ left, top, right, bottom ],
trueSize: [width, height],
onRelease: onSelectRelease,
onSelect: onSelect,
onChange: onChange
});
})
.error(function() {
// default to no avatar look of UI
renderNoAvatar(avatarSpace);
})
.attr('src', photo_url)
.attr('alt', 'profile avatar')
.addClass('preview_profile_avatar');
})
.fail(app.ajaxError);
}
}
function afterImageUpload(fpfile) {
$.cookie('original_fpfile', JSON.stringify(fpfile));
renderAvatar(fpfile, null);
}
function renderNoAvatar(avatarSpace) {
// no avatar found for account
removeAvatarSpinner();
var noAvatarSpace = $('<div></div>');
noAvatarSpace.addClass('no-avatar-space');
noAvatarSpace.text('Please upload a photo');
avatarSpace.append(noAvatarSpace);
}
function handleUpdateAvatar(event) {
if(self.updatingAvatar) {
// protect against concurrent update attempts
return;
}
if(selection) {
var currentSelection = selection;
self.updatingAvatar = true;
renderAvatarSpinner();
console.log("Converting...");
// we convert two times; first we crop to the selected region,
// then we scale to 88x88 (targetCropSize X targetCropSize), which is the largest size we use throughout the site.
var fpfile = determineCurrentFpfile();
rest.getFilepickerPolicy({ handle: fpfile.url, convert: true })
.done(function(filepickerPolicy) {
filepicker.setKey(gon.fp_apikey);
filepicker.convert(fpfile, {
crop: [
Math.round(currentSelection.x),
Math.round(currentSelection.y),
Math.round(currentSelection.w),
Math.round(currentSelection.w)],
fit: 'crop',
format: 'jpg',
quality: 90,
policy: filepickerPolicy.policy,
signature: filepickerPolicy.signature
}, { path: createStorePath(self.userDetail) + 'cropped-' + new Date().getTime() + '.jpg', access: 'public' },
function(cropped) {
logger.debug("converting cropped");
rest.getFilepickerPolicy({handle: cropped.url, convert: true})
.done(function(filepickerPolicy) {
filepicker.convert(cropped, {
height: targetCropSize,
width: targetCropSize,
fit: 'scale',
format: 'jpg',
quality: 75,
policy: filepickerPolicy.policy,
signature: filepickerPolicy.signature
}, { path: createStorePath(self.userDetail), access: 'public' },
function(scaled) {
logger.debug("converted and scaled final image %o", scaled);
rest.updateAvatar({
original_fpfile: determineCurrentFpfile(),
cropped_fpfile: scaled,
crop_selection: currentSelection
})
.done(updateAvatarSuccess)
.fail(app.ajaxError)
.always(function() { removeAvatarSpinner(); self.updatingAvatar = false;})
},
function(fperror) {
alert("unable to scale selection. error code: " + fperror.code);
removeAvatarSpinner();
self.updatingAvatar = false;
})
})
.fail(app.ajaxError);
},
function(fperror) {
alert("unable to crop selection. error code: " + fperror.code);
removeAvatarSpinner();
self.updatingAvatar = false;
}
);
})
.fail(app.ajaxError);
}
else {
app.notify(
{ title: "Upload an Avatar First",
text: "To update your avatar, first you must upload an image using the UPLOAD button"
},
{ no_cancel: true });
}
}
function updateAvatarSuccess(response) {
$.cookie('original_fpfile', null);
self.userDetail = response;
// notify any listeners that the avatar changed
userDropdown.loadMe();
// $('.avatar_large img').trigger('avatar_changed', [self.userDetail.photo_url]);
app.notify(
{ title: "Avatar Changed",
text: "You have updated your avatar successfully."
},
{ no_cancel: true });
}
function onSelectRelease(event) {
}
function onSelect(event) {
selection = event;
}
function onChange(event) {
}
function createStorePath(userDetail) {
return gon.fp_upload_dir + '/' + userDetail.id + '/'
}
function createOriginalFilename(userDetail) {
// get the s3
var fpfile = userDetail.original_fpfile ? JSON.parse(userDetail.original_fpfile) : null;
return 'original_avatar.jpg'
}
// retrieves a file that has not yet been used as an avatar (uploaded, but not cropped)
function getWorkingFpfile() {
return JSON.parse($.cookie('original_fpfile'))
}
function determineCurrentFpfile() {
// precedence is as follows:
// * tempOriginal: if set, then the user is working on a new upload
// * storedOriginal: if set, then the user has previously uploaded and cropped an avatar
// * null: neither are set above
var tempOriginal = getWorkingFpfile();
var storedOriginal = self.userDetail.original_fpfile ? JSON.parse(self.userDetail.original_fpfile) : null;
return tempOriginal ? tempOriginal : storedOriginal;
}
function determineCurrentSelection(userDetail) {
// if the cookie is set, don't use the storage selection, just default to null
return $.cookie('original_fpfile') == null ? userDetail.crop_selection : null;
}
function initialize(userDropdownInstance) {
var screenBindings = {
'beforeShow': beforeShow,
'afterShow': afterShow
};
app.bindScreen('account/profile/avatar', screenBindings);
events();
userDropdown = userDropdownInstance;
}
this.initialize = initialize;
this.beforeShow = beforeShow;
this.afterShow = afterShow;
return this;
};
})(window,jQuery);