Skip to content

Instantly share code, notes, and snippets.

@vapask
Created April 7, 2014 14:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vapask/10021258 to your computer and use it in GitHub Desktop.
Save vapask/10021258 to your computer and use it in GitHub Desktop.
Image Free Crop Field for image cropping
from image_cropping.fields import ImageRatioField
class ImageFreeCropField(ImageRatioField):
def __init__(self, *args, **kwargs):
self.keep_ratio = kwargs.pop("keep_ratio", False)
super(ImageFreeCropField, self).__init__(*args, **kwargs)
def formfield(self, *args, **kwargs):
kwargs['widget'] = forms.TextInput(attrs={
'data-width': int(self.width),
'data-height': int(self.height),
'data-image-field': self.image_field,
'data-my-name': self.name,
'data-adapt-rotation': str(self.adapt_rotation).lower(),
'data-allow-fullsize': str(self.allow_fullsize).lower(),
'data-size-warning': str(self.size_warning).lower(),
'data-keep-ratio': str(self.keep_ratio).lower(),
'class': 'image-ratio',
})
return super(ImageRatioField, self).formfield(*args, **kwargs)
def south_field_triple(self):
"""
Return a suitable description of this field for South.
"""
# We'll just introspect ourselves, since we inherit.
from south.modelsinspector import introspector
field_class = "django.db.models.fields.CharField"
args, kwargs = introspector(self)
return (field_class, args, kwargs)
var image_cropping = {
$: jQuery.noConflict(),
init: function() {
// set styles for size-warning
var style_img_warning = 'div.jcrop-image.size-warning .jcrop-vline{border:1px solid red; background: none;}' +
'div.jcrop-image.size-warning .jcrop-hline{border:1px solid red; background: none;}';
image_cropping.$("<style type='text/css'>" + style_img_warning + "</style>").appendTo('head');
image_cropping.$('input.image-ratio').each(function() {
var $this = image_cropping.$(this),
// find the image field corresponding to this cropping value
// by stripping the last part of our id and appending the image field name
field = $this.attr('name').replace($this.data('my-name'), $this.data('image-field')),
// there should only be one file field we're referencing but in special cases
// there can be several. Deal with it gracefully.
$image_input = image_cropping.$('input.crop-thumb[data-field-name=' + field + ']:first');
// skip this image if it's empty and hide the whole field, within admin and by itself
if (!$image_input.length || $image_input.data('thumbnail-url') == undefined) {
$this.hide().parents('div.form-row:first').hide();
return;
}
// check if the image field should be hidden
if ($image_input.data('hide-field')) {
$image_input.hide().parents('div.form-row:first').hide();
}
var image_id = $this.attr('id') + '-image',
org_width = $image_input.data('org-width'),
org_height = $image_input.data('org-height'),
min_width = $this.data('width'),
min_height = $this.data('height'),
dont_keep_ratio = ($this.data('keep-ratio') == false) ? true: false;
var is_image_portrait = (org_height > org_width);
var is_select_portrait = (min_height > min_width);
if ($this.data('adapt-rotation') == true) {
if (is_image_portrait != is_select_portrait) {
// cropping height/width need to be switched, picture is in portrait mode
var x = min_width;
min_width = min_height;
min_height = x;
}
}
var $image = image_cropping.$('<img>', {
'id': image_id,
'src': $image_input.data('thumbnail-url')
});
var options = {
// aspectRatio: min_width/min_height,
minSize: [5, 5],
trueSize: [org_width, org_height],
onSelect: image_cropping.update_selection($this),
addClass: ($this.data('size-warning') && ((org_width < min_width) || (org_height < min_height))) ? 'size-warning jcrop-image': 'jcrop-image'
}
if (dont_keep_ratio){
options.minSize = [min_width, min_height];
} else {
options.aspectRatio = min_width/min_height;
}
var cropping_disabled = false;
if($this.val()[0] == "-"){
cropping_disabled = true;
$this.val($this.val().substr(1));
}
// is the image bigger than the minimal cropping values?
// otherwise lock cropping area on full image
var initial;
if ($this.val()) {
initial = image_cropping.initial_cropping($this.val());
} else {
var initial_min_width = min_width || min_height;
var initial_min_height = min_height || min_width;
initial = image_cropping.max_cropping(initial_min_width, initial_min_height, org_width, org_height);
// set cropfield to initial value
$this.val(initial.join(','));
}
image_cropping.$.extend(options, {setSelect: initial});
// hide the input field, show image to crop instead
$this.hide().after($image);
var jcrop = {};
image_cropping.$('#' + image_id).Jcrop(options, function(){jcrop[image_id]=this;});
if ($this.data('allow-fullsize') == true) {
if(cropping_disabled){
jcrop[image_id].release();
$this.val('-'+$this.val());
}
var label = 'allow-fullsize-'+image_id;
var checked = cropping_disabled ? '' : ' checked="checked"';
image_cropping.$('<div class="field-box allow-fullsize">' +
'<input type="checkbox" id="'+label+'" name="'+label+'"'+checked+'></div>').appendTo($this.parent());
image_cropping.$('<style type="text/css">div.allow-fullsize{padding: 5px 0 0 10px;}</style>').appendTo('head');
image_cropping.$('#'+label).click(function(){
if (cropping_disabled==true){
$this.val($this.val().substr(1));
jcrop[image_id].setSelect($this.val().split(','));
cropping_disabled = false;
} else {
$this.val('-'+$this.val());
jcrop[image_id].release();
cropping_disabled = true;
}
});
$this.parent().find('.jcrop-tracker').mousedown(function(){
if (cropping_disabled){
image_cropping.$('#'+label).attr('checked','checked')
cropping_disabled = false;
}
});
}
});
if (image_cropping.$('body').hasClass('change-form')) {
// if we're in the Django admin, the holder needs to be floated
// so it clears the label
image_cropping.$("<style type='text/css'>div.jcrop-holder{float:left;}</style>").appendTo('head');
}
},
max_cropping: function(width, height, image_width, image_height) {
var ratio = width/height;
var offset;
if (image_width < image_height * ratio) {
// width fits fully, height needs to be cropped
offset = Math.round((image_height-(image_width/ratio))/2);
return [0, offset, image_width, image_height - offset];
}
// height fits fully, width needs to be cropped
offset = Math.round((image_width-(image_height * ratio))/2);
return [offset, 0, image_width - offset, image_height];
},
initial_cropping: function(val) {
if (val == '') { return; }
var s = val.split(',');
return [
parseInt(s[0], 10),
parseInt(s[1], 10),
parseInt(s[2], 10),
parseInt(s[3], 10)
];
},
_update_selection: function(sel, $crop_field) {
if ($crop_field.data('size-warning')) {
image_cropping.crop_indication(sel, $crop_field);
}
$crop_field.val(new Array(
Math.floor(sel.x),
Math.floor(sel.y),
Math.floor(sel.x2),
Math.floor(sel.y2)
).join(','));
},
update_selection: function($crop_field) {
return function(sel) { image_cropping._update_selection(sel, $crop_field); };
},
crop_indication: function(sel, $crop_field) {
// indicate if cropped area gets smaller than the specified minimal cropping
var $jcrop_holder = $crop_field.siblings('.jcrop-holder');
var min_width = $crop_field.data("width");
var min_height = $crop_field.data("height");
if ((sel.w < min_width) || (sel.h < min_height)) {
$jcrop_holder.addClass('size-warning');
} else {
$jcrop_holder.removeClass('size-warning');
}
}
};
image_cropping.$(function() {image_cropping.init();});
@vapask
Copy link
Author

vapask commented Apr 7, 2014

image_cropping.js should be available by URL /static/image_cropping/image_cropping.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment