15392fdcf4c04f98b29c82c4a5ae9e6b517d5956
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Resources / Public / JavaScript / ImageManipulation.js
1 /*
2 * This file is part of the TYPO3 CMS project.
3 *
4 * It is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License, either version 2
6 * of the License, or any later version.
7 *
8 * For the full copyright and license information, please read the
9 * LICENSE.txt file that was distributed with this source code.
10 *
11 * The TYPO3 project - inspiring people to share!
12 */
13
14 /**
15 * contains all logic for the image crop GUI
16 */
17 define('TYPO3/CMS/Backend/ImageManipulation', ['jquery', 'TYPO3/CMS/Backend/Modal'], function ($) {
18
19 var ImageManipulation = {
20 margin: 20,
21 currentModal: null,
22 cropperSelector: '.t3js-cropper-image-container > img',
23 $trigger: null
24 };
25
26
27 /**
28 * Initialize triggers
29 */
30 ImageManipulation.initializeTrigger = function() {
31 var $triggers = $('.t3js-image-manipulation-trigger');
32 // Remove existing bind function
33 $triggers.off('click', ImageManipulation.buttonClick);
34 // Bind new function
35 $triggers.on('click', ImageManipulation.buttonClick);
36 };
37
38 /**
39 * Functions that should be bind to the trigger button
40 *
41 * @param e click event
42 */
43 ImageManipulation.buttonClick = function(e) {
44 e.preventDefault();
45 // Prevent double trigger
46 if (ImageManipulation.$trigger !== $(this)) {
47 ImageManipulation.$trigger = $(this);
48 ImageManipulation.show();
49 }
50 };
51
52 /**
53 * Open modal with image to crop
54 */
55 ImageManipulation.show = function() {
56 ImageManipulation.currentModal = top.TYPO3.Modal.loadUrl(
57 ImageManipulation.$trigger.data('image-name'),
58 TYPO3.Severity.notice,
59 [],
60 ImageManipulation.$trigger.data('url'),
61 ImageManipulation.initializeCropperModal,
62 '.modal-content'
63 );
64 ImageManipulation.currentModal.addClass('modal-dark');
65 };
66
67 /**
68 * Initialize the cropper modal
69 */
70 ImageManipulation.initializeCropperModal = function() {
71 top.require(['cropper', 'imagesloaded'], function(cropperJs, imagesLoaded) {
72 var $image = ImageManipulation.getCropper();
73
74 // wait until image is loaded
75 imagesLoaded($image, function() {
76 var $modal = ImageManipulation.currentModal.find('.modal-dialog');
77 var $modalContent = $modal.find('.modal-content');
78 var $modalPanelSidebar = $modal.find('.modal-panel-sidebar');
79 var $modalPanelBody = $modal.find('.modal-panel-body');
80 // Let modal auto-fill width
81 $modal.css({width:'auto', marginLeft: ImageManipulation.margin, marginRight: ImageManipulation.margin})
82 .addClass('modal-image-manipulation modal-resize');
83
84 $modalContent.addClass('cropper-bg');
85
86 // Determine available height
87 var height = top.TYPO3.jQuery(window).height()
88 - (ImageManipulation.margin * 2);
89 $image.css({maxHeight: height});
90
91 // Wait a few microseconds before calculating available width (DOM isn't always updated direct)
92 setTimeout(function() {
93 $modalPanelBody.css({width: $modalContent.innerWidth() - $modalPanelSidebar.outerWidth() - (ImageManipulation.margin * 2)});
94
95 setTimeout(function() {
96 // Shrink modal when possible (the set left/right margin + width auto above makes it fill 100%)
97 var minWidth = Math.max(500, $image.outerWidth() + $modalPanelSidebar.outerWidth() + (ImageManipulation.margin * 2));
98 var width = $modal.width() > minWidth ? minWidth : $modal.width();
99 $modal.width(width);
100 $modalPanelBody.width(width - $modalPanelSidebar.outerWidth() - (ImageManipulation.margin * 2));
101
102 var modalBodyMinHeight = $modalContent.height() -
103 ($modalPanelSidebar.find('.modal-header').outerHeight() + $modalPanelSidebar.find('.modal-body-footer').outerHeight());
104 $modalPanelSidebar.find('.modal-body').css('min-height', modalBodyMinHeight);
105
106 // Center modal horizontal
107 $modal.css({marginLeft: 'auto', marginRight: 'auto'});
108
109 // Center modal vertical
110 top.TYPO3.Modal.center();
111
112 // Wait a few microseconds to let the modal resize
113 setTimeout(ImageManipulation.initializeCropper, 100);
114 }, 100);
115
116 }, 100);
117 });
118
119 });
120 };
121
122 /**
123 * Initialize cropper
124 */
125 ImageManipulation.initializeCropper = function() {
126 var $image = ImageManipulation.getCropper(), cropData;
127
128 // Give img-container same dimensions as the image
129 ImageManipulation.currentModal.find('.t3js-cropper-image-container').
130 css({width: $image.width(), height: $image.height()});
131
132 var $trigger = ImageManipulation.$trigger;
133 var jsonString = $trigger.parent().find('#' + $trigger.data('field')).val();
134 if (jsonString.length) {
135 cropData = $.parseJSON(jsonString);
136 }
137
138 // Calculate current resize factor
139 var factor = $image.width() / $image.prop('naturalWidth');
140 var $infoX = ImageManipulation.currentModal.find('.t3js-image-manipulation-info-crop-x');
141 var $infoY = ImageManipulation.currentModal.find('.t3js-image-manipulation-info-crop-y');
142 var $infoWidth = ImageManipulation.currentModal.find('.t3js-image-manipulation-info-crop-width');
143 var $infoHeight = ImageManipulation.currentModal.find('.t3js-image-manipulation-info-crop-height');
144
145 $image.cropper({
146 autoCropArea: 0.5,
147 strict: false,
148 zoomable: ImageManipulation.currentModal.find('.t3js-setting-zoom').length > 0,
149 built: function() {
150 if (cropData) {
151 var cropBox = {};
152 cropBox.left = cropData.x * factor;
153 cropBox.top = cropData.y * factor;
154 cropBox.width = cropData.width * factor;
155 cropBox.height = cropData.height * factor;
156 $image.cropper('setCropBoxData', cropBox);
157 }
158 },
159 crop: function (data) {
160 $infoX.text(Math.round(data.x) + 'px');
161 $infoY.text(Math.round(data.y) + 'px');
162 $infoWidth.text(Math.round(data.height) + 'px');
163 $infoHeight.text(Math.round(data.width) + 'px');
164 }
165 });
166
167 // Destroy cropper when modal is closed
168 ImageManipulation.currentModal.on('hidden.bs.modal', function() {
169 $image.cropper('destroy');
170 });
171
172 ImageManipulation.initializeCroppingActions();
173 };
174
175 /**
176 * Get image to be cropped
177 *
178 * @returns jQuery object
179 */
180 ImageManipulation.getCropper = function() {
181 return ImageManipulation.currentModal.find(ImageManipulation.cropperSelector);
182 };
183
184 /**
185 * Bind buttons from cropper tool panel
186 */
187 ImageManipulation.initializeCroppingActions = function() {
188 ImageManipulation.currentModal.find('[data-method]').click(function(e) {
189 e.preventDefault();
190 var method = $(this).data('method');
191 var options = $(this).data('option') || {};
192 if (typeof ImageManipulation[method] === 'function') {
193 ImageManipulation[method](options);
194 }
195 });
196 };
197
198 /**
199 * Change the aspect ratio of the crop box
200 *
201 * @param aspectRatio Number
202 */
203 ImageManipulation.setAspectRatio = function(aspectRatio) {
204 var $cropper = ImageManipulation.getCropper();
205 $cropper.cropper('setAspectRatio', aspectRatio);
206 };
207
208 /**
209 * Set zoom ratio
210 *
211 * Zoom in: requires a positive number (ratio > 0)
212 * Zoom out: requires a negative number (ratio < 0)
213 *
214 * @param ratio Number
215 */
216 ImageManipulation.zoom = function(ratio) {
217 var $cropper = ImageManipulation.getCropper();
218 $cropper.cropper('zoom', ratio);
219 };
220
221 /**
222 * Save crop values in form and close modal
223 */
224 ImageManipulation.save = function() {
225 var $image = ImageManipulation.getCropper();
226 var $trigger = ImageManipulation.$trigger;
227 var formFieldId = $trigger.data('field');
228 var $formField = $trigger.parent().find('#' + formFieldId);
229 var $formGroup = $formField.closest('.form-group');
230 var cropData = $image.cropper('getData');
231 var newValue = '';
232 $formGroup.addClass('has-change');
233 if (cropData.width > 0 && cropData.height > 0) {
234 newValue = JSON.stringify(cropData);
235 $formGroup.find('.t3js-image-manipulation-info').removeClass('hide');
236 $formGroup.find('.t3js-image-manipulation-info-crop-x').text(Math.round(cropData.x) + 'px');
237 $formGroup.find('.t3js-image-manipulation-info-crop-y').text(Math.round(cropData.y) + 'px');
238 $formGroup.find('.t3js-image-manipulation-info-crop-width').text(Math.round(cropData.width) + 'px');
239 $formGroup.find('.t3js-image-manipulation-info-crop-height').text(Math.round(cropData.height) + 'px');
240
241 } else {
242 $formGroup.find('.t3js-image-manipulation-info').addClass('hide');
243 $formGroup.find('.t3js-image-manipulation-preview').addClass('hide');
244 }
245 $formField.val(newValue);
246 ImageManipulation.dismiss();
247 };
248
249 /**
250 * Reset crop selection
251 */
252 ImageManipulation.reset = function() {
253 var $image = ImageManipulation.getCropper();
254 $image.cropper('clear');
255 };
256
257 /**
258 * Close the current open modal
259 */
260 ImageManipulation.dismiss = function() {
261 if (ImageManipulation.currentModal) {
262 ImageManipulation.currentModal.modal('hide').remove();
263 ImageManipulation.currentModal = null;
264 }
265 };
266
267 return function() {
268 TYPO3.ImageManipulation = ImageManipulation;
269 return ImageManipulation;
270 }();
271 });