(function(context,$) { "use strict"; context.JK = context.JK || {}; context.JK.AccountProfileAvatarScreen = function(app) { var self = this; var logger = context.JK.logger; var EVENTS = context.JK.EVENTS; 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" : encodeURIComponent(filepicker_policy.policy), "fp_signature" : encodeURIComponent(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; } ); enableSubmits() //$('#account-profile-avatar-content-scroller').on('change', 'input[type=filepicker-dragdrop]', function(evt) { evt.stopPropagation(); afterImageUpload(evt.originalEvent.fpfile); return false; } ); } function enableSubmits() { $('#account-profile-avatar-content-scroller').on('click', '#account-edit-avatar-submit', function(evt) { evt.stopPropagation(); handleUpdateAvatar(); return false; } ); $("#account-edit-avatar-submit").removeClass("disabled") } function disableSubmits() { $("#account-edit-avatar-submit").addClass("disabled") $("#account-edit-avatar-submit").off("click") } 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 = $('
') 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 = $('
'); noAvatarSpace.addClass('no-avatar-space'); noAvatarSpace.text('Please upload a photo'); avatarSpace.append(noAvatarSpace); } function handleUpdateAvatar(event) { disableSubmits() if(self.updatingAvatar) { // protect against concurrent update attempts return; } if(selection) { var currentSelection = selection; self.updatingAvatar = true; renderAvatarSpinner(); logger.debug("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" }, null, true); } enableSubmits() } 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]); $(document).triggerHandler(EVENTS.USER_UPDATED, response); app.notify( { title: "Avatar Changed", text: "You have updated your avatar successfully." }, null, 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);