apostrophe
Version:
The Apostrophe Content Management System.
164 lines (145 loc) • 5.45 kB
JavaScript
apos.define('apostrophe-attachments-crop-editor', {
extend: 'apostrophe-modal',
source: 'crop-editor',
construct: function(self, options) {
self.attachment = options.attachment;
// Use afterShow so that .width() works
self.afterShow = function() {
self.$cropImage = self.$el.find('[data-crop-image]');
self.cropWidth = self.attachment.width;
self.$saveButton = self.$el.find('[data-apos-save]');
self.busy(true);
self.$cropImage.attr('src', apos.attachments.url(self.attachment, {size: 'original'}));
self.viewWidth = self.scaleDown(self.attachment.width);
self.viewHeight = self.scaleDown(self.attachment.height);
self.$cropImage.css('width', self.viewWidth + 'px');
self.$cropImage.css('height', self.viewHeight + 'px');
self.initCropper();
self.busy(false);
};
self.initCropper = function() {
var cropperOpts = { viewMode: 1, autoCropArea: 1, checkCrossOrigin: false };
if (options.aspectRatio) {
cropperOpts.aspectRatio = options.aspectRatio[0] / options.aspectRatio[1];
}
if (options.minSize) {
_.assign(cropperOpts, {
// minCropBoxWidth: self.scaleDown(options.minSize[0]),
// minCropBoxHeight: self.scaleDown(options.minSize[1]),
viewMode: 1,
// This is a bad idea, it prevents starting a new crop
dragMode: 'crop',
autoCrop: true,
cropmove: function(e) {
self.testMinimumSize(e);
},
cropstart: function(e) {
self.testMinimumSize(e);
},
cropend: function(e) {
self.testMinimumSize(e);
},
zoom: function(e) {
self.zoom(e);
}
});
}
if (options.crop) {
// TODO: this doesn't work so far, but recropping isn't that hard,
// and you can always cancel
cropperOpts.data = self.deserializeCrop(options.crop);
}
self.$cropImage.cropper(cropperOpts);
};
self.testMinimumSize = function(e) {
var minWidth = self.options.minSize[0];
var minHeight = self.options.minSize[1];
var data = $(e.target).cropper('getData');
var realHeight = data.height;
var realWidth = data.width;
if (realHeight < minHeight) {
data.height = minHeight + 1; // +1 for rounding margin of error
}
if (realWidth < minWidth) {
data.width = minWidth + 1; // +1 for rounding margin of error
}
if (realWidth < minWidth || realHeight < minHeight) {
$(e.target).cropper('setData', data); // revert back to minimums since cropper can over-crop
return false; // fails test
}
return true; // passes test
};
self.zoom = function(e) {
var minWidth = self.options.minSize[0];
var minHeight = self.options.minSize[1];
if (e.ratio > 1) { // prevent zooming in past natural resolution
e.preventDefault();
return;
}
if (e.ratio > e.oldRatio) { // Zooming In
var displayedWidth = $(e.target).cropper('getImageData').width;
var displayedHeight = $(e.target).cropper('getImageData').height;
var newNaturalWidth = displayedWidth / e.ratio;
var newNaturalHeight = displayedHeight / e.ratio;
if (newNaturalWidth < minWidth || newNaturalHeight < minHeight) {
// Prevent zooming in to a point where you
// can't possibly crop enough
e.preventDefault();
}
}
// Clear the crop box on a successful zoom, keeping it the same
// display size is just weird and unhelpful. Make clear they need to
// crop differently at the new zoom
$(e.target).cropper('clear');
};
self.getCropperData = function() {
if (!self.$cropImage) {
return null;
}
return self.$cropImage.cropper("getData", true);
};
self.saveContent = function(callback) {
// Ask the server to render this crop
self.busy(true);
self.attachment.crop = self.serializeCrop(self.getCropperData());
self.api('crop', { _id: self.attachment._id, crop: self.attachment.crop }, function(data) {
if (data.status !== 'ok') {
apos.notify('An error occurred.', { type: 'error' });
return callback('fail');
}
self.busy(false);
self.options.cropped(self.attachment.crop);
return callback(null);
}, function() {
self.busy(false);
apos.notify('Server error, please retry', { type: 'error' });
return callback('fail');
});
};
self.scaleDown = function(coord) {
return Math.round(coord * self.cropWidth / self.attachment.width);
};
self.scaleUp = function(coord) {
return Math.round(coord * self.attachment.width / self.cropWidth);
};
self.deserializeCrop = function(data) {
return {
y: self.scaleDown(data.top),
x: self.scaleDown(data.left),
width: self.scaleDown(data.width),
height: self.scaleDown(data.height)
};
};
self.serializeCrop = function(data) {
return {
top: self.scaleUp(data.y),
left: self.scaleUp(data.x),
width: self.scaleUp(data.width),
height: self.scaleUp(data.height)
};
};
self.busy = function(state) {
self.$saveButton.attr('data-busy', state ? '1' : '0');
};
}
});