alchemy-crop-image 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +15 -0
  6. data/Gemfile.lock +289 -0
  7. data/MIT-LICENSE +20 -0
  8. data/README.md +37 -0
  9. data/Rakefile +32 -0
  10. data/alchemy-crop-image.gemspec +42 -0
  11. data/app/assets/config/alchemy_crop_image_manifest.js +2 -0
  12. data/app/assets/images/alchemy/crop/image/.keep +0 -0
  13. data/app/assets/javascripts/alchemy/crop/image/application.js +4 -0
  14. data/app/assets/javascripts/alchemy/crop/image/cropping.js +277 -0
  15. data/app/assets/javascripts/alchemy/crop/image/routes.js.erb +1 -0
  16. data/app/assets/stylesheets/alchemy/crop/image/application.scss +17 -0
  17. data/app/assets/stylesheets/alchemy/crop/image/cropping.scss +144 -0
  18. data/app/controllers/alchemy/admin/pictures/crops_controller.rb +46 -0
  19. data/app/helpers/alchemy/crop/image/application_helper.rb +8 -0
  20. data/app/jobs/alchemy/crop/image/application_job.rb +8 -0
  21. data/app/mailers/alchemy/crop/image/application_mailer.rb +10 -0
  22. data/app/views/alchemy/admin/pictures/_cropping.html.erb +93 -0
  23. data/app/views/alchemy/admin/pictures/show.html.erb +44 -0
  24. data/app/views/layouts/alchemy/crop/image/application.html.erb +15 -0
  25. data/bin/rails +14 -0
  26. data/config/initializers/assets.rb +1 -0
  27. data/config/locales/en.yml +30 -0
  28. data/config/locales/it.yml +30 -0
  29. data/config/routes.rb +13 -0
  30. data/lib/alchemy/crop/image/configuration.rb +23 -0
  31. data/lib/alchemy/crop/image/engine.rb +13 -0
  32. data/lib/alchemy/crop/image/version.rb +7 -0
  33. data/lib/alchemy/crop/image.rb +9 -0
  34. data/lib/generators/alchemy/crop/image/install_generator.rb +26 -0
  35. data/lib/tasks/alchemy/crop/image_tasks.rake +4 -0
  36. metadata +203 -0
@@ -0,0 +1,277 @@
1
+ $(document).on("page:change turbolinks:load", function () {
2
+
3
+ var cropper = null;
4
+ var cropperData = null;
5
+
6
+ var enableCropperStructure = function () {
7
+ var imageContainer = $(".zoomed-picture-background")[0];
8
+ var picture = $(imageContainer).find("img")[0];
9
+
10
+ if (picture) {
11
+
12
+ //awful trick: disable click on background to prevent closing popup
13
+ $(".zoomed-picture-background").css("pointer-events", "none");
14
+
15
+ var middleBox = $(imageContainer).find(".middle-box-for-cropping")[0]
16
+
17
+ if (!middleBox) {
18
+ var box = "<div class='middle-box-for-cropping'><div class=\"cropperjs-container\"></div></div>"
19
+ $(imageContainer).append(box)
20
+ middleBox = $(box)
21
+ var cropperContainer = $(imageContainer).find(".cropperjs-container")
22
+
23
+ $(cropperContainer).append(picture)
24
+
25
+
26
+ }
27
+
28
+ //awful trick: enable click on cropping element and prevent propagation of click on it
29
+ middleBox = $(imageContainer).find(".middle-box-for-cropping")[0]
30
+ $(middleBox).css("pointer-events", "auto")
31
+ $(middleBox).click(function (e) {
32
+ e.stopPropagation();
33
+ e.preventDefault();
34
+ })
35
+
36
+
37
+ }
38
+
39
+
40
+ }
41
+
42
+ var onCrop = function (e) {
43
+ var roundedWidth = Math.round(e.detail.width)
44
+ var roundedHeight = Math.round(e.detail.height)
45
+ $("#field_width_crop").val(roundedWidth)
46
+ $("#field_height_crop").val(roundedHeight)
47
+ }
48
+
49
+ var enableCropperWithOptions = function (options) {
50
+ var imageContainer = $(".zoomed-picture-background")[0];
51
+ var picture = $(imageContainer).find("img")[0];
52
+
53
+ if (picture) {
54
+ // var cropperData = $(picture).data("cropperjs-data")
55
+ //
56
+ // if(cropperData) {
57
+ // options = $.extend(options,{
58
+ // autoCrop: true,
59
+ // data: cropperData
60
+ // });
61
+ // }
62
+
63
+
64
+ if (cropper) {
65
+ destroyCropper();
66
+ // var currentOptions = cropper.options
67
+ // var cropeprElement = cropper.element
68
+ // disableCropper()
69
+ // var newOptions = $.extend(currentOptions, options)
70
+ // cropper = new Cropper(cropeprElement, newOptions)
71
+ }
72
+
73
+ cropper = new Cropper(picture, options)
74
+
75
+ $(picture).unbind("crop").on("crop", onCrop)
76
+
77
+
78
+ }
79
+
80
+
81
+ }
82
+
83
+ var disableCropper = function () {
84
+ if (cropper) {
85
+ var picture = cropper.element
86
+ $(picture).data("cropperjs-data", cropper.getData())
87
+ cropper.destroy();
88
+ }
89
+ }
90
+
91
+ var destroyCropper = function () {
92
+ if (cropper) {
93
+ cropper.destroy();
94
+ cropper = null;
95
+ }
96
+ //awful trick: enable click on background to prevent closing popup
97
+ $(".zoomed-picture-background").css("pointer-events", "auto");
98
+ }
99
+
100
+
101
+ var showPanelCropping = function () {
102
+ $(".buttons-action-crop").show()
103
+ $(".crop-panel").show()
104
+ $("#enable-crop").hide()
105
+ }
106
+
107
+ var hidePanelCropping = function () {
108
+ $(".buttons-action-crop").hide()
109
+ $(".crop-panel").hide()
110
+ $("#enable-crop").show()
111
+ }
112
+
113
+
114
+ $(document).on("click", "#enable-crop", function () {
115
+ showPanelCropping();
116
+ enableCropperStructure();
117
+ enableCropperWithOptions(
118
+ {
119
+ dragMode: 'none',
120
+ modal: false,
121
+ rotatable: false,
122
+ scalable: false,
123
+ viewMode: 1,
124
+ autoCrop: true
125
+ }
126
+ );
127
+
128
+ // abilito il plugin del cropping
129
+ })
130
+
131
+ $(document).on("click", "#button-cancel-crop", function () {
132
+ hidePanelCropping();
133
+ destroyCropper();
134
+ //disabilito il plugin del cropping
135
+ })
136
+
137
+ $(document).on("click", "#button-save-crop", function () {
138
+
139
+ var imageContainer = $(".zoomed-picture-background")[0];
140
+ var originalPictureId = $(imageContainer).data("displayed-image-id");
141
+
142
+ if (cropper) {
143
+
144
+ //disable button for submit
145
+ $(this).prop("disabled", true)
146
+ $(this).attr("disabled", true)
147
+
148
+ var canvas = cropper.getCroppedCanvas();
149
+ if (canvas) {
150
+ canvas.toBlob(function (blob) {
151
+ var formData = new FormData();
152
+ var imgInfo = cropper.getCropBoxData()
153
+
154
+ formData.append('picture[image_file]', blob);
155
+ formData.append('image_info[width]', imgInfo.width);
156
+ formData.append('image_info[height]', imgInfo.height);
157
+
158
+ // Use `jQuery.ajax` method for example1
159
+ $.ajax(Routes.alchemy_admin_picture_crop_path(originalPictureId), {
160
+ method: 'POST',
161
+ data: formData,
162
+ processData: false,
163
+ contentType: false
164
+ });
165
+ });
166
+ }
167
+ }
168
+
169
+ })
170
+
171
+ $(document).on("click", "#drag-image", function () {
172
+
173
+ if (cropper) {
174
+
175
+ if ($(this).hasClass("active")) {
176
+ cropper.setDragMode("none")
177
+ $(this).removeClass("active")
178
+ } else {
179
+ cropper.setDragMode("move")
180
+ $(this).addClass("active")
181
+ }
182
+ }
183
+
184
+ // if($(this).hasClass("active")) {
185
+ // disableCropper()
186
+ // $(this).removeClass("active")
187
+ // } else {
188
+ // enableCropperWithOptions({
189
+ // dragMode: "move"
190
+ // })
191
+ // $(this).addClass("active")
192
+ // }
193
+
194
+
195
+ })
196
+
197
+ $(document).on("click", "#preview-image", function () {
198
+ if (cropper) {
199
+
200
+ if ($(this).hasClass("active-no-change")) {
201
+
202
+ var imageContainer = $(".zoomed-picture-background")[0];
203
+ var middlebox = $(imageContainer).find(".middle-box-for-cropping")[0];
204
+ var containerCropper = $(middlebox).find(".cropperjs-container")
205
+ var previewBox = $(middlebox).find(".preview-crop");
206
+ var buttonsActions = $(".crop-block .block-command .direct-action");
207
+
208
+ $(previewBox).remove();
209
+ $(containerCropper).show()
210
+
211
+ $(buttonsActions).prop("disabled", false);
212
+ $(buttonsActions).removeAttr("disabled");
213
+
214
+
215
+ $(this).removeClass("active-no-change")
216
+ $(this).html("<span class= \"fa fa-eye\"></span>")
217
+ } else {
218
+
219
+ var imageContainer = $(".zoomed-picture-background")[0];
220
+ var middlebox = $(imageContainer).find(".middle-box-for-cropping")[0];
221
+ var containerCropper = $(middlebox).find(".cropperjs-container");
222
+ var buttonsActions = $(".crop-block .block-command .direct-action");
223
+ $(containerCropper).css("display", "none");
224
+
225
+ var previewBox = $("<div class=\"preview-crop\"></div>");
226
+
227
+ var thisButton = this;
228
+
229
+ cropper.getCroppedCanvas().toBlob(function (blob) {
230
+
231
+ var urlCreator = window.URL || window.webkitURL;
232
+ var imageUrl = urlCreator.createObjectURL(blob);
233
+ $(previewBox).append("<img src='" + imageUrl + "'>");
234
+ $(middlebox).append(previewBox);
235
+
236
+ $(buttonsActions).prop("disabled", true);
237
+ $(buttonsActions).attr("disabled", true);
238
+
239
+ $(thisButton).addClass("active-no-change")
240
+ $(thisButton).html("<span class=\"fa fa-edit\"></span>")
241
+ });
242
+
243
+
244
+ }
245
+
246
+
247
+ }
248
+ })
249
+
250
+ $(document).on("click", "#crop-zoom-in", function () {
251
+ if (cropper) {
252
+ cropper.zoom(0.05)
253
+ }
254
+ })
255
+
256
+ $(document).on("click", "#crop-zoom-out", function () {
257
+ if (cropper) {
258
+ cropper.zoom(-0.05)
259
+ }
260
+ })
261
+
262
+ $(document).on("click", ".cropper-action-preset-ratio", function () {
263
+ if ($(this).hasClass("active")) {
264
+ $(this).removeClass("active");
265
+ } else {
266
+ $(".cropper-action-preset-ratio").removeClass("active");
267
+ var aspectRatioVal = $(this).data("value")
268
+
269
+ if (cropper) {
270
+ cropper.setAspectRatio(aspectRatioVal)
271
+ }
272
+ $(this).addClass("active");
273
+ }
274
+ })
275
+
276
+
277
+ })
@@ -0,0 +1 @@
1
+ Routes = <%= JsRoutes.generate() %>
@@ -0,0 +1,17 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ * require_tree .
14
+ * require_self
15
+ *= require cropperjs/dist/cropper
16
+ *= require ./cropping
17
+ */
@@ -0,0 +1,144 @@
1
+
2
+ @import "alchemy/variables";
3
+ $background_command: #214166;
4
+ $color_command: #ffffff;
5
+
6
+
7
+ .crop-block {
8
+ margin-top: 1rem;
9
+
10
+ #enable-crop, #button-save-crop{
11
+ background-color: $background_command;
12
+ color: $color_command;
13
+ }
14
+
15
+
16
+ .buttons-action-crop {
17
+ display: none;
18
+ }
19
+
20
+ .crop-panel {
21
+ display: none;
22
+ }
23
+
24
+ .block-command {
25
+ margin-top: 0.5rem;
26
+
27
+ .btn-group {
28
+ display: inline-block;
29
+ &.isolated{
30
+ margin-left: 15px;
31
+ }
32
+ }
33
+
34
+ .cropper-action {
35
+ padding: 0.55em 1.5em;
36
+ &.active {
37
+ color: red
38
+ }
39
+ }
40
+ }
41
+
42
+ .block-info {
43
+ margin-top: 0.5rem;
44
+
45
+ .form-group {
46
+ margin-bottom: 0.5rem;
47
+
48
+ label {
49
+ display: inline-block;
50
+ margin-bottom: .5rem;
51
+ cursor: default;
52
+ }
53
+
54
+ .form-control {
55
+ display: block;
56
+ width: 100%;
57
+ padding: .375rem .75rem;
58
+ font-size: 1rem;
59
+ line-height: 1.5;
60
+ color: #495057;
61
+ background-color: #fff;
62
+ background-clip: padding-box;
63
+ border: 1px solid #ced4da;
64
+ border-radius: .25rem;
65
+ transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
66
+ margin: auto;
67
+ }
68
+
69
+ .input-group {
70
+ position: relative;
71
+ display: -webkit-box;
72
+ display: -ms-flexbox;
73
+ display: flex;
74
+ flex-wrap: wrap;
75
+ align-items: stretch;
76
+ width: 100%;
77
+
78
+ .form-control {
79
+ position: relative;
80
+ flex: 1 1 auto;
81
+ width: 1%;
82
+ margin-bottom: 0;
83
+ }
84
+
85
+ .input-group-append {
86
+ margin-left: -1px;
87
+ display: flex;
88
+
89
+ .input-group-text {
90
+ display: flex;
91
+ -webkit-box-align: center;
92
+ -ms-flex-align: center;
93
+ align-items: center;
94
+ padding: .375rem .75rem;
95
+ margin-bottom: 0;
96
+ font-size: 1rem;
97
+ font-weight: 400;
98
+ line-height: 1.5;
99
+ color: #495057;
100
+ text-align: center;
101
+ white-space: nowrap;
102
+ background-color: #e9ecef;
103
+ border: 1px solid #ced4da;
104
+ border-radius: .25rem;
105
+ border-top-left-radius: 0;
106
+ border-bottom-left-radius: 0;
107
+ }
108
+ }
109
+ }
110
+
111
+ }
112
+ }
113
+
114
+ .block-default-crop-size {
115
+ margin-top: 1rem;
116
+
117
+ .suggestion {
118
+ margin: 5px 0px;
119
+ }
120
+
121
+ .cropper-action-preset-ratio {
122
+ &.active {
123
+ color: red;
124
+ }
125
+ }
126
+ }
127
+
128
+
129
+ .button_with_label {
130
+ margin: 0px;
131
+ }
132
+
133
+ }
134
+
135
+ .middle-box-for-cropping {
136
+ display: inline-block;
137
+ height: auto;
138
+ max-width: 100%;
139
+ max-height: 100%;
140
+ box-shadow: 0 0 8px rgba(51, 59, 81, 0.8);
141
+ background-color: #666666;
142
+ vertical-align: middle;
143
+ cursor: default;
144
+ }
@@ -0,0 +1,46 @@
1
+ module Alchemy
2
+ module Admin
3
+ module Pictures
4
+ class CropsController < Alchemy::Admin::BaseController
5
+
6
+ before_action :load_original_picture
7
+
8
+
9
+ def create
10
+ @picture = Picture.new(picture_params)
11
+ authorize! :create, @picture
12
+ if !params[:image_info].nil?
13
+ puts params[:image_info]
14
+ width = params[:image_info][:width]
15
+ height = params[:image_info][:height]
16
+
17
+ @picture.name = "#{@original_picture.name}_#{width}x#{height}"
18
+ else
19
+ @picture.name = "#{@original_picture.name}_#{SecureRandom.hex}"
20
+ end
21
+ @picture.image_file_name = @picture.name
22
+ @picture.tag_list = @original_picture.tag_list + ["#{width}x#{height}"]
23
+ if @picture.save
24
+ flash.notice = Alchemy.t(:cropped_image_succesfully_save, scope: :alchemy_crop_image)
25
+ redirect_to admin_pictures_path(filter: "last_upload")
26
+ else
27
+ flash[:error] = Alchemy.t(:cropped_image_not_saved, scope: :alchemy_crop_image)
28
+ redirect_to admin_pictures_path
29
+ end
30
+ end
31
+
32
+
33
+ private
34
+
35
+ def load_original_picture
36
+ @original_picture = Alchemy::Picture.find params[:picture_id]
37
+ end
38
+
39
+ def picture_params
40
+ params.require(:picture).permit(:image_file)
41
+ end
42
+
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,8 @@
1
+ module Alchemy
2
+ module Crop
3
+ module Image
4
+ module ApplicationHelper
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Alchemy
2
+ module Crop
3
+ module Image
4
+ class ApplicationJob < ActiveJob::Base
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,10 @@
1
+ module Alchemy
2
+ module Crop
3
+ module Image
4
+ class ApplicationMailer < ActionMailer::Base
5
+ default from: 'from@example.com'
6
+ layout 'mailer'
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,93 @@
1
+ <div class="crop-block">
2
+ <div class="block-button">
3
+ <button id="enable-crop">
4
+ <%= Alchemy.t(:enable_cropping, scope: :alchemy_crop_image) %>
5
+ </button>
6
+ <div class="buttons-action-crop">
7
+ <button id="button-cancel-crop">
8
+ <%= Alchemy.t(:cancel, scope: :alchemy_crop_image) %>
9
+ </button>
10
+ <button id="button-save-crop">
11
+ <%= Alchemy.t(:save_crop, scope: :alchemy_crop_image) %>
12
+ </button>
13
+ </div>
14
+ </div>
15
+ <div class="crop-panel">
16
+ <div class="block-command">
17
+ <div class="btn-group">
18
+ <button type="button" id="drag-image" class="cropper-action button_with_label direct-action">
19
+ <span class="docs-tooltip" data-toggle="tooltip" title="" data-original-title="<%= Alchemy.t(:move_image, scope: [:alchemy_crop_image, :cropping_actions]) %>">
20
+ <span class="fa fa-arrows-alt"></span>
21
+ </span>
22
+ <label><%= Alchemy.t(:label_drag_image, scope: [:alchemy_crop_image, :button_labels]) %></label>
23
+
24
+ </button>
25
+ <button id="crop-zoom-in" type="button" class="btn btn-primary button_with_label cropper-action direct-action">
26
+ <span class="docs-tooltip" data-toggle="tooltip" title="" data-original-title="">
27
+ <span class="fa fa-search-plus"></span>
28
+ </span>
29
+ <label><%= Alchemy.t(:label_zoom_in, scope: [:alchemy_crop_image, :button_labels]) %></label>
30
+
31
+ </button>
32
+ <button id="crop-zoom-out" type="button" class="btn btn-primary button_with_label cropper-action direct-action">
33
+ <span class="docs-tooltip" data-toggle="tooltip" title="" data-original-title="">
34
+ <span class="fa fa-search-minus"></span>
35
+ </span>
36
+ <label><%= Alchemy.t(:label_zoom_out, scope: [:alchemy_crop_image, :button_labels]) %></label>
37
+ </button>
38
+ </div>
39
+
40
+
41
+ <div class="btn-group isolated">
42
+ <button type="button" id="preview-image" class="button_with_label cropper-action">
43
+ <span class="docs-tooltip" data-toggle="tooltip" title="" data-original-title="<%= Alchemy.t(:crop_image, scope: [:alchemy_crop_image, :cropping_actions]) %>">
44
+ <span class="fa fa-eye"></span>
45
+ </span>
46
+ <label><%= Alchemy.t(:label_preview_image, scope: [:alchemy_crop_image, :button_labels]) %></label>
47
+ </button>
48
+ </div>
49
+
50
+
51
+ </div>
52
+ <div class="block-info">
53
+
54
+
55
+ <div class="form-group">
56
+ <label><%= Alchemy.t(:width_crop, scope: :alchemy_crop_image) %></label>
57
+ <div class="input-group">
58
+ <input type="text" class="form-control" id="field_width_crop" readonly="true">
59
+ <div class="input-group-append">
60
+ <span class="input-group-text">px</span>
61
+ </div>
62
+ </div>
63
+ </div>
64
+
65
+ <div class="form-group">
66
+ <label><%= Alchemy.t(:height_crop, scope: :alchemy_crop_image) %></label>
67
+ <div class="input-group">
68
+ <input type="text" class="form-control" id="field_height_crop" readonly="true">
69
+ <div class="input-group-append">
70
+ <span class="input-group-text">px</span>
71
+ </div>
72
+ </div>
73
+ </div>
74
+
75
+ </div>
76
+
77
+ <div class="block-default-crop-size">
78
+ <div class="suggestion"><%= Alchemy.t(:you_can_select_a_preset_ratio_for_cropping, scope: :alchemy_crop_image) %></div>
79
+ <div class="btn-group">
80
+ <% Alchemy::Crop::Image::Configuration.available_preset_cropbox.each do |preset| %>
81
+ <button type="button" class="button_with_label cropper-action-preset-ratio" data-value="<%= preset[:value] %>">
82
+ <span class="label-ratio"><%= preset[:label] %></span>
83
+ <label><%= Alchemy.t(:label_crop_preset, scope: [:alchemy_crop_image, :button_labels], preset: preset[:label]) %></label>
84
+ </button>
85
+ <% end %>
86
+ <button type="button" class="button_with_label cropper-action-preset-ratio active" data-value="NaN">
87
+ <span class="label-ratio"><%= Alchemy.t(:preset_crop_ratio_none, scope: :alchemy_crop_image) %></span>
88
+ <label><%= Alchemy.t(:label_crop_preset_none, scope: [:alchemy_crop_image, :button_labels]) %></label>
89
+ </button>
90
+ </div>
91
+ </div>
92
+ </div>
93
+ </div>
@@ -0,0 +1,44 @@
1
+ <div class="zoomed-picture-background" data-displayed-image-id="<%= @picture.id %>">
2
+ <%= image_tag @picture.url(format: @picture.image_file_format) %>
3
+ </div>
4
+
5
+ <div class="picture-overlay-navigation">
6
+ <% if @previous %>
7
+ <%= link_to alchemy.admin_picture_path(
8
+ id: @previous,
9
+ q: search_filter_params[:q],
10
+ page: params[:page],
11
+ tagged_with: search_filter_params[:tagged_with],
12
+ size: params[:size],
13
+ filter: search_filter_params[:filter]
14
+ ),
15
+ class: "previous-picture",
16
+ remote: true do %>
17
+ <i class="icon fas fa-angle-left fa-fw"></i>
18
+ <% end %>
19
+ <% end %>
20
+ <% if @next %>
21
+ <%= link_to alchemy.admin_picture_path(
22
+ id: @next,
23
+ q: search_filter_params[:q],
24
+ page: params[:page],
25
+ tagged_with: search_filter_params[:tagged_with],
26
+ size: params[:size],
27
+ filter: search_filter_params[:filter]
28
+ ),
29
+ class: "next-picture",
30
+ remote: true do %>
31
+ <i class="icon fas fa-angle-right fa-fw"></i>
32
+ <% end %>
33
+ <% end %>
34
+ </div>
35
+
36
+ <div class="picture-details-overlay">
37
+ <%= render 'form' %>
38
+ <%= render 'infos' %>
39
+ <%= render partial: "cropping" %>
40
+ </div>
41
+
42
+ <div class="picture-overlay-handle">
43
+ <i class="icon fas fa-angle-double-right fa-fw"></i>
44
+ </div>
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Alchemy crop image</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= stylesheet_link_tag "alchemy/crop/image/application", media: "all" %>
9
+ </head>
10
+ <body>
11
+
12
+ <%= yield %>
13
+
14
+ </body>
15
+ </html>
data/bin/rails ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails gems
3
+ # installed from the root of your application.
4
+
5
+ ENGINE_ROOT = File.expand_path('..', __dir__)
6
+ ENGINE_PATH = File.expand_path('../lib/alchemy/crop/image/engine', __dir__)
7
+ APP_PATH = File.expand_path('../test/dummy/config/application', __dir__)
8
+
9
+ # Set up gems listed in the Gemfile.
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
11
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
12
+
13
+ require 'rails/all'
14
+ require 'rails/engine/commands'
@@ -0,0 +1 @@
1
+ Alchemy::Crop::Image::Engine.config.assets.paths << Rails.root.join('node_modules')