1 /* global imageEditL10n, ajaxurl, confirm */ |
1 /* global imageEditL10n, ajaxurl, confirm */ |
|
2 /** |
|
3 * @summary The functions necessary for editing images. |
|
4 * |
|
5 * @since 2.9.0 |
|
6 */ |
2 |
7 |
3 (function($) { |
8 (function($) { |
4 var imageEdit = window.imageEdit = { |
9 |
|
10 /** |
|
11 * Contains all the methods to initialise and control the image editor. |
|
12 * |
|
13 * @namespace imageEdit |
|
14 */ |
|
15 var imageEdit = window.imageEdit = { |
5 iasapi : {}, |
16 iasapi : {}, |
6 hold : {}, |
17 hold : {}, |
7 postid : '', |
18 postid : '', |
8 _view : false, |
19 _view : false, |
9 |
20 |
|
21 /** |
|
22 * @summary Converts a value to an integer. |
|
23 * |
|
24 * @memberof imageEdit |
|
25 * @since 2.9.0 |
|
26 * |
|
27 * @param {number} f The float value that should be converted. |
|
28 * |
|
29 * @return {number} The integer representation from the float value. |
|
30 */ |
10 intval : function(f) { |
31 intval : function(f) { |
|
32 /* |
|
33 * Bitwise OR operator: one of the obscure ways to truncate floating point figures, |
|
34 * worth reminding JavaScript doesn't have a distinct "integer" type. |
|
35 */ |
11 return f | 0; |
36 return f | 0; |
12 }, |
37 }, |
13 |
38 |
14 setDisabled : function(el, s) { |
39 /** |
|
40 * @summary Adds the disabled attribute and class to a single form element |
|
41 * or a field set. |
|
42 * |
|
43 * @memberof imageEdit |
|
44 * @since 2.9.0 |
|
45 * |
|
46 * @param {jQuery} el The element that should be modified. |
|
47 * @param {bool|number} s The state for the element. If set to true |
|
48 * the element is disabled, |
|
49 * otherwise the element is enabled. |
|
50 * The function is sometimes called with a 0 or 1 |
|
51 * instead of true or false. |
|
52 * |
|
53 * @returns {void} |
|
54 */ |
|
55 setDisabled : function( el, s ) { |
|
56 /* |
|
57 * `el` can be a single form element or a fieldset. Before #28864, the disabled state on |
|
58 * some text fields was handled targeting $('input', el). Now we need to handle the |
|
59 * disabled state on buttons too so we can just target `el` regardless if it's a single |
|
60 * element or a fieldset because when a fieldset is disabled, its descendants are disabled too. |
|
61 */ |
15 if ( s ) { |
62 if ( s ) { |
16 el.removeClass('disabled'); |
63 el.removeClass( 'disabled' ).prop( 'disabled', false ); |
17 $('input', el).removeAttr('disabled'); |
|
18 } else { |
64 } else { |
19 el.addClass('disabled'); |
65 el.addClass( 'disabled' ).prop( 'disabled', true ); |
20 $('input', el).prop('disabled', true); |
66 } |
21 } |
67 }, |
22 }, |
68 |
23 |
69 /** |
|
70 * @summary Initializes the image editor. |
|
71 * |
|
72 * @memberof imageEdit |
|
73 * @since 2.9.0 |
|
74 * |
|
75 * @param {number} postid The post id. |
|
76 * |
|
77 * @returns {void} |
|
78 */ |
24 init : function(postid) { |
79 init : function(postid) { |
25 var t = this, old = $('#image-editor-' + t.postid), |
80 var t = this, old = $('#image-editor-' + t.postid), |
26 x = t.intval( $('#imgedit-x-' + postid).val() ), |
81 x = t.intval( $('#imgedit-x-' + postid).val() ), |
27 y = t.intval( $('#imgedit-y-' + postid).val() ); |
82 y = t.intval( $('#imgedit-y-' + postid).val() ); |
28 |
83 |
38 $('#imgedit-response-' + postid).empty(); |
93 $('#imgedit-response-' + postid).empty(); |
39 |
94 |
40 $('input[type="text"]', '#imgedit-panel-' + postid).keypress(function(e) { |
95 $('input[type="text"]', '#imgedit-panel-' + postid).keypress(function(e) { |
41 var k = e.keyCode; |
96 var k = e.keyCode; |
42 |
97 |
|
98 // Key codes 37 thru 40 are the arrow keys. |
43 if ( 36 < k && k < 41 ) { |
99 if ( 36 < k && k < 41 ) { |
44 $(this).blur(); |
100 $(this).blur(); |
45 } |
101 } |
46 |
102 |
|
103 // The key code 13 is the enter key. |
47 if ( 13 === k ) { |
104 if ( 13 === k ) { |
48 e.preventDefault(); |
105 e.preventDefault(); |
49 e.stopPropagation(); |
106 e.stopPropagation(); |
50 return false; |
107 return false; |
51 } |
108 } |
52 }); |
109 }); |
53 }, |
110 }, |
54 |
111 |
|
112 /** |
|
113 * @summary Toggles the wait/load icon in the editor. |
|
114 * |
|
115 * @memberof imageEdit |
|
116 * @since 2.9.0 |
|
117 * |
|
118 * @param {number} postid The post id. |
|
119 * @param {number} toggle Is 0 or 1, fades the icon in then 1 and out when 0. |
|
120 * |
|
121 * @returns {void} |
|
122 */ |
55 toggleEditor : function(postid, toggle) { |
123 toggleEditor : function(postid, toggle) { |
56 var wait = $('#imgedit-wait-' + postid); |
124 var wait = $('#imgedit-wait-' + postid); |
57 |
125 |
58 if ( toggle ) { |
126 if ( toggle ) { |
59 wait.height( $('#imgedit-panel-' + postid).height() ).fadeIn('fast'); |
127 wait.fadeIn( 'fast' ); |
60 } else { |
128 } else { |
61 wait.fadeOut('fast'); |
129 wait.fadeOut('fast'); |
62 } |
130 } |
63 }, |
131 }, |
64 |
132 |
|
133 /** |
|
134 * @summary Shows or hides the image edit help box. |
|
135 * |
|
136 * @memberof imageEdit |
|
137 * @since 2.9.0 |
|
138 * |
|
139 * @param {HTMLElement} el The element to create the help window in. |
|
140 * |
|
141 * @returns {boolean} Always returns false. |
|
142 */ |
65 toggleHelp : function(el) { |
143 toggleHelp : function(el) { |
66 $( el ).parents( '.imgedit-group-top' ).toggleClass( 'imgedit-help-toggled' ).find( '.imgedit-help' ).slideToggle( 'fast' ); |
144 var $el = $( el ); |
|
145 $el |
|
146 .attr( 'aria-expanded', 'false' === $el.attr( 'aria-expanded' ) ? 'true' : 'false' ) |
|
147 .parents( '.imgedit-group-top' ).toggleClass( 'imgedit-help-toggled' ).find( '.imgedit-help' ).slideToggle( 'fast' ); |
|
148 |
67 return false; |
149 return false; |
68 }, |
150 }, |
69 |
151 |
|
152 /** |
|
153 * @summary Gets the value from the image edit target. |
|
154 * |
|
155 * The image edit target contains the image sizes where the (possible) changes |
|
156 * have to be applied to. |
|
157 * |
|
158 * @memberof imageEdit |
|
159 * @since 2.9.0 |
|
160 * |
|
161 * @param {number} postid The post id. |
|
162 * |
|
163 * @returns {string} The value from the imagedit-save-target input field when available, |
|
164 * or 'full' when not available. |
|
165 */ |
70 getTarget : function(postid) { |
166 getTarget : function(postid) { |
71 return $('input[name="imgedit-target-' + postid + '"]:checked', '#imgedit-save-target-' + postid).val() || 'full'; |
167 return $('input[name="imgedit-target-' + postid + '"]:checked', '#imgedit-save-target-' + postid).val() || 'full'; |
72 }, |
168 }, |
73 |
169 |
74 scaleChanged : function(postid, x) { |
170 /** |
|
171 * @summary Recalculates the height or width and keeps the original aspect ratio. |
|
172 * |
|
173 * If the original image size is exceeded a red exclamation mark is shown. |
|
174 * |
|
175 * @memberof imageEdit |
|
176 * @since 2.9.0 |
|
177 * |
|
178 * @param {number} postid The current post id. |
|
179 * @param {number} x Is 0 when it applies the y-axis |
|
180 * and 1 when applicable for the x-axis. |
|
181 * @param {jQuery} el Element. |
|
182 * |
|
183 * @returns {void} |
|
184 */ |
|
185 scaleChanged : function( postid, x, el ) { |
75 var w = $('#imgedit-scale-width-' + postid), h = $('#imgedit-scale-height-' + postid), |
186 var w = $('#imgedit-scale-width-' + postid), h = $('#imgedit-scale-height-' + postid), |
76 warn = $('#imgedit-scale-warn-' + postid), w1 = '', h1 = ''; |
187 warn = $('#imgedit-scale-warn-' + postid), w1 = '', h1 = ''; |
|
188 |
|
189 if ( false === this.validateNumeric( el ) ) { |
|
190 return; |
|
191 } |
77 |
192 |
78 if ( x ) { |
193 if ( x ) { |
79 h1 = ( w.val() !== '' ) ? Math.round( w.val() / this.hold.xy_ratio ) : ''; |
194 h1 = ( w.val() !== '' ) ? Math.round( w.val() / this.hold.xy_ratio ) : ''; |
80 h.val( h1 ); |
195 h.val( h1 ); |
81 } else { |
196 } else { |
88 } else { |
203 } else { |
89 warn.css('visibility', 'hidden'); |
204 warn.css('visibility', 'hidden'); |
90 } |
205 } |
91 }, |
206 }, |
92 |
207 |
|
208 /** |
|
209 * @summary Gets the selected aspect ratio. |
|
210 * |
|
211 * @memberof imageEdit |
|
212 * @since 2.9.0 |
|
213 * |
|
214 * @param {number} postid The post id. |
|
215 * |
|
216 * @returns {string} The aspect ratio. |
|
217 */ |
93 getSelRatio : function(postid) { |
218 getSelRatio : function(postid) { |
94 var x = this.hold.w, y = this.hold.h, |
219 var x = this.hold.w, y = this.hold.h, |
95 X = this.intval( $('#imgedit-crop-width-' + postid).val() ), |
220 X = this.intval( $('#imgedit-crop-width-' + postid).val() ), |
96 Y = this.intval( $('#imgedit-crop-height-' + postid).val() ); |
221 Y = this.intval( $('#imgedit-crop-height-' + postid).val() ); |
97 |
222 |
104 } |
229 } |
105 |
230 |
106 return '1:1'; |
231 return '1:1'; |
107 }, |
232 }, |
108 |
233 |
|
234 /** |
|
235 * @summary Removes the last action from the image edit history |
|
236 * The history consist of (edit) actions performed on the image. |
|
237 * |
|
238 * @memberof imageEdit |
|
239 * @since 2.9.0 |
|
240 * |
|
241 * @param {number} postid The post id. |
|
242 * @param {number} setSize 0 or 1, when 1 the image resets to its original size. |
|
243 * |
|
244 * @returns {string} JSON string containing the history or an empty string if no history exists. |
|
245 */ |
109 filterHistory : function(postid, setSize) { |
246 filterHistory : function(postid, setSize) { |
110 // apply undo state to history |
247 // Apply undo state to history. |
111 var history = $('#imgedit-history-' + postid).val(), pop, n, o, i, op = []; |
248 var history = $('#imgedit-history-' + postid).val(), pop, n, o, i, op = []; |
112 |
249 |
113 if ( history !== '' ) { |
250 if ( history !== '' ) { |
|
251 // Read the JSON string with the image edit history. |
114 history = JSON.parse(history); |
252 history = JSON.parse(history); |
115 pop = this.intval( $('#imgedit-undone-' + postid).val() ); |
253 pop = this.intval( $('#imgedit-undone-' + postid).val() ); |
116 if ( pop > 0 ) { |
254 if ( pop > 0 ) { |
117 while ( pop > 0 ) { |
255 while ( pop > 0 ) { |
118 history.pop(); |
256 history.pop(); |
119 pop--; |
257 pop--; |
120 } |
258 } |
121 } |
259 } |
122 |
260 |
|
261 // Reset size to it's original state. |
123 if ( setSize ) { |
262 if ( setSize ) { |
124 if ( !history.length ) { |
263 if ( !history.length ) { |
125 this.hold.w = this.hold.ow; |
264 this.hold.w = this.hold.ow; |
126 this.hold.h = this.hold.oh; |
265 this.hold.h = this.hold.oh; |
127 return ''; |
266 return ''; |
128 } |
267 } |
129 |
268 |
130 // restore |
269 // Restore original 'o'. |
131 o = history[history.length - 1]; |
270 o = history[history.length - 1]; |
|
271 |
|
272 // c = 'crop', r = 'rotate', f = 'flip' |
132 o = o.c || o.r || o.f || false; |
273 o = o.c || o.r || o.f || false; |
133 |
274 |
134 if ( o ) { |
275 if ( o ) { |
|
276 // fw = Full image width |
135 this.hold.w = o.fw; |
277 this.hold.w = o.fw; |
|
278 // fh = Full image height |
136 this.hold.h = o.fh; |
279 this.hold.h = o.fh; |
137 } |
280 } |
138 } |
281 } |
139 |
282 |
140 // filter the values |
283 // Filter the last step/action from the history. |
141 for ( n in history ) { |
284 for ( n in history ) { |
142 i = history[n]; |
285 i = history[n]; |
143 if ( i.hasOwnProperty('c') ) { |
286 if ( i.hasOwnProperty('c') ) { |
144 op[n] = { 'c': { 'x': i.c.x, 'y': i.c.y, 'w': i.c.w, 'h': i.c.h } }; |
287 op[n] = { 'c': { 'x': i.c.x, 'y': i.c.y, 'w': i.c.w, 'h': i.c.h } }; |
145 } else if ( i.hasOwnProperty('r') ) { |
288 } else if ( i.hasOwnProperty('r') ) { |
150 } |
293 } |
151 return JSON.stringify(op); |
294 return JSON.stringify(op); |
152 } |
295 } |
153 return ''; |
296 return ''; |
154 }, |
297 }, |
155 |
298 /** |
|
299 * @summary Binds the necessary events to the image. |
|
300 * |
|
301 * When the image source is reloaded the image will be reloaded. |
|
302 * |
|
303 * @memberof imageEdit |
|
304 * @since 2.9.0 |
|
305 * |
|
306 * @param {number} postid The post id. |
|
307 * @param {string} nonce The nonce to verify the request. |
|
308 * @param {function} callback Function to execute when the image is loaded. |
|
309 * |
|
310 * @returns {void} |
|
311 */ |
156 refreshEditor : function(postid, nonce, callback) { |
312 refreshEditor : function(postid, nonce, callback) { |
157 var t = this, data, img; |
313 var t = this, data, img; |
158 |
314 |
159 t.toggleEditor(postid, 1); |
315 t.toggleEditor(postid, 1); |
160 data = { |
316 data = { |
163 'postid': postid, |
319 'postid': postid, |
164 'history': t.filterHistory(postid, 1), |
320 'history': t.filterHistory(postid, 1), |
165 'rand': t.intval(Math.random() * 1000000) |
321 'rand': t.intval(Math.random() * 1000000) |
166 }; |
322 }; |
167 |
323 |
168 img = $('<img id="image-preview-' + postid + '" />') |
324 img = $( '<img id="image-preview-' + postid + '" alt="" />' ) |
169 .on('load', function() { |
325 .on( 'load', { history: data.history }, function( event ) { |
170 var max1, max2, parent = $('#imgedit-crop-' + postid), t = imageEdit; |
326 var max1, max2, |
|
327 parent = $( '#imgedit-crop-' + postid ), |
|
328 t = imageEdit, |
|
329 historyObj; |
|
330 |
|
331 // Checks if there already is some image-edit history. |
|
332 if ( '' !== event.data.history ) { |
|
333 historyObj = JSON.parse( event.data.history ); |
|
334 // If last executed action in history is a crop action. |
|
335 if ( historyObj[historyObj.length - 1].hasOwnProperty( 'c' ) ) { |
|
336 /* |
|
337 * A crop action has completed and the crop button gets disabled |
|
338 * ensure the undo button is enabled. |
|
339 */ |
|
340 t.setDisabled( $( '#image-undo-' + postid) , true ); |
|
341 // Move focus to the undo button to avoid a focus loss. |
|
342 $( '#image-undo-' + postid ).focus(); |
|
343 } |
|
344 } |
171 |
345 |
172 parent.empty().append(img); |
346 parent.empty().append(img); |
173 |
347 |
174 // w, h are the new full size dims |
348 // w, h are the new full size dims |
175 max1 = Math.max( t.hold.w, t.hold.h ); |
349 max1 = Math.max( t.hold.w, t.hold.h ); |
195 $('#imgedit-crop-' + postid).empty().append('<div class="error"><p>' + imageEditL10n.error + '</p></div>'); |
369 $('#imgedit-crop-' + postid).empty().append('<div class="error"><p>' + imageEditL10n.error + '</p></div>'); |
196 t.toggleEditor(postid, 0); |
370 t.toggleEditor(postid, 0); |
197 }) |
371 }) |
198 .attr('src', ajaxurl + '?' + $.param(data)); |
372 .attr('src', ajaxurl + '?' + $.param(data)); |
199 }, |
373 }, |
200 |
374 /** |
|
375 * @summary Performs an image edit action. |
|
376 * |
|
377 * @memberof imageEdit |
|
378 * @since 2.9.0 |
|
379 * |
|
380 * @param {number} postid The post id. |
|
381 * @param {string} nonce The nonce to verify the request. |
|
382 * @param {string} action The action to perform on the image. |
|
383 * The possible actions are: "scale" and "restore". |
|
384 * |
|
385 * @returns {boolean|void} Executes a post request that refreshes the page |
|
386 * when the action is performed. |
|
387 * Returns false if a invalid action is given, |
|
388 * or when the action cannot be performed. |
|
389 */ |
201 action : function(postid, nonce, action) { |
390 action : function(postid, nonce, action) { |
202 var t = this, data, w, h, fw, fh; |
391 var t = this, data, w, h, fw, fh; |
203 |
392 |
204 if ( t.notsaved(postid) ) { |
393 if ( t.notsaved(postid) ) { |
205 return false; |
394 return false; |
247 t._view.refresh(); |
436 t._view.refresh(); |
248 } |
437 } |
249 }); |
438 }); |
250 }, |
439 }, |
251 |
440 |
|
441 /** |
|
442 * @summary Stores the changes that are made to the image. |
|
443 * |
|
444 * @memberof imageEdit |
|
445 * @since 2.9.0 |
|
446 * |
|
447 * @param {number} postid The post id to get the image from the database. |
|
448 * @param {string} nonce The nonce to verify the request. |
|
449 * |
|
450 * @returns {boolean|void} If the actions are successfully saved a response message is shown. |
|
451 * Returns false if there is no image editing history, |
|
452 * thus there are not edit-actions performed on the image. |
|
453 */ |
252 save : function(postid, nonce) { |
454 save : function(postid, nonce) { |
253 var data, |
455 var data, |
254 target = this.getTarget(postid), |
456 target = this.getTarget(postid), |
255 history = this.filterHistory(postid, 0), |
457 history = this.filterHistory(postid, 0), |
256 self = this; |
458 self = this; |
267 'history': history, |
469 'history': history, |
268 'target': target, |
470 'target': target, |
269 'context': $('#image-edit-context').length ? $('#image-edit-context').val() : null, |
471 'context': $('#image-edit-context').length ? $('#image-edit-context').val() : null, |
270 'do': 'save' |
472 'do': 'save' |
271 }; |
473 }; |
272 |
474 // Post the image edit data to the backend. |
273 $.post(ajaxurl, data, function(r) { |
475 $.post(ajaxurl, data, function(r) { |
|
476 // Read the response. |
274 var ret = JSON.parse(r); |
477 var ret = JSON.parse(r); |
275 |
478 |
|
479 // If a response is returned, close the editor and show an error. |
276 if ( ret.error ) { |
480 if ( ret.error ) { |
277 $('#imgedit-response-' + postid).html('<div class="error"><p>' + ret.error + '</p></div>'); |
481 $('#imgedit-response-' + postid).html('<div class="error"><p>' + ret.error + '</p></div>'); |
278 imageEdit.close(postid); |
482 imageEdit.close(postid); |
279 return; |
483 return; |
280 } |
484 } |
297 imageEdit.close(postid); |
501 imageEdit.close(postid); |
298 } |
502 } |
299 }); |
503 }); |
300 }, |
504 }, |
301 |
505 |
|
506 /** |
|
507 * @summary Creates the image edit window. |
|
508 * |
|
509 * @memberof imageEdit |
|
510 * @since 2.9.0 |
|
511 * |
|
512 * @param {number} postid The post id for the image. |
|
513 * @param {string} nonce The nonce to verify the request. |
|
514 * @param {object} view The image editor view to be used for the editing. |
|
515 * |
|
516 * @returns {void|promise} Either returns void if the button was already activated |
|
517 * or returns an instance of the image editor, wrapped in a promise. |
|
518 */ |
302 open : function( postid, nonce, view ) { |
519 open : function( postid, nonce, view ) { |
303 this._view = view; |
520 this._view = view; |
304 |
521 |
305 var dfd, data, elem = $('#image-editor-' + postid), head = $('#media-head-' + postid), |
522 var dfd, data, elem = $('#image-editor-' + postid), head = $('#media-head-' + postid), |
306 btn = $('#imgedit-open-btn-' + postid), spin = btn.siblings('.spinner'); |
523 btn = $('#imgedit-open-btn-' + postid), spin = btn.siblings('.spinner'); |
307 |
524 |
308 btn.prop('disabled', true); |
525 /* |
|
526 * Instead of disabling the button, which causes a focus loss and makes screen |
|
527 * readers announce "unavailable", return if the button was already clicked. |
|
528 */ |
|
529 if ( btn.hasClass( 'button-activated' ) ) { |
|
530 return; |
|
531 } |
|
532 |
309 spin.addClass( 'is-active' ); |
533 spin.addClass( 'is-active' ); |
310 |
534 |
311 data = { |
535 data = { |
312 'action': 'image-editor', |
536 'action': 'image-editor', |
313 '_ajax_nonce': nonce, |
537 '_ajax_nonce': nonce, |
316 }; |
540 }; |
317 |
541 |
318 dfd = $.ajax({ |
542 dfd = $.ajax({ |
319 url: ajaxurl, |
543 url: ajaxurl, |
320 type: 'post', |
544 type: 'post', |
321 data: data |
545 data: data, |
|
546 beforeSend: function() { |
|
547 btn.addClass( 'button-activated' ); |
|
548 } |
322 }).done(function( html ) { |
549 }).done(function( html ) { |
323 elem.html( html ); |
550 elem.html( html ); |
324 head.fadeOut('fast', function(){ |
551 head.fadeOut('fast', function(){ |
325 elem.fadeIn('fast'); |
552 elem.fadeIn('fast'); |
326 btn.removeAttr('disabled'); |
553 btn.removeClass( 'button-activated' ); |
327 spin.removeClass( 'is-active' ); |
554 spin.removeClass( 'is-active' ); |
328 }); |
555 }); |
|
556 // Initialise the Image Editor now that everything is ready. |
|
557 imageEdit.init( postid ); |
329 }); |
558 }); |
330 |
559 |
331 return dfd; |
560 return dfd; |
332 }, |
561 }, |
333 |
562 |
|
563 /** |
|
564 * @summary Initializes the cropping tool and sets a default cropping selection. |
|
565 * |
|
566 * @memberof imageEdit |
|
567 * @since 2.9.0 |
|
568 * |
|
569 * @param {number} postid The post id. |
|
570 * |
|
571 * @returns {void} |
|
572 */ |
334 imgLoaded : function(postid) { |
573 imgLoaded : function(postid) { |
335 var img = $('#image-preview-' + postid), parent = $('#imgedit-crop-' + postid); |
574 var img = $('#image-preview-' + postid), parent = $('#imgedit-crop-' + postid); |
|
575 |
|
576 // Ensure init has run even when directly loaded. |
|
577 if ( 'undefined' === typeof this.hold.sizer ) { |
|
578 this.init( postid ); |
|
579 } |
336 |
580 |
337 this.initCrop(postid, img, parent); |
581 this.initCrop(postid, img, parent); |
338 this.setCropSelection(postid, 0); |
582 this.setCropSelection(postid, 0); |
339 this.toggleEditor(postid, 0); |
583 this.toggleEditor(postid, 0); |
340 }, |
584 // Editor is ready, move focus to the first focusable element. |
341 |
585 $( '.imgedit-wrap .imgedit-help-toggle' ).eq( 0 ).focus(); |
|
586 }, |
|
587 |
|
588 /** |
|
589 * @summary Initializes the cropping tool. |
|
590 * |
|
591 * @memberof imageEdit |
|
592 * @since 2.9.0 |
|
593 * |
|
594 * @param {number} postid The post id. |
|
595 * @param {HTMLElement} image The preview image. |
|
596 * @param {HTMLElement} parent The preview image container. |
|
597 * |
|
598 * @returns {void} |
|
599 */ |
342 initCrop : function(postid, image, parent) { |
600 initCrop : function(postid, image, parent) { |
343 var t = this, |
601 var t = this, |
344 selW = $('#imgedit-sel-width-' + postid), |
602 selW = $('#imgedit-sel-width-' + postid), |
345 selH = $('#imgedit-sel-height-' + postid), |
603 selH = $('#imgedit-sel-height-' + postid), |
346 $img; |
604 $img; |
351 handles: true, |
609 handles: true, |
352 keys: true, |
610 keys: true, |
353 minWidth: 3, |
611 minWidth: 3, |
354 minHeight: 3, |
612 minHeight: 3, |
355 |
613 |
|
614 /** |
|
615 * @summary Sets the CSS styles and binds events for locking the aspect ratio. |
|
616 * |
|
617 * @param {jQuery} img The preview image. |
|
618 */ |
356 onInit: function( img ) { |
619 onInit: function( img ) { |
357 // Ensure that the imgareaselect wrapper elements are position:absolute |
620 // Ensure that the imgAreaSelect wrapper elements are position:absolute. |
358 // (even if we're in a position:fixed modal) |
621 // (even if we're in a position:fixed modal) |
359 $img = $( img ); |
622 $img = $( img ); |
360 $img.next().css( 'position', 'absolute' ) |
623 $img.next().css( 'position', 'absolute' ) |
361 .nextAll( '.imgareaselect-outer' ).css( 'position', 'absolute' ); |
624 .nextAll( '.imgareaselect-outer' ).css( 'position', 'absolute' ); |
362 |
625 /** |
363 parent.children().mousedown(function(e){ |
626 * @summary Binds mouse down event to the cropping container. |
|
627 * |
|
628 * @returns {void} |
|
629 */ |
|
630 parent.children().on( 'mousedown, touchstart', function(e){ |
364 var ratio = false, sel, defRatio; |
631 var ratio = false, sel, defRatio; |
365 |
632 |
366 if ( e.shiftKey ) { |
633 if ( e.shiftKey ) { |
367 sel = t.iasapi.getSelection(); |
634 sel = t.iasapi.getSelection(); |
368 defRatio = t.getSelRatio(postid); |
635 defRatio = t.getSelRatio(postid); |
373 aspectRatio: ratio |
640 aspectRatio: ratio |
374 }); |
641 }); |
375 }); |
642 }); |
376 }, |
643 }, |
377 |
644 |
|
645 /** |
|
646 * @summary Event triggered when starting a selection. |
|
647 * |
|
648 * @returns {void} |
|
649 */ |
378 onSelectStart: function() { |
650 onSelectStart: function() { |
379 imageEdit.setDisabled($('#imgedit-crop-sel-' + postid), 1); |
651 imageEdit.setDisabled($('#imgedit-crop-sel-' + postid), 1); |
380 }, |
652 }, |
381 |
653 /** |
|
654 * @summary Event triggered when the selection is ended. |
|
655 * |
|
656 * @param {object} img jQuery object representing the image. |
|
657 * @param {object} c The selection. |
|
658 * |
|
659 * @returns {object} |
|
660 */ |
382 onSelectEnd: function(img, c) { |
661 onSelectEnd: function(img, c) { |
383 imageEdit.setCropSelection(postid, c); |
662 imageEdit.setCropSelection(postid, c); |
384 }, |
663 }, |
385 |
664 |
|
665 /** |
|
666 * @summary Event triggered when the selection changes. |
|
667 * |
|
668 * @param {object} img jQuery object representing the image. |
|
669 * @param {object} c The selection. |
|
670 * |
|
671 * @returns {void} |
|
672 */ |
386 onSelectChange: function(img, c) { |
673 onSelectChange: function(img, c) { |
387 var sizer = imageEdit.hold.sizer; |
674 var sizer = imageEdit.hold.sizer; |
388 selW.val( imageEdit.round(c.width / sizer) ); |
675 selW.val( imageEdit.round(c.width / sizer) ); |
389 selH.val( imageEdit.round(c.height / sizer) ); |
676 selH.val( imageEdit.round(c.height / sizer) ); |
390 } |
677 } |
391 }); |
678 }); |
392 }, |
679 }, |
393 |
680 |
|
681 /** |
|
682 * @summary Stores the current crop selection. |
|
683 * |
|
684 * @memberof imageEdit |
|
685 * @since 2.9.0 |
|
686 * |
|
687 * @param {number} postid The post id. |
|
688 * @param {object} c The selection. |
|
689 * |
|
690 * @returns {boolean} |
|
691 */ |
394 setCropSelection : function(postid, c) { |
692 setCropSelection : function(postid, c) { |
395 var sel; |
693 var sel; |
396 |
694 |
397 c = c || 0; |
695 c = c || 0; |
398 |
696 |
408 sel = { 'x': c.x1, 'y': c.y1, 'w': c.width, 'h': c.height }; |
706 sel = { 'x': c.x1, 'y': c.y1, 'w': c.width, 'h': c.height }; |
409 this.setDisabled($('.imgedit-crop', '#imgedit-panel-' + postid), 1); |
707 this.setDisabled($('.imgedit-crop', '#imgedit-panel-' + postid), 1); |
410 $('#imgedit-selection-' + postid).val( JSON.stringify(sel) ); |
708 $('#imgedit-selection-' + postid).val( JSON.stringify(sel) ); |
411 }, |
709 }, |
412 |
710 |
|
711 |
|
712 /** |
|
713 * @summary Closes the image editor. |
|
714 * |
|
715 * @memberof imageEdit |
|
716 * @since 2.9.0 |
|
717 * |
|
718 * @param {number} postid The post id. |
|
719 * @param {bool} warn Warning message. |
|
720 * |
|
721 * @returns {void|bool} Returns false if there is a warning. |
|
722 */ |
413 close : function(postid, warn) { |
723 close : function(postid, warn) { |
414 warn = warn || false; |
724 warn = warn || false; |
415 |
725 |
416 if ( warn && this.notsaved(postid) ) { |
726 if ( warn && this.notsaved(postid) ) { |
417 return false; |
727 return false; |
427 } |
737 } |
428 |
738 |
429 // In case we are not accessing the image editor in the context of a View, close the editor the old-skool way |
739 // In case we are not accessing the image editor in the context of a View, close the editor the old-skool way |
430 else { |
740 else { |
431 $('#image-editor-' + postid).fadeOut('fast', function() { |
741 $('#image-editor-' + postid).fadeOut('fast', function() { |
432 $('#media-head-' + postid).fadeIn('fast'); |
742 $( '#media-head-' + postid ).fadeIn( 'fast', function() { |
|
743 // Move focus back to the Edit Image button. Runs also when saving. |
|
744 $( '#imgedit-open-btn-' + postid ).focus(); |
|
745 }); |
433 $(this).empty(); |
746 $(this).empty(); |
434 }); |
747 }); |
435 } |
748 } |
436 |
749 |
437 |
750 |
438 }, |
751 }, |
439 |
752 |
|
753 /** |
|
754 * @summary Checks if the image edit history is saved. |
|
755 * |
|
756 * @memberof imageEdit |
|
757 * @since 2.9.0 |
|
758 * |
|
759 * @param {number} postid The post id. |
|
760 * |
|
761 * @returns {boolean} Returns true if the history is not saved. |
|
762 */ |
440 notsaved : function(postid) { |
763 notsaved : function(postid) { |
441 var h = $('#imgedit-history-' + postid).val(), |
764 var h = $('#imgedit-history-' + postid).val(), |
442 history = ( h !== '' ) ? JSON.parse(h) : [], |
765 history = ( h !== '' ) ? JSON.parse(h) : [], |
443 pop = this.intval( $('#imgedit-undone-' + postid).val() ); |
766 pop = this.intval( $('#imgedit-undone-' + postid).val() ); |
444 |
767 |
449 return true; |
772 return true; |
450 } |
773 } |
451 return false; |
774 return false; |
452 }, |
775 }, |
453 |
776 |
|
777 /** |
|
778 * @summary Adds an image edit action to the history. |
|
779 * |
|
780 * @memberof imageEdit |
|
781 * @since 2.9.0 |
|
782 * |
|
783 * @param {object} op The original position. |
|
784 * @param {number} postid The post id. |
|
785 * @param {string} nonce The nonce. |
|
786 * |
|
787 * @returns {void} |
|
788 */ |
454 addStep : function(op, postid, nonce) { |
789 addStep : function(op, postid, nonce) { |
455 var t = this, elem = $('#imgedit-history-' + postid), |
790 var t = this, elem = $('#imgedit-history-' + postid), |
456 history = ( elem.val() !== '' ) ? JSON.parse( elem.val() ) : [], |
791 history = ( elem.val() !== '' ) ? JSON.parse( elem.val() ) : [], |
457 undone = $('#imgedit-undone-' + postid), |
792 undone = $( '#imgedit-undone-' + postid ), |
458 pop = t.intval(undone.val()); |
793 pop = t.intval( undone.val() ); |
459 |
794 |
460 while ( pop > 0 ) { |
795 while ( pop > 0 ) { |
461 history.pop(); |
796 history.pop(); |
462 pop--; |
797 pop--; |
463 } |
798 } |
470 t.setDisabled($('#image-undo-' + postid), true); |
805 t.setDisabled($('#image-undo-' + postid), true); |
471 t.setDisabled($('#image-redo-' + postid), false); |
806 t.setDisabled($('#image-redo-' + postid), false); |
472 }); |
807 }); |
473 }, |
808 }, |
474 |
809 |
|
810 /** |
|
811 * @summary Rotates the image. |
|
812 * |
|
813 * @memberof imageEdit |
|
814 * @since 2.9.0 |
|
815 * |
|
816 * @param {string} angle The angle the image is rotated with. |
|
817 * @param {number} postid The post id. |
|
818 * @param {string} nonce The nonce. |
|
819 * @param {object} t The target element. |
|
820 * |
|
821 * @returns {boolean} |
|
822 */ |
475 rotate : function(angle, postid, nonce, t) { |
823 rotate : function(angle, postid, nonce, t) { |
476 if ( $(t).hasClass('disabled') ) { |
824 if ( $(t).hasClass('disabled') ) { |
477 return false; |
825 return false; |
478 } |
826 } |
479 |
827 |
480 this.addStep({ 'r': { 'r': angle, 'fw': this.hold.h, 'fh': this.hold.w }}, postid, nonce); |
828 this.addStep({ 'r': { 'r': angle, 'fw': this.hold.h, 'fh': this.hold.w }}, postid, nonce); |
481 }, |
829 }, |
482 |
830 |
|
831 /** |
|
832 * @summary Flips the image. |
|
833 * |
|
834 * @memberof imageEdit |
|
835 * @since 2.9.0 |
|
836 * |
|
837 * @param {number} axis The axle the image is flipped on. |
|
838 * @param {number} postid The post id. |
|
839 * @param {string} nonce The nonce. |
|
840 * @param {object} t The target element. |
|
841 * |
|
842 * @returns {boolean} |
|
843 */ |
483 flip : function (axis, postid, nonce, t) { |
844 flip : function (axis, postid, nonce, t) { |
484 if ( $(t).hasClass('disabled') ) { |
845 if ( $(t).hasClass('disabled') ) { |
485 return false; |
846 return false; |
486 } |
847 } |
487 |
848 |
488 this.addStep({ 'f': { 'f': axis, 'fw': this.hold.w, 'fh': this.hold.h }}, postid, nonce); |
849 this.addStep({ 'f': { 'f': axis, 'fw': this.hold.w, 'fh': this.hold.h }}, postid, nonce); |
489 }, |
850 }, |
490 |
851 |
|
852 /** |
|
853 * @summary Crops the image. |
|
854 * |
|
855 * @memberof imageEdit |
|
856 * @since 2.9.0 |
|
857 * |
|
858 * @param {number} postid The post id. |
|
859 * @param {string} nonce The nonce. |
|
860 * @param {object} t The target object. |
|
861 * |
|
862 * @returns {void|boolean} Returns false if the crop button is disabled. |
|
863 */ |
491 crop : function (postid, nonce, t) { |
864 crop : function (postid, nonce, t) { |
492 var sel = $('#imgedit-selection-' + postid).val(), |
865 var sel = $('#imgedit-selection-' + postid).val(), |
493 w = this.intval( $('#imgedit-sel-width-' + postid).val() ), |
866 w = this.intval( $('#imgedit-sel-width-' + postid).val() ), |
494 h = this.intval( $('#imgedit-sel-height-' + postid).val() ); |
867 h = this.intval( $('#imgedit-sel-height-' + postid).val() ); |
495 |
868 |
503 sel.fh = h; |
876 sel.fh = h; |
504 this.addStep({ 'c': sel }, postid, nonce); |
877 this.addStep({ 'c': sel }, postid, nonce); |
505 } |
878 } |
506 }, |
879 }, |
507 |
880 |
|
881 /** |
|
882 * @summary Undoes an image edit action. |
|
883 * |
|
884 * @memberof imageEdit |
|
885 * @since 2.9.0 |
|
886 * |
|
887 * @param {number} postid The post id. |
|
888 * @param {string} nonce The nonce. |
|
889 * |
|
890 * @returns {void|false} Returns false if the undo button is disabled. |
|
891 */ |
508 undo : function (postid, nonce) { |
892 undo : function (postid, nonce) { |
509 var t = this, button = $('#image-undo-' + postid), elem = $('#imgedit-undone-' + postid), |
893 var t = this, button = $('#image-undo-' + postid), elem = $('#imgedit-undone-' + postid), |
510 pop = t.intval( elem.val() ) + 1; |
894 pop = t.intval( elem.val() ) + 1; |
511 |
895 |
512 if ( button.hasClass('disabled') ) { |
896 if ( button.hasClass('disabled') ) { |
514 } |
898 } |
515 |
899 |
516 elem.val(pop); |
900 elem.val(pop); |
517 t.refreshEditor(postid, nonce, function() { |
901 t.refreshEditor(postid, nonce, function() { |
518 var elem = $('#imgedit-history-' + postid), |
902 var elem = $('#imgedit-history-' + postid), |
519 history = ( elem.val() !== '' ) ? JSON.parse( elem.val() ) : []; |
903 history = ( elem.val() !== '' ) ? JSON.parse( elem.val() ) : []; |
520 |
904 |
521 t.setDisabled($('#image-redo-' + postid), true); |
905 t.setDisabled($('#image-redo-' + postid), true); |
522 t.setDisabled(button, pop < history.length); |
906 t.setDisabled(button, pop < history.length); |
|
907 // When undo gets disabled, move focus to the redo button to avoid a focus loss. |
|
908 if ( history.length === pop ) { |
|
909 $( '#image-redo-' + postid ).focus(); |
|
910 } |
523 }); |
911 }); |
524 }, |
912 }, |
525 |
913 |
|
914 /** |
|
915 * Reverts a undo action. |
|
916 * |
|
917 * @memberof imageEdit |
|
918 * @since 2.9.0 |
|
919 * |
|
920 * @param {number} postid The post id. |
|
921 * @param {string} nonce The nonce. |
|
922 * |
|
923 * @returns {void} |
|
924 */ |
526 redo : function(postid, nonce) { |
925 redo : function(postid, nonce) { |
527 var t = this, button = $('#image-redo-' + postid), elem = $('#imgedit-undone-' + postid), |
926 var t = this, button = $('#image-redo-' + postid), elem = $('#imgedit-undone-' + postid), |
528 pop = t.intval( elem.val() ) - 1; |
927 pop = t.intval( elem.val() ) - 1; |
529 |
928 |
530 if ( button.hasClass('disabled') ) { |
929 if ( button.hasClass('disabled') ) { |
533 |
932 |
534 elem.val(pop); |
933 elem.val(pop); |
535 t.refreshEditor(postid, nonce, function() { |
934 t.refreshEditor(postid, nonce, function() { |
536 t.setDisabled($('#image-undo-' + postid), true); |
935 t.setDisabled($('#image-undo-' + postid), true); |
537 t.setDisabled(button, pop > 0); |
936 t.setDisabled(button, pop > 0); |
|
937 // When redo gets disabled, move focus to the undo button to avoid a focus loss. |
|
938 if ( 0 === pop ) { |
|
939 $( '#image-undo-' + postid ).focus(); |
|
940 } |
538 }); |
941 }); |
539 }, |
942 }, |
540 |
943 |
541 setNumSelection : function(postid) { |
944 /** |
|
945 * @summary Sets the selection for the height and width in pixels. |
|
946 * |
|
947 * @memberof imageEdit |
|
948 * @since 2.9.0 |
|
949 * |
|
950 * @param {number} postid The post id. |
|
951 * @param {jQuery} el The element containing the values. |
|
952 * |
|
953 * @returns {void|boolean} Returns false when the x or y value is lower than 1, |
|
954 * void when the value is not numeric or when the operation |
|
955 * is successful. |
|
956 */ |
|
957 setNumSelection : function( postid, el ) { |
542 var sel, elX = $('#imgedit-sel-width-' + postid), elY = $('#imgedit-sel-height-' + postid), |
958 var sel, elX = $('#imgedit-sel-width-' + postid), elY = $('#imgedit-sel-height-' + postid), |
543 x = this.intval( elX.val() ), y = this.intval( elY.val() ), |
959 x = this.intval( elX.val() ), y = this.intval( elY.val() ), |
544 img = $('#image-preview-' + postid), imgh = img.height(), imgw = img.width(), |
960 img = $('#image-preview-' + postid), imgh = img.height(), imgw = img.width(), |
545 sizer = this.hold.sizer, x1, y1, x2, y2, ias = this.iasapi; |
961 sizer = this.hold.sizer, x1, y1, x2, y2, ias = this.iasapi; |
|
962 |
|
963 if ( false === this.validateNumeric( el ) ) { |
|
964 return; |
|
965 } |
546 |
966 |
547 if ( x < 1 ) { |
967 if ( x < 1 ) { |
548 elX.val(''); |
968 elX.val(''); |
549 return false; |
969 return false; |
550 } |
970 } |
595 } |
1025 } |
596 |
1026 |
597 return num; |
1027 return num; |
598 }, |
1028 }, |
599 |
1029 |
|
1030 /** |
|
1031 * Sets a locked aspect ratio for the selection. |
|
1032 * |
|
1033 * @memberof imageEdit |
|
1034 * @since 2.9.0 |
|
1035 * |
|
1036 * @param {number} postid The post id. |
|
1037 * @param {number} n The ratio to set. |
|
1038 * @param {jQuery} el The element containing the values. |
|
1039 * |
|
1040 * @returns {void} |
|
1041 */ |
600 setRatioSelection : function(postid, n, el) { |
1042 setRatioSelection : function(postid, n, el) { |
601 var sel, r, x = this.intval( $('#imgedit-crop-width-' + postid).val() ), |
1043 var sel, r, x = this.intval( $('#imgedit-crop-width-' + postid).val() ), |
602 y = this.intval( $('#imgedit-crop-height-' + postid).val() ), |
1044 y = this.intval( $('#imgedit-crop-height-' + postid).val() ), |
603 h = $('#image-preview-' + postid).height(); |
1045 h = $('#image-preview-' + postid).height(); |
604 |
1046 |
605 if ( !this.intval( $(el).val() ) ) { |
1047 if ( false === this.validateNumeric( el ) ) { |
606 $(el).val(''); |
|
607 return; |
1048 return; |
608 } |
1049 } |
609 |
1050 |
610 if ( x && y ) { |
1051 if ( x && y ) { |
611 this.iasapi.setOptions({ |
1052 this.iasapi.setOptions({ |