UNPKG

linagora-rse

Version:
542 lines (477 loc) 15.8 kB
'use strict'; angular.module('esn.avatar', [ 'esn.constants', 'mgcrea.ngStrap', 'ngAnimate', 'mgcrea.ngStrap.modal', 'angularFileUpload', 'mgcrea.ngStrap.alert', 'ng.deviceDetector', 'esn.http', 'esn.url', 'esn.session' ]) .constant('AVATAR_OFFSET', 10) .provider('avatarDefaultUrl', function() { var url = '/images/community.png'; return { set: function(value) { url = value || '/images/community.png'; }, $get: function() { return { get: function() { return url; } }; } }; }) .provider('jcropExtendOptions', function() { var options = {}; return { set: function(value) { options = value || {}; }, $get: function() { return { get: function() { return options; } }; } }; }) .controller('avatarEdit', function($rootScope, $scope, session, selectionService, avatarAPI, $alert, $modal) { selectionService.clear(); var createModal = $modal({scope: $scope, templateUrl: '/views/modules/avatar/avatar-edit-modal.html', show: false, backdrop: 'static', keyboard: false}); var alertInstance; function destroyAlertInstance() { if (alertInstance) { alertInstance.destroy(); alertInstance = null; } } $scope.showAvatarEditModal = function() { $scope.initUploadContext(); createModal.$promise.then(createModal.show); }; $scope.initUploadContext = function() { $scope.uploadError = false; $scope.progress = 0; $scope.status = 'Upload'; $scope.uploading = false; $scope.step = 'none'; $scope.preview = false; }; $scope.preview = false; function done() { $scope.uploading = false; if (createModal) { createModal.hide(); } selectionService.clear(); } $scope.send = function(blob, mime) { $scope.uploading = true; $scope.step = 'uploading'; var uploadAvatar = $scope.user._id === session.user._id ? avatarAPI.uploadAvatar(blob, mime) : avatarAPI.uploadUserAvatar(blob, mime, $scope.user._id, session.domain._id); uploadAvatar .progress(function(evt) { $scope.progress = parseInt(100.0 * evt.loaded / evt.total, 10); }) .success(function() { $scope.progress = 100; $scope.step = 'redirect'; $rootScope.$broadcast('avatar:updated', $scope.user); done(); }) .error(function(error) { $scope.progress = 100; $scope.step = 'uploadfailed'; $scope.error = error; done(); }); }; $scope.upload = function() { $scope.uploading = true; $scope.status = 'Uploading'; $scope.progress = 1; var mime = 'image/png'; selectionService.getBlob(mime, function(blob) { $scope.send(blob, mime); }); }; $scope.$on('crop:loaded', function() { destroyAlertInstance(); $scope.initUploadContext(); $scope.preview = true; $scope.$apply(); }); $scope.$on('crop:error', function(context, error) { destroyAlertInstance(); if (error) { alertInstance = $alert({ title: 'Error', content: error, type: 'danger', show: true, position: 'bottom', container: '#edit-avatar-dialog .error', animation: 'am-fade' }); } }); $scope.$on('crop:reset', function() { destroyAlertInstance(); selectionService.clear(); }); $scope.initUploadContext(); }) .factory('avatarAPI', function($upload, httpConfigurer) { return { uploadAvatar: uploadAvatar, uploadUserAvatar: uploadUserAvatar }; function uploadAvatar(blob, mime) { return $upload.http({ method: 'POST', url: httpConfigurer.getUrl('/api/user/profile/avatar'), headers: {'Content-Type': mime}, data: blob, params: {mimetype: mime, size: blob.size}, withCredentials: true }); } function uploadUserAvatar(blob, mime, userId, domainId) { return $upload.http({ method: 'PUT', url: httpConfigurer.getUrl('/api/users/' + userId + '/profile/avatar?domain_id=' + domainId), headers: { 'Content-Type': mime }, data: blob, params: { mimetype: mime, size: blob.size }, withCredentials: true }); } }) .factory('selectionService', function($rootScope, AVATAR_MIN_SIZE_PX) { var sharedService = {}; sharedService.image = null; sharedService.selection = {}; sharedService.error = null; sharedService.setImage = function(image) { this.image = image; $rootScope.$broadcast('crop:loaded'); }; sharedService.getImage = function() { return this.image; }; sharedService.getError = function() { return this.error; }; sharedService.setError = function(error) { this.error = error; $rootScope.$broadcast('crop:error', error); }; sharedService.reset = function() { $rootScope.$broadcast('crop:reset'); }; sharedService.broadcastSelection = function(x) { this.selection = x; $rootScope.$broadcast('crop:selected', x); }; sharedService.computeCanvasSelection = function computeCanvasSelection(img, ratio, selection) { var w = selection.w * ratio; if (w > img.naturalWidth) { w = img.naturalWidth; } var h = selection.h * ratio; if (h > img.naturalHeight) { h = img.naturalHeight; } return { x: selection.x * ratio, y: selection.y * ratio, w: w, h: h }; }; sharedService.getBlob = function getBlob(mime, callback) { var canvas = document.createElement('canvas'); canvas.width = AVATAR_MIN_SIZE_PX; canvas.height = AVATAR_MIN_SIZE_PX; var context = canvas.getContext('2d'); var image = sharedService.getImage(); var ratio = sharedService.selection.ratio || 1; var selection = sharedService.selection.cords; if (selection.w === 0 || selection.h === 0) { context.drawImage(image, 0, 0, AVATAR_MIN_SIZE_PX, AVATAR_MIN_SIZE_PX); } else { var canvasSelection = sharedService.computeCanvasSelection(image, ratio, selection); context.drawImage(image, canvasSelection.x, canvasSelection.y, canvasSelection.w, canvasSelection.h, 0, 0, canvas.width, canvas.height); } canvas.toBlob(callback, mime); }; sharedService.clear = function() { sharedService.image = null; sharedService.selection = {}; sharedService.error = null; }; return sharedService; }) .directive('imgPreview', function(selectionService, AVATAR_MIN_SIZE_PX) { return { restrict: 'A', replace: true, link: function($scope, element) { var canvas = element[0]; canvas.width = canvas.height = AVATAR_MIN_SIZE_PX / 2; var ctx = canvas.getContext('2d'); $scope.$on('crop:reset', function() { canvas.width = canvas.width; }); $scope.$on('crop:selected', function(context, data) { var selection = data.cords; var ratio = data.ratio || 1; var img = selectionService.getImage(); canvas.width = canvas.width; if (Math.round(selection.w * ratio) < AVATAR_MIN_SIZE_PX || Math.round(selection.h * ratio) < AVATAR_MIN_SIZE_PX) { ctx.drawImage(img, 0, 0, AVATAR_MIN_SIZE_PX, AVATAR_MIN_SIZE_PX); } else { var canvasSelection = selectionService.computeCanvasSelection(img, ratio, selection); ctx.drawImage(img, canvasSelection.x, canvasSelection.y, canvasSelection.w, canvasSelection.h, 0, 0, canvas.width, canvas.height); } }); } }; }) .directive('imgLoaded', function(selectionService, AVATAR_MIN_SIZE_PX, AVATAR_OFFSET, deviceDetector, jcropExtendOptions) { return { restrict: 'E', scope: { width: '=' }, link: function(scope, element) { var myImg, myJcropAPI; var clear = function() { if (myJcropAPI) { myJcropAPI.destroy(); myJcropAPI = null; } if (myImg) { myImg.next().remove(); myImg.remove(); myImg = undefined; } }; scope.$on('crop:reset', clear); scope.$on('crop:loaded', function() { clear(); var image = selectionService.getImage(); var canvas = document.createElement('canvas'); var width = scope.width || 380; var height = image.height * (width / image.width); var ratio = image.width / width; var minsize = AVATAR_MIN_SIZE_PX / ratio; var minSetSelectSizeX = width - AVATAR_OFFSET; var minSetSelectSizeY = height - AVATAR_OFFSET; canvas.width = width; canvas.height = height; var ctx = canvas.getContext('2d'); ctx.drawImage(image, 0, 0, canvas.width, canvas.height); element.after('<img />'); myImg = element.next(); myImg.attr('src', canvas.toDataURL()); myImg.attr('width', width); myImg.attr('height', height); var broadcastSelection = function(x) { selectionService.broadcastSelection({cords: x, ratio: ratio}); }; angular.element(myImg).Jcrop(angular.extend({}, { bgColor: 'black', bgOpacity: 0.6, setSelect: [AVATAR_OFFSET, AVATAR_OFFSET, minSetSelectSizeX, minSetSelectSizeY], minSize: [minsize, minsize], aspectRatio: 1, touchSupport: true, onSelect: broadcastSelection, onChange: deviceDetector.isDesktop() ? broadcastSelection : function() {} }, jcropExtendOptions.get()), function() { myJcropAPI = this; }); }); scope.$on('$destroy', clear); } }; }) .directive('loadButton', function(selectionService, AVATAR_MIN_SIZE_PX, AVATAR_MAX_SIZE_MB) { return { restrict: 'A', replace: true, link: function(scope, element) { element.bind('change', function(evt) { evt.stopPropagation(); evt.preventDefault(); selectionService.reset(); selectionService.clear(); var file = evt.dataTransfer !== undefined ? evt.dataTransfer.files[0] : evt.target.files[0]; if (!file || !file.type.match(/^image\//)) { selectionService.setError('Wrong file type, please select a valid image'); } else { var megabyte = Math.pow(2, 20); if (file.size > AVATAR_MAX_SIZE_MB * megabyte) { selectionService.setError('File is too large (maximum size is ' + AVATAR_MAX_SIZE_MB + ' Mb)'); } else { var reader = new FileReader(); reader.onload = (function() { return function(e) { var image = new Image(); image.src = e.target.result; image.onload = function() { var imgHeight = image.naturalHeight, imgWidth = image.naturalWidth; if (imgHeight < AVATAR_MIN_SIZE_PX || imgWidth < AVATAR_MIN_SIZE_PX) { selectionService.setError('This image is too small, please select a picture with a minimum size of ' + AVATAR_MIN_SIZE_PX + 'x' + AVATAR_MIN_SIZE_PX + 'px'); } else { selectionService.setError(); selectionService.setImage(image); } }; }; })(file); reader.readAsDataURL(file); } } }); } }; }) .directive('avatarPicker', function(selectionService, $alert, avatarDefaultUrl) { function link($scope, element, attrs) { $scope.image = { selected: false }; $scope.avatarPlaceholder = attrs.avatarPlaceholder ? attrs.avatarPlaceholder : avatarDefaultUrl.get(); var alertInstance = null; function destroyAlertInstance() { if (alertInstance) { alertInstance.destroy(); alertInstance = null; } } $scope.removeSelectedImage = function() { selectionService.clear(); $scope.image.selected = false; }; $scope.$on('crop:loaded', function() { destroyAlertInstance(); $scope.image.selected = true; $scope.$apply(); }); $scope.$on('crop:error', function(context, error) { if (error) { alertInstance = $alert({ title: '', content: error, type: 'danger', show: true, position: 'bottom', container: element.find('.avatar-picker-error'), animation: 'am-fade' }); } }); } return { scope: {}, restrict: 'E', templateUrl: '/views/modules/avatar/avatar-picker.html', link: link }; }) .directive('avatarImg', function($timeout) { function link(scope) { scope.getURL = function() { if (scope.avatarUser) { return '/api/users/' + scope.avatarUser._id + '/profile/avatar?cb=' + Date.now(); } return '/api/user/profile/avatar?cb=' + Date.now(); }; scope.avatarURL = scope.getURL(); scope.$on('avatar:updated', function() { $timeout(function() { scope.avatarURL = scope.getURL(); }); }); } return { restrict: 'E', replace: true, scope: { avatarUser: '=?' }, template: '<img ng-src="{{avatarURL}}" />', link: link }; }) .component('esnAvatar', { templateUrl: '/views/modules/avatar/avatar.html', controller: 'EsnAvatarController', bindings: { userId: '<', userEmail: '<', avatarUrl: '<', hideUserStatus: '<', noCache: '<', resolveAvatar: '&' } }) .controller('EsnAvatarController', function($attrs, $q, $log, userAPI, esnAvatarUrlService) { var self = this; self.$onInit = setProperties; self.$onChanges = setProperties; self.displayUserStatus = displayUserStatus; self.avatar = {}; function setProperties() { if ($attrs.resolveAvatar) { return self.resolveAvatar().then(function(avatar) { self.avatar = avatar; }); } self.avatar = {}; if (self.userId) { self.avatar.id = self.userId; } if (self.userEmail) { self.avatar.email = self.userEmail; } self.avatar.url = self.avatarUrl || generateAvatarUrl(self.avatar.id, self.avatar.email); if (self.userEmail && !self.avatar.id) { getUserIdByEmail(self.userEmail).then(function(userId) { self.avatar.id = userId; }); } } function generateAvatarUrl(id, email) { if (id) { return esnAvatarUrlService.generateUrlByUserId(id, self.noCache); } if (email) { return esnAvatarUrlService.generateUrl(email, self.noCache); } } function getUserIdByEmail(userEmail) { return userAPI.getUsersByEmail(userEmail) .then(function(response) { if (response.data && response.data[0]) { return response.data[0]._id; } }); } function displayUserStatus() { return !!self.avatar.id && !self.hideUserStatus; } });