simple_crop 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/CHANGELOG.rdoc +9 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +86 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +81 -0
- data/Rakefile +53 -0
- data/TODO +13 -0
- data/VERSION +1 -0
- data/init.rb +1 -0
- data/lib/generators/simple_crop/USAGE +3 -0
- data/lib/generators/simple_crop/install_generator.rb +22 -0
- data/lib/generators/simple_crop/templates/Jcrop.gif +0 -0
- data/lib/generators/simple_crop/templates/README +32 -0
- data/lib/generators/simple_crop/templates/jquery.Jcrop.css +35 -0
- data/lib/generators/simple_crop/templates/jquery.Jcrop.min.js +163 -0
- data/lib/generators/simple_crop/templates/jquery.simple-crop.js +54 -0
- data/lib/simple_crop.rb +14 -0
- data/lib/simple_crop/action_view_extensions/builder.rb +13 -0
- data/lib/simple_crop/action_view_extensions/form_helper.rb +39 -0
- data/lib/simple_crop/action_view_extensions/instance_tag.rb +27 -0
- data/lib/simple_crop/active_record/simple_crop.rb +37 -0
- data/lib/simple_crop/paperclip_patches/attachment.rb +63 -0
- data/lib/simple_crop/paperclip_patches/style.rb +14 -0
- data/lib/simple_crop/paperclip_processors/processor.rb +24 -0
- data/simple_crop.gemspec +91 -0
- data/test/helper.rb +18 -0
- data/test/test_simple_crop.rb +7 -0
- metadata +204 -0
data/.document
ADDED
data/CHANGELOG.rdoc
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
gem "rails", ">= 3.0.0"
|
5
|
+
gem "paperclip", ">= 2.3.7"
|
6
|
+
|
7
|
+
# Add dependencies to develop your gem here.
|
8
|
+
# Include everything needed to run rake, tests, features, etc.
|
9
|
+
group :development do
|
10
|
+
gem "shoulda", ">= 0"
|
11
|
+
gem "bundler", "~> 1.0.0"
|
12
|
+
gem "jeweler", "~> 1.5.1"
|
13
|
+
gem "rcov", ">= 0"
|
14
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
abstract (1.0.0)
|
5
|
+
actionmailer (3.0.3)
|
6
|
+
actionpack (= 3.0.3)
|
7
|
+
mail (~> 2.2.9)
|
8
|
+
actionpack (3.0.3)
|
9
|
+
activemodel (= 3.0.3)
|
10
|
+
activesupport (= 3.0.3)
|
11
|
+
builder (~> 2.1.2)
|
12
|
+
erubis (~> 2.6.6)
|
13
|
+
i18n (~> 0.4)
|
14
|
+
rack (~> 1.2.1)
|
15
|
+
rack-mount (~> 0.6.13)
|
16
|
+
rack-test (~> 0.5.6)
|
17
|
+
tzinfo (~> 0.3.23)
|
18
|
+
activemodel (3.0.3)
|
19
|
+
activesupport (= 3.0.3)
|
20
|
+
builder (~> 2.1.2)
|
21
|
+
i18n (~> 0.4)
|
22
|
+
activerecord (3.0.3)
|
23
|
+
activemodel (= 3.0.3)
|
24
|
+
activesupport (= 3.0.3)
|
25
|
+
arel (~> 2.0.2)
|
26
|
+
tzinfo (~> 0.3.23)
|
27
|
+
activeresource (3.0.3)
|
28
|
+
activemodel (= 3.0.3)
|
29
|
+
activesupport (= 3.0.3)
|
30
|
+
activesupport (3.0.3)
|
31
|
+
arel (2.0.6)
|
32
|
+
builder (2.1.2)
|
33
|
+
erubis (2.6.6)
|
34
|
+
abstract (>= 1.0.0)
|
35
|
+
git (1.2.5)
|
36
|
+
i18n (0.5.0)
|
37
|
+
jeweler (1.5.1)
|
38
|
+
bundler (~> 1.0.0)
|
39
|
+
git (>= 1.2.5)
|
40
|
+
rake
|
41
|
+
mail (2.2.12)
|
42
|
+
activesupport (>= 2.3.6)
|
43
|
+
i18n (>= 0.4.0)
|
44
|
+
mime-types (~> 1.16)
|
45
|
+
treetop (~> 1.4.8)
|
46
|
+
mime-types (1.16)
|
47
|
+
paperclip (2.3.8)
|
48
|
+
activerecord
|
49
|
+
activesupport
|
50
|
+
polyglot (0.3.1)
|
51
|
+
rack (1.2.1)
|
52
|
+
rack-mount (0.6.13)
|
53
|
+
rack (>= 1.0.0)
|
54
|
+
rack-test (0.5.6)
|
55
|
+
rack (>= 1.0)
|
56
|
+
rails (3.0.3)
|
57
|
+
actionmailer (= 3.0.3)
|
58
|
+
actionpack (= 3.0.3)
|
59
|
+
activerecord (= 3.0.3)
|
60
|
+
activeresource (= 3.0.3)
|
61
|
+
activesupport (= 3.0.3)
|
62
|
+
bundler (~> 1.0)
|
63
|
+
railties (= 3.0.3)
|
64
|
+
railties (3.0.3)
|
65
|
+
actionpack (= 3.0.3)
|
66
|
+
activesupport (= 3.0.3)
|
67
|
+
rake (>= 0.8.7)
|
68
|
+
thor (~> 0.14.4)
|
69
|
+
rake (0.8.7)
|
70
|
+
rcov (0.9.9)
|
71
|
+
shoulda (2.11.3)
|
72
|
+
thor (0.14.6)
|
73
|
+
treetop (1.4.9)
|
74
|
+
polyglot (>= 0.3.1)
|
75
|
+
tzinfo (0.3.23)
|
76
|
+
|
77
|
+
PLATFORMS
|
78
|
+
ruby
|
79
|
+
|
80
|
+
DEPENDENCIES
|
81
|
+
bundler (~> 1.0.0)
|
82
|
+
jeweler (~> 1.5.1)
|
83
|
+
paperclip (>= 2.3.7)
|
84
|
+
rails (>= 3.0.0)
|
85
|
+
rcov
|
86
|
+
shoulda
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Marian André Plösch
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
= SimpleCrop
|
2
|
+
|
3
|
+
Easy rails implementation for Jcrop that lets you arbitrarily crop paperclip images.
|
4
|
+
|
5
|
+
This gem is very young and under development, so take a look at the source code before usage.
|
6
|
+
|
7
|
+
There are other implementations of Jcrop for rails, which served as a source of inspiration to this gem.
|
8
|
+
Take a look at:
|
9
|
+
* http://github.com/syfo/jcropper
|
10
|
+
* http://github.com/jschwindt/rjcrop
|
11
|
+
|
12
|
+
== Installation
|
13
|
+
|
14
|
+
Install the gem:
|
15
|
+
|
16
|
+
gem install simple_crop
|
17
|
+
|
18
|
+
Add it to your Gemfile and run the generator:
|
19
|
+
|
20
|
+
rails generate simple_crop:install
|
21
|
+
|
22
|
+
== Basic Usage
|
23
|
+
|
24
|
+
Now create a model with a paperclip attachment defined, simple_crop defined as processor and call the simple_crop method.
|
25
|
+
|
26
|
+
class Image < ActiveRecord::Base
|
27
|
+
has_attached_file :image, :processors => "simple_crop"
|
28
|
+
simple_crop
|
29
|
+
end
|
30
|
+
|
31
|
+
Ensure you load these javascripts in your view.
|
32
|
+
|
33
|
+
<%= javascript_include_tag 'jquery.Jcrop.min.js' %>
|
34
|
+
<%= javascript_include_tag 'simple_crop.js' %>
|
35
|
+
<%= stylesheet_link_tag 'jquery.Jcrop.css' %>
|
36
|
+
|
37
|
+
Call simple_crop on a paperclip attachment in your **edit** action's view (won't work on a new object, as no image is there to crop yet).
|
38
|
+
For example:
|
39
|
+
|
40
|
+
<%= form_for @image do |f| %>
|
41
|
+
<%= f.simple_crop :image %>
|
42
|
+
<%= f.submit :submit %>
|
43
|
+
<% end %>
|
44
|
+
|
45
|
+
== Advanced Usage
|
46
|
+
|
47
|
+
Let's assume your model setup is like this:
|
48
|
+
|
49
|
+
class Image < ActiveRecord::Base
|
50
|
+
has_attached_file :image,
|
51
|
+
:styles => {
|
52
|
+
:small => "170x100#",
|
53
|
+
:medium => "600x500>",
|
54
|
+
:original => "950x800>" },
|
55
|
+
:processors => "simple_crop"
|
56
|
+
simple_crop
|
57
|
+
end
|
58
|
+
|
59
|
+
The default crop occurs on the original style of your image.
|
60
|
+
If you want to let users select on a certain style and crop another style, just do:
|
61
|
+
|
62
|
+
<%= f.simple_crop :image, {:crop => :small, :select => :medium} %>
|
63
|
+
|
64
|
+
If the :crop and :select parameters aren't the same, it will add a preview panel next to your crop image.
|
65
|
+
|
66
|
+
The simple crop form helper also outputs some javascript for initialization.
|
67
|
+
If you want to manually fire it instead, use this:
|
68
|
+
|
69
|
+
<%= f.simple_crop :image, {:script => false} %>
|
70
|
+
|
71
|
+
You can make the helper output the jQuery plugin initialization to any yield section in your layouts too:
|
72
|
+
|
73
|
+
<%= f.simple_crop :image, {:script => :js_bottom} %>
|
74
|
+
|
75
|
+
== TODO
|
76
|
+
|
77
|
+
See TODO document.
|
78
|
+
|
79
|
+
== Copyright
|
80
|
+
|
81
|
+
Copyright (c) 2010 Marian André. Bitflut Media.
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'rake'
|
11
|
+
|
12
|
+
require 'jeweler'
|
13
|
+
Jeweler::Tasks.new do |gem|
|
14
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
15
|
+
gem.name = "simple_crop"
|
16
|
+
gem.homepage = "http://github.com/bitflut/simple_crop"
|
17
|
+
gem.license = "MIT"
|
18
|
+
gem.summary = "Makes cropping images easy"
|
19
|
+
gem.description = "Arbitrarily crop different styles of your paperclip images"
|
20
|
+
gem.email = "marian@bitflut.com"
|
21
|
+
gem.authors = ["Marian"]
|
22
|
+
# Include your dependencies below. Runtime dependencies are required when using your gem,
|
23
|
+
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
|
24
|
+
gem.add_runtime_dependency 'paperclip', '>= 2.3.7'
|
25
|
+
# gem.add_development_dependency 'rspec', '> 1.2.3'
|
26
|
+
end
|
27
|
+
Jeweler::RubygemsDotOrgTasks.new
|
28
|
+
|
29
|
+
require 'rake/testtask'
|
30
|
+
Rake::TestTask.new(:test) do |test|
|
31
|
+
test.libs << 'lib' << 'test'
|
32
|
+
test.pattern = 'test/**/test_*.rb'
|
33
|
+
test.verbose = true
|
34
|
+
end
|
35
|
+
|
36
|
+
require 'rcov/rcovtask'
|
37
|
+
Rcov::RcovTask.new do |test|
|
38
|
+
test.libs << 'test'
|
39
|
+
test.pattern = 'test/**/test_*.rb'
|
40
|
+
test.verbose = true
|
41
|
+
end
|
42
|
+
|
43
|
+
task :default => :test
|
44
|
+
|
45
|
+
require 'rake/rdoctask'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
+
|
49
|
+
rdoc.rdoc_dir = 'rdoc'
|
50
|
+
rdoc.title = "simple_crop #{version}"
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
* Only use simple crop processor when necessary
|
2
|
+
* Remove necessity of defining simple_crop as paperclip processor and dynamically add it on request
|
3
|
+
|
4
|
+
* Turn aspect-ratio on and off
|
5
|
+
* Default use of aspect-ratio on styles that will crop
|
6
|
+
* Suggest no use of aspect-ratio for styles that only resize
|
7
|
+
* Add aspect-ratio == (true || false) to options
|
8
|
+
|
9
|
+
* Refactor attr_accessors of simple_crop models to arrays with naming conventions for possible crops on multiple styles on one request
|
10
|
+
|
11
|
+
* Error handling when form input is used on a new object
|
12
|
+
|
13
|
+
* Write tests! :)
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.1
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'simple_crop'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module SimpleCrop
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
desc "Copy SimpleCrop default files"
|
5
|
+
source_root File.expand_path('../templates', __FILE__)
|
6
|
+
|
7
|
+
def copy_simple_crop_file
|
8
|
+
copy_file 'jquery.simple-crop.js', 'public/javascripts/jquery.simple-crop.js'
|
9
|
+
end
|
10
|
+
|
11
|
+
def copy_jcrop_files
|
12
|
+
copy_file 'jquery.Jcrop.min.js', 'public/javascripts/jquery.Jcrop.min.js'
|
13
|
+
copy_file 'jquery.Jcrop.css', 'public/stylesheets/jquery.Jcrop.css'
|
14
|
+
copy_file 'Jcrop.gif', 'public/images/Jcrop.gif'
|
15
|
+
end
|
16
|
+
|
17
|
+
def show_readme
|
18
|
+
readme "README" if behavior == :invoke
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
Binary file
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
===============================================================================
|
3
|
+
|
4
|
+
We got this far, there's not much left to do:
|
5
|
+
|
6
|
+
1. Now create a model with a paperclip attachment defined, simple_crop
|
7
|
+
defined as processor and call the simple_crop method:
|
8
|
+
|
9
|
+
has_attached_file :image,
|
10
|
+
:styles => {
|
11
|
+
:small => "170x100#",
|
12
|
+
:medium => "600x500>",
|
13
|
+
:original => "950x800>" },
|
14
|
+
:processors => "simple_crop"
|
15
|
+
simple_crop
|
16
|
+
|
17
|
+
2. Ensure you load these javascripts in your view.
|
18
|
+
|
19
|
+
<%= javascript_include_tag 'jquery.Jcrop.min.js' %>
|
20
|
+
<%= javascript_include_tag 'jquery.simple-crop.js' %>
|
21
|
+
<%= stylesheet_link_tag 'jquery.Jcrop.css' %>
|
22
|
+
|
23
|
+
3. Call simple_crop on a paperclip attachment in your form.
|
24
|
+
For example:
|
25
|
+
|
26
|
+
<%= f.simple_crop :image %>
|
27
|
+
|
28
|
+
4. See README for options like defining the selection style and crop style.
|
29
|
+
|
30
|
+
http://github.com/bitflut/simple_crop
|
31
|
+
|
32
|
+
===============================================================================
|
@@ -0,0 +1,35 @@
|
|
1
|
+
/* Fixes issue here http://code.google.com/p/jcrop/issues/detail?id=1 */
|
2
|
+
.jcrop-holder { text-align: left; }
|
3
|
+
|
4
|
+
.jcrop-vline, .jcrop-hline
|
5
|
+
{
|
6
|
+
font-size: 0;
|
7
|
+
position: absolute;
|
8
|
+
background: white url('/images/Jcrop.gif') top left repeat;
|
9
|
+
}
|
10
|
+
.jcrop-vline { height: 100%; width: 1px !important; }
|
11
|
+
.jcrop-hline { width: 100%; height: 1px !important; }
|
12
|
+
.jcrop-handle {
|
13
|
+
font-size: 1px;
|
14
|
+
width: 7px !important;
|
15
|
+
height: 7px !important;
|
16
|
+
border: 1px #eee solid;
|
17
|
+
background-color: #333;
|
18
|
+
*width: 9px;
|
19
|
+
*height: 9px;
|
20
|
+
}
|
21
|
+
|
22
|
+
.jcrop-tracker { width: 100%; height: 100%; }
|
23
|
+
|
24
|
+
.custom .jcrop-vline,
|
25
|
+
.custom .jcrop-hline
|
26
|
+
{
|
27
|
+
background: yellow;
|
28
|
+
}
|
29
|
+
.custom .jcrop-handle
|
30
|
+
{
|
31
|
+
border-color: black;
|
32
|
+
background-color: #C7BB00;
|
33
|
+
-moz-border-radius: 3px;
|
34
|
+
-webkit-border-radius: 3px;
|
35
|
+
}
|
@@ -0,0 +1,163 @@
|
|
1
|
+
/**
|
2
|
+
* Jcrop v.0.9.8 (minimized)
|
3
|
+
* (c) 2008 Kelly Hallman and DeepLiquid.com
|
4
|
+
* More information: http://deepliquid.com/content/Jcrop.html
|
5
|
+
* Released under MIT License - this header must remain with code
|
6
|
+
*/
|
7
|
+
|
8
|
+
|
9
|
+
(function($){$.Jcrop=function(obj,opt)
|
10
|
+
{var obj=obj,opt=opt;if(typeof(obj)!=='object')obj=$(obj)[0];if(typeof(opt)!=='object')opt={};if(!('trackDocument'in opt))
|
11
|
+
{opt.trackDocument=$.browser.msie?false:true;if($.browser.msie&&$.browser.version.split('.')[0]=='8')
|
12
|
+
opt.trackDocument=true;}
|
13
|
+
if(!('keySupport'in opt))
|
14
|
+
opt.keySupport=$.browser.msie?false:true;var defaults={trackDocument:false,baseClass:'jcrop',addClass:null,bgColor:'black',bgOpacity:.6,borderOpacity:.4,handleOpacity:.5,handlePad:5,handleSize:9,handleOffset:5,edgeMargin:14,aspectRatio:0,keySupport:true,cornerHandles:true,sideHandles:true,drawBorders:true,dragEdges:true,boxWidth:0,boxHeight:0,boundary:8,animationDelay:20,swingSpeed:3,allowSelect:true,allowMove:true,allowResize:true,minSelect:[0,0],maxSize:[0,0],minSize:[0,0],onChange:function(){},onSelect:function(){}};var options=defaults;setOptions(opt);var $origimg=$(obj);var $img=$origimg.clone().removeAttr('id').css({position:'absolute'});$img.width($origimg.width());$img.height($origimg.height());$origimg.after($img).hide();presize($img,options.boxWidth,options.boxHeight);var boundx=$img.width(),boundy=$img.height(),$div=$('<div />').width(boundx).height(boundy).addClass(cssClass('holder')).css({position:'relative',backgroundColor:options.bgColor}).insertAfter($origimg).append($img);;if(options.addClass)$div.addClass(options.addClass);var $img2=$('<img />').attr('src',$img.attr('src')).css('position','absolute').width(boundx).height(boundy);var $img_holder=$('<div />').width(pct(100)).height(pct(100)).css({zIndex:310,position:'absolute',overflow:'hidden'}).append($img2);var $hdl_holder=$('<div />').width(pct(100)).height(pct(100)).css('zIndex',320);var $sel=$('<div />').css({position:'absolute',zIndex:300}).insertBefore($img).append($img_holder,$hdl_holder);var bound=options.boundary;var $trk=newTracker().width(boundx+(bound*2)).height(boundy+(bound*2)).css({position:'absolute',top:px(-bound),left:px(-bound),zIndex:290}).mousedown(newSelection);var xlimit,ylimit,xmin,ymin;var xscale,yscale,enabled=true;var docOffset=getPos($img),btndown,lastcurs,dimmed,animating,shift_down;var Coords=function()
|
15
|
+
{var x1=0,y1=0,x2=0,y2=0,ox,oy;function setPressed(pos)
|
16
|
+
{var pos=rebound(pos);x2=x1=pos[0];y2=y1=pos[1];};function setCurrent(pos)
|
17
|
+
{var pos=rebound(pos);ox=pos[0]-x2;oy=pos[1]-y2;x2=pos[0];y2=pos[1];};function getOffset()
|
18
|
+
{return[ox,oy];};function moveOffset(offset)
|
19
|
+
{var ox=offset[0],oy=offset[1];if(0>x1+ox)ox-=ox+x1;if(0>y1+oy)oy-=oy+y1;if(boundy<y2+oy)oy+=boundy-(y2+oy);if(boundx<x2+ox)ox+=boundx-(x2+ox);x1+=ox;x2+=ox;y1+=oy;y2+=oy;};function getCorner(ord)
|
20
|
+
{var c=getFixed();switch(ord)
|
21
|
+
{case'ne':return[c.x2,c.y];case'nw':return[c.x,c.y];case'se':return[c.x2,c.y2];case'sw':return[c.x,c.y2];}};function getFixed()
|
22
|
+
{if(!options.aspectRatio)return getRect();var aspect=options.aspectRatio,min_x=options.minSize[0]/xscale,min_y=options.minSize[1]/yscale,max_x=options.maxSize[0]/xscale,max_y=options.maxSize[1]/yscale,rw=x2-x1,rh=y2-y1,rwa=Math.abs(rw),rha=Math.abs(rh),real_ratio=rwa/rha,xx,yy;if(max_x==0){max_x=boundx*10}
|
23
|
+
if(max_y==0){max_y=boundy*10}
|
24
|
+
if(real_ratio<aspect)
|
25
|
+
{yy=y2;w=rha*aspect;xx=rw<0?x1-w:w+x1;if(xx<0)
|
26
|
+
{xx=0;h=Math.abs((xx-x1)/aspect);yy=rh<0?y1-h:h+y1;}
|
27
|
+
else if(xx>boundx)
|
28
|
+
{xx=boundx;h=Math.abs((xx-x1)/aspect);yy=rh<0?y1-h:h+y1;}}
|
29
|
+
else
|
30
|
+
{xx=x2;h=rwa/aspect;yy=rh<0?y1-h:y1+h;if(yy<0)
|
31
|
+
{yy=0;w=Math.abs((yy-y1)*aspect);xx=rw<0?x1-w:w+x1;}
|
32
|
+
else if(yy>boundy)
|
33
|
+
{yy=boundy;w=Math.abs(yy-y1)*aspect;xx=rw<0?x1-w:w+x1;}}
|
34
|
+
if(xx>x1){if(xx-x1<min_x){xx=x1+min_x;}else if(xx-x1>max_x){xx=x1+max_x;}
|
35
|
+
if(yy>y1){yy=y1+(xx-x1)/aspect;}else{yy=y1-(xx-x1)/aspect;}}else if(xx<x1){if(x1-xx<min_x){xx=x1-min_x}else if(x1-xx>max_x){xx=x1-max_x;}
|
36
|
+
if(yy>y1){yy=y1+(x1-xx)/aspect;}else{yy=y1-(x1-xx)/aspect;}}
|
37
|
+
if(xx<0){x1-=xx;xx=0;}else if(xx>boundx){x1-=xx-boundx;xx=boundx;}
|
38
|
+
if(yy<0){y1-=yy;yy=0;}else if(yy>boundy){y1-=yy-boundy;yy=boundy;}
|
39
|
+
return last=makeObj(flipCoords(x1,y1,xx,yy));};function rebound(p)
|
40
|
+
{if(p[0]<0)p[0]=0;if(p[1]<0)p[1]=0;if(p[0]>boundx)p[0]=boundx;if(p[1]>boundy)p[1]=boundy;return[p[0],p[1]];};function flipCoords(x1,y1,x2,y2)
|
41
|
+
{var xa=x1,xb=x2,ya=y1,yb=y2;if(x2<x1)
|
42
|
+
{xa=x2;xb=x1;}
|
43
|
+
if(y2<y1)
|
44
|
+
{ya=y2;yb=y1;}
|
45
|
+
return[Math.round(xa),Math.round(ya),Math.round(xb),Math.round(yb)];};function getRect()
|
46
|
+
{var xsize=x2-x1;var ysize=y2-y1;if(xlimit&&(Math.abs(xsize)>xlimit))
|
47
|
+
x2=(xsize>0)?(x1+xlimit):(x1-xlimit);if(ylimit&&(Math.abs(ysize)>ylimit))
|
48
|
+
y2=(ysize>0)?(y1+ylimit):(y1-ylimit);if(ymin&&(Math.abs(ysize)<ymin))
|
49
|
+
y2=(ysize>0)?(y1+ymin):(y1-ymin);if(xmin&&(Math.abs(xsize)<xmin))
|
50
|
+
x2=(xsize>0)?(x1+xmin):(x1-xmin);if(x1<0){x2-=x1;x1-=x1;}
|
51
|
+
if(y1<0){y2-=y1;y1-=y1;}
|
52
|
+
if(x2<0){x1-=x2;x2-=x2;}
|
53
|
+
if(y2<0){y1-=y2;y2-=y2;}
|
54
|
+
if(x2>boundx){var delta=x2-boundx;x1-=delta;x2-=delta;}
|
55
|
+
if(y2>boundy){var delta=y2-boundy;y1-=delta;y2-=delta;}
|
56
|
+
if(x1>boundx){var delta=x1-boundy;y2-=delta;y1-=delta;}
|
57
|
+
if(y1>boundy){var delta=y1-boundy;y2-=delta;y1-=delta;}
|
58
|
+
return makeObj(flipCoords(x1,y1,x2,y2));};function makeObj(a)
|
59
|
+
{return{x:a[0],y:a[1],x2:a[2],y2:a[3],w:a[2]-a[0],h:a[3]-a[1]};};return{flipCoords:flipCoords,setPressed:setPressed,setCurrent:setCurrent,getOffset:getOffset,moveOffset:moveOffset,getCorner:getCorner,getFixed:getFixed};}();var Selection=function()
|
60
|
+
{var start,end,dragmode,awake,hdep=370;var borders={};var handle={};var seehandles=false;var hhs=options.handleOffset;if(options.drawBorders){borders={top:insertBorder('hline').css('top',$.browser.msie?px(-1):px(0)),bottom:insertBorder('hline'),left:insertBorder('vline'),right:insertBorder('vline')};}
|
61
|
+
if(options.dragEdges){handle.t=insertDragbar('n');handle.b=insertDragbar('s');handle.r=insertDragbar('e');handle.l=insertDragbar('w');}
|
62
|
+
options.sideHandles&&createHandles(['n','s','e','w']);options.cornerHandles&&createHandles(['sw','nw','ne','se']);function insertBorder(type)
|
63
|
+
{var jq=$('<div />').css({position:'absolute',opacity:options.borderOpacity}).addClass(cssClass(type));$img_holder.append(jq);return jq;};function dragDiv(ord,zi)
|
64
|
+
{var jq=$('<div />').mousedown(createDragger(ord)).css({cursor:ord+'-resize',position:'absolute',zIndex:zi});$hdl_holder.append(jq);return jq;};function insertHandle(ord)
|
65
|
+
{return dragDiv(ord,hdep++).css({top:px(-hhs+1),left:px(-hhs+1),opacity:options.handleOpacity}).addClass(cssClass('handle'));};function insertDragbar(ord)
|
66
|
+
{var s=options.handleSize,o=hhs,h=s,w=s,t=o,l=o;switch(ord)
|
67
|
+
{case'n':case's':w=pct(100);break;case'e':case'w':h=pct(100);break;}
|
68
|
+
return dragDiv(ord,hdep++).width(w).height(h).css({top:px(-t+1),left:px(-l+1)});};function createHandles(li)
|
69
|
+
{for(i in li)handle[li[i]]=insertHandle(li[i]);};function moveHandles(c)
|
70
|
+
{var midvert=Math.round((c.h/2)-hhs),midhoriz=Math.round((c.w/2)-hhs),north=west=-hhs+1,east=c.w-hhs,south=c.h-hhs,x,y;'e'in handle&&handle.e.css({top:px(midvert),left:px(east)})&&handle.w.css({top:px(midvert)})&&handle.s.css({top:px(south),left:px(midhoriz)})&&handle.n.css({left:px(midhoriz)});'ne'in handle&&handle.ne.css({left:px(east)})&&handle.se.css({top:px(south),left:px(east)})&&handle.sw.css({top:px(south)});'b'in handle&&handle.b.css({top:px(south)})&&handle.r.css({left:px(east)});};function moveto(x,y)
|
71
|
+
{$img2.css({top:px(-y),left:px(-x)});$sel.css({top:px(y),left:px(x)});};function resize(w,h)
|
72
|
+
{$sel.width(w).height(h);};function refresh()
|
73
|
+
{var c=Coords.getFixed();Coords.setPressed([c.x,c.y]);Coords.setCurrent([c.x2,c.y2]);updateVisible();};function updateVisible()
|
74
|
+
{if(awake)return update();};function update()
|
75
|
+
{var c=Coords.getFixed();resize(c.w,c.h);moveto(c.x,c.y);options.drawBorders&&borders['right'].css({left:px(c.w-1)})&&borders['bottom'].css({top:px(c.h-1)});seehandles&&moveHandles(c);awake||show();options.onChange(unscale(c));};function show()
|
76
|
+
{$sel.show();$img.css('opacity',options.bgOpacity);awake=true;};function release()
|
77
|
+
{disableHandles();$sel.hide();$img.css('opacity',1);awake=false;};function showHandles()
|
78
|
+
{if(seehandles)
|
79
|
+
{moveHandles(Coords.getFixed());$hdl_holder.show();}};function enableHandles()
|
80
|
+
{seehandles=true;if(options.allowResize)
|
81
|
+
{moveHandles(Coords.getFixed());$hdl_holder.show();return true;}};function disableHandles()
|
82
|
+
{seehandles=false;$hdl_holder.hide();};function animMode(v)
|
83
|
+
{(animating=v)?disableHandles():enableHandles();};function done()
|
84
|
+
{animMode(false);refresh();};var $track=newTracker().mousedown(createDragger('move')).css({cursor:'move',position:'absolute',zIndex:360})
|
85
|
+
$img_holder.append($track);disableHandles();return{updateVisible:updateVisible,update:update,release:release,refresh:refresh,setCursor:function(cursor){$track.css('cursor',cursor);},enableHandles:enableHandles,enableOnly:function(){seehandles=true;},showHandles:showHandles,disableHandles:disableHandles,animMode:animMode,done:done};}();var Tracker=function()
|
86
|
+
{var onMove=function(){},onDone=function(){},trackDoc=options.trackDocument;if(!trackDoc)
|
87
|
+
{$trk.mousemove(trackMove).mouseup(trackUp).mouseout(trackUp);}
|
88
|
+
function toFront()
|
89
|
+
{$trk.css({zIndex:450});if(trackDoc)
|
90
|
+
{$(document).mousemove(trackMove).mouseup(trackUp);}}
|
91
|
+
function toBack()
|
92
|
+
{$trk.css({zIndex:290});if(trackDoc)
|
93
|
+
{$(document).unbind('mousemove',trackMove).unbind('mouseup',trackUp);}}
|
94
|
+
function trackMove(e)
|
95
|
+
{onMove(mouseAbs(e));};function trackUp(e)
|
96
|
+
{e.preventDefault();e.stopPropagation();if(btndown)
|
97
|
+
{btndown=false;onDone(mouseAbs(e));options.onSelect(unscale(Coords.getFixed()));toBack();onMove=function(){};onDone=function(){};}
|
98
|
+
return false;};function activateHandlers(move,done)
|
99
|
+
{btndown=true;onMove=move;onDone=done;toFront();return false;};function setCursor(t){$trk.css('cursor',t);};$img.before($trk);return{activateHandlers:activateHandlers,setCursor:setCursor};}();var KeyManager=function()
|
100
|
+
{var $keymgr=$('<input type="radio" />').css({position:'absolute',left:'-30px'}).keypress(parseKey).blur(onBlur),$keywrap=$('<div />').css({position:'absolute',overflow:'hidden'}).append($keymgr);function watchKeys()
|
101
|
+
{if(options.keySupport)
|
102
|
+
{$keymgr.show();$keymgr.focus();}};function onBlur(e)
|
103
|
+
{$keymgr.hide();};function doNudge(e,x,y)
|
104
|
+
{if(options.allowMove){Coords.moveOffset([x,y]);Selection.updateVisible();};e.preventDefault();e.stopPropagation();};function parseKey(e)
|
105
|
+
{if(e.ctrlKey)return true;shift_down=e.shiftKey?true:false;var nudge=shift_down?10:1;switch(e.keyCode)
|
106
|
+
{case 37:doNudge(e,-nudge,0);break;case 39:doNudge(e,nudge,0);break;case 38:doNudge(e,0,-nudge);break;case 40:doNudge(e,0,nudge);break;case 27:Selection.release();break;case 9:return true;}
|
107
|
+
return nothing(e);};if(options.keySupport)$keywrap.insertBefore($img);return{watchKeys:watchKeys};}();function px(n){return''+parseInt(n)+'px';};function pct(n){return''+parseInt(n)+'%';};function cssClass(cl){return options.baseClass+'-'+cl;};function getPos(obj)
|
108
|
+
{var pos=$(obj).offset();return[pos.left,pos.top];};function mouseAbs(e)
|
109
|
+
{return[(e.pageX-docOffset[0]),(e.pageY-docOffset[1])];};function myCursor(type)
|
110
|
+
{if(type!=lastcurs)
|
111
|
+
{Tracker.setCursor(type);lastcurs=type;}};function startDragMode(mode,pos)
|
112
|
+
{docOffset=getPos($img);Tracker.setCursor(mode=='move'?mode:mode+'-resize');if(mode=='move')
|
113
|
+
return Tracker.activateHandlers(createMover(pos),doneSelect);var fc=Coords.getFixed();var opp=oppLockCorner(mode);var opc=Coords.getCorner(oppLockCorner(opp));Coords.setPressed(Coords.getCorner(opp));Coords.setCurrent(opc);Tracker.activateHandlers(dragmodeHandler(mode,fc),doneSelect);};function dragmodeHandler(mode,f)
|
114
|
+
{return function(pos){if(!options.aspectRatio)switch(mode)
|
115
|
+
{case'e':pos[1]=f.y2;break;case'w':pos[1]=f.y2;break;case'n':pos[0]=f.x2;break;case's':pos[0]=f.x2;break;}
|
116
|
+
else switch(mode)
|
117
|
+
{case'e':pos[1]=f.y+1;break;case'w':pos[1]=f.y+1;break;case'n':pos[0]=f.x+1;break;case's':pos[0]=f.x+1;break;}
|
118
|
+
Coords.setCurrent(pos);Selection.update();};};function createMover(pos)
|
119
|
+
{var lloc=pos;KeyManager.watchKeys();return function(pos)
|
120
|
+
{Coords.moveOffset([pos[0]-lloc[0],pos[1]-lloc[1]]);lloc=pos;Selection.update();};};function oppLockCorner(ord)
|
121
|
+
{switch(ord)
|
122
|
+
{case'n':return'sw';case's':return'nw';case'e':return'nw';case'w':return'ne';case'ne':return'sw';case'nw':return'se';case'se':return'nw';case'sw':return'ne';};};function createDragger(ord)
|
123
|
+
{return function(e){if(options.disabled)return false;if((ord=='move')&&!options.allowMove)return false;btndown=true;startDragMode(ord,mouseAbs(e));e.stopPropagation();e.preventDefault();return false;};};function presize($obj,w,h)
|
124
|
+
{var nw=$obj.width(),nh=$obj.height();if((nw>w)&&w>0)
|
125
|
+
{nw=w;nh=(w/$obj.width())*$obj.height();}
|
126
|
+
if((nh>h)&&h>0)
|
127
|
+
{nh=h;nw=(h/$obj.height())*$obj.width();}
|
128
|
+
xscale=$obj.width()/nw;yscale=$obj.height()/nh;$obj.width(nw).height(nh);};function unscale(c)
|
129
|
+
{return{x:parseInt(c.x*xscale),y:parseInt(c.y*yscale),x2:parseInt(c.x2*xscale),y2:parseInt(c.y2*yscale),w:parseInt(c.w*xscale),h:parseInt(c.h*yscale)};};function doneSelect(pos)
|
130
|
+
{var c=Coords.getFixed();if(c.w>options.minSelect[0]&&c.h>options.minSelect[1])
|
131
|
+
{Selection.enableHandles();Selection.done();}
|
132
|
+
else
|
133
|
+
{Selection.release();}
|
134
|
+
Tracker.setCursor(options.allowSelect?'crosshair':'default');};function newSelection(e)
|
135
|
+
{if(options.disabled)return false;if(!options.allowSelect)return false;btndown=true;docOffset=getPos($img);Selection.disableHandles();myCursor('crosshair');var pos=mouseAbs(e);Coords.setPressed(pos);Tracker.activateHandlers(selectDrag,doneSelect);KeyManager.watchKeys();Selection.update();e.stopPropagation();e.preventDefault();return false;};function selectDrag(pos)
|
136
|
+
{Coords.setCurrent(pos);Selection.update();};function newTracker()
|
137
|
+
{var trk=$('<div></div>').addClass(cssClass('tracker'));$.browser.msie&&trk.css({opacity:0,backgroundColor:'white'});return trk;};function animateTo(a)
|
138
|
+
{var x1=a[0]/xscale,y1=a[1]/yscale,x2=a[2]/xscale,y2=a[3]/yscale;if(animating)return;var animto=Coords.flipCoords(x1,y1,x2,y2);var c=Coords.getFixed();var animat=initcr=[c.x,c.y,c.x2,c.y2];var interv=options.animationDelay;var x=animat[0];var y=animat[1];var x2=animat[2];var y2=animat[3];var ix1=animto[0]-initcr[0];var iy1=animto[1]-initcr[1];var ix2=animto[2]-initcr[2];var iy2=animto[3]-initcr[3];var pcent=0;var velocity=options.swingSpeed;Selection.animMode(true);var animator=function()
|
139
|
+
{return function()
|
140
|
+
{pcent+=(100-pcent)/velocity;animat[0]=x+((pcent/100)*ix1);animat[1]=y+((pcent/100)*iy1);animat[2]=x2+((pcent/100)*ix2);animat[3]=y2+((pcent/100)*iy2);if(pcent<100)animateStart();else Selection.done();if(pcent>=99.8)pcent=100;setSelectRaw(animat);};}();function animateStart()
|
141
|
+
{window.setTimeout(animator,interv);};animateStart();};function setSelect(rect)
|
142
|
+
{setSelectRaw([rect[0]/xscale,rect[1]/yscale,rect[2]/xscale,rect[3]/yscale]);};function setSelectRaw(l)
|
143
|
+
{Coords.setPressed([l[0],l[1]]);Coords.setCurrent([l[2],l[3]]);Selection.update();};function setOptions(opt)
|
144
|
+
{if(typeof(opt)!='object')opt={};options=$.extend(options,opt);if(typeof(options.onChange)!=='function')
|
145
|
+
options.onChange=function(){};if(typeof(options.onSelect)!=='function')
|
146
|
+
options.onSelect=function(){};};function tellSelect()
|
147
|
+
{return unscale(Coords.getFixed());};function tellScaled()
|
148
|
+
{return Coords.getFixed();};function setOptionsNew(opt)
|
149
|
+
{setOptions(opt);interfaceUpdate();};function disableCrop()
|
150
|
+
{options.disabled=true;Selection.disableHandles();Selection.setCursor('default');Tracker.setCursor('default');};function enableCrop()
|
151
|
+
{options.disabled=false;interfaceUpdate();};function cancelCrop()
|
152
|
+
{Selection.done();Tracker.activateHandlers(null,null);};function destroy()
|
153
|
+
{$div.remove();$origimg.show();};function interfaceUpdate(alt)
|
154
|
+
{options.allowResize?alt?Selection.enableOnly():Selection.enableHandles():Selection.disableHandles();Tracker.setCursor(options.allowSelect?'crosshair':'default');Selection.setCursor(options.allowMove?'move':'default');$div.css('backgroundColor',options.bgColor);if('setSelect'in options){setSelect(opt.setSelect);Selection.done();delete(options.setSelect);}
|
155
|
+
if('trueSize'in options){xscale=options.trueSize[0]/boundx;yscale=options.trueSize[1]/boundy;}
|
156
|
+
xlimit=options.maxSize[0]||0;ylimit=options.maxSize[1]||0;xmin=options.minSize[0]||0;ymin=options.minSize[1]||0;if('outerImage'in options)
|
157
|
+
{$img.attr('src',options.outerImage);delete(options.outerImage);}
|
158
|
+
Selection.refresh();};$hdl_holder.hide();interfaceUpdate(true);var api={animateTo:animateTo,setSelect:setSelect,setOptions:setOptionsNew,tellSelect:tellSelect,tellScaled:tellScaled,disable:disableCrop,enable:enableCrop,cancel:cancelCrop,focus:KeyManager.watchKeys,getBounds:function(){return[boundx*xscale,boundy*yscale];},getWidgetSize:function(){return[boundx,boundy];},release:Selection.release,destroy:destroy};$origimg.data('Jcrop',api);return api;};$.fn.Jcrop=function(options)
|
159
|
+
{function attachWhenDone(from)
|
160
|
+
{var loadsrc=options.useImg||from.src;var img=new Image();img.onload=function(){$.Jcrop(from,options);};img.src=loadsrc;};if(typeof(options)!=='object')options={};this.each(function()
|
161
|
+
{if($(this).data('Jcrop'))
|
162
|
+
{if(options=='api')return $(this).data('Jcrop');else $(this).data('Jcrop').setOptions(options);}
|
163
|
+
else attachWhenDone(this);});return this;};})(jQuery);
|
@@ -0,0 +1,54 @@
|
|
1
|
+
(function($){
|
2
|
+
var methods = {
|
3
|
+
|
4
|
+
onChange : function(coords) {
|
5
|
+
methods.updateCoords(coords);
|
6
|
+
|
7
|
+
var rx = 170 / coords.w;
|
8
|
+
var ry = 100 / coords.h;
|
9
|
+
|
10
|
+
if (methods.hasPreviewMask())
|
11
|
+
{
|
12
|
+
methods.previewMask().css({
|
13
|
+
width: Math.round(rx * width * ratio) + 'px',
|
14
|
+
height: Math.round(ry * height * ratio) + 'px',
|
15
|
+
marginLeft: '-' + Math.round(rx * coords.x) + 'px',
|
16
|
+
marginTop: '-' + Math.round(ry * coords.y) + 'px'
|
17
|
+
});
|
18
|
+
}
|
19
|
+
},
|
20
|
+
|
21
|
+
updateCoords : function(coords) {
|
22
|
+
["x", "y", "h", "w"].forEach( function(obj) {
|
23
|
+
$('#'+croppable_name+'_simple_crop_'+obj).val(coords[obj]);
|
24
|
+
});
|
25
|
+
},
|
26
|
+
|
27
|
+
hasPreviewMask : function() {
|
28
|
+
return methods.previewMask().length > 0;
|
29
|
+
},
|
30
|
+
|
31
|
+
previewMask : function() {
|
32
|
+
return $('#'+croppable_name+'_simple_crop_preview img');
|
33
|
+
}
|
34
|
+
};
|
35
|
+
|
36
|
+
var width;
|
37
|
+
var height;
|
38
|
+
var ratio;
|
39
|
+
var croppable_name;
|
40
|
+
|
41
|
+
$.fn.SimpleCrop = function() {
|
42
|
+
width = $(this[0]).width();
|
43
|
+
height = $(this[0]).height();
|
44
|
+
croppable_name = $(this[0]).attr('data-crop-for');
|
45
|
+
ratio = $(this[0]).attr('data-crop-ratio').split('/');
|
46
|
+
ratio = ratio[0] / ratio[1];
|
47
|
+
|
48
|
+
return $(this).Jcrop({
|
49
|
+
onChange: methods.onChange,
|
50
|
+
aspectRatio: 170/100,
|
51
|
+
trueSize: [width*ratio, height*ratio]
|
52
|
+
});
|
53
|
+
};
|
54
|
+
})(jQuery);
|
data/lib/simple_crop.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'action_view'
|
2
|
+
require 'active_record'
|
3
|
+
require 'paperclip'
|
4
|
+
|
5
|
+
require 'simple_crop/action_view_extensions/form_helper'
|
6
|
+
require 'simple_crop/action_view_extensions/builder'
|
7
|
+
require 'simple_crop/action_view_extensions/instance_tag'
|
8
|
+
|
9
|
+
require 'simple_crop/active_record/simple_crop'
|
10
|
+
|
11
|
+
require 'simple_crop/paperclip_patches/attachment'
|
12
|
+
require 'simple_crop/paperclip_patches/style'
|
13
|
+
|
14
|
+
require 'simple_crop/paperclip_processors/processor'
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module SimpleCrop
|
2
|
+
module ActionViewExtensions
|
3
|
+
|
4
|
+
module FormBuilder
|
5
|
+
def simple_crop(method, options = {})
|
6
|
+
@template.send("simple_crop", @object_name, method, objectify_options(options))
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
ActionView::Helpers::FormBuilder.send :include, SimpleCrop::ActionViewExtensions::FormBuilder
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module ActionView
|
2
|
+
module Helpers
|
3
|
+
module FormHelper
|
4
|
+
def simple_crop(object_name, method, options = {})
|
5
|
+
default_options = {"select" => :original, "crop" => :original, "script" => true}
|
6
|
+
options = options.stringify_keys!
|
7
|
+
options = default_options.merge(options)
|
8
|
+
script = options["script"]
|
9
|
+
|
10
|
+
# Build image and preview
|
11
|
+
output = InstanceTag.new(object_name, method, self, options).to_simple_crop_tag(options)
|
12
|
+
output << InstanceTag.new(object_name, method, self, options).to_simple_crop_preview_tag(options) unless (options["select"] == options["crop"])
|
13
|
+
|
14
|
+
# Build hidden fields for accessors on model used by simple_crop
|
15
|
+
output << InstanceTag.new(object_name, :simple_crop_x, self).to_input_field_tag("hidden")
|
16
|
+
output << InstanceTag.new(object_name, :simple_crop_y, self).to_input_field_tag("hidden")
|
17
|
+
output << InstanceTag.new(object_name, :simple_crop_w, self).to_input_field_tag("hidden")
|
18
|
+
output << InstanceTag.new(object_name, :simple_crop_h, self).to_input_field_tag("hidden")
|
19
|
+
output << InstanceTag.new(object_name, :simple_crop_style, self).to_input_field_tag("hidden", {:value => options["crop"]})
|
20
|
+
|
21
|
+
# Initialize jQuery plugin
|
22
|
+
# Either output inline or yield to provided template
|
23
|
+
if script === true
|
24
|
+
output << %Q(<script type="text/javascript" charset="utf-8">$(window).load(function() {$('img[data-crop-for="#{sanitized_object_name(object_name)}"]').SimpleCrop();});</script>).html_safe
|
25
|
+
elsif not script === false
|
26
|
+
content_for script do
|
27
|
+
%Q($('img[data-crop-for="#{sanitized_object_name(object_name)}"]').SimpleCrop();).html_safe
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
output
|
32
|
+
end
|
33
|
+
|
34
|
+
def sanitized_object_name(object_name)
|
35
|
+
object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module ActionView
|
2
|
+
module Helpers
|
3
|
+
module InstanceTagMethods
|
4
|
+
def to_simple_crop_preview_tag(options)
|
5
|
+
paperclip = options["object"].send(@method_name)
|
6
|
+
target_crop_geometry = paperclip_geometry(paperclip, options["crop"])
|
7
|
+
div_style = "padding:0;overflow:hidden;width:#{target_crop_geometry.width.to_i.to_s}px;height:#{target_crop_geometry.height.to_i.to_s}px;"
|
8
|
+
content_tag("div", tag("img",{:src => paperclip.url(options["select"])}), {:class => "simple-crop-preview", :id => "#{sanitized_object_name}_simple_crop_preview", :style => div_style})
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_simple_crop_tag(options)
|
12
|
+
paperclip = options["object"].send(@method_name)
|
13
|
+
tag("img",{:src => paperclip.url(options["select"]), :"data-crop-ratio" => crop_ratio(paperclip, options["select"]), :"data-crop-for" => sanitized_object_name})
|
14
|
+
end
|
15
|
+
|
16
|
+
def crop_ratio paperclip, select_style
|
17
|
+
original_geometry = paperclip_geometry(paperclip, :original)
|
18
|
+
select_geometry = paperclip_geometry(paperclip, select_style)
|
19
|
+
"#{original_geometry.width.to_i.to_s}/#{select_geometry.width.to_i.to_s}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def paperclip_geometry(paperclip, style)
|
23
|
+
Paperclip::Geometry.from_file paperclip.to_file(style)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module SimpleCrop
|
2
|
+
module ActiveRecord
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def simple_crop
|
9
|
+
class_eval <<-EOV
|
10
|
+
include SimpleCrop::ActiveRecord::InstanceMethods
|
11
|
+
before_save :simple_crop_reprocess
|
12
|
+
attr_accessor :simple_crop_x, :simple_crop_y, :simple_crop_h, :simple_crop_w, :simple_crop_style
|
13
|
+
EOV
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module InstanceMethods
|
18
|
+
def wants_simple_crop?
|
19
|
+
eval (%w(x y w h).collect {|v| "!simple_crop_#{v}.blank?"}.join(" and ")+" and !simple_crop_style.blank?")
|
20
|
+
end
|
21
|
+
|
22
|
+
def simple_crop_string
|
23
|
+
"#{simple_crop_w}x#{simple_crop_h}+#{simple_crop_x}+#{simple_crop_y}"
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def simple_crop_reprocess
|
29
|
+
if wants_simple_crop?
|
30
|
+
self.image.reprocess!(self.simple_crop_style.to_sym)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
ActiveRecord::Base.class_eval { include SimpleCrop::ActiveRecord }
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Paperclip
|
2
|
+
class Attachment
|
3
|
+
def reprocess!(*style_args)
|
4
|
+
new_original = Tempfile.new("paperclip-reprocess")
|
5
|
+
new_original.binmode
|
6
|
+
if old_original = to_file(:original)
|
7
|
+
new_original.write( old_original.respond_to?(:get) ? old_original.get : old_original.read )
|
8
|
+
new_original.rewind
|
9
|
+
|
10
|
+
@queued_for_write = { :original => new_original }
|
11
|
+
post_process(*style_args)
|
12
|
+
|
13
|
+
old_original.close if old_original.respond_to?(:close)
|
14
|
+
|
15
|
+
save
|
16
|
+
else
|
17
|
+
true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def post_process(*style_args) #:nodoc:
|
22
|
+
return if @queued_for_write[:original].nil?
|
23
|
+
instance.run_paperclip_callbacks(:post_process) do
|
24
|
+
instance.run_paperclip_callbacks(:"#{name}_post_process") do
|
25
|
+
post_process_styles(*style_args)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def post_process_styles(*style_args) #:nodoc:
|
31
|
+
styles.each do |name, style|
|
32
|
+
begin
|
33
|
+
if style_args.empty? || style_args.include?(name)
|
34
|
+
raise RuntimeError.new("Style #{name} has no processors defined.") if style.processors.blank?
|
35
|
+
@queued_for_write[name] = style.processors.inject(@queued_for_write[:original]) do |file, processor|
|
36
|
+
Paperclip.processor(processor).make(file, style.processor_options, self)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
rescue PaperclipError => e
|
40
|
+
log("An error was received while processing: #{e.inspect}")
|
41
|
+
(@errors[:processing] ||= []) << e.message if @whiny
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def styles
|
47
|
+
unless @normalized_styles
|
48
|
+
@normalized_styles = {}
|
49
|
+
(@styles.respond_to?(:call) ? @styles.call(self) : @styles).each do |name, args|
|
50
|
+
@normalized_styles[name] = Paperclip::Style.new(name, args.dup, self)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
if @normalized_styles.blank? and instance.wants_simple_crop?
|
55
|
+
temporary_simple_crop_style = {}
|
56
|
+
temporary_simple_crop_style[:original] = Paperclip::Style.new(:original, "100000x100000>", self)
|
57
|
+
return temporary_simple_crop_style
|
58
|
+
end
|
59
|
+
|
60
|
+
@normalized_styles
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Paperclip
|
2
|
+
class Style
|
3
|
+
def processor_options
|
4
|
+
args = {}
|
5
|
+
@other_args.each do |k,v|
|
6
|
+
args[k] = v.respond_to?(:call) ? v.call(attachment) : v
|
7
|
+
end
|
8
|
+
[:processors, :geometry, :format, :whiny, :convert_options, :name].each do |k|
|
9
|
+
(arg = send(k)) && args[k] = arg
|
10
|
+
end
|
11
|
+
args
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Paperclip
|
2
|
+
class SimpleCrop < Thumbnail
|
3
|
+
alias :normal_transformation_command :transformation_command
|
4
|
+
def transformation_command
|
5
|
+
if wants_simpe_crop?
|
6
|
+
scale, crop = @current_geometry.transformation_to(@target_geometry, crop?)
|
7
|
+
trans = []
|
8
|
+
trans << "-crop" << %["#{simple_crop_string}"]
|
9
|
+
trans << "-resize" << %["#{scale}"] unless scale.nil? || scale.empty?
|
10
|
+
trans
|
11
|
+
else
|
12
|
+
normal_transformation_command
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def wants_simpe_crop?
|
17
|
+
eval (%w(x y w h).collect {|v| "!@attachment.instance.simple_crop_#{v}.blank?"}.join(" and ")+" and !@attachment.instance.simple_crop_style.blank? and @attachment.instance.simple_crop_style.to_sym == options[:name]")
|
18
|
+
end
|
19
|
+
|
20
|
+
def simple_crop_string
|
21
|
+
@attachment.instance.simple_crop_string
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/simple_crop.gemspec
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{simple_crop}
|
8
|
+
s.version = "0.1.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Marian"]
|
12
|
+
s.date = %q{2010-12-17}
|
13
|
+
s.description = %q{Arbitrarily crop different styles of your paperclip images}
|
14
|
+
s.email = %q{marian@bitflut.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc",
|
18
|
+
"TODO"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
".document",
|
22
|
+
"CHANGELOG.rdoc",
|
23
|
+
"Gemfile",
|
24
|
+
"Gemfile.lock",
|
25
|
+
"LICENSE.txt",
|
26
|
+
"README.rdoc",
|
27
|
+
"Rakefile",
|
28
|
+
"TODO",
|
29
|
+
"VERSION",
|
30
|
+
"init.rb",
|
31
|
+
"lib/generators/simple_crop/USAGE",
|
32
|
+
"lib/generators/simple_crop/install_generator.rb",
|
33
|
+
"lib/generators/simple_crop/templates/Jcrop.gif",
|
34
|
+
"lib/generators/simple_crop/templates/README",
|
35
|
+
"lib/generators/simple_crop/templates/jquery.Jcrop.css",
|
36
|
+
"lib/generators/simple_crop/templates/jquery.Jcrop.min.js",
|
37
|
+
"lib/generators/simple_crop/templates/jquery.simple-crop.js",
|
38
|
+
"lib/simple_crop.rb",
|
39
|
+
"lib/simple_crop/action_view_extensions/builder.rb",
|
40
|
+
"lib/simple_crop/action_view_extensions/form_helper.rb",
|
41
|
+
"lib/simple_crop/action_view_extensions/instance_tag.rb",
|
42
|
+
"lib/simple_crop/active_record/simple_crop.rb",
|
43
|
+
"lib/simple_crop/paperclip_patches/attachment.rb",
|
44
|
+
"lib/simple_crop/paperclip_patches/style.rb",
|
45
|
+
"lib/simple_crop/paperclip_processors/processor.rb",
|
46
|
+
"simple_crop.gemspec",
|
47
|
+
"test/helper.rb",
|
48
|
+
"test/test_simple_crop.rb"
|
49
|
+
]
|
50
|
+
s.homepage = %q{http://github.com/bitflut/simple_crop}
|
51
|
+
s.licenses = ["MIT"]
|
52
|
+
s.require_paths = ["lib"]
|
53
|
+
s.rubygems_version = %q{1.3.7}
|
54
|
+
s.summary = %q{Makes cropping images easy}
|
55
|
+
s.test_files = [
|
56
|
+
"test/helper.rb",
|
57
|
+
"test/test_simple_crop.rb"
|
58
|
+
]
|
59
|
+
|
60
|
+
if s.respond_to? :specification_version then
|
61
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
62
|
+
s.specification_version = 3
|
63
|
+
|
64
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
65
|
+
s.add_runtime_dependency(%q<rails>, [">= 3.0.0"])
|
66
|
+
s.add_runtime_dependency(%q<paperclip>, [">= 2.3.7"])
|
67
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
68
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
69
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.1"])
|
70
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
71
|
+
s.add_runtime_dependency(%q<paperclip>, [">= 2.3.7"])
|
72
|
+
else
|
73
|
+
s.add_dependency(%q<rails>, [">= 3.0.0"])
|
74
|
+
s.add_dependency(%q<paperclip>, [">= 2.3.7"])
|
75
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
76
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
77
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
|
78
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
79
|
+
s.add_dependency(%q<paperclip>, [">= 2.3.7"])
|
80
|
+
end
|
81
|
+
else
|
82
|
+
s.add_dependency(%q<rails>, [">= 3.0.0"])
|
83
|
+
s.add_dependency(%q<paperclip>, [">= 2.3.7"])
|
84
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
85
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
86
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
|
87
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
88
|
+
s.add_dependency(%q<paperclip>, [">= 2.3.7"])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
data/test/helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
require 'shoulda'
|
12
|
+
|
13
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
+
require 'simple_crop'
|
16
|
+
|
17
|
+
class Test::Unit::TestCase
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple_crop
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Marian
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-12-17 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
name: rails
|
25
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 7
|
31
|
+
segments:
|
32
|
+
- 3
|
33
|
+
- 0
|
34
|
+
- 0
|
35
|
+
version: 3.0.0
|
36
|
+
requirement: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
name: paperclip
|
41
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
hash: 13
|
47
|
+
segments:
|
48
|
+
- 2
|
49
|
+
- 3
|
50
|
+
- 7
|
51
|
+
version: 2.3.7
|
52
|
+
requirement: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
name: shoulda
|
57
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
hash: 3
|
63
|
+
segments:
|
64
|
+
- 0
|
65
|
+
version: "0"
|
66
|
+
requirement: *id003
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
name: bundler
|
71
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 23
|
77
|
+
segments:
|
78
|
+
- 1
|
79
|
+
- 0
|
80
|
+
- 0
|
81
|
+
version: 1.0.0
|
82
|
+
requirement: *id004
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
type: :development
|
85
|
+
prerelease: false
|
86
|
+
name: jeweler
|
87
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ~>
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
hash: 1
|
93
|
+
segments:
|
94
|
+
- 1
|
95
|
+
- 5
|
96
|
+
- 1
|
97
|
+
version: 1.5.1
|
98
|
+
requirement: *id005
|
99
|
+
- !ruby/object:Gem::Dependency
|
100
|
+
type: :development
|
101
|
+
prerelease: false
|
102
|
+
name: rcov
|
103
|
+
version_requirements: &id006 !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
hash: 3
|
109
|
+
segments:
|
110
|
+
- 0
|
111
|
+
version: "0"
|
112
|
+
requirement: *id006
|
113
|
+
- !ruby/object:Gem::Dependency
|
114
|
+
type: :runtime
|
115
|
+
prerelease: false
|
116
|
+
name: paperclip
|
117
|
+
version_requirements: &id007 !ruby/object:Gem::Requirement
|
118
|
+
none: false
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
hash: 13
|
123
|
+
segments:
|
124
|
+
- 2
|
125
|
+
- 3
|
126
|
+
- 7
|
127
|
+
version: 2.3.7
|
128
|
+
requirement: *id007
|
129
|
+
description: Arbitrarily crop different styles of your paperclip images
|
130
|
+
email: marian@bitflut.com
|
131
|
+
executables: []
|
132
|
+
|
133
|
+
extensions: []
|
134
|
+
|
135
|
+
extra_rdoc_files:
|
136
|
+
- LICENSE.txt
|
137
|
+
- README.rdoc
|
138
|
+
- TODO
|
139
|
+
files:
|
140
|
+
- .document
|
141
|
+
- CHANGELOG.rdoc
|
142
|
+
- Gemfile
|
143
|
+
- Gemfile.lock
|
144
|
+
- LICENSE.txt
|
145
|
+
- README.rdoc
|
146
|
+
- Rakefile
|
147
|
+
- TODO
|
148
|
+
- VERSION
|
149
|
+
- init.rb
|
150
|
+
- lib/generators/simple_crop/USAGE
|
151
|
+
- lib/generators/simple_crop/install_generator.rb
|
152
|
+
- lib/generators/simple_crop/templates/Jcrop.gif
|
153
|
+
- lib/generators/simple_crop/templates/README
|
154
|
+
- lib/generators/simple_crop/templates/jquery.Jcrop.css
|
155
|
+
- lib/generators/simple_crop/templates/jquery.Jcrop.min.js
|
156
|
+
- lib/generators/simple_crop/templates/jquery.simple-crop.js
|
157
|
+
- lib/simple_crop.rb
|
158
|
+
- lib/simple_crop/action_view_extensions/builder.rb
|
159
|
+
- lib/simple_crop/action_view_extensions/form_helper.rb
|
160
|
+
- lib/simple_crop/action_view_extensions/instance_tag.rb
|
161
|
+
- lib/simple_crop/active_record/simple_crop.rb
|
162
|
+
- lib/simple_crop/paperclip_patches/attachment.rb
|
163
|
+
- lib/simple_crop/paperclip_patches/style.rb
|
164
|
+
- lib/simple_crop/paperclip_processors/processor.rb
|
165
|
+
- simple_crop.gemspec
|
166
|
+
- test/helper.rb
|
167
|
+
- test/test_simple_crop.rb
|
168
|
+
has_rdoc: true
|
169
|
+
homepage: http://github.com/bitflut/simple_crop
|
170
|
+
licenses:
|
171
|
+
- MIT
|
172
|
+
post_install_message:
|
173
|
+
rdoc_options: []
|
174
|
+
|
175
|
+
require_paths:
|
176
|
+
- lib
|
177
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
178
|
+
none: false
|
179
|
+
requirements:
|
180
|
+
- - ">="
|
181
|
+
- !ruby/object:Gem::Version
|
182
|
+
hash: 3
|
183
|
+
segments:
|
184
|
+
- 0
|
185
|
+
version: "0"
|
186
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
187
|
+
none: false
|
188
|
+
requirements:
|
189
|
+
- - ">="
|
190
|
+
- !ruby/object:Gem::Version
|
191
|
+
hash: 3
|
192
|
+
segments:
|
193
|
+
- 0
|
194
|
+
version: "0"
|
195
|
+
requirements: []
|
196
|
+
|
197
|
+
rubyforge_project:
|
198
|
+
rubygems_version: 1.3.7
|
199
|
+
signing_key:
|
200
|
+
specification_version: 3
|
201
|
+
summary: Makes cropping images easy
|
202
|
+
test_files:
|
203
|
+
- test/helper.rb
|
204
|
+
- test/test_simple_crop.rb
|