jcropper 0.1.0

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.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 [name of plugin creator]
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.
@@ -0,0 +1,35 @@
1
+ = Overview
2
+
3
+ jcropper wraps jcrop 0.98, http://deepliquid.com/content/Jcrop.html, a javascript cropping tool.
4
+
5
+ jcropper currently only works with paperclip.
6
+
7
+ = Installation
8
+
9
+ Include the jcropper processor in the paperclip style.
10
+
11
+ class User < ActiveRecord::Base
12
+ has_attached_file :avatar, :styles => { :thumb => "100x100>", :processors => "jcropper" }
13
+ ...
14
+ end
15
+
16
+ Tell jcropper to crop the paperclip attachment
17
+
18
+ jcrop :avatar, :thumb
19
+
20
+ Generate and install your migration
21
+
22
+ script/generate jcropper user avatar thumb
23
+ rake db:migrate
24
+
25
+ Install js/css/images for jcropper
26
+
27
+ rake js_cropper:install
28
+
29
+ Add the helpers to your view:
30
+
31
+ <%= croppable_image @user, :avatar, :thumb -%>
32
+ <%= croppable_image_preview @user, :avatar, :thumb -%>
33
+
34
+
35
+ Copyright (c) 2010 Symbolforce, LLC., released under the MIT license
@@ -0,0 +1,38 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |gemspec|
8
+ gemspec.name = "jcropper"
9
+ gemspec.summary = "gem plugin wrapping jquery "
10
+ gemspec.description = ""
11
+ gemspec.email = "ryan@symbolforce.com"
12
+ gemspec.homepage = "http://github.com/syfo/jcropper"
13
+ gemspec.authors = ["Ryan Ziegler"]
14
+ end
15
+ Jeweler::GemcutterTasks.new
16
+ rescue LoadError
17
+ puts "Jeweler not available. Install it with: gem install jeweler"
18
+ end
19
+
20
+ desc 'Default: run unit tests.'
21
+ task :default => :test
22
+
23
+ desc 'Test the jcropper plugin.'
24
+ Rake::TestTask.new(:test) do |t|
25
+ t.libs << 'lib'
26
+ t.libs << 'test'
27
+ t.pattern = 'test/**/*_test.rb'
28
+ t.verbose = true
29
+ end
30
+
31
+ desc 'Generate documentation for the jcropper plugin.'
32
+ Rake::RDocTask.new(:rdoc) do |rdoc|
33
+ rdoc.rdoc_dir = 'rdoc'
34
+ rdoc.title = 'JCropper'
35
+ rdoc.options << '--line-numbers' << '--inline-source'
36
+ rdoc.rdoc_files.include('README')
37
+ rdoc.rdoc_files.include('lib/**/*.rb')
38
+ end
data/TODO ADDED
@@ -0,0 +1,20 @@
1
+ - make helper a FormHelper
2
+ - support for multiple crops per model
3
+ - support for more than just paperclip
4
+ - decouple to paperclip
5
+ - what others to support?
6
+ - test with latest jquery as well as 1.3.1
7
+ - write tests, of course
8
+ - what should the syntax be for including in a model?
9
+ a) simply be adding a style with :processor => 'jcropper', its added to the model
10
+ b) js_crop :icon, :style => { ... } mirrors paperclip options, and ADDS that style to the preexisting paperclip options
11
+ c) maybe you add a style with whatever processor, then js_crop :ikon, :style_name overrides that style
12
+
13
+ - syntax for view helpers?
14
+ - editable image
15
+ - previewable image?
16
+ - need to be able to turn on/off aspect ratio lock and resize lock
17
+ - is it really necc to have a paperclip processor?
18
+ - use inline css or separate css
19
+ - reset cropping when uploading new image
20
+ - make options[:view_size] work
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,6 @@
1
+ Description:
2
+ Generates a migration for jcropper one class's attachment. x/y/w/h crop variables
3
+
4
+ Example:
5
+ ./script/generate jcropper class attachment
6
+
@@ -0,0 +1,22 @@
1
+ class JcropperGenerator < Rails::Generator::NamedBase
2
+ attr_accessor :attachment, :migration_name
3
+
4
+ def initialize(args, options = {})
5
+ super
6
+ @class_name, @attachment, @style = args[0], args[1], args[2]
7
+ end
8
+
9
+ def manifest
10
+ file_name = generate_file_name
11
+ @migration_name = file_name.camelize
12
+ record do |m|
13
+ m.migration_template "jcropper_migration.rb.erb", File.join('db', 'migrate'), :migration_file_name => file_name
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def generate_file_name
20
+ "add_crop_variables_for_#{@attachment}_style_#{@style}_to_#{@class_name.underscore}"
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ class <%= migration_name %> < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_<%= style %>_crop_x, :integer, :default => 0
4
+ add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_<%= style %>_crop_y, :integer, :default => 0
5
+ add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_<%= style %>_crop_w, :integer, :default => 0
6
+ add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_<%= style %>_crop_h, :integer, :default => 0
7
+ end
8
+
9
+ def self.down
10
+ remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_<%= style %>_crop_x
11
+ remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_<%= style %>_crop_y
12
+ remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_<%= style %>_crop_w
13
+ remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_<%= style %>_crop_h
14
+ end
15
+ end
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,3 @@
1
+ ROOT = File.join(File.dirname(__FILE__), 'jcropper')
2
+ require File.join(ROOT, 'jcropper')
3
+ require File.join(ROOT, 'helpers')
@@ -0,0 +1,137 @@
1
+ module JCropper
2
+ module Helpers
3
+ def croppable_image(object_name, attachment, style, options = {})
4
+ object = eval("@#{object_name.to_s}") unless object_name.is_a? ActiveRecord::Base
5
+ paperclip_options = object.send(attachment).styles[style.to_sym]
6
+ options = options.merge(default_options)
7
+
8
+ x, y, w, h = ['x', 'y', 'w', 'h'].map{|v| "#{attachment}_crop_#{v}"}
9
+
10
+ target_geometry = Paperclip::Geometry.parse(paperclip_options[:geometry])
11
+ file_geometry = Paperclip::Geometry.from_file(object.send(attachment).path(:original))
12
+ options[:view_size] ||= {:width => file_geometry.width, :height => file_geometry.height}
13
+
14
+ resized_ratio = options[:view_size][:width] / file_geometry.width
15
+
16
+ s = "<div class='#{options[:css_prefix]}'>#{image_tag(object.send(attachment).url, options[:view_size])}</div>" + "\n"
17
+ s += hidden_field_tag("#{object_name}[#{x}]", object.send(x), :id => x) + "\n"
18
+ s += hidden_field_tag("#{object_name}[#{y}]", object.send(y), :id => y) + "\n"
19
+ s += hidden_field_tag("#{object_name}[#{w}]", object.send(w), :id => w) + "\n"
20
+ s += hidden_field_tag("#{object_name}[#{h}]", object.send(h), :id => h) + "\n"
21
+ s += <<-CSS
22
+ <style type="text/css">
23
+ /* Fixes issue here http://code.google.com/p/jcrop/issues/detail?id=1 */
24
+ .jcrop-holder { text-align: left; }
25
+
26
+ .jcrop-vline, .jcrop-hline
27
+ {
28
+ font-size: 0;
29
+ position: absolute;
30
+ background: white url('Jcrop.gif') top left repeat;
31
+ }
32
+ .jcrop-vline { height: 100%; width: 1px !important; }
33
+ .jcrop-hline { width: 100%; height: 1px !important; }
34
+ .jcrop-handle {
35
+ font-size: 1px;
36
+ width: 7px !important;
37
+ height: 7px !important;
38
+ border: 1px #eee solid;
39
+ background-color: #333;
40
+ *width: 9px;
41
+ *height: 9px;
42
+ }
43
+
44
+ .jcrop-tracker { width: 100%; height: 100%; }
45
+
46
+ .custom .jcrop-vline,
47
+ .custom .jcrop-hline
48
+ {
49
+ background: yellow;
50
+ }
51
+ .custom .jcrop-handle
52
+ {
53
+ border-color: black;
54
+ background-color: #C7BB00;
55
+ -moz-border-radius: 3px;
56
+ -webkit-border-radius: 3px;
57
+ }
58
+ </style>
59
+ CSS
60
+
61
+ s += <<-HTML
62
+ <script type='text/javascript'>
63
+ function findBoundingScale(img, container) {
64
+ imgAspect = img[0] / img[1]
65
+ containerAspect = container[0] / container[1]
66
+
67
+ if(imgAspect < containerAspect) {
68
+ return (container[0] / img[0]);
69
+ }
70
+ else {
71
+ return (container[1] / img[1]);
72
+ }
73
+ }
74
+
75
+ $('.#{options[:css_prefix]} img').load(function() {
76
+ var trueWidth = #{file_geometry.width};
77
+ var trueHeight = #{file_geometry.height};
78
+
79
+ var targetWidth = #{target_geometry.width};
80
+ var targetHeight = #{target_geometry.height};
81
+
82
+ function cropOnChange(coords) {
83
+ var rx = $('##{options[:css_prefix]}-preview').parent().width() / coords.w;
84
+ var ry = $('##{options[:css_prefix]}-preview').parent().height() / coords.h;
85
+
86
+ $('##{options[:css_prefix]}-preview').css({
87
+ width: Math.round(rx * trueWidth) + 'px',
88
+ height: Math.round(ry * trueHeight) + 'px',
89
+ marginLeft: '-' + Math.round(rx * coords.x) + 'px',
90
+ marginTop: '-' + Math.round(ry * coords.y) + 'px'
91
+ });
92
+
93
+ $('##{x}').val(coords.x);
94
+ $('##{y}').val(coords.y);
95
+ $('##{w}').val(coords.w);
96
+ $('##{h}').val(coords.h);
97
+ console.log(coords);
98
+ }
99
+
100
+ api = $('.#{options[:css_prefix]} img').Jcrop({
101
+ #{options[:jcrop_options].map{|k,v| "#{k.to_s}: #{v.to_s},\n"}}
102
+ setSelect: #{[object.send(x), object.send(y),
103
+ object.send(x) + object.send(w), object.send(y) + object.send(h)].map {|v| v*resized_ratio}.to_json},
104
+ onChange: cropOnChange,
105
+ onSelect: cropOnChange,
106
+ // aspectRatio: targetWidth / targetHeight,
107
+ trueSize: [trueWidth, trueHeight]
108
+ });
109
+ });
110
+ </script>
111
+ HTML
112
+ end
113
+
114
+ def croppable_image_preview(object_name, attachment, style, options = {})
115
+ object = eval("@#{object_name.to_s}") unless object_name.is_a? ActiveRecord::Base
116
+ options = options.merge(default_options)
117
+
118
+ <<-HTML
119
+ <div style="overflow:hidden;height:100px;width:100px;border:1px solid black;">
120
+ #{image_tag(object.send(attachment).url(:original), :id => "#{options[:css_prefix]}-preview")}
121
+ </div>
122
+ HTML
123
+ end
124
+
125
+ private
126
+ def default_options
127
+ {
128
+ :css_prefix => 'js_crop',
129
+ :jcrop_options => {}
130
+ }
131
+ end
132
+ end
133
+ end
134
+
135
+ class ActionView::Base
136
+ include JCropper::Helpers
137
+ end
@@ -0,0 +1,55 @@
1
+ module JCropper
2
+ module ClassMethods
3
+ def jcrop(attachment, style, options = {})
4
+ raise "jcropper requires attachment to be of type Paperclip::Attachment" if self.attachment_definitions[attachment.to_sym].nil?
5
+ require File.join(ROOT, '../paperclip_processors/jcropper.rb')
6
+
7
+ options[:attachment] = attachment = attachment.to_s
8
+ options[:lock_aspect] ||= true
9
+
10
+ x, y, w, h = [:x, :y, :w, :h].map{|coord| jattr(attachment, style, coord) }
11
+
12
+ class_exec(options) do |options|
13
+ write_inheritable_attribute :jcropper_options, options.dup
14
+ class_inheritable_reader :jcropper_options
15
+
16
+ attr_accessor :jcropper_should_reprocess
17
+ before_save :jcropper_check_for_reprocess
18
+ after_save :jcropper_reprocess
19
+
20
+ def jcropper_reprocess
21
+ send(self.class.jcropper_options[:attachment]).reprocess! if @jcropper_should_reprocess
22
+ end
23
+ end
24
+
25
+ to_eval = <<-TO_EVAL
26
+ def jcropper_check_for_reprocess
27
+ @jcropper_should_reprocess ||= !(changed & %w(#{x} #{y} #{w} #{h})).empty?
28
+ return true
29
+ end
30
+
31
+ def jcropper_crop_string
32
+ if not [#{x}, #{y}, #{w}, #{h}].all?{|v| v.nil? or v.zero?}
33
+ "-crop \#{#{w}}x\#{#{h}}+\#{#{x}}+\#{#{y}}"
34
+ else
35
+ ""
36
+ end
37
+ end
38
+ TO_EVAL
39
+ class_eval to_eval
40
+ end
41
+ end
42
+
43
+ private
44
+ def self.jattr(attachment, style, coord)
45
+ "#{attachment}_#{style}_crop_#{coord}"
46
+ end
47
+
48
+ module InstanceMethods
49
+ end
50
+ end
51
+
52
+ class ActiveRecord::Base
53
+ include JCropper::InstanceMethods
54
+ extend JCropper::ClassMethods
55
+ end
@@ -0,0 +1,28 @@
1
+ module Paperclip
2
+ class JCropper < Thumbnail
3
+ def transformation_command
4
+ scale, crop = @current_geometry.transformation_to(@target_geometry, crop?)
5
+ trans = ''
6
+ if crop_string?
7
+ trans << " #{crop_string}"
8
+ trans << " -resize \"#{scale}\""
9
+ else
10
+ trans << " -resize \"#{scale}\""
11
+ trans << " -crop \"#{crop}\" +repage" if crop
12
+ trans.sub!('-crop', '-gravity North -crop') # add north gravity
13
+ trans.sub!(/\d+\+\d+"/, '0+0"') # remove calculated offset
14
+ end
15
+ trans << " #{convert_options}" if convert_options?
16
+ puts trans
17
+ trans
18
+ end
19
+
20
+ def crop_string
21
+ @attachment.instance.jcropper_crop_string
22
+ end
23
+
24
+ def crop_string?
25
+ not crop_string.blank?
26
+ end
27
+ end
28
+ end
Binary file
@@ -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,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('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 @@
1
+ require 'jcropper'
@@ -0,0 +1,9 @@
1
+ namespace :jcropper do
2
+ desc "install files"
3
+ task :install => :environment do
4
+ root = File.join(File.dirname(__FILE__), '..')
5
+ FileUtils.cp(File.join(root, 'public/images/Jcrop.gif'), File.join(Rails.root, 'public/images/Jcrop.gif'))
6
+ FileUtils.cp(File.join(root, 'public/javascripts/jquery.Jcrop.min.js'), File.join(Rails.root, 'public/javascripts/jquery.Jcrop.min.js'))
7
+ FileUtils.cp(File.join(root, 'public/stylesheets/jquery.Jcrop.css'), File.join(Rails.root, 'public/stylesheets/jquery.Jcrop.css'))
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ require 'test_helper'
2
+
3
+ class JCropperTest < ActiveSupport::TestCase
4
+ # Replace this with your real tests.
5
+ test "the truth" do
6
+ assert true
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ require 'rubygems'
2
+ require 'active_support'
3
+ require 'active_support/test_case'
@@ -0,0 +1 @@
1
+ # Uninstall hook code here
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jcropper
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Ryan Ziegler
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-05-13 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: ""
22
+ email: ryan@symbolforce.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - README.rdoc
29
+ - TODO
30
+ files:
31
+ - MIT-LICENSE
32
+ - README.rdoc
33
+ - Rakefile
34
+ - TODO
35
+ - VERSION
36
+ - generators/jcropper/USAGE
37
+ - generators/jcropper/jcropper_generator.rb
38
+ - generators/jcropper/templates/jcropper_migration.rb.erb
39
+ - install.rb
40
+ - lib/jcropper.rb
41
+ - lib/jcropper/helpers.rb
42
+ - lib/jcropper/jcropper.rb
43
+ - lib/paperclip_processors/jcropper.rb
44
+ - public/images/Jcrop.gif
45
+ - public/javascripts/jquery.Jcrop.min.js
46
+ - public/stylesheets/jquery.Jcrop.css
47
+ - rails/init.rb
48
+ - tasks/js_cropper.rake
49
+ - test/jcropper_test.rb
50
+ - test/test_helper.rb
51
+ - uninstall.rb
52
+ has_rdoc: true
53
+ homepage: http://github.com/syfo/jcropper
54
+ licenses: []
55
+
56
+ post_install_message:
57
+ rdoc_options:
58
+ - --charset=UTF-8
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ segments:
66
+ - 0
67
+ version: "0"
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ requirements: []
76
+
77
+ rubyforge_project:
78
+ rubygems_version: 1.3.6
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: gem plugin wrapping jquery
82
+ test_files:
83
+ - test/jcropper_test.rb
84
+ - test/test_helper.rb