453 lines
20 KiB
JavaScript
453 lines
20 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 user = {};
|
|
var tmpUploadPath = null;
|
|
var userDetail = null;
|
|
var avatar;
|
|
var selection = null;
|
|
var targetCropSize = 88;
|
|
var largerCropSize = 200;
|
|
var updatingAvatar = false;
|
|
var userDropdown;
|
|
|
|
function beforeShow(data) {
|
|
}
|
|
|
|
|
|
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) {
|
|
filepicker.convert(cropped, {
|
|
height: largerCropSize,
|
|
width: largerCropSize,
|
|
fit: 'scale',
|
|
format: 'jpg',
|
|
quality: 75,
|
|
policy: filepickerPolicy.policy,
|
|
signature: filepickerPolicy.signature
|
|
}, { path: createStorePath(self.userDetail) + 'large.jpg', access: 'public' },
|
|
function(scaledLarger) {
|
|
logger.debug("converted and scaled final image %o", scaled);
|
|
rest.updateAvatar({
|
|
original_fpfile: determineCurrentFpfile(),
|
|
cropped_fpfile: scaled,
|
|
cropped_large_fpfile: scaledLarger,
|
|
crop_selection: currentSelection
|
|
})
|
|
.done(updateAvatarSuccess)
|
|
.fail(app.ajaxError)
|
|
.always(function() { removeAvatarSpinner(); self.updatingAvatar = false;})
|
|
},
|
|
function(fperror) {
|
|
alert("unable to scale larger selection. error code: " + fperror.code);
|
|
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); |