alchemy-crop-image 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.
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')