rails_admin_jcrop 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/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
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.markdown ADDED
@@ -0,0 +1,82 @@
1
+ # RailsAdmin Jcrop Plugin
2
+
3
+ ## Image cropping made easy! ##
4
+
5
+ 1. Add it to your Gemfile and run `bundle install`:
6
+
7
+ gem 'rails_admin'
8
+ gem 'rails_admin_jcrop' #, git: 'git://github.com/janx/rails_admin_jcrop.git'
9
+
10
+ 2. Configure your model field to use Jcrop:
11
+
12
+ # RAILS_ROOT/config/initializers/rails_admin.rb
13
+ config.model User do
14
+ configure :avatar, :jcrop
15
+
16
+ # Below is optional
17
+ edit do
18
+ field :avatar do
19
+ # use jcrop_options to pass any options you like to .Jcrop call in javascript
20
+ jcrop_options aspectRatio: 500.0/320.0
21
+ end
22
+ end
23
+ end
24
+
25
+ 3. Call :rails_admin_crop in your uploader:
26
+
27
+ class AvatarUploader < CarrierWave::Uploader::Base
28
+
29
+ version :thumb do
30
+ process :rails_admin_crop
31
+ process :resize_to_fill: [500,320]
32
+ end
33
+
34
+ end
35
+
36
+ 4. Done! Click the image on your RailsAdmin model edit page and enjoy cropping!
37
+ ![Cropping Screenshot](https://github.com/janx/rails_admin_jcrop/raw/master/screenshot.png)
38
+
39
+ ## Localization ##
40
+
41
+ Localize the crop form by adding these entries:
42
+
43
+ zh:
44
+ admin:
45
+ actions:
46
+ crop:
47
+ title: '剪裁'
48
+ menu: '剪裁'
49
+
50
+ ## Dependencies ##
51
+
52
+ * MRI 1.9.3 (All above 1.8.6 should work, I only tested on 1.9.3)
53
+ * Rails 3.x
54
+ * ORM
55
+ - ActiveRecord
56
+ * Upload plugin
57
+ - CarrierWave
58
+ * Image processor
59
+ - MiniMagick
60
+
61
+ ## TODO ##
62
+
63
+ * MongoDB support
64
+ * Paperclip support
65
+ * RMagick support
66
+
67
+ ## Contributing ##
68
+
69
+ Any help is encouraged. Here are some ways you can contribute:
70
+
71
+ * by using it
72
+ * by telling people this plugin
73
+ * by reporting bugs or suggesting new features on github issue tracker
74
+ * by fixing bugs or implementing features
75
+ * by giving author a hug (especially if you're girl)
76
+
77
+ ## Thanks ##
78
+
79
+ Life is easier with you.
80
+
81
+ * [RailsAdmin](https://github.com/sferik/rails_admin/)
82
+ * [Jcrop](http://deepliquid.com/content/Jcrop.html)
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'RailsAdminJcrop'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+
24
+
25
+
26
+ Bundler::GemHelper.install_tasks
27
+
@@ -0,0 +1,141 @@
1
+ (function($) {
2
+ $.widget("ra.jcropForm", {
3
+
4
+ _create: function() {
5
+ var widget = this;
6
+ var dom_widget = widget.element;
7
+
8
+ dom_widget.find('a.thumbnail').unbind().bind("click", function(e){
9
+ widget._bindModalOpening(e, dom_widget.find('a.jcrop_handle').data('link'));
10
+ return false;
11
+ });
12
+ },
13
+
14
+ _bindModalOpening: function(e, url) {
15
+ e.preventDefault();
16
+ widget = this;
17
+ if($("#modal").length)
18
+ return false;
19
+
20
+ var dialog = this._getModal();
21
+
22
+ setTimeout(function(){ // fix race condition with modal insertion in the dom (Chrome => Team/add a new fan => #modal not found when it should have). Somehow .on('show') is too early, tried it too.
23
+ $.ajax({
24
+ url: url,
25
+ beforeSend: function(xhr) {
26
+ xhr.setRequestHeader("Accept", "text/javascript");
27
+ },
28
+ success: function(data, status, xhr) {
29
+ dialog.find('.modal-body').html(data);
30
+ widget._bindFormEvents();
31
+ },
32
+ error: function(xhr, status, error) {
33
+ dialog.find('.modal-body').html(xhr.responseText);
34
+ },
35
+ dataType: 'text'
36
+ });
37
+ },100);
38
+
39
+ },
40
+
41
+ _bindFormEvents: function() {
42
+ var widget = this,
43
+ dialog = this._getModal(),
44
+ form = dialog.find("form"),
45
+ saveButtonText = dialog.find(":submit[name=_save]").html(),
46
+ cancelButtonText = dialog.find(":submit[name=_continue]").html();
47
+ dialog.find('.form-actions').remove();
48
+
49
+ var jcrop_options = $.extend({
50
+ bgColor: 'white',
51
+ keySupport: false,
52
+ onSelect: widget.updateCoordinates
53
+ }, rails_admin_jcrop_options);
54
+ dialog.find('img.jcrop-subject').Jcrop(jcrop_options)
55
+
56
+ form.attr("data-remote", true);
57
+ dialog.find('.modal-header-title').text(form.data('title'));
58
+ dialog.find('.cancel-action').unbind().click(function(){
59
+ dialog.modal('hide');
60
+ return false;
61
+ }).html(cancelButtonText);
62
+
63
+ dialog.find('.save-action').unbind().click(function(){
64
+ form.submit();
65
+ return false;
66
+ }).html(saveButtonText);
67
+
68
+ $(document).trigger('rails_admin.dom_ready');
69
+
70
+ form.bind("ajax:complete", function(xhr, data, status) {
71
+ if (status == 'error') {
72
+ dialog.find('.modal-body').html(data.responseText);
73
+ widget._bindFormEvents();
74
+ } else {
75
+ var json = $.parseJSON(data.responseText);
76
+ var select = widget.element.find('select').filter(":hidden");
77
+
78
+ thumb = widget.element.find('a.jcrop_handle').data('thumb');
79
+ widget.element.find('a.thumbnail > img').removeAttr('src').attr('src', json.urls[thumb] + '?' + new Date().valueOf());
80
+
81
+ widget._trigger("success");
82
+ dialog.modal("hide");
83
+ }
84
+ });
85
+ },
86
+
87
+ updateCoordinates: function(c) {
88
+ var rx = 100/c.w;
89
+ var ry = 100/c.h;
90
+ var lw = $('img.jcrop-subject').width();
91
+ var lh = $('img.jcrop-subject').height();
92
+ var ratio = $('img.jcrop-subject').data('geometry').split(',')[0] / lw ;
93
+
94
+ $('#preview').css({
95
+ width: Math.round(rx * lw) + 'px',
96
+ height: Math.round(ry * lh) + 'px',
97
+ marginLeft: '-' + Math.round(rx * c.x) + 'px',
98
+ marginTop: '-' + Math.round(ry * c.y) + 'px'
99
+ });
100
+
101
+ $("#crop_x").val(Math.round(c.x * ratio));
102
+ $("#crop_y").val(Math.round(c.y * ratio));
103
+ $("#crop_w").val(Math.round(c.w * ratio));
104
+ $("#crop_h").val(Math.round(c.h * ratio));
105
+ },
106
+
107
+ _getModal: function() {
108
+ var widget = this;
109
+ if (!widget.dialog) {
110
+ widget.dialog = $('\
111
+ <div id="modal" class="modal fade">\
112
+ <div class="modal-header">\
113
+ <a href="#" class="close" data-dismiss="modal">&times;</a>\
114
+ <h3 class="modal-header-title">...</h3>\
115
+ </div>\
116
+ <div class="modal-body">\
117
+ ...\
118
+ </div>\
119
+ <div class="modal-footer">\
120
+ <a href="#" class="btn cancel-action">...</a>\
121
+ <a href="#" class="btn btn-primary save-action">...</a>\
122
+ </div>\
123
+ </div>')
124
+ .modal({
125
+ keyboard: true,
126
+ backdrop: true,
127
+ show: true
128
+ })
129
+ .on('hidden', function(){
130
+ widget.dialog.remove(); // We don't want to reuse closed modals
131
+ widget.dialog = null;
132
+ });
133
+ }
134
+ return this.dialog;
135
+ }
136
+ });
137
+ })(jQuery);
138
+
139
+ $(function() {
140
+ $('div.jcrop_type').jcropForm();
141
+ });
@@ -0,0 +1,52 @@
1
+ module RailsAdmin
2
+
3
+ class JcropController < RailsAdmin::ApplicationController
4
+ skip_before_filter :get_model
5
+ before_filter :get_model, :get_object, :get_field
6
+
7
+ helper_method :abstract_model, :geometry
8
+
9
+ def edit
10
+ respond_to do |format|
11
+ format.html
12
+ format.js { render :edit, :layout => false }
13
+ end
14
+ end
15
+
16
+ def update
17
+ @object.rails_admin_crop! params
18
+
19
+ respond_to do |format|
20
+ format.html { redirect_to_on_success }
21
+ format.js do
22
+ asset = @object.send @field
23
+ urls = {:original => asset.url}
24
+ @object.class.uploaders[@field.to_sym].versions.each {|name, _| urls[name] = asset.url(name)}
25
+
26
+ render :json => {
27
+ :id => @object.id,
28
+ :label => @model_config.with(:object => @object).object_label,
29
+ :field => @field,
30
+ :urls => urls
31
+ }
32
+ end
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def get_field
39
+ @field = params[:field]
40
+ end
41
+
42
+ def geometry(image_path)
43
+ image = MiniMagick::Image.open(image_path)
44
+ [image[:width], image[:height]]
45
+ end
46
+
47
+ def cropping?
48
+ [:crop_x, :crop_y, :crop_w, :crop_h].all? {|c| params[c].present?}
49
+ end
50
+ end
51
+
52
+ end
@@ -0,0 +1,8 @@
1
+ = form_tag nil, :method => :put, 'data-title' => "#{I18n.t('admin.actions.crop.menu').capitalize} #{abstract_model.model.human_attribute_name @field}" do
2
+ = image_tag @object.send(@field).url, :class => 'jcrop-subject', 'data-geometry' => geometry(@object.send(@field).path).join(',')
3
+ = hidden_field_tag :crop_x
4
+ = hidden_field_tag :crop_y
5
+ = hidden_field_tag :crop_w
6
+ = hidden_field_tag :crop_h
7
+ = hidden_field_tag :crop_field, @field
8
+ = render :partial => 'rails_admin/main/submit_buttons'
@@ -0,0 +1,21 @@
1
+ :javascript
2
+ var rails_admin_jcrop_options = #{field.jcrop_options.to_json};
3
+
4
+ = stylesheet_link_tag 'rails_admin/jquery.Jcrop'
5
+ = javascript_include_tag 'rails_admin/jquery.Jcrop'
6
+ = javascript_include_tag 'rails_admin/ra.jcrop'
7
+
8
+ - file = form.object.send(field.method_name).presence
9
+
10
+ .toggle{:style => ('display:none;' if file && field.delete_method && form.object.send(field.delete_method) == '1')}
11
+ - if value = field.pretty_value
12
+ = value
13
+ = form.file_field(field.name, field.html_attributes.reverse_merge({ :data => { :fileupload => true } }))
14
+ = link_to 'Edit Image', '#', :data => {:link => jcrop_path(:model_name => form.object.class.to_param, :modal => true, :id => form.object.id, :field => field.name), :thumb => field.thumb_method}, :style => 'display:none', :class => "jcrop_handle"
15
+ - if field.optional? && field.errors.blank? && file && field.delete_method
16
+ %a.btn.btn-info{:href => '#', :'data-toggle' => 'button', :onclick => "$(this).siblings('[type=checkbox]').click(); $(this).siblings('.toggle').toggle('slow'); jQuery(this).toggleClass('btn-danger btn-info'); return false"}
17
+ %i.icon-white.icon-trash
18
+ = I18n.t('admin.actions.delete.menu').capitalize + " #{field.method_name}"
19
+ = form.check_box(field.delete_method, :style => 'display:none;' )
20
+ - if field.cache_method
21
+ = form.hidden_field(field.cache_method)
@@ -0,0 +1,7 @@
1
+ en:
2
+ admin:
3
+ actions:
4
+ crop:
5
+ title: Crop
6
+ menu: Crop
7
+ breadcrumb: Crop
data/config/routes.rb ADDED
@@ -0,0 +1,10 @@
1
+ RailsAdmin::Engine.routes.draw do
2
+ controller "main" do
3
+ scope ":model_name" do
4
+ scope "(:id)/:field" do
5
+ get "/jcrop", :to => 'jcrop#edit', :as => :jcrop
6
+ put "/jcrop", :to => 'jcrop#update'
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ module RailsAdminJcrop
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace RailsAdmin
4
+
5
+ initializer "RailsAdminJcrop precompile hook" do |app|
6
+ app.config.assets.precompile += ['rails_admin/ra.jcrop.js', 'rails_admin/jquery.Jcrop.js', 'rails_admin/jquery.Jcrop.css']
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module CarrierWave
2
+ module Mount
3
+ module Extension
4
+ def self.included(base)
5
+ base.send :include, RailsAdminJcrop::Orm::ActiveRecord
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,50 @@
1
+ require 'rails_admin/config/actions'
2
+ require 'rails_admin/config/actions/base'
3
+ require 'rails_admin/config/fields'
4
+ require 'rails_admin/config/fields/types/file_upload'
5
+
6
+ module RailsAdmin
7
+ module Config
8
+ module Fields
9
+ module Types
10
+ class Jcrop < RailsAdmin::Config::Fields::Types::FileUpload
11
+ RailsAdmin::Config::Fields::Types::register(self)
12
+
13
+ register_instance_option(:partial) do
14
+ :form_jcrop
15
+ end
16
+
17
+ register_instance_option(:thumb_method) do
18
+ @thumb_method ||= ((versions = bindings[:object].send(name).versions.keys).find{|k| k.in?([:thumb, :thumbnail, 'thumb', 'thumbnail'])} || versions.first.to_s)
19
+ end
20
+
21
+ register_instance_option(:delete_method) do
22
+ "remove_#{name}"
23
+ end
24
+
25
+ register_instance_option(:cache_method) do
26
+ "#{name}_cache"
27
+ end
28
+
29
+ register_instance_option(:jcrop_options) do
30
+ {}
31
+ end
32
+
33
+ def resource_url(thumb = false)
34
+ return nil unless (uploader = bindings[:object].send(name)).present?
35
+ thumb.present? ? uploader.send(thumb).url : uploader.url
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ RailsAdmin::Config::Fields.register_factory do |parent, properties, fields|
44
+ if properties[:name] == :jcrop
45
+ fields << RailsAdmin::Config::Fields::Types::Jcrop.new(parent, properties[:name], properties)
46
+ true
47
+ else
48
+ false
49
+ end
50
+ end