.rel.cropbox .row#fileChoose .col h3 Current Avatar .avatar include ../../includes/avatar.pug .col .alert.alert-danger#avatarAlert(style="display: none") form .form-check input.form-check-input#uploadType(type="radio", name="avi", checked) label(for="#uploadType") Upload Image .form-group.ml-5 label(for="#fileinput") Choose File input.form-control-file#fileinput(type="file", aria-labelledby="#maxFileText") small#maxFileText Max file size: 5 MB, only .png and .jpg allowed .form-check .row .col input.form-check-input#gravType(type="radio", name="avi") label(for="#gravType") Use a(href="https://en.gravatar.com/", target="_blank") Gravatar .col img#gravatarPic .content#crop(style="display: none;") h3 Crop the Image .alert.alert-info#cropAlert(style="display: none;") Uploading.. img.preview#imageCropTag script. function failAlert (msg) { $('#avatarAlert').text(msg) $('#avatarAlert').show() } function dataURItoBlob (dataURI) { // convert base64 to raw binary data held in a string // doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this var byteString = atob(dataURI.split(',')[1]) // separate out the mime component var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0] // write the bytes of the string to an ArrayBuffer var ab = new ArrayBuffer(byteString.length) // create a view into the buffer var ia = new Uint8Array(ab) // set the bytes of the buffer to the correct values for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i) } // write the ArrayBuffer to a blob, and you're done var blob = new Blob([ab], {type: mimeString}) return blob } function cropReady() { let cropargs = $('#imageCropTag').cropper('getData') let cropimage = $('#imageCropTag').cropper('getCroppedCanvas') $('#btnUpload').show() $('#btnDone').hide() $('#imageCropTag').attr('src', cropimage.toDataURL()) $('#imageCropTag').show() $('#imageCropTag').cropper('destroy') let called = false $('#btnUpload').click(function (e) { if (called) return called = true $('#btnUpload').hide() $('#cropAlert').show() let formData = new FormData() formData.append('image', dataURItoBlob(fr.result)) for (let i in cropargs) { formData.append(i, cropargs[i]) } $.ajax({ type: 'POST', url: '/api/avatar', data: formData, processData: false, contentType: false, success: function (data) { $('avatarModal').modal('hide') window.location.reload() }, error: function (err) { if (err.responseJSON && err.responseJSON.error) { failAlert(err.responseJSON.error) } cancel() } }) }) } function ready (blob) { let match = blob.match(/data:image\/(\w+);/) let screenlen = $('.mobview').is(':visible') if (!match) { return failAlert('Not an image file!') } if (match[1] !== 'png' && match[1] !== 'jpg' && match[1] !== 'jpeg') { return failAlert('Unsupported image file') } $('#imageCropTag').attr('src', fr.result).hide() $('#fileChoose').hide() $('#btnUpload').hide() $('#btnDone').show() $('#crop').show() $('#imageCropTag').cropper({ aspectRatio: 1 / 1, minContainerHeight: screenlen ? 128 : 512, minContainerWidth: screenlen ? 128 : 512, viewMode: 1 }) } function gravatarDance() { $.ajax({ type: 'GET', url: '/api/avatar/gravatar', success: function (ln) { if ($('#userAvatarFile').length) { var ufile = $('#userAvatarFile').attr('src') if (ufile.indexOf('/GRAV-') !== -1) { $('#gravType').prop('checked', true) } else { $('#uploadType').prop('checked', true) } } $('#gravatarPic').attr('src', ln) } }) $('#gravType').click(function (e) { $.ajax({ type: 'POST', url: '/api/avatar/gravatar', success: function () { window.location.reload() } }) }) } function cancel() { $('#fileChoose').show() $('#cropAlert').hide() $('#btnDone').hide() $('#btnUpload').hide() $('#crop').hide() $('#imageCropTag').cropper('destroy') fr = null } function handleFileSelect() { if (!window.File || !window.FileReader || !window.FileList || !window.Blob) { return failAlert('The File APIs are not fully supported in this browser.') } let input = document.getElementById('fileinput') if (!input.files) { return failAlert('This browser doesn\'t seem to support the `files` property of file inputs.') } if (!input.files[0]) { return failAlert('Please select a file.') } if (input.files[0].size > 5000000) { return failAlert('This file is too big. Max: 5 MB') } file = input.files[0] fr = new FileReader() fr.readAsDataURL(file) fr.addEventListener('load', function (e) { ready(fr.result) }) } $(window).ready(function () { $('#avatarModal').on('hidden.bs.modal', function (e) { cancel() }) $('#avatarModal').on('show.bs.modal', function (e) { gravatarDance() }) $('#fileinput').on('change', function (e) { e.preventDefault() handleFileSelect() }) $('#btnDone').click(function (e) { cropReady() }) })