at path:ROOT / wp-admin / js / image-edit.js
run:R W Run
DIR
2026-03-11 16:18:51
R W Run
2.86 KB
2026-03-11 16:18:51
R W Run
758 By
2026-03-11 16:18:51
R W Run
6.24 KB
2026-03-11 16:18:51
R W Run
2.95 KB
2026-03-11 16:18:51
R W Run
5.66 KB
2026-03-11 16:18:51
R W Run
2.04 KB
2026-03-11 16:18:51
R W Run
11.32 KB
2026-03-11 16:18:51
R W Run
3.01 KB
2026-03-11 16:18:51
R W Run
9.54 KB
2026-03-11 16:18:51
R W Run
3.4 KB
2026-03-11 16:18:51
R W Run
2.85 KB
2026-03-11 16:18:51
R W Run
1.28 KB
2026-03-11 16:18:51
R W Run
61.15 KB
2026-03-11 16:18:51
R W Run
23.12 KB
2026-03-11 16:18:51
R W Run
3.35 KB
2026-03-11 16:18:51
R W Run
1.18 KB
2026-03-11 16:18:51
R W Run
1.98 KB
2026-03-11 16:18:51
R W Run
288.41 KB
2026-03-11 16:18:51
R W Run
109.69 KB
2026-03-11 16:18:51
R W Run
111.46 KB
2026-03-11 16:18:51
R W Run
47.14 KB
2026-03-11 16:18:51
R W Run
70.05 KB
2026-03-11 16:18:51
R W Run
27.41 KB
2026-03-11 16:18:51
R W Run
27.02 KB
2026-03-11 16:18:51
R W Run
8.65 KB
2026-03-11 16:18:51
R W Run
37.12 KB
2026-03-11 16:18:51
R W Run
15.13 KB
2026-03-11 16:18:51
R W Run
41.61 KB
2026-03-11 16:18:51
R W Run
13.14 KB
2026-03-11 16:18:51
R W Run
44 KB
2026-03-11 16:18:51
R W Run
12.78 KB
2026-03-11 16:18:51
R W Run
7.67 KB
2026-03-11 16:18:51
R W Run
5.41 KB
2026-03-11 16:18:51
R W Run
3.65 KB
2026-03-11 16:18:51
R W Run
39.98 KB
2026-03-11 16:18:51
R W Run
15.15 KB
2026-03-11 16:18:51
R W Run
20.17 KB
2026-03-11 16:18:51
R W Run
9.41 KB
2026-03-11 16:18:51
R W Run
7.61 KB
2026-03-11 16:18:51
R W Run
2.93 KB
2026-03-11 16:18:51
R W Run
23.09 KB
2026-03-11 16:18:51
R W Run
890 By
2026-03-11 16:18:51
R W Run
423 By
2026-03-11 16:18:51
R W Run
3.89 KB
2026-03-11 16:18:51
R W Run
1.7 KB
2026-03-11 16:18:51
R W Run
1.27 KB
2026-03-11 16:18:51
R W Run
611 By
2026-03-11 16:18:51
R W Run
3.38 KB
2026-03-11 16:18:51
R W Run
1.13 KB
2026-03-11 16:18:51
R W Run
6.61 KB
2026-03-11 16:18:51
R W Run
2.38 KB
2026-03-11 16:18:51
R W Run
61.15 KB
2026-03-11 16:18:51
R W Run
30.06 KB
2026-03-11 16:18:51
R W Run
4.14 KB
2026-03-11 16:18:51
R W Run
1.1 KB
2026-03-11 16:18:51
R W Run
1.31 KB
2026-03-11 16:18:51
R W Run
847 By
2026-03-11 16:18:51
R W Run
6.92 KB
2026-03-11 16:18:51
R W Run
2.35 KB
2026-03-11 16:18:51
R W Run
38.68 KB
2026-03-11 16:18:51
R W Run
18.4 KB
2026-03-11 16:18:51
R W Run
18.49 KB
2026-03-11 16:18:51
R W Run
6.6 KB
2026-03-11 16:18:51
R W Run
10.67 KB
2026-03-11 16:18:51
R W Run
5.03 KB
2026-03-11 16:18:51
R W Run
33.92 KB
2026-03-11 16:18:51
R W Run
17.97 KB
2026-03-11 16:18:51
R W Run
876 By
2026-03-11 16:18:51
R W Run
620 By
2026-03-11 16:18:51
R W Run
13.15 KB
2026-03-11 16:18:51
R W Run
6.13 KB
2026-03-11 16:18:51
R W Run
6.1 KB
2026-03-11 16:18:51
R W Run
2.2 KB
2026-03-11 16:18:51
R W Run
3.2 KB
2026-03-11 16:18:51
R W Run
1.53 KB
2026-03-11 16:18:51
R W Run
10.88 KB
2026-03-11 16:18:51
R W Run
3 KB
2026-03-11 16:18:51
R W Run
5.64 KB
2026-03-11 16:18:51
R W Run
2.22 KB
2026-03-11 16:18:51
R W Run
5.96 KB
2026-03-11 16:18:51
R W Run
2.41 KB
2026-03-11 16:18:51
R W Run
24.77 KB
2026-03-11 16:18:51
R W Run
11.43 KB
2026-03-11 16:18:51
R W Run
54.94 KB
2026-03-11 16:18:51
R W Run
26.51 KB
2026-03-11 16:18:51
R W Run
109.37 KB
2026-03-11 16:18:51
R W Run
47.31 KB
2026-03-11 16:18:51
R W Run
17.91 KB
2026-03-11 16:18:51
R W Run
7.81 KB
2026-03-11 16:18:51
R W Run
2.25 KB
2026-03-11 16:18:51
R W Run
676 By
2026-03-11 16:18:51
R W Run
22.56 KB
2026-03-11 16:18:51
R W Run
12.31 KB
2026-03-11 16:18:51
R W Run
7.52 KB
2026-03-11 16:18:51
R W Run
1.49 KB
2026-03-11 16:18:51
R W Run
740 By
2026-03-11 16:18:51
R W Run
458 By
2026-03-11 16:18:51
R W Run
error_log
📄image-edit.js
1/**
2 * The functions necessary for editing images.
3 *
4 * @since 2.9.0
5 * @output wp-admin/js/image-edit.js
6 */
7
8 /* global ajaxurl, confirm */
9
10(function($) {
11 var __ = wp.i18n.__;
12
13 /**
14 * Contains all the methods to initialize and control the image editor.
15 *
16 * @namespace imageEdit
17 */
18 var imageEdit = window.imageEdit = {
19 iasapi : {},
20 hold : {},
21 postid : '',
22 _view : false,
23
24 /**
25 * Enable crop tool.
26 */
27 toggleCropTool: function( postid, nonce, cropButton ) {
28 var img = $( '#image-preview-' + postid ),
29 selection = this.iasapi.getSelection();
30
31 imageEdit.toggleControls( cropButton );
32 var $el = $( cropButton );
33 var state = ( $el.attr( 'aria-expanded' ) === 'true' ) ? 'true' : 'false';
34 // Crop tools have been closed.
35 if ( 'false' === state ) {
36 // Cancel selection, but do not unset inputs.
37 this.iasapi.cancelSelection();
38 imageEdit.setDisabled($('.imgedit-crop-clear'), 0);
39 } else {
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 {
80 // Otherwise, perform the crop.
81 imageEdit.crop( postid, nonce , cropButton );
82 }
83 },
84
85 /**
86 * Converts a value to an integer.
87 *
88 * @since 2.9.0
89 *
90 * @memberof imageEdit
91 *
92 * @param {number} f The float value that should be converted.
93 *
94 * @return {number} The integer representation from the float value.
95 */
96 intval : function(f) {
97 /*
98 * Bitwise OR operator: one of the obscure ways to truncate floating point figures,
99 * worth reminding JavaScript doesn't have a distinct "integer" type.
100 */
101 return f | 0;
102 },
103
104 /**
105 * Adds the disabled attribute and class to a single form element or a field set.
106 *
107 * @since 2.9.0
108 *
109 * @memberof imageEdit
110 *
111 * @param {jQuery} el The element that should be modified.
112 * @param {boolean|number} s The state for the element. If set to true
113 * the element is disabled,
114 * otherwise the element is enabled.
115 * The function is sometimes called with a 0 or 1
116 * instead of true or false.
117 *
118 * @return {void}
119 */
120 setDisabled : function( el, s ) {
121 /*
122 * `el` can be a single form element or a fieldset. Before #28864, the disabled state on
123 * some text fields was handled targeting $('input', el). Now we need to handle the
124 * disabled state on buttons too so we can just target `el` regardless if it's a single
125 * element or a fieldset because when a fieldset is disabled, its descendants are disabled too.
126 */
127 if ( s ) {
128 el.removeClass( 'disabled' ).prop( 'disabled', false );
129 } else {
130 el.addClass( 'disabled' ).prop( 'disabled', true );
131 }
132 },
133
134 /**
135 * Initializes the image editor.
136 *
137 * @since 2.9.0
138 *
139 * @memberof imageEdit
140 *
141 * @param {number} postid The post ID.
142 *
143 * @return {void}
144 */
145 init : function(postid) {
146 var t = this, old = $('#image-editor-' + t.postid);
147
148 if ( t.postid !== postid && old.length ) {
149 t.close(t.postid);
150 }
151
152 t.hold.sizer = parseFloat( $('#imgedit-sizer-' + postid).val() );
153 t.postid = postid;
154 $('#imgedit-response-' + postid).empty();
155
156 $('#imgedit-panel-' + postid).on( 'keypress', function(e) {
157 var nonce = $( '#imgedit-nonce-' + postid ).val();
158 if ( e.which === 26 && e.ctrlKey ) {
159 imageEdit.undo( postid, nonce );
160 }
161
162 if ( e.which === 25 && e.ctrlKey ) {
163 imageEdit.redo( postid, nonce );
164 }
165 });
166
167 $('#imgedit-panel-' + postid).on( 'keypress', 'input[type="text"]', function(e) {
168 var k = e.keyCode;
169
170 // Key codes 37 through 40 are the arrow keys.
171 if ( 36 < k && k < 41 ) {
172 $(this).trigger( 'blur' );
173 }
174
175 // The key code 13 is the Enter key.
176 if ( 13 === k ) {
177 e.preventDefault();
178 e.stopPropagation();
179 return false;
180 }
181 });
182
183 $( document ).on( 'image-editor-ui-ready', this.focusManager );
184 },
185
186 /**
187 * Calculate the image size and save it to memory.
188 *
189 * @since 6.7.0
190 *
191 * @memberof imageEdit
192 *
193 * @param {number} postid The post ID.
194 *
195 * @return {void}
196 */
197 calculateImgSize: function( postid ) {
198 var t = this,
199 x = t.intval( $( '#imgedit-x-' + postid ).val() ),
200 y = t.intval( $( '#imgedit-y-' + postid ).val() );
201
202 t.hold.w = t.hold.ow = x;
203 t.hold.h = t.hold.oh = y;
204 t.hold.xy_ratio = x / y;
205 t.hold.sizer = parseFloat( $( '#imgedit-sizer-' + postid ).val() );
206 t.currentCropSelection = null;
207 },
208
209 /**
210 * Toggles the wait/load icon in the editor.
211 *
212 * @since 2.9.0
213 * @since 5.5.0 Added the triggerUIReady parameter.
214 *
215 * @memberof imageEdit
216 *
217 * @param {number} postid The post ID.
218 * @param {number} toggle Is 0 or 1, fades the icon in when 1 and out when 0.
219 * @param {boolean} triggerUIReady Whether to trigger a custom event when the UI is ready. Default false.
220 *
221 * @return {void}
222 */
223 toggleEditor: function( postid, toggle, triggerUIReady ) {
224 var wait = $('#imgedit-wait-' + postid);
225
226 if ( toggle ) {
227 wait.fadeIn( 'fast' );
228 } else {
229 wait.fadeOut( 'fast', function() {
230 if ( triggerUIReady ) {
231 $( document ).trigger( 'image-editor-ui-ready' );
232 }
233 } );
234 }
235 },
236
237 /**
238 * Shows or hides image menu popup.
239 *
240 * @since 6.3.0
241 *
242 * @memberof imageEdit
243 *
244 * @param {HTMLElement} el The activated control element.
245 *
246 * @return {boolean} Always returns false.
247 */
248 togglePopup : function(el) {
249 var $el = $( el );
250 var $targetEl = $( el ).attr( 'aria-controls' );
251 var $target = $( '#' + $targetEl );
252 $el
253 .attr( 'aria-expanded', 'false' === $el.attr( 'aria-expanded' ) ? 'true' : 'false' );
254 // Open menu and set z-index to appear above image crop area if it is enabled.
255 $target
256 .toggleClass( 'imgedit-popup-menu-open' ).slideToggle( 'fast' ).css( { 'z-index' : 200000 } );
257 // Move focus to first item in menu when opening menu.
258 if ( 'true' === $el.attr( 'aria-expanded' ) ) {
259 $target.find( 'button' ).first().trigger( 'focus' );
260 }
261
262 return false;
263 },
264
265 /**
266 * Observes whether the popup should remain open based on focus position.
267 *
268 * @since 6.4.0
269 *
270 * @memberof imageEdit
271 *
272 * @param {HTMLElement} el The activated control element.
273 *
274 * @return {boolean} Always returns false.
275 */
276 monitorPopup : function() {
277 var $parent = document.querySelector( '.imgedit-rotate-menu-container' );
278 var $toggle = document.querySelector( '.imgedit-rotate-menu-container .imgedit-rotate' );
279
280 setTimeout( function() {
281 var $focused = document.activeElement;
282 var $contains = $parent.contains( $focused );
283
284 // If $focused is defined and not inside the menu container, close the popup.
285 if ( $focused && ! $contains ) {
286 if ( 'true' === $toggle.getAttribute( 'aria-expanded' ) ) {
287 imageEdit.togglePopup( $toggle );
288 }
289 }
290 }, 100 );
291
292 return false;
293 },
294
295 /**
296 * Navigate popup menu by arrow keys.
297 *
298 * @since 6.3.0
299 * @since 6.7.0 Added the event parameter.
300 *
301 * @memberof imageEdit
302 *
303 * @param {Event} event The key or click event.
304 * @param {HTMLElement} el The current element.
305 *
306 * @return {boolean} Always returns false.
307 */
308 browsePopup : function(event, el) {
309 var $el = $( el );
310 var $collection = $( el ).parent( '.imgedit-popup-menu' ).find( 'button' );
311 var $index = $collection.index( $el );
312 var $prev = $index - 1;
313 var $next = $index + 1;
314 var $last = $collection.length;
315 if ( $prev < 0 ) {
316 $prev = $last - 1;
317 }
318 if ( $next === $last ) {
319 $next = 0;
320 }
321 var target = false;
322 if ( event.keyCode === 40 ) {
323 target = $collection.get( $next );
324 } else if ( event.keyCode === 38 ) {
325 target = $collection.get( $prev );
326 }
327 if ( target ) {
328 target.focus();
329 event.preventDefault();
330 }
331
332 return false;
333 },
334
335 /**
336 * Close popup menu and reset focus on feature activation.
337 *
338 * @since 6.3.0
339 *
340 * @memberof imageEdit
341 *
342 * @param {HTMLElement} el The current element.
343 *
344 * @return {boolean} Always returns false.
345 */
346 closePopup : function(el) {
347 var $parent = $(el).parent( '.imgedit-popup-menu' );
348 var $controlledID = $parent.attr( 'id' );
349 var $target = $( 'button[aria-controls="' + $controlledID + '"]' );
350 $target
351 .attr( 'aria-expanded', 'false' ).trigger( 'focus' );
352 $parent
353 .toggleClass( 'imgedit-popup-menu-open' ).slideToggle( 'fast' );
354
355 return false;
356 },
357
358 /**
359 * Shows or hides the image edit help box.
360 *
361 * @since 2.9.0
362 *
363 * @memberof imageEdit
364 *
365 * @param {HTMLElement} el The element to create the help window in.
366 *
367 * @return {boolean} Always returns false.
368 */
369 toggleHelp : function(el) {
370 var $el = $( el );
371 $el
372 .attr( 'aria-expanded', 'false' === $el.attr( 'aria-expanded' ) ? 'true' : 'false' )
373 .parents( '.imgedit-group-top' ).toggleClass( 'imgedit-help-toggled' ).find( '.imgedit-help' ).slideToggle( 'fast' );
374
375 return false;
376 },
377
378 /**
379 * Shows or hides image edit input fields when enabled.
380 *
381 * @since 6.3.0
382 *
383 * @memberof imageEdit
384 *
385 * @param {HTMLElement} el The element to trigger the edit panel.
386 *
387 * @return {boolean} Always returns false.
388 */
389 toggleControls : function(el) {
390 var $el = $( el );
391 var $target = $( '#' + $el.attr( 'aria-controls' ) );
392 $el
393 .attr( 'aria-expanded', 'false' === $el.attr( 'aria-expanded' ) ? 'true' : 'false' );
394 $target
395 .parent( '.imgedit-group' ).toggleClass( 'imgedit-panel-active' );
396
397 return false;
398 },
399
400 /**
401 * Gets the value from the image edit target.
402 *
403 * The image edit target contains the image sizes where the (possible) changes
404 * have to be applied to.
405 *
406 * @since 2.9.0
407 *
408 * @memberof imageEdit
409 *
410 * @param {number} postid The post ID.
411 *
412 * @return {string} The value from the imagedit-save-target input field when available,
413 * 'full' when not selected, or 'all' if it doesn't exist.
414 */
415 getTarget : function( postid ) {
416 var element = $( '#imgedit-save-target-' + postid );
417
418 if ( element.length ) {
419 return element.find( 'input[name="imgedit-target-' + postid + '"]:checked' ).val() || 'full';
420 }
421
422 return 'all';
423 },
424
425 /**
426 * Recalculates the height or width and keeps the original aspect ratio.
427 *
428 * If the original image size is exceeded a red exclamation mark is shown.
429 *
430 * @since 2.9.0
431 *
432 * @memberof imageEdit
433 *
434 * @param {number} postid The current post ID.
435 * @param {number} x Is 0 when it applies the y-axis
436 * and 1 when applicable for the x-axis.
437 * @param {jQuery} el Element.
438 *
439 * @return {void}
440 */
441 scaleChanged : function( postid, x, el ) {
442 var w = $('#imgedit-scale-width-' + postid), h = $('#imgedit-scale-height-' + postid),
443 warn = $('#imgedit-scale-warn-' + postid), w1 = '', h1 = '',
444 scaleBtn = $('#imgedit-scale-button');
445
446 if ( false === this.validateNumeric( el ) ) {
447 return;
448 }
449
450 if ( x ) {
451 h1 = ( w.val() !== '' ) ? Math.round( w.val() / this.hold.xy_ratio ) : '';
452 h.val( h1 );
453 } else {
454 w1 = ( h.val() !== '' ) ? Math.round( h.val() * this.hold.xy_ratio ) : '';
455 w.val( w1 );
456 }
457
458 if ( ( h1 && h1 > this.hold.oh ) || ( w1 && w1 > this.hold.ow ) ) {
459 warn.css('visibility', 'visible');
460 scaleBtn.prop('disabled', true);
461 } else {
462 warn.css('visibility', 'hidden');
463 scaleBtn.prop('disabled', false);
464 }
465 },
466
467 /**
468 * Gets the selected aspect ratio.
469 *
470 * @since 2.9.0
471 *
472 * @memberof imageEdit
473 *
474 * @param {number} postid The post ID.
475 *
476 * @return {string} The aspect ratio.
477 */
478 getSelRatio : function(postid) {
479 var x = this.hold.w, y = this.hold.h,
480 X = this.intval( $('#imgedit-crop-width-' + postid).val() ),
481 Y = this.intval( $('#imgedit-crop-height-' + postid).val() );
482
483 if ( X && Y ) {
484 return X + ':' + Y;
485 }
486
487 if ( x && y ) {
488 return x + ':' + y;
489 }
490
491 return '1:1';
492 },
493
494 /**
495 * Removes the last action from the image edit history.
496 * The history consist of (edit) actions performed on the image.
497 *
498 * @since 2.9.0
499 *
500 * @memberof imageEdit
501 *
502 * @param {number} postid The post ID.
503 * @param {number} setSize 0 or 1, when 1 the image resets to its original size.
504 *
505 * @return {string} JSON string containing the history or an empty string if no history exists.
506 */
507 filterHistory : function(postid, setSize) {
508 // Apply undo state to history.
509 var history = $('#imgedit-history-' + postid).val(), pop, n, o, i, op = [];
510
511 if ( history !== '' ) {
512 // Read the JSON string with the image edit history.
513 history = JSON.parse(history);
514 pop = this.intval( $('#imgedit-undone-' + postid).val() );
515 if ( pop > 0 ) {
516 while ( pop > 0 ) {
517 history.pop();
518 pop--;
519 }
520 }
521
522 // Reset size to its original state.
523 if ( setSize ) {
524 if ( !history.length ) {
525 this.hold.w = this.hold.ow;
526 this.hold.h = this.hold.oh;
527 return '';
528 }
529
530 // Restore original 'o'.
531 o = history[history.length - 1];
532
533 // c = 'crop', r = 'rotate', f = 'flip'.
534 o = o.c || o.r || o.f || false;
535
536 if ( o ) {
537 // fw = Full image width.
538 this.hold.w = o.fw;
539 // fh = Full image height.
540 this.hold.h = o.fh;
541 }
542 }
543
544 // Filter the last step/action from the history.
545 for ( n in history ) {
546 i = history[n];
547 if ( i.hasOwnProperty('c') ) {
548 op[n] = { 'c': { 'x': i.c.x, 'y': i.c.y, 'w': i.c.w, 'h': i.c.h, 'r': i.c.r } };
549 } else if ( i.hasOwnProperty('r') ) {
550 op[n] = { 'r': i.r.r };
551 } else if ( i.hasOwnProperty('f') ) {
552 op[n] = { 'f': i.f.f };
553 }
554 }
555 return JSON.stringify(op);
556 }
557 return '';
558 },
559 /**
560 * Binds the necessary events to the image.
561 *
562 * When the image source is reloaded the image will be reloaded.
563 *
564 * @since 2.9.0
565 *
566 * @memberof imageEdit
567 *
568 * @param {number} postid The post ID.
569 * @param {string} nonce The nonce to verify the request.
570 * @param {function} callback Function to execute when the image is loaded.
571 *
572 * @return {void}
573 */
574 refreshEditor : function(postid, nonce, callback) {
575 var t = this, data, img;
576
577 t.toggleEditor(postid, 1);
578 data = {
579 'action': 'imgedit-preview',
580 '_ajax_nonce': nonce,
581 'postid': postid,
582 'history': t.filterHistory(postid, 1),
583 'rand': t.intval(Math.random() * 1000000)
584 };
585
586 img = $( '<img id="image-preview-' + postid + '" alt="" />' )
587 .on( 'load', { history: data.history }, function( event ) {
588 var max1, max2,
589 parent = $( '#imgedit-crop-' + postid ),
590 t = imageEdit,
591 historyObj;
592
593 // Checks if there already is some image-edit history.
594 if ( '' !== event.data.history ) {
595 historyObj = JSON.parse( event.data.history );
596 // If last executed action in history is a crop action.
597 if ( historyObj[historyObj.length - 1].hasOwnProperty( 'c' ) ) {
598 /*
599 * A crop action has completed and the crop button gets disabled
600 * ensure the undo button is enabled.
601 */
602 t.setDisabled( $( '#image-undo-' + postid) , true );
603 // Move focus to the undo button to avoid a focus loss.
604 $( '#image-undo-' + postid ).trigger( 'focus' );
605 }
606 }
607
608 parent.empty().append(img);
609
610 // w, h are the new full size dimensions.
611 max1 = Math.max( t.hold.w, t.hold.h );
612 max2 = Math.max( $(img).width(), $(img).height() );
613 t.hold.sizer = max1 > max2 ? max2 / max1 : 1;
614
615 t.initCrop(postid, img, parent);
616
617 if ( (typeof callback !== 'undefined') && callback !== null ) {
618 callback();
619 }
620
621 if ( $('#imgedit-history-' + postid).val() && $('#imgedit-undone-' + postid).val() === '0' ) {
622 $('button.imgedit-submit-btn', '#imgedit-panel-' + postid).prop('disabled', false);
623 } else {
624 $('button.imgedit-submit-btn', '#imgedit-panel-' + postid).prop('disabled', true);
625 }
626 var successMessage = __( 'Image updated.' );
627
628 t.toggleEditor(postid, 0);
629 wp.a11y.speak( successMessage, 'assertive' );
630 })
631 .on( 'error', function() {
632 var errorMessage = __( 'Could not load the preview image. Please reload the page and try again.' );
633
634 $( '#imgedit-crop-' + postid )
635 .empty()
636 .append( '<div class="notice notice-error" tabindex="-1" role="alert"><p>' + errorMessage + '</p></div>' );
637
638 t.toggleEditor( postid, 0, true );
639 wp.a11y.speak( errorMessage, 'assertive' );
640 } )
641 .attr('src', ajaxurl + '?' + $.param(data));
642 },
643 /**
644 * Performs an image edit action.
645 *
646 * @since 2.9.0
647 *
648 * @memberof imageEdit
649 *
650 * @param {number} postid The post ID.
651 * @param {string} nonce The nonce to verify the request.
652 * @param {string} action The action to perform on the image.
653 * The possible actions are: "scale" and "restore".
654 *
655 * @return {boolean|void} Executes a post request that refreshes the page
656 * when the action is performed.
657 * Returns false if an invalid action is given,
658 * or when the action cannot be performed.
659 */
660 action : function(postid, nonce, action) {
661 var t = this, data, w, h, fw, fh;
662
663 if ( t.notsaved(postid) ) {
664 return false;
665 }
666
667 data = {
668 'action': 'image-editor',
669 '_ajax_nonce': nonce,
670 'postid': postid
671 };
672
673 if ( 'scale' === action ) {
674 w = $('#imgedit-scale-width-' + postid),
675 h = $('#imgedit-scale-height-' + postid),
676 fw = t.intval(w.val()),
677 fh = t.intval(h.val());
678
679 if ( fw < 1 ) {
680 w.trigger( 'focus' );
681 return false;
682 } else if ( fh < 1 ) {
683 h.trigger( 'focus' );
684 return false;
685 }
686
687 if ( fw === t.hold.ow || fh === t.hold.oh ) {
688 return false;
689 }
690
691 data['do'] = 'scale';
692 data.fwidth = fw;
693 data.fheight = fh;
694 } else if ( 'restore' === action ) {
695 data['do'] = 'restore';
696 } else {
697 return false;
698 }
699
700 t.toggleEditor(postid, 1);
701 $.post( ajaxurl, data, function( response ) {
702 $( '#image-editor-' + postid ).empty().append( response.data.html );
703 t.toggleEditor( postid, 0, true );
704 // Refresh the attachment model so that changes propagate.
705 if ( t._view ) {
706 t._view.refresh();
707 }
708 } ).done( function( response ) {
709 // Whether the executed action was `scale` or `restore`, the response does have a message.
710 if ( response && response.data.message.msg ) {
711 wp.a11y.speak( response.data.message.msg );
712 return;
713 }
714
715 if ( response && response.data.message.error ) {
716 wp.a11y.speak( response.data.message.error );
717 }
718 } );
719 },
720
721 /**
722 * Stores the changes that are made to the image.
723 *
724 * @since 2.9.0
725 *
726 * @memberof imageEdit
727 *
728 * @param {number} postid The post ID to get the image from the database.
729 * @param {string} nonce The nonce to verify the request.
730 *
731 * @return {boolean|void} If the actions are successfully saved a response message is shown.
732 * Returns false if there is no image editing history,
733 * thus there are not edit-actions performed on the image.
734 */
735 save : function(postid, nonce) {
736 var data,
737 target = this.getTarget(postid),
738 history = this.filterHistory(postid, 0),
739 self = this;
740
741 if ( '' === history ) {
742 return false;
743 }
744
745 this.toggleEditor(postid, 1);
746 data = {
747 'action': 'image-editor',
748 '_ajax_nonce': nonce,
749 'postid': postid,
750 'history': history,
751 'target': target,
752 'context': $('#image-edit-context').length ? $('#image-edit-context').val() : null,
753 'do': 'save'
754 };
755 // Post the image edit data to the backend.
756 $.post( ajaxurl, data, function( response ) {
757 // If a response is returned, close the editor and show an error.
758 if ( response.data.error ) {
759 $( '#imgedit-response-' + postid )
760 .html( '<div class="notice notice-error" tabindex="-1" role="alert"><p>' + response.data.error + '</p></div>' );
761
762 imageEdit.close(postid);
763 wp.a11y.speak( response.data.error );
764 return;
765 }
766
767 if ( response.data.fw && response.data.fh ) {
768 $( '#media-dims-' + postid ).html( response.data.fw + ' &times; ' + response.data.fh );
769 }
770
771 if ( response.data.thumbnail ) {
772 $( '.thumbnail', '#thumbnail-head-' + postid ).attr( 'src', '' + response.data.thumbnail );
773 }
774
775 if ( response.data.msg ) {
776 $( '#imgedit-response-' + postid )
777 .html( '<div class="notice notice-success" tabindex="-1" role="alert"><p>' + response.data.msg + '</p></div>' );
778
779 wp.a11y.speak( response.data.msg );
780 }
781
782 if ( self._view ) {
783 self._view.save();
784 } else {
785 imageEdit.close(postid);
786 }
787 });
788 },
789
790 /**
791 * Creates the image edit window.
792 *
793 * @since 2.9.0
794 *
795 * @memberof imageEdit
796 *
797 * @param {number} postid The post ID for the image.
798 * @param {string} nonce The nonce to verify the request.
799 * @param {Object} view The image editor view to be used for the editing.
800 *
801 * @return {void|promise} Either returns void if the button was already activated
802 * or returns an instance of the image editor, wrapped in a promise.
803 */
804 open : function( postid, nonce, view ) {
805 this._view = view;
806
807 var dfd, data,
808 elem = $( '#image-editor-' + postid ),
809 head = $( '#media-head-' + postid ),
810 btn = $( '#imgedit-open-btn-' + postid ),
811 spin = btn.siblings( '.spinner' );
812
813 /*
814 * Instead of disabling the button, which causes a focus loss and makes screen
815 * readers announce "unavailable", return if the button was already clicked.
816 */
817 if ( btn.hasClass( 'button-activated' ) ) {
818 return;
819 }
820
821 spin.addClass( 'is-active' );
822
823 data = {
824 'action': 'image-editor',
825 '_ajax_nonce': nonce,
826 'postid': postid,
827 'do': 'open'
828 };
829
830 dfd = $.ajax( {
831 url: ajaxurl,
832 type: 'post',
833 data: data,
834 beforeSend: function() {
835 btn.addClass( 'button-activated' );
836 }
837 } ).done( function( response ) {
838 var errorMessage;
839
840 if ( '-1' === response ) {
841 errorMessage = __( 'Could not load the preview image.' );
842 elem.html( '<div class="notice notice-error" tabindex="-1" role="alert"><p>' + errorMessage + '</p></div>' );
843 }
844
845 if ( response.data && response.data.html ) {
846 elem.html( response.data.html );
847 }
848
849 head.fadeOut( 'fast', function() {
850 elem.fadeIn( 'fast', function() {
851 if ( errorMessage ) {
852 $( document ).trigger( 'image-editor-ui-ready' );
853 }
854 } );
855 btn.removeClass( 'button-activated' );
856 spin.removeClass( 'is-active' );
857 } );
858 // Initialize the Image Editor now that everything is ready.
859 imageEdit.init( postid );
860 } );
861
862 return dfd;
863 },
864
865 /**
866 * Initializes the cropping tool and sets a default cropping selection.
867 *
868 * @since 2.9.0
869 *
870 * @memberof imageEdit
871 *
872 * @param {number} postid The post ID.
873 *
874 * @return {void}
875 */
876 imgLoaded : function(postid) {
877 var img = $('#image-preview-' + postid), parent = $('#imgedit-crop-' + postid);
878
879 // Ensure init has run even when directly loaded.
880 if ( 'undefined' === typeof this.hold.sizer ) {
881 this.init( postid );
882 }
883 this.calculateImgSize( postid );
884
885 this.initCrop(postid, img, parent);
886 this.setCropSelection( postid, { 'x1': 0, 'y1': 0, 'x2': 0, 'y2': 0, 'width': img.innerWidth(), 'height': img.innerHeight() } );
887
888 this.toggleEditor( postid, 0, true );
889 },
890
891 /**
892 * Manages keyboard focus in the Image Editor user interface.
893 *
894 * @since 5.5.0
895 *
896 * @return {void}
897 */
898 focusManager: function() {
899 /*
900 * Editor is ready. Move focus to one of the admin alert notices displayed
901 * after a user action or to the first focusable element. Since the DOM
902 * update is pretty large, the timeout helps browsers update their
903 * accessibility tree to better support assistive technologies.
904 */
905 setTimeout( function() {
906 var elementToSetFocusTo = $( '.notice[role="alert"]' );
907
908 if ( ! elementToSetFocusTo.length ) {
909 elementToSetFocusTo = $( '.imgedit-wrap' ).find( ':tabbable:first' );
910 }
911
912 elementToSetFocusTo.attr( 'tabindex', '-1' ).trigger( 'focus' );
913 }, 100 );
914 },
915
916 /**
917 * Initializes the cropping tool.
918 *
919 * @since 2.9.0
920 *
921 * @memberof imageEdit
922 *
923 * @param {number} postid The post ID.
924 * @param {HTMLElement} image The preview image.
925 * @param {HTMLElement} parent The preview image container.
926 *
927 * @return {void}
928 */
929 initCrop : function(postid, image, parent) {
930 var t = this,
931 selW = $('#imgedit-sel-width-' + postid),
932 selH = $('#imgedit-sel-height-' + postid),
933 $image = $( image ),
934 $img;
935
936 // Already initialized?
937 if ( $image.data( 'imgAreaSelect' ) ) {
938 return;
939 }
940
941 t.iasapi = $image.imgAreaSelect({
942 parent: parent,
943 instance: true,
944 handles: true,
945 keys: true,
946 minWidth: 3,
947 minHeight: 3,
948
949 /**
950 * Sets the CSS styles and binds events for locking the aspect ratio.
951 *
952 * @ignore
953 *
954 * @param {jQuery} img The preview image.
955 */
956 onInit: function( img ) {
957 // Ensure that the imgAreaSelect wrapper elements are position:absolute
958 // (even if we're in a position:fixed modal).
959 $img = $( img );
960 $img.next().css( 'position', 'absolute' )
961 .nextAll( '.imgareaselect-outer' ).css( 'position', 'absolute' );
962 /**
963 * Binds mouse down event to the cropping container.
964 *
965 * @return {void}
966 */
967 parent.children().on( 'mousedown touchstart', function(e) {
968 var ratio = false,
969 sel = t.iasapi.getSelection(),
970 cx = t.intval( $( '#imgedit-crop-width-' + postid ).val() ),
971 cy = t.intval( $( '#imgedit-crop-height-' + postid ).val() );
972
973 if ( cx && cy ) {
974 ratio = t.getSelRatio( postid );
975 } else if ( e.shiftKey && sel && sel.width && sel.height ) {
976 ratio = sel.width + ':' + sel.height;
977 }
978
979 t.iasapi.setOptions({
980 aspectRatio: ratio
981 });
982 });
983 },
984
985 /**
986 * Event triggered when starting a selection.
987 *
988 * @ignore
989 *
990 * @return {void}
991 */
992 onSelectStart: function() {
993 imageEdit.setDisabled($('#imgedit-crop-sel-' + postid), 1);
994 imageEdit.setDisabled($('.imgedit-crop-clear'), 1);
995 imageEdit.setDisabled($('.imgedit-crop-apply'), 1);
996 },
997 /**
998 * Event triggered when the selection is ended.
999 *
1000 * @ignore
1001 *
1002 * @param {Object} img jQuery object representing the image.
1003 * @param {Object} c The selection.
1004 *
1005 * @return {Object}
1006 */
1007 onSelectEnd: function(img, c) {
1008 imageEdit.setCropSelection(postid, c);
1009 if ( ! $('#imgedit-crop > *').is(':visible') ) {
1010 imageEdit.toggleControls($('.imgedit-crop.button'));
1011 }
1012 },
1013
1014 /**
1015 * Event triggered when the selection changes.
1016 *
1017 * @ignore
1018 *
1019 * @param {Object} img jQuery object representing the image.
1020 * @param {Object} c The selection.
1021 *
1022 * @return {void}
1023 */
1024 onSelectChange: function(img, c) {
1025 var sizer = imageEdit.hold.sizer,
1026 oldSel = imageEdit.currentCropSelection;
1027
1028 if ( oldSel != null && oldSel.width == c.width && oldSel.height == c.height ) {
1029 return;
1030 }
1031
1032 selW.val( Math.min( imageEdit.hold.w, imageEdit.round( c.width / sizer ) ) );
1033 selH.val( Math.min( imageEdit.hold.h, imageEdit.round( c.height / sizer ) ) );
1034
1035 t.currentCropSelection = c;
1036 }
1037 });
1038 },
1039
1040 /**
1041 * Stores the current crop selection.
1042 *
1043 * @since 2.9.0
1044 *
1045 * @memberof imageEdit
1046 *
1047 * @param {number} postid The post ID.
1048 * @param {Object} c The selection.
1049 *
1050 * @return {boolean}
1051 */
1052 setCropSelection : function(postid, c) {
1053 var sel,
1054 selW = $( '#imgedit-sel-width-' + postid ),
1055 selH = $( '#imgedit-sel-height-' + postid ),
1056 sizer = this.hold.sizer,
1057 hold = this.hold;
1058
1059 c = c || 0;
1060
1061 if ( !c || ( c.width < 3 && c.height < 3 ) ) {
1062 this.setDisabled( $( '.imgedit-crop', '#imgedit-panel-' + postid ), 1 );
1063 this.setDisabled( $( '#imgedit-crop-sel-' + postid ), 1 );
1064 $('#imgedit-sel-width-' + postid).val('');
1065 $('#imgedit-sel-height-' + postid).val('');
1066 $('#imgedit-start-x-' + postid).val('0');
1067 $('#imgedit-start-y-' + postid).val('0');
1068 $('#imgedit-selection-' + postid).val('');
1069 return false;
1070 }
1071
1072 // adjust the selection within the bounds of the image on 100% scale
1073 var excessW = hold.w - ( Math.round( c.x1 / sizer ) + parseInt( selW.val() ) );
1074 var excessH = hold.h - ( Math.round( c.y1 / sizer ) + parseInt( selH.val() ) );
1075 var x = Math.round( c.x1 / sizer ) + Math.min( 0, excessW );
1076 var y = Math.round( c.y1 / sizer ) + Math.min( 0, excessH );
1077
1078 // use 100% scaling to prevent rounding errors
1079 sel = { 'r': 1, 'x': x, 'y': y, 'w': selW.val(), 'h': selH.val() };
1080
1081 this.setDisabled($('.imgedit-crop', '#imgedit-panel-' + postid), 1);
1082 $('#imgedit-selection-' + postid).val( JSON.stringify(sel) );
1083 },
1084
1085
1086 /**
1087 * Closes the image editor.
1088 *
1089 * @since 2.9.0
1090 *
1091 * @memberof imageEdit
1092 *
1093 * @param {number} postid The post ID.
1094 * @param {boolean} warn Warning message.
1095 *
1096 * @return {void|boolean} Returns false if there is a warning.
1097 */
1098 close : function(postid, warn) {
1099 warn = warn || false;
1100
1101 if ( warn && this.notsaved(postid) ) {
1102 return false;
1103 }
1104
1105 this.iasapi = {};
1106 this.hold = {};
1107
1108 // If we've loaded the editor in the context of a Media Modal,
1109 // then switch to the previous view, whatever that might have been.
1110 if ( this._view ){
1111 this._view.back();
1112 }
1113
1114 // In case we are not accessing the image editor in the context of a View,
1115 // close the editor the old-school way.
1116 else {
1117 $('#image-editor-' + postid).fadeOut('fast', function() {
1118 $( '#media-head-' + postid ).fadeIn( 'fast', function() {
1119 // Move focus back to the Edit Image button. Runs also when saving.
1120 $( '#imgedit-open-btn-' + postid ).trigger( 'focus' );
1121 });
1122 $(this).empty();
1123 });
1124 }
1125
1126
1127 },
1128
1129 /**
1130 * Checks if the image edit history is saved.
1131 *
1132 * @since 2.9.0
1133 *
1134 * @memberof imageEdit
1135 *
1136 * @param {number} postid The post ID.
1137 *
1138 * @return {boolean} Returns true if the history is not saved.
1139 */
1140 notsaved : function(postid) {
1141 var h = $('#imgedit-history-' + postid).val(),
1142 history = ( h !== '' ) ? JSON.parse(h) : [],
1143 pop = this.intval( $('#imgedit-undone-' + postid).val() );
1144
1145 if ( pop < history.length ) {
1146 if ( confirm( $('#imgedit-leaving-' + postid).text() ) ) {
1147 return false;
1148 }
1149 return true;
1150 }
1151 return false;
1152 },
1153
1154 /**
1155 * Adds an image edit action to the history.
1156 *
1157 * @since 2.9.0
1158 *
1159 * @memberof imageEdit
1160 *
1161 * @param {Object} op The original position.
1162 * @param {number} postid The post ID.
1163 * @param {string} nonce The nonce.
1164 *
1165 * @return {void}
1166 */
1167 addStep : function(op, postid, nonce) {
1168 var t = this, elem = $('#imgedit-history-' + postid),
1169 history = ( elem.val() !== '' ) ? JSON.parse( elem.val() ) : [],
1170 undone = $( '#imgedit-undone-' + postid ),
1171 pop = t.intval( undone.val() );
1172
1173 while ( pop > 0 ) {
1174 history.pop();
1175 pop--;
1176 }
1177 undone.val(0); // Reset.
1178
1179 history.push(op);
1180 elem.val( JSON.stringify(history) );
1181
1182 t.refreshEditor(postid, nonce, function() {
1183 t.setDisabled($('#image-undo-' + postid), true);
1184 t.setDisabled($('#image-redo-' + postid), false);
1185 });
1186 },
1187
1188 /**
1189 * Rotates the image.
1190 *
1191 * @since 2.9.0
1192 *
1193 * @memberof imageEdit
1194 *
1195 * @param {string} angle The angle the image is rotated with.
1196 * @param {number} postid The post ID.
1197 * @param {string} nonce The nonce.
1198 * @param {Object} t The target element.
1199 *
1200 * @return {boolean}
1201 */
1202 rotate : function(angle, postid, nonce, t) {
1203 if ( $(t).hasClass('disabled') ) {
1204 return false;
1205 }
1206 this.closePopup(t);
1207 this.addStep({ 'r': { 'r': angle, 'fw': this.hold.h, 'fh': this.hold.w }}, postid, nonce);
1208
1209 // Clear the selection fields after rotating.
1210 $( '#imgedit-sel-width-' + postid ).val( '' );
1211 $( '#imgedit-sel-height-' + postid ).val( '' );
1212 this.currentCropSelection = null;
1213 },
1214
1215 /**
1216 * Flips the image.
1217 *
1218 * @since 2.9.0
1219 *
1220 * @memberof imageEdit
1221 *
1222 * @param {number} axis The axle the image is flipped on.
1223 * @param {number} postid The post ID.
1224 * @param {string} nonce The nonce.
1225 * @param {Object} t The target element.
1226 *
1227 * @return {boolean}
1228 */
1229 flip : function (axis, postid, nonce, t) {
1230 if ( $(t).hasClass('disabled') ) {
1231 return false;
1232 }
1233 this.closePopup(t);
1234 this.addStep({ 'f': { 'f': axis, 'fw': this.hold.w, 'fh': this.hold.h }}, postid, nonce);
1235
1236 // Clear the selection fields after flipping.
1237 $( '#imgedit-sel-width-' + postid ).val( '' );
1238 $( '#imgedit-sel-height-' + postid ).val( '' );
1239 this.currentCropSelection = null;
1240 },
1241
1242 /**
1243 * Crops the image.
1244 *
1245 * @since 2.9.0
1246 *
1247 * @memberof imageEdit
1248 *
1249 * @param {number} postid The post ID.
1250 * @param {string} nonce The nonce.
1251 * @param {Object} t The target object.
1252 *
1253 * @return {void|boolean} Returns false if the crop button is disabled.
1254 */
1255 crop : function (postid, nonce, t) {
1256 var sel = $('#imgedit-selection-' + postid).val(),
1257 w = this.intval( $('#imgedit-sel-width-' + postid).val() ),
1258 h = this.intval( $('#imgedit-sel-height-' + postid).val() );
1259
1260 if ( $(t).hasClass('disabled') || sel === '' ) {
1261 return false;
1262 }
1263
1264 sel = JSON.parse(sel);
1265 if ( sel.w > 0 && sel.h > 0 && w > 0 && h > 0 ) {
1266 sel.fw = w;
1267 sel.fh = h;
1268 this.addStep({ 'c': sel }, postid, nonce);
1269 }
1270
1271 // Clear the selection fields after cropping.
1272 $( '#imgedit-sel-width-' + postid ).val( '' );
1273 $( '#imgedit-sel-height-' + postid ).val( '' );
1274 $( '#imgedit-start-x-' + postid ).val( '0' );
1275 $( '#imgedit-start-y-' + postid ).val( '0' );
1276 this.currentCropSelection = null;
1277 },
1278
1279 /**
1280 * Undoes an image edit action.
1281 *
1282 * @since 2.9.0
1283 *
1284 * @memberof imageEdit
1285 *
1286 * @param {number} postid The post ID.
1287 * @param {string} nonce The nonce.
1288 *
1289 * @return {void|false} Returns false if the undo button is disabled.
1290 */
1291 undo : function (postid, nonce) {
1292 var t = this, button = $('#image-undo-' + postid), elem = $('#imgedit-undone-' + postid),
1293 pop = t.intval( elem.val() ) + 1;
1294
1295 if ( button.hasClass('disabled') ) {
1296 return;
1297 }
1298
1299 elem.val(pop);
1300 t.refreshEditor(postid, nonce, function() {
1301 var elem = $('#imgedit-history-' + postid),
1302 history = ( elem.val() !== '' ) ? JSON.parse( elem.val() ) : [];
1303
1304 t.setDisabled($('#image-redo-' + postid), true);
1305 t.setDisabled(button, pop < history.length);
1306 // When undo gets disabled, move focus to the redo button to avoid a focus loss.
1307 if ( history.length === pop ) {
1308 $( '#image-redo-' + postid ).trigger( 'focus' );
1309 }
1310 });
1311 },
1312
1313 /**
1314 * Reverts a undo action.
1315 *
1316 * @since 2.9.0
1317 *
1318 * @memberof imageEdit
1319 *
1320 * @param {number} postid The post ID.
1321 * @param {string} nonce The nonce.
1322 *
1323 * @return {void}
1324 */
1325 redo : function(postid, nonce) {
1326 var t = this, button = $('#image-redo-' + postid), elem = $('#imgedit-undone-' + postid),
1327 pop = t.intval( elem.val() ) - 1;
1328
1329 if ( button.hasClass('disabled') ) {
1330 return;
1331 }
1332
1333 elem.val(pop);
1334 t.refreshEditor(postid, nonce, function() {
1335 t.setDisabled($('#image-undo-' + postid), true);
1336 t.setDisabled(button, pop > 0);
1337 // When redo gets disabled, move focus to the undo button to avoid a focus loss.
1338 if ( 0 === pop ) {
1339 $( '#image-undo-' + postid ).trigger( 'focus' );
1340 }
1341 });
1342 },
1343
1344 /**
1345 * Sets the selection for the height and width in pixels.
1346 *
1347 * @since 2.9.0
1348 *
1349 * @memberof imageEdit
1350 *
1351 * @param {number} postid The post ID.
1352 * @param {jQuery} el The element containing the values.
1353 *
1354 * @return {void|boolean} Returns false when the x or y value is lower than 1,
1355 * void when the value is not numeric or when the operation
1356 * is successful.
1357 */
1358 setNumSelection : function( postid, el ) {
1359 var sel, elX = $('#imgedit-sel-width-' + postid), elY = $('#imgedit-sel-height-' + postid),
1360 elX1 = $('#imgedit-start-x-' + postid), elY1 = $('#imgedit-start-y-' + postid),
1361 xS = this.intval( elX1.val() ), yS = this.intval( elY1.val() ),
1362 x = this.intval( elX.val() ), y = this.intval( elY.val() ),
1363 img = $('#image-preview-' + postid), imgh = img.height(), imgw = img.width(),
1364 sizer = this.hold.sizer, x1, y1, x2, y2, ias = this.iasapi;
1365
1366 this.currentCropSelection = null;
1367
1368 if ( false === this.validateNumeric( el ) ) {
1369 return;
1370 }
1371
1372 if ( x < 1 ) {
1373 elX.val('');
1374 return false;
1375 }
1376
1377 if ( y < 1 ) {
1378 elY.val('');
1379 return false;
1380 }
1381
1382 if ( ( ( x && y ) || ( xS && yS ) ) && ( sel = ias.getSelection() ) ) {
1383 x2 = sel.x1 + Math.round( x * sizer );
1384 y2 = sel.y1 + Math.round( y * sizer );
1385 x1 = ( xS === sel.x1 ) ? sel.x1 : Math.round( xS * sizer );
1386 y1 = ( yS === sel.y1 ) ? sel.y1 : Math.round( yS * sizer );
1387
1388 if ( x2 > imgw ) {
1389 x1 = 0;
1390 x2 = imgw;
1391 elX.val( Math.min( this.hold.w, Math.round( x2 / sizer ) ) );
1392 }
1393
1394 if ( y2 > imgh ) {
1395 y1 = 0;
1396 y2 = imgh;
1397 elY.val( Math.min( this.hold.h, Math.round( y2 / sizer ) ) );
1398 }
1399
1400 ias.setSelection( x1, y1, x2, y2 );
1401 ias.update();
1402 this.setCropSelection(postid, ias.getSelection());
1403 this.currentCropSelection = ias.getSelection();
1404 }
1405 },
1406
1407 /**
1408 * Rounds a number to a whole.
1409 *
1410 * @since 2.9.0
1411 *
1412 * @memberof imageEdit
1413 *
1414 * @param {number} num The number.
1415 *
1416 * @return {number} The number rounded to a whole number.
1417 */
1418 round : function(num) {
1419 var s;
1420 num = Math.round(num);
1421
1422 if ( this.hold.sizer > 0.6 ) {
1423 return num;
1424 }
1425
1426 s = num.toString().slice(-1);
1427
1428 if ( '1' === s ) {
1429 return num - 1;
1430 } else if ( '9' === s ) {
1431 return num + 1;
1432 }
1433
1434 return num;
1435 },
1436
1437 /**
1438 * Sets a locked aspect ratio for the selection.
1439 *
1440 * @since 2.9.0
1441 *
1442 * @memberof imageEdit
1443 *
1444 * @param {number} postid The post ID.
1445 * @param {number} n The ratio to set.
1446 * @param {jQuery} el The element containing the values.
1447 *
1448 * @return {void}
1449 */
1450 setRatioSelection : function(postid, n, el) {
1451 var sel, r, x = this.intval( $('#imgedit-crop-width-' + postid).val() ),
1452 y = this.intval( $('#imgedit-crop-height-' + postid).val() ),
1453 h = $('#image-preview-' + postid).height();
1454
1455 if ( false === this.validateNumeric( el ) ) {
1456 this.iasapi.setOptions({
1457 aspectRatio: null
1458 });
1459
1460 return;
1461 }
1462
1463 if ( x && y ) {
1464 this.iasapi.setOptions({
1465 aspectRatio: x + ':' + y
1466 });
1467
1468 if ( sel = this.iasapi.getSelection(true) ) {
1469 r = Math.ceil( sel.y1 + ( ( sel.x2 - sel.x1 ) / ( x / y ) ) );
1470
1471 if ( r > h ) {
1472 r = h;
1473 var errorMessage = __( 'Selected crop ratio exceeds the boundaries of the image. Try a different ratio.' );
1474
1475 $( '#imgedit-crop-' + postid )
1476 .prepend( '<div class="notice notice-error" tabindex="-1" role="alert"><p>' + errorMessage + '</p></div>' );
1477
1478 wp.a11y.speak( errorMessage, 'assertive' );
1479 if ( n ) {
1480 $('#imgedit-crop-height-' + postid).val( '' );
1481 } else {
1482 $('#imgedit-crop-width-' + postid).val( '');
1483 }
1484 } else {
1485 var error = $( '#imgedit-crop-' + postid ).find( '.notice-error' );
1486 if ( 'undefined' !== typeof( error ) ) {
1487 error.remove();
1488 }
1489 }
1490
1491 this.iasapi.setSelection( sel.x1, sel.y1, sel.x2, r );
1492 this.iasapi.update();
1493 }
1494 }
1495 },
1496
1497 /**
1498 * Validates if a value in a jQuery.HTMLElement is numeric.
1499 *
1500 * @since 4.6.0
1501 *
1502 * @memberof imageEdit
1503 *
1504 * @param {jQuery} el The html element.
1505 *
1506 * @return {void|boolean} Returns false if the value is not numeric,
1507 * void when it is.
1508 */
1509 validateNumeric: function( el ) {
1510 if ( false === this.intval( $( el ).val() ) ) {
1511 $( el ).val( '' );
1512 return false;
1513 }
1514 }
1515};
1516})(jQuery);
1517
Ui Ux Design – Teachers Night Out https://cardgames4educators.com Wed, 16 Oct 2024 22:24:18 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.4 https://cardgames4educators.com/wp-content/uploads/2024/06/cropped-Card-4-Educators-logo-32x32.png Ui Ux Design – Teachers Night Out https://cardgames4educators.com 32 32 Masters In English How English Speaker https://cardgames4educators.com/masters-in-english-how-english-speaker/ https://cardgames4educators.com/masters-in-english-how-english-speaker/#comments Mon, 27 May 2024 08:54:45 +0000 https://themexriver.com/wp/kadu/?p=1

Erat himenaeos neque id sagittis massa. Hac suscipit pulvinar dignissim platea magnis eu. Don tellus a pharetra inceptos efficitur dui pulvinar. Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent pulvinar odio volutpat parturient. Quisque risus finibus suspendisse mus purus magnis facilisi condimentum consectetur dui. Curae elit suspendisse cursus vehicula.

Turpis taciti class non vel pretium quis pulvinar tempor lobortis nunc. Libero phasellus parturient sapien volutpat malesuada ornare. Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae. Porta est tempor ex eget feugiat vulputate ipsum. Justo nec iaculis habitant diam arcu fermentum.

We offer comprehen sive emplo ment services such as assistance wit employer compliance.Our company is your strategic HR partner as instead of HR. john smithson

Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae.

Exploring Learning Landscapes in Academic

Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent.

]]>
https://cardgames4educators.com/masters-in-english-how-english-speaker/feed/ 1