Compare commits

...

1 Commits

Author SHA1 Message Date
Barış Soner Uşaklı
a30feeab96 cropperjs2 2025-03-22 21:18:44 -04:00
6 changed files with 71 additions and 104 deletions

View File

@@ -94,8 +94,6 @@ define('forum/account/header', [
title: '[[user:upload-cover-picture]]',
socketMethod: 'user.updateCover',
aspectRatio: NaN,
allowSkippingCrop: true,
restrictImageDimension: false,
paramName: 'uid',
paramValue: ajaxify.data.theirid,
accept: '.png,.jpg,.bmp',

View File

@@ -50,8 +50,6 @@ define('forum/groups/details', [
title: '[[groups:upload-group-cover]]',
socketMethod: 'groups.cover.update',
aspectRatio: NaN,
allowSkippingCrop: true,
restrictImageDimension: false,
paramName: 'groupName',
paramValue: groupName,
}, function (imageUrlOnServer) {

View File

@@ -171,11 +171,10 @@ define('accounts/picture', [
pictureCropper.show({
socketMethod: 'user.uploadCroppedPicture',
route: config.relative_path + '/api/user/' + ajaxify.data.userslug + '/uploadpicture',
aspectRatio: 1 / 1,
aspectRatio: 1,
paramName: 'uid',
paramValue: ajaxify.data.theirid,
fileSize: ajaxify.data.maximumProfileImageSize,
allowSkippingCrop: false,
title: '[[user:upload-picture]]',
description: '[[user:upload-a-picture]]',
accept: ajaxify.data.allowedProfileImageExtensions,
@@ -203,7 +202,6 @@ define('accounts/picture', [
url: url,
socketMethod: 'user.uploadCroppedPicture',
aspectRatio: 1,
allowSkippingCrop: false,
paramName: 'uid',
paramValue: ajaxify.data.theirid,
}, onUploadComplete);

View File

@@ -48,99 +48,71 @@ define('pictureCropper', ['alerts'], function (alerts) {
const Cropper = (await import(/* webpackChunkName: "cropperjs" */ 'cropperjs')).default;
let cropperTool = new Cropper(img, {
aspectRatio: data.aspectRatio,
autoCropArea: 1,
viewMode: 1,
checkCrossOrigin: true,
cropmove: function () {
if (data.restrictImageDimension) {
if (cropperTool.cropBoxData.width > data.imageDimension) {
cropperTool.setCropBoxData({
width: data.imageDimension,
});
}
if (cropperTool.cropBoxData.height > data.imageDimension) {
cropperTool.setCropBoxData({
height: data.imageDimension,
});
}
template: `<cropper-canvas background style="height: 300px;">
<cropper-image rotatable scalable translatable></cropper-image>
<cropper-shade hidden></cropper-shade>
<cropper-handle action="move" plain></cropper-handle>
<cropper-selection initial-coverage="1" movable resizable>
<cropper-grid role="grid" bordered covered></cropper-grid><cropper-crosshair centered></cropper-crosshair><cropper-handle action="move" theme-color="rgba(255, 255, 255, 0.35)"></cropper-handle><cropper-handle action="n-resize"></cropper-handle><cropper-handle action="e-resize"></cropper-handle><cropper-handle action="s-resize"></cropper-handle><cropper-handle action="w-resize"></cropper-handle><cropper-handle action="ne-resize"></cropper-handle><cropper-handle action="nw-resize"></cropper-handle><cropper-handle action="se-resize"></cropper-handle><cropper-handle action="sw-resize"></cropper-handle></cropper-selection>
</cropper-canvas>`,
});
const cropperSelection = cropperTool.getCropperSelection();
const cropperImage = cropperTool.getCropperImage();
cropperSelection.aspectRatio = data.aspectRatio;
cropperImage.$ready(async function () {
if (!await checkCORS(cropperSelection, data)) {
return cropperModal.modal('hide');
}
cropperModal.find('.rotate').on('click', function () {
const degrees = this.getAttribute('data-degrees');
const radians = degrees * Math.PI / 180;
cropperImage.$rotate(radians);
});
cropperModal.find('.flip').on('click', function () {
const method = this.getAttribute('data-method');
if (method === 'scaleX') {
cropperImage.$scale(-1, 1);
} else {
cropperImage.$scale(1, -1);
}
},
ready: function () {
if (!checkCORS(cropperTool, data)) {
return cropperModal.modal('hide');
});
cropperModal.find('.reset').on('click', function () {
cropperImage.$resetTransform();
cropperSelection.$reset();
});
cropperModal.find('.crop-btn').on('click', async function () {
$(this).addClass('disabled');
const imageData = await checkCORS(cropperSelection, data);
if (!imageData) {
return;
}
if (data.restrictImageDimension) {
const origDimension = (img.width < img.height) ? img.width : img.height;
const dimension = (origDimension > data.imageDimension) ? data.imageDimension : origDimension;
cropperTool.setCropBoxData({
width: dimension,
height: dimension,
});
}
cropperModal.find('#upload-progress-bar').css('width', '0%');
cropperModal.find('#upload-progress-box').show().removeClass('hide');
cropperModal.find('.rotate').on('click', function () {
const degrees = this.getAttribute('data-degrees');
cropperTool.rotate(degrees);
});
cropperModal.find('.flip').on('click', function () {
const option = this.getAttribute('data-option');
const method = this.getAttribute('data-method');
if (method === 'scaleX') {
cropperTool.scaleX(option);
} else {
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');
const imageData = checkCORS(cropperTool, data);
if (!imageData) {
return;
socketUpload({
data: data,
imageData: imageData,
progressBarEl: cropperModal.find('#upload-progress-bar'),
}, function (err, result) {
if (err) {
cropperModal.find('#upload-progress-box').hide();
cropperModal.find('.upload-btn').removeClass('disabled');
cropperModal.find('.crop-btn').removeClass('disabled');
return alerts.error(err);
}
cropperModal.find('#upload-progress-bar').css('width', '0%');
cropperModal.find('#upload-progress-box').show().removeClass('hide');
socketUpload({
data: data,
imageData: imageData,
progressBarEl: cropperModal.find('#upload-progress-bar'),
}, function (err, result) {
if (err) {
cropperModal.find('#upload-progress-box').hide();
cropperModal.find('.upload-btn').removeClass('disabled');
cropperModal.find('.crop-btn').removeClass('disabled');
return alerts.error(err);
}
callback(result.url);
cropperModal.modal('hide');
});
callback(result.url);
cropperModal.modal('hide');
});
cropperModal.find('.upload-btn').on('click', async function () {
$(this).addClass('disabled');
cropperTool.destroy();
const Cropper = (await import(/* webpackChunkName: "cropperjs" */ 'cropperjs')).default;
cropperTool = new Cropper(img, {
viewMode: 1,
autoCropArea: 1,
ready: function () {
cropperModal.find('.crop-btn').trigger('click');
},
});
});
},
});
});
});
};
@@ -175,12 +147,18 @@ define('pictureCropper', ['alerts'], function (alerts) {
doUpload();
}
function checkCORS(cropperTool, data) {
async function checkCORS(selection, data) {
let imageData;
try {
const canvasOpts = {
beforeDraw: function (context) {
context.imageSmoothingQuality = 'high';
},
};
const canvas = await selection.$toCanvas(canvasOpts);
imageData = data.imageType ?
cropperTool.getCroppedCanvas().toDataURL(data.imageType) :
cropperTool.getCroppedCanvas().toDataURL();
canvas.toDataURL(data.imageType) :
canvas.toDataURL();
} catch (err) {
const corsErrors = [
'The operation is insecure.',
@@ -232,8 +210,6 @@ define('pictureCropper', ['alerts'], function (alerts) {
imageType: file.type,
socketMethod: data.socketMethod,
aspectRatio: data.aspectRatio,
allowSkippingCrop: data.allowSkippingCrop,
restrictImageDimension: data.restrictImageDimension,
imageDimension: data.imageDimension,
paramName: data.paramName,
paramValue: data.paramValue,

View File

@@ -29,7 +29,6 @@ const buildImports = {
'@import "@adactive/bootstrap-tagsinput/src/bootstrap-tagsinput";',
source,
'@import "jquery-ui";',
'@import "cropperjs/dist/cropper";',
].join('\n');
},
admin: function (source) {

View File

@@ -1,16 +1,15 @@
<div id="crop-picture-modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="crop-picture" aria-hidden="true">
<div id="crop-picture-modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="crop-picture">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 id="crop-picture">[[user:crop-picture]]</h3>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-hidden="true"></button>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div id="upload-progress-box" class="progress hide">
<div id="upload-progress-bar" class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="0" aria-valuemin="0">
</div>
</div>
<div class="cropper">
<img id="cropped-image" class="mw-100" crossorigin="anonymous" src="{url}" alt="">
</div>
@@ -30,8 +29,7 @@
</div>
</div>
<div class="modal-footer">
<button class="btn btn-outline-secondary" data-bs-dismiss="modal" aria-hidden="true">Close</button>
<button class="btn btn-primary upload-btn {{{ if !allowSkippingCrop }}}hidden{{{ end }}}">[[user:upload-picture]]</button>
<button class="btn btn-outline-secondary" data-bs-dismiss="modal">Close</button>
<button class="btn btn-primary crop-btn">[[user:upload-cropped-picture]]</button>
</div>
</div>