9 |
9 |
10 (function($) { |
10 (function($) { |
11 var __ = wp.i18n.__; |
11 var __ = wp.i18n.__; |
12 |
12 |
13 /** |
13 /** |
14 * Contains all the methods to initialise and control the image editor. |
14 * Contains all the methods to initialize and control the image editor. |
15 * |
15 * |
16 * @namespace imageEdit |
16 * @namespace imageEdit |
17 */ |
17 */ |
18 var imageEdit = window.imageEdit = { |
18 var imageEdit = window.imageEdit = { |
19 iasapi : {}, |
19 iasapi : {}, |
20 hold : {}, |
20 hold : {}, |
21 postid : '', |
21 postid : '', |
22 _view : false, |
22 _view : false, |
23 |
23 |
24 /** |
24 /** |
25 * Handle crop tool clicks. |
25 * Enable crop tool. |
26 */ |
26 */ |
27 handleCropToolClick: function( postid, nonce, cropButton ) { |
27 toggleCropTool: function( postid, nonce, cropButton ) { |
28 var img = $( '#image-preview-' + postid ), |
28 var img = $( '#image-preview-' + postid ), |
29 selection = this.iasapi.getSelection(); |
29 selection = this.iasapi.getSelection(); |
30 |
30 |
31 // Ensure selection is available, otherwise reset to full image. |
31 imageEdit.toggleControls( cropButton ); |
32 if ( isNaN( selection.x1 ) ) { |
32 var $el = $( cropButton ); |
33 this.setCropSelection( postid, { 'x1': 0, 'y1': 0, 'x2': img.innerWidth(), 'y2': img.innerHeight(), 'width': img.innerWidth(), 'height': img.innerHeight() } ); |
33 var state = ( $el.attr( 'aria-expanded' ) === 'true' ) ? 'true' : 'false'; |
34 selection = this.iasapi.getSelection(); |
34 // Crop tools have been closed. |
35 } |
35 if ( 'false' === state ) { |
36 |
36 // Cancel selection, but do not unset inputs. |
37 // If we don't already have a selection, select the entire image. |
37 this.iasapi.cancelSelection(); |
38 if ( 0 === selection.x1 && 0 === selection.y1 && 0 === selection.x2 && 0 === selection.y2 ) { |
38 imageEdit.setDisabled($('.imgedit-crop-clear'), 0); |
39 this.iasapi.setSelection( 0, 0, img.innerWidth(), img.innerHeight(), true ); |
|
40 this.iasapi.setOptions( { show: true } ); |
|
41 this.iasapi.update(); |
|
42 } else { |
39 } else { |
43 |
40 imageEdit.setDisabled($('.imgedit-crop-clear'), 1); |
|
41 // Get values from inputs to restore previous selection. |
|
42 var startX = ( $( '#imgedit-start-x-' + postid ).val() ) ? $('#imgedit-start-x-' + postid).val() : 0; |
|
43 var startY = ( $( '#imgedit-start-y-' + postid ).val() ) ? $('#imgedit-start-y-' + postid).val() : 0; |
|
44 var width = ( $( '#imgedit-sel-width-' + postid ).val() ) ? $('#imgedit-sel-width-' + postid).val() : img.innerWidth(); |
|
45 var height = ( $( '#imgedit-sel-height-' + postid ).val() ) ? $('#imgedit-sel-height-' + postid).val() : img.innerHeight(); |
|
46 // Ensure selection is available, otherwise reset to full image. |
|
47 if ( isNaN( selection.x1 ) ) { |
|
48 this.setCropSelection( postid, { 'x1': startX, 'y1': startY, 'x2': width, 'y2': height, 'width': width, 'height': height } ); |
|
49 selection = this.iasapi.getSelection(); |
|
50 } |
|
51 |
|
52 // If we don't already have a selection, select the entire image. |
|
53 if ( 0 === selection.x1 && 0 === selection.y1 && 0 === selection.x2 && 0 === selection.y2 ) { |
|
54 this.iasapi.setSelection( 0, 0, img.innerWidth(), img.innerHeight(), true ); |
|
55 this.iasapi.setOptions( { show: true } ); |
|
56 this.iasapi.update(); |
|
57 } else { |
|
58 this.iasapi.setSelection( startX, startY, width, height, true ); |
|
59 this.iasapi.setOptions( { show: true } ); |
|
60 this.iasapi.update(); |
|
61 } |
|
62 } |
|
63 }, |
|
64 |
|
65 /** |
|
66 * Handle crop tool clicks. |
|
67 */ |
|
68 handleCropToolClick: function( postid, nonce, cropButton ) { |
|
69 |
|
70 if ( cropButton.classList.contains( 'imgedit-crop-clear' ) ) { |
|
71 this.iasapi.cancelSelection(); |
|
72 imageEdit.setDisabled($('.imgedit-crop-apply'), 0); |
|
73 |
|
74 $('#imgedit-sel-width-' + postid).val(''); |
|
75 $('#imgedit-sel-height-' + postid).val(''); |
|
76 $('#imgedit-start-x-' + postid).val('0'); |
|
77 $('#imgedit-start-y-' + postid).val('0'); |
|
78 $('#imgedit-selection-' + postid).val(''); |
|
79 } else { |
44 // Otherwise, perform the crop. |
80 // Otherwise, perform the crop. |
45 imageEdit.crop( postid, nonce , cropButton ); |
81 imageEdit.crop( postid, nonce , cropButton ); |
46 } |
82 } |
47 }, |
83 }, |
48 |
84 |
120 t.hold.xy_ratio = x / y; |
156 t.hold.xy_ratio = x / y; |
121 t.hold.sizer = parseFloat( $('#imgedit-sizer-' + postid).val() ); |
157 t.hold.sizer = parseFloat( $('#imgedit-sizer-' + postid).val() ); |
122 t.postid = postid; |
158 t.postid = postid; |
123 $('#imgedit-response-' + postid).empty(); |
159 $('#imgedit-response-' + postid).empty(); |
124 |
160 |
|
161 $('#imgedit-panel-' + postid).on( 'keypress', function(e) { |
|
162 var nonce = $( '#imgedit-nonce-' + postid ).val(); |
|
163 if ( e.which === 26 && e.ctrlKey ) { |
|
164 imageEdit.undo( postid, nonce ); |
|
165 } |
|
166 |
|
167 if ( e.which === 25 && e.ctrlKey ) { |
|
168 imageEdit.redo( postid, nonce ); |
|
169 } |
|
170 }); |
|
171 |
125 $('#imgedit-panel-' + postid).on( 'keypress', 'input[type="text"]', function(e) { |
172 $('#imgedit-panel-' + postid).on( 'keypress', 'input[type="text"]', function(e) { |
126 var k = e.keyCode; |
173 var k = e.keyCode; |
127 |
174 |
128 // Key codes 37 through 40 are the arrow keys. |
175 // Key codes 37 through 40 are the arrow keys. |
129 if ( 36 < k && k < 41 ) { |
176 if ( 36 < k && k < 41 ) { |
168 } ); |
215 } ); |
169 } |
216 } |
170 }, |
217 }, |
171 |
218 |
172 /** |
219 /** |
|
220 * Shows or hides image menu popup. |
|
221 * |
|
222 * @since 6.3.0 |
|
223 * |
|
224 * @memberof imageEdit |
|
225 * |
|
226 * @param {HTMLElement} el The activated control element. |
|
227 * |
|
228 * @return {boolean} Always returns false. |
|
229 */ |
|
230 togglePopup : function(el) { |
|
231 var $el = $( el ); |
|
232 var $targetEl = $( el ).attr( 'aria-controls' ); |
|
233 var $target = $( '#' + $targetEl ); |
|
234 $el |
|
235 .attr( 'aria-expanded', 'false' === $el.attr( 'aria-expanded' ) ? 'true' : 'false' ); |
|
236 // Open menu and set z-index to appear above image crop area if it is enabled. |
|
237 $target |
|
238 .toggleClass( 'imgedit-popup-menu-open' ).slideToggle( 'fast' ).css( { 'z-index' : 200000 } ); |
|
239 // Move focus to first item in menu when opening menu. |
|
240 if ( 'true' === $el.attr( 'aria-expanded' ) ) { |
|
241 $target.find( 'button' ).first().trigger( 'focus' ); |
|
242 } |
|
243 |
|
244 return false; |
|
245 }, |
|
246 |
|
247 /** |
|
248 * Observes whether the popup should remain open based on focus position. |
|
249 * |
|
250 * @since 6.4.0 |
|
251 * |
|
252 * @memberof imageEdit |
|
253 * |
|
254 * @param {HTMLElement} el The activated control element. |
|
255 * |
|
256 * @return {boolean} Always returns false. |
|
257 */ |
|
258 monitorPopup : function() { |
|
259 var $parent = document.querySelector( '.imgedit-rotate-menu-container' ); |
|
260 var $toggle = document.querySelector( '.imgedit-rotate-menu-container .imgedit-rotate' ); |
|
261 |
|
262 setTimeout( function() { |
|
263 var $focused = document.activeElement; |
|
264 var $contains = $parent.contains( $focused ); |
|
265 |
|
266 // If $focused is defined and not inside the menu container, close the popup. |
|
267 if ( $focused && ! $contains ) { |
|
268 if ( 'true' === $toggle.getAttribute( 'aria-expanded' ) ) { |
|
269 imageEdit.togglePopup( $toggle ); |
|
270 } |
|
271 } |
|
272 }, 100 ); |
|
273 |
|
274 return false; |
|
275 }, |
|
276 |
|
277 /** |
|
278 * Navigate popup menu by arrow keys. |
|
279 * |
|
280 * @since 6.3.0 |
|
281 * |
|
282 * @memberof imageEdit |
|
283 * |
|
284 * @param {HTMLElement} el The current element. |
|
285 * |
|
286 * @return {boolean} Always returns false. |
|
287 */ |
|
288 browsePopup : function(el) { |
|
289 var $el = $( el ); |
|
290 var $collection = $( el ).parent( '.imgedit-popup-menu' ).find( 'button' ); |
|
291 var $index = $collection.index( $el ); |
|
292 var $prev = $index - 1; |
|
293 var $next = $index + 1; |
|
294 var $last = $collection.length; |
|
295 if ( $prev < 0 ) { |
|
296 $prev = $last - 1; |
|
297 } |
|
298 if ( $next === $last ) { |
|
299 $next = 0; |
|
300 } |
|
301 var $target = false; |
|
302 if ( event.keyCode === 40 ) { |
|
303 $target = $collection.get( $next ); |
|
304 } else if ( event.keyCode === 38 ) { |
|
305 $target = $collection.get( $prev ); |
|
306 } |
|
307 if ( $target ) { |
|
308 $target.focus(); |
|
309 event.preventDefault(); |
|
310 } |
|
311 |
|
312 return false; |
|
313 }, |
|
314 |
|
315 /** |
|
316 * Close popup menu and reset focus on feature activation. |
|
317 * |
|
318 * @since 6.3.0 |
|
319 * |
|
320 * @memberof imageEdit |
|
321 * |
|
322 * @param {HTMLElement} el The current element. |
|
323 * |
|
324 * @return {boolean} Always returns false. |
|
325 */ |
|
326 closePopup : function(el) { |
|
327 var $parent = $(el).parent( '.imgedit-popup-menu' ); |
|
328 var $controlledID = $parent.attr( 'id' ); |
|
329 var $target = $( 'button[aria-controls="' + $controlledID + '"]' ); |
|
330 $target |
|
331 .attr( 'aria-expanded', 'false' ).trigger( 'focus' ); |
|
332 $parent |
|
333 .toggleClass( 'imgedit-popup-menu-open' ).slideToggle( 'fast' ); |
|
334 |
|
335 return false; |
|
336 }, |
|
337 |
|
338 /** |
173 * Shows or hides the image edit help box. |
339 * Shows or hides the image edit help box. |
174 * |
340 * |
175 * @since 2.9.0 |
341 * @since 2.9.0 |
176 * |
342 * |
177 * @memberof imageEdit |
343 * @memberof imageEdit |
200 * @memberof imageEdit |
388 * @memberof imageEdit |
201 * |
389 * |
202 * @param {number} postid The post ID. |
390 * @param {number} postid The post ID. |
203 * |
391 * |
204 * @return {string} The value from the imagedit-save-target input field when available, |
392 * @return {string} The value from the imagedit-save-target input field when available, |
205 * or 'full' when not available. |
393 * 'full' when not selected, or 'all' if it doesn't exist. |
206 */ |
394 */ |
207 getTarget : function(postid) { |
395 getTarget : function( postid ) { |
208 return $('input[name="imgedit-target-' + postid + '"]:checked', '#imgedit-save-target-' + postid).val() || 'full'; |
396 var element = $( '#imgedit-save-target-' + postid ); |
|
397 |
|
398 if ( element.length ) { |
|
399 return element.find( 'input[name="imgedit-target-' + postid + '"]:checked' ).val() || 'full'; |
|
400 } |
|
401 |
|
402 return 'all'; |
209 }, |
403 }, |
210 |
404 |
211 /** |
405 /** |
212 * Recalculates the height or width and keeps the original aspect ratio. |
406 * Recalculates the height or width and keeps the original aspect ratio. |
213 * |
407 * |
400 if ( (typeof callback !== 'undefined') && callback !== null ) { |
597 if ( (typeof callback !== 'undefined') && callback !== null ) { |
401 callback(); |
598 callback(); |
402 } |
599 } |
403 |
600 |
404 if ( $('#imgedit-history-' + postid).val() && $('#imgedit-undone-' + postid).val() === '0' ) { |
601 if ( $('#imgedit-history-' + postid).val() && $('#imgedit-undone-' + postid).val() === '0' ) { |
405 $('input.imgedit-submit-btn', '#imgedit-panel-' + postid).prop('disabled', false); |
602 $('button.imgedit-submit-btn', '#imgedit-panel-' + postid).prop('disabled', false); |
406 } else { |
603 } else { |
407 $('input.imgedit-submit-btn', '#imgedit-panel-' + postid).prop('disabled', true); |
604 $('button.imgedit-submit-btn', '#imgedit-panel-' + postid).prop('disabled', true); |
408 } |
605 } |
|
606 var successMessage = __( 'Image updated.' ); |
409 |
607 |
410 t.toggleEditor(postid, 0); |
608 t.toggleEditor(postid, 0); |
|
609 wp.a11y.speak( successMessage, 'assertive' ); |
411 }) |
610 }) |
412 .on( 'error', function() { |
611 .on( 'error', function() { |
413 var errorMessage = __( 'Could not load the preview image. Please reload the page and try again.' ); |
612 var errorMessage = __( 'Could not load the preview image. Please reload the page and try again.' ); |
414 |
613 |
415 $( '#imgedit-crop-' + postid ) |
614 $( '#imgedit-crop-' + postid ) |
433 * @param {string} action The action to perform on the image. |
632 * @param {string} action The action to perform on the image. |
434 * The possible actions are: "scale" and "restore". |
633 * The possible actions are: "scale" and "restore". |
435 * |
634 * |
436 * @return {boolean|void} Executes a post request that refreshes the page |
635 * @return {boolean|void} Executes a post request that refreshes the page |
437 * when the action is performed. |
636 * when the action is performed. |
438 * Returns false if a invalid action is given, |
637 * Returns false if an invalid action is given, |
439 * or when the action cannot be performed. |
638 * or when the action cannot be performed. |
440 */ |
639 */ |
441 action : function(postid, nonce, action) { |
640 action : function(postid, nonce, action) { |
442 var t = this, data, w, h, fw, fh; |
641 var t = this, data, w, h, fw, fh; |
443 |
642 |
821 if ( !c || ( c.width < 3 && c.height < 3 ) ) { |
1029 if ( !c || ( c.width < 3 && c.height < 3 ) ) { |
822 this.setDisabled( $( '.imgedit-crop', '#imgedit-panel-' + postid ), 1 ); |
1030 this.setDisabled( $( '.imgedit-crop', '#imgedit-panel-' + postid ), 1 ); |
823 this.setDisabled( $( '#imgedit-crop-sel-' + postid ), 1 ); |
1031 this.setDisabled( $( '#imgedit-crop-sel-' + postid ), 1 ); |
824 $('#imgedit-sel-width-' + postid).val(''); |
1032 $('#imgedit-sel-width-' + postid).val(''); |
825 $('#imgedit-sel-height-' + postid).val(''); |
1033 $('#imgedit-sel-height-' + postid).val(''); |
|
1034 $('#imgedit-start-x-' + postid).val('0'); |
|
1035 $('#imgedit-start-y-' + postid).val('0'); |
826 $('#imgedit-selection-' + postid).val(''); |
1036 $('#imgedit-selection-' + postid).val(''); |
827 return false; |
1037 return false; |
828 } |
1038 } |
829 |
1039 |
830 sel = { 'x': c.x1, 'y': c.y1, 'w': c.width, 'h': c.height }; |
1040 sel = { 'x': c.x1, 'y': c.y1, 'w': c.width, 'h': c.height }; |
1092 * void when the value is not numeric or when the operation |
1304 * void when the value is not numeric or when the operation |
1093 * is successful. |
1305 * is successful. |
1094 */ |
1306 */ |
1095 setNumSelection : function( postid, el ) { |
1307 setNumSelection : function( postid, el ) { |
1096 var sel, elX = $('#imgedit-sel-width-' + postid), elY = $('#imgedit-sel-height-' + postid), |
1308 var sel, elX = $('#imgedit-sel-width-' + postid), elY = $('#imgedit-sel-height-' + postid), |
|
1309 elX1 = $('#imgedit-start-x-' + postid), elY1 = $('#imgedit-start-y-' + postid), |
|
1310 xS = this.intval( elX1.val() ), yS = this.intval( elY1.val() ), |
1097 x = this.intval( elX.val() ), y = this.intval( elY.val() ), |
1311 x = this.intval( elX.val() ), y = this.intval( elY.val() ), |
1098 img = $('#image-preview-' + postid), imgh = img.height(), imgw = img.width(), |
1312 img = $('#image-preview-' + postid), imgh = img.height(), imgw = img.width(), |
1099 sizer = this.hold.sizer, x1, y1, x2, y2, ias = this.iasapi; |
1313 sizer = this.hold.sizer, x1, y1, x2, y2, ias = this.iasapi; |
1100 |
1314 |
1101 if ( false === this.validateNumeric( el ) ) { |
1315 if ( false === this.validateNumeric( el ) ) { |
1200 if ( sel = this.iasapi.getSelection(true) ) { |
1414 if ( sel = this.iasapi.getSelection(true) ) { |
1201 r = Math.ceil( sel.y1 + ( ( sel.x2 - sel.x1 ) / ( x / y ) ) ); |
1415 r = Math.ceil( sel.y1 + ( ( sel.x2 - sel.x1 ) / ( x / y ) ) ); |
1202 |
1416 |
1203 if ( r > h ) { |
1417 if ( r > h ) { |
1204 r = h; |
1418 r = h; |
|
1419 var errorMessage = __( 'Selected crop ratio exceeds the boundaries of the image. Try a different ratio.' ); |
|
1420 |
|
1421 $( '#imgedit-crop-' + postid ) |
|
1422 .prepend( '<div class="notice notice-error" tabindex="-1" role="alert"><p>' + errorMessage + '</p></div>' ); |
|
1423 |
|
1424 wp.a11y.speak( errorMessage, 'assertive' ); |
1205 if ( n ) { |
1425 if ( n ) { |
1206 $('#imgedit-crop-height-' + postid).val(''); |
1426 $('#imgedit-crop-height-' + postid).val( '' ); |
1207 } else { |
1427 } else { |
1208 $('#imgedit-crop-width-' + postid).val(''); |
1428 $('#imgedit-crop-width-' + postid).val( ''); |
|
1429 } |
|
1430 } else { |
|
1431 var error = $( '#imgedit-crop-' + postid ).find( '.notice-error' ); |
|
1432 if ( 'undefined' !== typeof( error ) ) { |
|
1433 error.remove(); |
1209 } |
1434 } |
1210 } |
1435 } |
1211 |
1436 |
1212 this.iasapi.setSelection( sel.x1, sel.y1, sel.x2, r ); |
1437 this.iasapi.setSelection( sel.x1, sel.y1, sel.x2, r ); |
1213 this.iasapi.update(); |
1438 this.iasapi.update(); |