diff --git a/public/src/client/account/edit.js b/public/src/client/account/edit.js index 2856d10f99..bc80fd49e5 100644 --- a/public/src/client/account/edit.js +++ b/public/src/client/account/edit.js @@ -2,7 +2,7 @@ /* globals define, ajaxify, socket, app, config, templates, bootbox */ -define('forum/account/edit', ['forum/account/header', 'uploader', 'translator', 'components', 'cropper'], function (header, uploader, translator, components, cropper) { +define('forum/account/edit', ['forum/account/header', 'translator', 'components', 'pictureCropper'], function (header, translator, components, pictureCropper) { var AccountEdit = {}; AccountEdit.init = function () { @@ -210,86 +210,21 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator', updateHeader(); } } - - function handleImageCrop(data) { - $('#crop-picture-modal').remove(); - templates.parse('modals/crop_picture', {url: data.url}, function (cropperHtml) { - translator.translate(cropperHtml, function (translated) { - var cropperModal = $(translated); - cropperModal.modal('show'); - - var img = document.getElementById('cropped-image'); - var cropperTool = new cropper.default(img, { - aspectRatio: 1 / 1, - viewMode: 1 - }); - - cropperModal.find('.rotate').on('click', function () { - var degrees = this.getAttribute("data-degrees"); - cropperTool.rotate(degrees); - }); - - cropperModal.find('.flip').on('click', function () { - var option = this.getAttribute("data-option"); - var method = this.getAttribute("data-method"); - method === 'scaleX' ? cropperTool.scaleX(option) : cropperTool.scaleY(option); - this.setAttribute("data-option", option * -1); - }); - - cropperModal.find('.reset').on('click', function () { - cropperTool.reset(); - }); - - cropperModal.find('.crop-btn').on('click', function () { - $(this).addClass('disabled'); - var imageData = data.imageType ? cropperTool.getCroppedCanvas().toDataURL(data.imageType) : cropperTool.getCroppedCanvas().toDataURL(); - - cropperModal.find('#upload-progress-bar').css('width', '100%'); - cropperModal.find('#upload-progress-box').show().removeClass('hide'); - - socket.emit('user.uploadCroppedPicture', { - uid: ajaxify.data.theirid, - imageData: imageData - }, function (err, imageData) { - if (err) { - cropperModal.find('#upload-progress-box').hide(); - cropperModal.find('.upload-btn').removeClass('disabled'); - cropperModal.find('.crop-btn').removeClass('disabled'); - app.alertError(err.message); - } - - onUploadComplete(imageData.url); - cropperModal.modal('hide'); - }); - }); - - cropperModal.find('.upload-btn').on('click', function () { - $(this).addClass('disabled'); - cropperTool.destroy(); - - cropperTool = new cropper.default(img, { - viewMode: 1, - autoCropArea: 1 - }); - - cropperModal.find('.crop-btn').trigger('click'); - }); - }); - }); - } modal.find('[data-action="upload"]').on('click', function () { modal.modal('hide'); - uploader.show({ - route: config.relative_path + '/api/user/' + ajaxify.data.userslug + '/uploadpicture', - params: {}, + pictureCropper.show({ + socketMethod: 'user.uploadCroppedPicture', + aspectRatio: '1 / 1', + paramName: 'uid', + paramValue: ajaxify.data.theirid, fileSize: ajaxify.data.maximumProfileImageSize, title: '[[user:upload_picture]]', description: '[[user:upload_a_picture]]', accept: '.png,.jpg,.bmp' - }, function (data) { - handleImageCrop(data); + }, function (url) { + onUploadComplete(url); }); return false; @@ -309,7 +244,14 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator', } uploadModal.modal('hide'); - handleImageCrop({url: url}); + + pictureCropper.handleImageCrop({ + url: url, + socketMethod: 'user.uploadCroppedPicture', + aspectRatio: '1 / 1', + paramName: 'uid', + paramValue: ajaxify.data.theirid, + }, onUploadComplete); return false; }); diff --git a/public/src/client/account/header.js b/public/src/client/account/header.js index 6b33d70e16..9e1a29577b 100644 --- a/public/src/client/account/header.js +++ b/public/src/client/account/header.js @@ -3,10 +3,10 @@ define('forum/account/header', [ 'coverPhoto', - 'uploader', + 'pictureCropper', 'components', 'translator' -], function (coverPhoto, uploader, components, translator) { +], function (coverPhoto, pictureCropper, components, translator) { var AccountHeader = {}; var isAdminOrSelfOrGlobalMod; @@ -80,10 +80,12 @@ define('forum/account/header', [ }, callback); }, function () { - uploader.show({ + pictureCropper.show({ title: '[[user:upload_cover_picture]]', - route: config.relative_path + '/api/user/' + ajaxify.data.userslug + '/uploadcover', - params: {uid: ajaxify.data.uid }, + socketMethod: 'user.updateCover', + aspectRatio: '16 / 9', + paramName: 'uid', + paramValue: ajaxify.data.theirid, accept: '.png,.jpg,.bmp' }, function (imageUrlOnServer) { components.get('account/cover').css('background-image', 'url(' + imageUrlOnServer + '?' + config['cache-buster'] + ')'); diff --git a/public/src/client/groups/details.js b/public/src/client/groups/details.js index 96610baf38..771c355fcc 100644 --- a/public/src/client/groups/details.js +++ b/public/src/client/groups/details.js @@ -6,10 +6,10 @@ define('forum/groups/details', [ 'iconSelect', 'components', 'coverPhoto', - 'uploader', + 'pictureCropper', 'translator', 'vendor/colorpicker/colorpicker' -], function (memberList, iconSelect, components, coverPhoto, uploader, translator) { +], function (memberList, iconSelect, components, coverPhoto, pictureCropper, translator) { var Details = {}; var groupName; @@ -31,10 +31,12 @@ define('forum/groups/details', [ }, callback); }, function () { - uploader.show({ + pictureCropper.show({ title: '[[groups:upload-group-cover]]', - route: config.relative_path + '/api/groups/uploadpicture', - params: {groupName: groupName} + socketMethod: 'groups.cover.update', + aspectRatio: '16 / 9', + paramName: 'groupName', + paramValue: groupName }, function (imageUrlOnServer) { components.get('groups/cover').css('background-image', 'url(' + imageUrlOnServer + ')'); }); diff --git a/public/src/modules/pictureCropper.js b/public/src/modules/pictureCropper.js new file mode 100644 index 0000000000..40be206c19 --- /dev/null +++ b/public/src/modules/pictureCropper.js @@ -0,0 +1,150 @@ +'use strict'; + +/* globals define, socket, app, templates */ + +define('pictureCropper', ['translator', 'cropper'], function (translator, cropper) { + var module = {}; + + module.show = function (data, callback) { + var fileSize = data.hasOwnProperty('fileSize') && data.fileSize !== undefined ? parseInt(data.fileSize, 10) : false; + parseModal({ + showHelp: data.hasOwnProperty('showHelp') && data.showHelp !== undefined ? data.showHelp : true, + fileSize: fileSize, + title: data.title || '[[global:upload_file]]', + description: data.description || '', + button: data.button || '[[global:upload]]', + accept: data.accept ? data.accept.replace(/,/g, ', ') : '' + }, function (uploadModal) { + uploadModal = $(uploadModal); + + uploadModal.modal('show'); + uploadModal.on('hidden.bs.modal', function () { + uploadModal.remove(); + }); + + uploadModal.find('#fileUploadSubmitBtn').on('click', function () { + $(this).addClass('disabled'); + data.uploadModal = uploadModal; + onSubmit(data, callback); + return false; + }); + }); + }; + + module.handleImageCrop = function (data, callback) { + $('#crop-picture-modal').remove(); + templates.parse('modals/crop_picture', {url: data.url}, function (cropperHtml) { + translator.translate(cropperHtml, function (translated) { + var cropperModal = $(translated); + cropperModal.modal('show'); + + var img = document.getElementById('cropped-image'); + var cropperTool = new cropper.default(img, { + aspectRatio: data.aspectRatio, + viewMode: 1, + ready: function () { + cropperModal.find('.rotate').on('click', function () { + var degrees = this.getAttribute("data-degrees"); + cropperTool.rotate(degrees); + }); + + cropperModal.find('.flip').on('click', function () { + var option = this.getAttribute("data-option"); + var method = this.getAttribute("data-method"); + method === 'scaleX' ? cropperTool.scaleX(option) : cropperTool.scaleY(option); + this.setAttribute("data-option", option * -1); + }); + + cropperModal.find('.reset').on('click', function () { + cropperTool.reset(); + }); + + cropperModal.find('.crop-btn').on('click', function () { + $(this).addClass('disabled'); + var imageData = data.imageType ? cropperTool.getCroppedCanvas().toDataURL(data.imageType) : cropperTool.getCroppedCanvas().toDataURL(); + + cropperModal.find('#upload-progress-bar').css('width', '100%'); + cropperModal.find('#upload-progress-box').show().removeClass('hide'); + + var socketData = {}; + socketData[data.paramName] = data.paramValue; + socketData['imageData'] = imageData; + + socket.emit(data.socketMethod, socketData, function (err, imageData) { + if (err) { + cropperModal.find('#upload-progress-box').hide(); + cropperModal.find('.upload-btn').removeClass('disabled'); + cropperModal.find('.crop-btn').removeClass('disabled'); + return app.alertError(err.message); + } + + callback(imageData.url); + cropperModal.modal('hide'); + }); + }); + + cropperModal.find('.upload-btn').on('click', function () { + $(this).addClass('disabled'); + cropperTool.destroy(); + + cropperTool = new cropper.default(img, { + viewMode: 1, + autoCropArea: 1, + ready: function () { + cropperModal.find('.crop-btn').trigger('click'); + } + }); + }); + } + }); + }); + }); + }; + + function onSubmit(data, callback) { + function showAlert(type, message) { + module.hideAlerts(data.uploadModal); + if (type === 'error') { + data.uploadModal.find('#fileUploadSubmitBtn').removeClass('disabled'); + } + data.uploadModal.find('#alert-' + type).translateText(message).removeClass('hide'); + } + + var fileInput = data.uploadModal.find('#fileInput'); + if (!fileInput.val()) { + return showAlert('error', '[[uploads:select-file-to-upload]]'); + } + + var file = fileInput[0].files[0]; + var reader = new FileReader(); + var imageUrl; + var imageType = file.type; + + reader.addEventListener("load", function () { + imageUrl = reader.result; + + data.uploadModal.modal('hide'); + + module.handleImageCrop({ + url: imageUrl, + imageType: imageType, + socketMethod: data.socketMethod, + aspectRatio: data.aspectRatio, + paramName: data.paramName, + paramValue: data.paramValue + }, callback); + }, false); + + if (file) { + reader.readAsDataURL(file); + } + } + + function parseModal(tplVals, callback) { + templates.parse('partials/modals/upload_file_modal', tplVals, function (html) { + translator.translate(html, callback); + }); + } + + return module; +}); diff --git a/public/src/modules/uploader.js b/public/src/modules/uploader.js index bae1a2ce00..0e2a7e7ec8 100644 --- a/public/src/modules/uploader.js +++ b/public/src/modules/uploader.js @@ -1,8 +1,8 @@ 'use strict'; -/* globals define, ajaxify, socket, app, templates */ +/* globals define, templates */ -define('uploader', ['translator', 'cropper'], function (translator, cropper) { +define('uploader', ['translator'], function (translator) { var module = {}; @@ -61,27 +61,46 @@ define('uploader', ['translator', 'cropper'], function (translator, cropper) { uploadModal.find('#alert-' + type).translateText(message).removeClass('hide'); } + showAlert('status', '[[uploads:uploading-file]]'); + + uploadModal.find('#upload-progress-bar').css('width', '0%'); + uploadModal.find('#upload-progress-box').show().removeClass('hide'); + var fileInput = uploadModal.find('#fileInput'); if (!fileInput.val()) { return showAlert('error', '[[uploads:select-file-to-upload]]'); } - - var file = fileInput[0].files[0]; - var reader = new FileReader(); - var imageUrl; - var imageType = file.type; - - reader.addEventListener("load", function () { - imageUrl = reader.result; - - uploadModal.modal('hide'); - - callback({url: imageUrl, imageType: imageType}); - }, false); - - if (file) { - reader.readAsDataURL(file); + if (!hasValidFileSize(fileInput[0], fileSize)) { + return showAlert('error', '[[error:file-too-big, ' + fileSize + ']]'); } + + uploadModal.find('#uploadForm').ajaxSubmit({ + headers: { + 'x-csrf-token': config.csrf_token + }, + error: function (xhr) { + xhr = maybeParse(xhr); + showAlert('error', xhr.responseJSON ? (xhr.responseJSON.error || xhr.statusText) : 'error uploading, code : ' + xhr.status); + }, + uploadProgress: function (event, position, total, percent) { + uploadModal.find('#upload-progress-bar').css('width', percent + '%'); + }, + success: function (response) { + response = maybeParse(response); + + if (response.error) { + return showAlert('error', response.error); + } + + callback(response[0].url); + + showAlert('success', '[[uploads:upload-success]]'); + setTimeout(function () { + module.hideAlerts(uploadModal); + uploadModal.modal('hide'); + }, 750); + } + }); } function parseModal(tplVals, callback) { @@ -90,5 +109,23 @@ define('uploader', ['translator', 'cropper'], function (translator, cropper) { }); } + function maybeParse(response) { + if (typeof response === 'string') { + try { + return $.parseJSON(response); + } catch (e) { + return {error: '[[error:parse-error]]'}; + } + } + return response; + } + + function hasValidFileSize(fileElement, maxSize) { + if (window.FileReader && maxSize) { + return fileElement.files[0].size <= maxSize * 1000; + } + return true; + } + return module; -}); +}); \ No newline at end of file