balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

widgets.js (23098B)


      1 /**
      2  * @output wp-admin/js/widgets.js
      3  */
      4 
      5 /* global ajaxurl, isRtl, wpWidgets */
      6 
      7 (function($) {
      8 	var $document = $( document );
      9 
     10 window.wpWidgets = {
     11 	/**
     12 	 * A closed Sidebar that gets a Widget dragged over it.
     13 	 *
     14 	 * @var {element|null}
     15 	 */
     16 	hoveredSidebar: null,
     17 
     18 	/**
     19 	 * Lookup of which widgets have had change events triggered.
     20 	 *
     21 	 * @var {object}
     22 	 */
     23 	dirtyWidgets: {},
     24 
     25 	init : function() {
     26 		var rem, the_id,
     27 			self = this,
     28 			chooser = $('.widgets-chooser'),
     29 			selectSidebar = chooser.find('.widgets-chooser-sidebars'),
     30 			sidebars = $('div.widgets-sortables'),
     31 			isRTL = !! ( 'undefined' !== typeof isRtl && isRtl );
     32 
     33 		// Handle the widgets containers in the right column.
     34 		$( '#widgets-right .sidebar-name' )
     35 			/*
     36 			 * Toggle the widgets containers when clicked and update the toggle
     37 			 * button `aria-expanded` attribute value.
     38 			 */
     39 			.on( 'click', function() {
     40 				var $this = $( this ),
     41 					$wrap = $this.closest( '.widgets-holder-wrap '),
     42 					$toggle = $this.find( '.handlediv' );
     43 
     44 				if ( $wrap.hasClass( 'closed' ) ) {
     45 					$wrap.removeClass( 'closed' );
     46 					$toggle.attr( 'aria-expanded', 'true' );
     47 					// Refresh the jQuery UI sortable items.
     48 					$this.parent().sortable( 'refresh' );
     49 				} else {
     50 					$wrap.addClass( 'closed' );
     51 					$toggle.attr( 'aria-expanded', 'false' );
     52 				}
     53 
     54 				// Update the admin menu "sticky" state.
     55 				$document.triggerHandler( 'wp-pin-menu' );
     56 			})
     57 			/*
     58 			 * Set the initial `aria-expanded` attribute value on the widgets
     59 			 * containers toggle button. The first one is expanded by default.
     60 			 */
     61 			.find( '.handlediv' ).each( function( index ) {
     62 				if ( 0 === index ) {
     63 					// jQuery equivalent of `continue` within an `each()` loop.
     64 					return;
     65 				}
     66 
     67 				$( this ).attr( 'aria-expanded', 'false' );
     68 			});
     69 
     70 		// Show AYS dialog when there are unsaved widget changes.
     71 		$( window ).on( 'beforeunload.widgets', function( event ) {
     72 			var dirtyWidgetIds = [], unsavedWidgetsElements;
     73 			$.each( self.dirtyWidgets, function( widgetId, dirty ) {
     74 				if ( dirty ) {
     75 					dirtyWidgetIds.push( widgetId );
     76 				}
     77 			});
     78 			if ( 0 !== dirtyWidgetIds.length ) {
     79 				unsavedWidgetsElements = $( '#widgets-right' ).find( '.widget' ).filter( function() {
     80 					return -1 !== dirtyWidgetIds.indexOf( $( this ).prop( 'id' ).replace( /^widget-\d+_/, '' ) );
     81 				});
     82 				unsavedWidgetsElements.each( function() {
     83 					if ( ! $( this ).hasClass( 'open' ) ) {
     84 						$( this ).find( '.widget-title-action:first' ).trigger( 'click' );
     85 					}
     86 				});
     87 
     88 				// Bring the first unsaved widget into view and focus on the first tabbable field.
     89 				unsavedWidgetsElements.first().each( function() {
     90 					if ( this.scrollIntoViewIfNeeded ) {
     91 						this.scrollIntoViewIfNeeded();
     92 					} else {
     93 						this.scrollIntoView();
     94 					}
     95 					$( this ).find( '.widget-inside :tabbable:first' ).trigger( 'focus' );
     96 				} );
     97 
     98 				event.returnValue = wp.i18n.__( 'The changes you made will be lost if you navigate away from this page.' );
     99 				return event.returnValue;
    100 			}
    101 		});
    102 
    103 		// Handle the widgets containers in the left column.
    104 		$( '#widgets-left .sidebar-name' ).on( 'click', function() {
    105 			var $wrap = $( this ).closest( '.widgets-holder-wrap' );
    106 
    107 			$wrap
    108 				.toggleClass( 'closed' )
    109 				.find( '.handlediv' ).attr( 'aria-expanded', ! $wrap.hasClass( 'closed' ) );
    110 
    111 			// Update the admin menu "sticky" state.
    112 			$document.triggerHandler( 'wp-pin-menu' );
    113 		});
    114 
    115 		$(document.body).on('click.widgets-toggle', function(e) {
    116 			var target = $(e.target), css = {},
    117 				widget, inside, targetWidth, widgetWidth, margin, saveButton, widgetId,
    118 				toggleBtn = target.closest( '.widget' ).find( '.widget-top button.widget-action' );
    119 
    120 			if ( target.parents('.widget-top').length && ! target.parents('#available-widgets').length ) {
    121 				widget = target.closest('div.widget');
    122 				inside = widget.children('.widget-inside');
    123 				targetWidth = parseInt( widget.find('input.widget-width').val(), 10 );
    124 				widgetWidth = widget.parent().width();
    125 				widgetId = inside.find( '.widget-id' ).val();
    126 
    127 				// Save button is initially disabled, but is enabled when a field is changed.
    128 				if ( ! widget.data( 'dirty-state-initialized' ) ) {
    129 					saveButton = inside.find( '.widget-control-save' );
    130 					saveButton.prop( 'disabled', true ).val( wp.i18n.__( 'Saved' ) );
    131 					inside.on( 'input change', function() {
    132 						self.dirtyWidgets[ widgetId ] = true;
    133 						widget.addClass( 'widget-dirty' );
    134 						saveButton.prop( 'disabled', false ).val( wp.i18n.__( 'Save' ) );
    135 					});
    136 					widget.data( 'dirty-state-initialized', true );
    137 				}
    138 
    139 				if ( inside.is(':hidden') ) {
    140 					if ( targetWidth > 250 && ( targetWidth + 30 > widgetWidth ) && widget.closest('div.widgets-sortables').length ) {
    141 						if ( widget.closest('div.widget-liquid-right').length ) {
    142 							margin = isRTL ? 'margin-right' : 'margin-left';
    143 						} else {
    144 							margin = isRTL ? 'margin-left' : 'margin-right';
    145 						}
    146 
    147 						css[ margin ] = widgetWidth - ( targetWidth + 30 ) + 'px';
    148 						widget.css( css );
    149 					}
    150 					/*
    151 					 * Don't change the order of attributes changes and animation:
    152 					 * it's important for screen readers, see ticket #31476.
    153 					 */
    154 					toggleBtn.attr( 'aria-expanded', 'true' );
    155 					inside.slideDown( 'fast', function() {
    156 						widget.addClass( 'open' );
    157 					});
    158 				} else {
    159 					/*
    160 					 * Don't change the order of attributes changes and animation:
    161 					 * it's important for screen readers, see ticket #31476.
    162 					 */
    163 					toggleBtn.attr( 'aria-expanded', 'false' );
    164 					inside.slideUp( 'fast', function() {
    165 						widget.attr( 'style', '' );
    166 						widget.removeClass( 'open' );
    167 					});
    168 				}
    169 			} else if ( target.hasClass('widget-control-save') ) {
    170 				wpWidgets.save( target.closest('div.widget'), 0, 1, 0 );
    171 				e.preventDefault();
    172 			} else if ( target.hasClass('widget-control-remove') ) {
    173 				wpWidgets.save( target.closest('div.widget'), 1, 1, 0 );
    174 			} else if ( target.hasClass('widget-control-close') ) {
    175 				widget = target.closest('div.widget');
    176 				widget.removeClass( 'open' );
    177 				toggleBtn.attr( 'aria-expanded', 'false' );
    178 				wpWidgets.close( widget );
    179 			} else if ( target.attr( 'id' ) === 'inactive-widgets-control-remove' ) {
    180 				wpWidgets.removeInactiveWidgets();
    181 				e.preventDefault();
    182 			}
    183 		});
    184 
    185 		sidebars.children('.widget').each( function() {
    186 			var $this = $(this);
    187 
    188 			wpWidgets.appendTitle( this );
    189 
    190 			if ( $this.find( 'p.widget-error' ).length ) {
    191 				$this.find( '.widget-action' ).trigger( 'click' ).attr( 'aria-expanded', 'true' );
    192 			}
    193 		});
    194 
    195 		$('#widget-list').children('.widget').draggable({
    196 			connectToSortable: 'div.widgets-sortables',
    197 			handle: '> .widget-top > .widget-title',
    198 			distance: 2,
    199 			helper: 'clone',
    200 			zIndex: 101,
    201 			containment: '#wpwrap',
    202 			refreshPositions: true,
    203 			start: function( event, ui ) {
    204 				var chooser = $(this).find('.widgets-chooser');
    205 
    206 				ui.helper.find('div.widget-description').hide();
    207 				the_id = this.id;
    208 
    209 				if ( chooser.length ) {
    210 					// Hide the chooser and move it out of the widget.
    211 					$( '#wpbody-content' ).append( chooser.hide() );
    212 					// Delete the cloned chooser from the drag helper.
    213 					ui.helper.find('.widgets-chooser').remove();
    214 					self.clearWidgetSelection();
    215 				}
    216 			},
    217 			stop: function() {
    218 				if ( rem ) {
    219 					$(rem).hide();
    220 				}
    221 
    222 				rem = '';
    223 			}
    224 		});
    225 
    226 		/**
    227 		 * Opens and closes previously closed Sidebars when Widgets are dragged over/out of them.
    228 		 */
    229 		sidebars.droppable( {
    230 			tolerance: 'intersect',
    231 
    232 			/**
    233 			 * Open Sidebar when a Widget gets dragged over it.
    234 			 *
    235 			 * @ignore
    236 			 *
    237 			 * @param {Object} event jQuery event object.
    238 			 */
    239 			over: function( event ) {
    240 				var $wrap = $( event.target ).parent();
    241 
    242 				if ( wpWidgets.hoveredSidebar && ! $wrap.is( wpWidgets.hoveredSidebar ) ) {
    243 					// Close the previous Sidebar as the Widget has been dragged onto another Sidebar.
    244 					wpWidgets.closeSidebar( event );
    245 				}
    246 
    247 				if ( $wrap.hasClass( 'closed' ) ) {
    248 					wpWidgets.hoveredSidebar = $wrap;
    249 					$wrap
    250 						.removeClass( 'closed' )
    251 						.find( '.handlediv' ).attr( 'aria-expanded', 'true' );
    252 				}
    253 
    254 				$( this ).sortable( 'refresh' );
    255 			},
    256 
    257 			/**
    258 			 * Close Sidebar when the Widget gets dragged out of it.
    259 			 *
    260 			 * @ignore
    261 			 *
    262 			 * @param {Object} event jQuery event object.
    263 			 */
    264 			out: function( event ) {
    265 				if ( wpWidgets.hoveredSidebar ) {
    266 					wpWidgets.closeSidebar( event );
    267 				}
    268 			}
    269 		} );
    270 
    271 		sidebars.sortable({
    272 			placeholder: 'widget-placeholder',
    273 			items: '> .widget',
    274 			handle: '> .widget-top > .widget-title',
    275 			cursor: 'move',
    276 			distance: 2,
    277 			containment: '#wpwrap',
    278 			tolerance: 'pointer',
    279 			refreshPositions: true,
    280 			start: function( event, ui ) {
    281 				var height, $this = $(this),
    282 					$wrap = $this.parent(),
    283 					inside = ui.item.children('.widget-inside');
    284 
    285 				if ( inside.css('display') === 'block' ) {
    286 					ui.item.removeClass('open');
    287 					ui.item.find( '.widget-top button.widget-action' ).attr( 'aria-expanded', 'false' );
    288 					inside.hide();
    289 					$(this).sortable('refreshPositions');
    290 				}
    291 
    292 				if ( ! $wrap.hasClass('closed') ) {
    293 					// Lock all open sidebars min-height when starting to drag.
    294 					// Prevents jumping when dragging a widget from an open sidebar to a closed sidebar below.
    295 					height = ui.item.hasClass('ui-draggable') ? $this.height() : 1 + $this.height();
    296 					$this.css( 'min-height', height + 'px' );
    297 				}
    298 			},
    299 
    300 			stop: function( event, ui ) {
    301 				var addNew, widgetNumber, $sidebar, $children, child, item,
    302 					$widget = ui.item,
    303 					id = the_id;
    304 
    305 				// Reset the var to hold a previously closed sidebar.
    306 				wpWidgets.hoveredSidebar = null;
    307 
    308 				if ( $widget.hasClass('deleting') ) {
    309 					wpWidgets.save( $widget, 1, 0, 1 ); // Delete widget.
    310 					$widget.remove();
    311 					return;
    312 				}
    313 
    314 				addNew = $widget.find('input.add_new').val();
    315 				widgetNumber = $widget.find('input.multi_number').val();
    316 
    317 				$widget.attr( 'style', '' ).removeClass('ui-draggable');
    318 				the_id = '';
    319 
    320 				if ( addNew ) {
    321 					if ( 'multi' === addNew ) {
    322 						$widget.html(
    323 							$widget.html().replace( /<[^<>]+>/g, function( tag ) {
    324 								return tag.replace( /__i__|%i%/g, widgetNumber );
    325 							})
    326 						);
    327 
    328 						$widget.attr( 'id', id.replace( '__i__', widgetNumber ) );
    329 						widgetNumber++;
    330 
    331 						$( 'div#' + id ).find( 'input.multi_number' ).val( widgetNumber );
    332 					} else if ( 'single' === addNew ) {
    333 						$widget.attr( 'id', 'new-' + id );
    334 						rem = 'div#' + id;
    335 					}
    336 
    337 					wpWidgets.save( $widget, 0, 0, 1 );
    338 					$widget.find('input.add_new').val('');
    339 					$document.trigger( 'widget-added', [ $widget ] );
    340 				}
    341 
    342 				$sidebar = $widget.parent();
    343 
    344 				if ( $sidebar.parent().hasClass('closed') ) {
    345 					$sidebar.parent()
    346 						.removeClass( 'closed' )
    347 						.find( '.handlediv' ).attr( 'aria-expanded', 'true' );
    348 
    349 					$children = $sidebar.children('.widget');
    350 
    351 					// Make sure the dropped widget is at the top.
    352 					if ( $children.length > 1 ) {
    353 						child = $children.get(0);
    354 						item = $widget.get(0);
    355 
    356 						if ( child.id && item.id && child.id !== item.id ) {
    357 							$( child ).before( $widget );
    358 						}
    359 					}
    360 				}
    361 
    362 				if ( addNew ) {
    363 					$widget.find( '.widget-action' ).trigger( 'click' );
    364 				} else {
    365 					wpWidgets.saveOrder( $sidebar.attr('id') );
    366 				}
    367 			},
    368 
    369 			activate: function() {
    370 				$(this).parent().addClass( 'widget-hover' );
    371 			},
    372 
    373 			deactivate: function() {
    374 				// Remove all min-height added on "start".
    375 				$(this).css( 'min-height', '' ).parent().removeClass( 'widget-hover' );
    376 			},
    377 
    378 			receive: function( event, ui ) {
    379 				var $sender = $( ui.sender );
    380 
    381 				// Don't add more widgets to orphaned sidebars.
    382 				if ( this.id.indexOf('orphaned_widgets') > -1 ) {
    383 					$sender.sortable('cancel');
    384 					return;
    385 				}
    386 
    387 				// If the last widget was moved out of an orphaned sidebar, close and remove it.
    388 				if ( $sender.attr('id').indexOf('orphaned_widgets') > -1 && ! $sender.children('.widget').length ) {
    389 					$sender.parents('.orphan-sidebar').slideUp( 400, function(){ $(this).remove(); } );
    390 				}
    391 			}
    392 		}).sortable( 'option', 'connectWith', 'div.widgets-sortables' );
    393 
    394 		$('#available-widgets').droppable({
    395 			tolerance: 'pointer',
    396 			accept: function(o){
    397 				return $(o).parent().attr('id') !== 'widget-list';
    398 			},
    399 			drop: function(e,ui) {
    400 				ui.draggable.addClass('deleting');
    401 				$('#removing-widget').hide().children('span').empty();
    402 			},
    403 			over: function(e,ui) {
    404 				ui.draggable.addClass('deleting');
    405 				$('div.widget-placeholder').hide();
    406 
    407 				if ( ui.draggable.hasClass('ui-sortable-helper') ) {
    408 					$('#removing-widget').show().children('span')
    409 					.html( ui.draggable.find( 'div.widget-title' ).children( 'h3' ).html() );
    410 				}
    411 			},
    412 			out: function(e,ui) {
    413 				ui.draggable.removeClass('deleting');
    414 				$('div.widget-placeholder').show();
    415 				$('#removing-widget').hide().children('span').empty();
    416 			}
    417 		});
    418 
    419 		// Area Chooser.
    420 		$( '#widgets-right .widgets-holder-wrap' ).each( function( index, element ) {
    421 			var $element = $( element ),
    422 				name = $element.find( '.sidebar-name h2' ).text() || '',
    423 				ariaLabel = $element.find( '.sidebar-name' ).data( 'add-to' ),
    424 				id = $element.find( '.widgets-sortables' ).attr( 'id' ),
    425 				li = $( '<li>' ),
    426 				button = $( '<button>', {
    427 					type: 'button',
    428 					'aria-pressed': 'false',
    429 					'class': 'widgets-chooser-button',
    430 					'aria-label': ariaLabel
    431 				} ).text( name.toString().trim() );
    432 
    433 			li.append( button );
    434 
    435 			if ( index === 0 ) {
    436 				li.addClass( 'widgets-chooser-selected' );
    437 				button.attr( 'aria-pressed', 'true' );
    438 			}
    439 
    440 			selectSidebar.append( li );
    441 			li.data( 'sidebarId', id );
    442 		});
    443 
    444 		$( '#available-widgets .widget .widget-top' ).on( 'click.widgets-chooser', function() {
    445 			var $widget = $( this ).closest( '.widget' ),
    446 				toggleButton = $( this ).find( '.widget-action' ),
    447 				chooserButtons = selectSidebar.find( '.widgets-chooser-button' );
    448 
    449 			if ( $widget.hasClass( 'widget-in-question' ) || $( '#widgets-left' ).hasClass( 'chooser' ) ) {
    450 				toggleButton.attr( 'aria-expanded', 'false' );
    451 				self.closeChooser();
    452 			} else {
    453 				// Open the chooser.
    454 				self.clearWidgetSelection();
    455 				$( '#widgets-left' ).addClass( 'chooser' );
    456 				// Add CSS class and insert the chooser after the widget description.
    457 				$widget.addClass( 'widget-in-question' ).children( '.widget-description' ).after( chooser );
    458 				// Open the chooser with a slide down animation.
    459 				chooser.slideDown( 300, function() {
    460 					// Update the toggle button aria-expanded attribute after previous DOM manipulations.
    461 					toggleButton.attr( 'aria-expanded', 'true' );
    462 				});
    463 
    464 				chooserButtons.on( 'click.widgets-chooser', function() {
    465 					selectSidebar.find( '.widgets-chooser-selected' ).removeClass( 'widgets-chooser-selected' );
    466 					chooserButtons.attr( 'aria-pressed', 'false' );
    467 					$( this )
    468 						.attr( 'aria-pressed', 'true' )
    469 						.closest( 'li' ).addClass( 'widgets-chooser-selected' );
    470 				} );
    471 			}
    472 		});
    473 
    474 		// Add event handlers.
    475 		chooser.on( 'click.widgets-chooser', function( event ) {
    476 			var $target = $( event.target );
    477 
    478 			if ( $target.hasClass('button-primary') ) {
    479 				self.addWidget( chooser );
    480 				self.closeChooser();
    481 			} else if ( $target.hasClass( 'widgets-chooser-cancel' ) ) {
    482 				self.closeChooser();
    483 			}
    484 		}).on( 'keyup.widgets-chooser', function( event ) {
    485 			if ( event.which === $.ui.keyCode.ESCAPE ) {
    486 				self.closeChooser();
    487 			}
    488 		});
    489 	},
    490 
    491 	saveOrder : function( sidebarId ) {
    492 		var data = {
    493 			action: 'widgets-order',
    494 			savewidgets: $('#_wpnonce_widgets').val(),
    495 			sidebars: []
    496 		};
    497 
    498 		if ( sidebarId ) {
    499 			$( '#' + sidebarId ).find( '.spinner:first' ).addClass( 'is-active' );
    500 		}
    501 
    502 		$('div.widgets-sortables').each( function() {
    503 			if ( $(this).sortable ) {
    504 				data['sidebars[' + $(this).attr('id') + ']'] = $(this).sortable('toArray').join(',');
    505 			}
    506 		});
    507 
    508 		$.post( ajaxurl, data, function() {
    509 			$( '#inactive-widgets-control-remove' ).prop( 'disabled' , ! $( '#wp_inactive_widgets .widget' ).length );
    510 			$( '.spinner' ).removeClass( 'is-active' );
    511 		});
    512 	},
    513 
    514 	save : function( widget, del, animate, order ) {
    515 		var self = this, data, a,
    516 			sidebarId = widget.closest( 'div.widgets-sortables' ).attr( 'id' ),
    517 			form = widget.find( 'form' ),
    518 			isAdd = widget.find( 'input.add_new' ).val();
    519 
    520 		if ( ! del && ! isAdd && form.prop( 'checkValidity' ) && ! form[0].checkValidity() ) {
    521 			return;
    522 		}
    523 
    524 		data = form.serialize();
    525 
    526 		widget = $(widget);
    527 		$( '.spinner', widget ).addClass( 'is-active' );
    528 
    529 		a = {
    530 			action: 'save-widget',
    531 			savewidgets: $('#_wpnonce_widgets').val(),
    532 			sidebar: sidebarId
    533 		};
    534 
    535 		if ( del ) {
    536 			a.delete_widget = 1;
    537 		}
    538 
    539 		data += '&' + $.param(a);
    540 
    541 		$.post( ajaxurl, data, function(r) {
    542 			var id = $('input.widget-id', widget).val();
    543 
    544 			if ( del ) {
    545 				if ( ! $('input.widget_number', widget).val() ) {
    546 					$('#available-widgets').find('input.widget-id').each(function(){
    547 						if ( $(this).val() === id ) {
    548 							$(this).closest('div.widget').show();
    549 						}
    550 					});
    551 				}
    552 
    553 				if ( animate ) {
    554 					order = 0;
    555 					widget.slideUp( 'fast', function() {
    556 						$( this ).remove();
    557 						wpWidgets.saveOrder();
    558 						delete self.dirtyWidgets[ id ];
    559 					});
    560 				} else {
    561 					widget.remove();
    562 					delete self.dirtyWidgets[ id ];
    563 
    564 					if ( sidebarId === 'wp_inactive_widgets' ) {
    565 						$( '#inactive-widgets-control-remove' ).prop( 'disabled' , ! $( '#wp_inactive_widgets .widget' ).length );
    566 					}
    567 				}
    568 			} else {
    569 				$( '.spinner' ).removeClass( 'is-active' );
    570 				if ( r && r.length > 2 ) {
    571 					$( 'div.widget-content', widget ).html( r );
    572 					wpWidgets.appendTitle( widget );
    573 
    574 					// Re-disable the save button.
    575 					widget.find( '.widget-control-save' ).prop( 'disabled', true ).val( wp.i18n.__( 'Saved' ) );
    576 
    577 					widget.removeClass( 'widget-dirty' );
    578 
    579 					// Clear the dirty flag from the widget.
    580 					delete self.dirtyWidgets[ id ];
    581 
    582 					$document.trigger( 'widget-updated', [ widget ] );
    583 
    584 					if ( sidebarId === 'wp_inactive_widgets' ) {
    585 						$( '#inactive-widgets-control-remove' ).prop( 'disabled' , ! $( '#wp_inactive_widgets .widget' ).length );
    586 					}
    587 				}
    588 			}
    589 
    590 			if ( order ) {
    591 				wpWidgets.saveOrder();
    592 			}
    593 		});
    594 	},
    595 
    596 	removeInactiveWidgets : function() {
    597 		var $element = $( '.remove-inactive-widgets' ), self = this, a, data;
    598 
    599 		$( '.spinner', $element ).addClass( 'is-active' );
    600 
    601 		a = {
    602 			action : 'delete-inactive-widgets',
    603 			removeinactivewidgets : $( '#_wpnonce_remove_inactive_widgets' ).val()
    604 		};
    605 
    606 		data = $.param( a );
    607 
    608 		$.post( ajaxurl, data, function() {
    609 			$( '#wp_inactive_widgets .widget' ).each(function() {
    610 				var $widget = $( this );
    611 				delete self.dirtyWidgets[ $widget.find( 'input.widget-id' ).val() ];
    612 				$widget.remove();
    613 			});
    614 			$( '#inactive-widgets-control-remove' ).prop( 'disabled', true );
    615 			$( '.spinner', $element ).removeClass( 'is-active' );
    616 		} );
    617 	},
    618 
    619 	appendTitle : function(widget) {
    620 		var title = $('input[id*="-title"]', widget).val() || '';
    621 
    622 		if ( title ) {
    623 			title = ': ' + title.replace(/<[^<>]+>/g, '').replace(/</g, '&lt;').replace(/>/g, '&gt;');
    624 		}
    625 
    626 		$(widget).children('.widget-top').children('.widget-title').children()
    627 				.children('.in-widget-title').html(title);
    628 
    629 	},
    630 
    631 	close : function(widget) {
    632 		widget.children('.widget-inside').slideUp('fast', function() {
    633 			widget.attr( 'style', '' )
    634 				.find( '.widget-top button.widget-action' )
    635 					.attr( 'aria-expanded', 'false' )
    636 					.focus();
    637 		});
    638 	},
    639 
    640 	addWidget: function( chooser ) {
    641 		var widget, widgetId, add, n, viewportTop, viewportBottom, sidebarBounds,
    642 			sidebarId = chooser.find( '.widgets-chooser-selected' ).data('sidebarId'),
    643 			sidebar = $( '#' + sidebarId );
    644 
    645 		widget = $('#available-widgets').find('.widget-in-question').clone();
    646 		widgetId = widget.attr('id');
    647 		add = widget.find( 'input.add_new' ).val();
    648 		n = widget.find( 'input.multi_number' ).val();
    649 
    650 		// Remove the cloned chooser from the widget.
    651 		widget.find('.widgets-chooser').remove();
    652 
    653 		if ( 'multi' === add ) {
    654 			widget.html(
    655 				widget.html().replace( /<[^<>]+>/g, function(m) {
    656 					return m.replace( /__i__|%i%/g, n );
    657 				})
    658 			);
    659 
    660 			widget.attr( 'id', widgetId.replace( '__i__', n ) );
    661 			n++;
    662 			$( '#' + widgetId ).find('input.multi_number').val(n);
    663 		} else if ( 'single' === add ) {
    664 			widget.attr( 'id', 'new-' + widgetId );
    665 			$( '#' + widgetId ).hide();
    666 		}
    667 
    668 		// Open the widgets container.
    669 		sidebar.closest( '.widgets-holder-wrap' )
    670 			.removeClass( 'closed' )
    671 			.find( '.handlediv' ).attr( 'aria-expanded', 'true' );
    672 
    673 		sidebar.append( widget );
    674 		sidebar.sortable('refresh');
    675 
    676 		wpWidgets.save( widget, 0, 0, 1 );
    677 		// No longer "new" widget.
    678 		widget.find( 'input.add_new' ).val('');
    679 
    680 		$document.trigger( 'widget-added', [ widget ] );
    681 
    682 		/*
    683 		 * Check if any part of the sidebar is visible in the viewport. If it is, don't scroll.
    684 		 * Otherwise, scroll up to so the sidebar is in view.
    685 		 *
    686 		 * We do this by comparing the top and bottom, of the sidebar so see if they are within
    687 		 * the bounds of the viewport.
    688 		 */
    689 		viewportTop = $(window).scrollTop();
    690 		viewportBottom = viewportTop + $(window).height();
    691 		sidebarBounds = sidebar.offset();
    692 
    693 		sidebarBounds.bottom = sidebarBounds.top + sidebar.outerHeight();
    694 
    695 		if ( viewportTop > sidebarBounds.bottom || viewportBottom < sidebarBounds.top ) {
    696 			$( 'html, body' ).animate({
    697 				scrollTop: sidebarBounds.top - 130
    698 			}, 200 );
    699 		}
    700 
    701 		window.setTimeout( function() {
    702 			// Cannot use a callback in the animation above as it fires twice,
    703 			// have to queue this "by hand".
    704 			widget.find( '.widget-title' ).trigger('click');
    705 			// At the end of the animation, announce the widget has been added.
    706 			window.wp.a11y.speak( wp.i18n.__( 'Widget has been added to the selected sidebar' ), 'assertive' );
    707 		}, 250 );
    708 	},
    709 
    710 	closeChooser: function() {
    711 		var self = this,
    712 			widgetInQuestion = $( '#available-widgets .widget-in-question' );
    713 
    714 		$( '.widgets-chooser' ).slideUp( 200, function() {
    715 			$( '#wpbody-content' ).append( this );
    716 			self.clearWidgetSelection();
    717 			// Move focus back to the toggle button.
    718 			widgetInQuestion.find( '.widget-action' ).attr( 'aria-expanded', 'false' ).focus();
    719 		});
    720 	},
    721 
    722 	clearWidgetSelection: function() {
    723 		$( '#widgets-left' ).removeClass( 'chooser' );
    724 		$( '.widget-in-question' ).removeClass( 'widget-in-question' );
    725 	},
    726 
    727 	/**
    728 	 * Closes a Sidebar that was previously closed, but opened by dragging a Widget over it.
    729 	 *
    730 	 * Used when a Widget gets dragged in/out of the Sidebar and never dropped.
    731 	 *
    732 	 * @param {Object} event jQuery event object.
    733 	 */
    734 	closeSidebar: function( event ) {
    735 		this.hoveredSidebar
    736 			.addClass( 'closed' )
    737 			.find( '.handlediv' ).attr( 'aria-expanded', 'false' );
    738 
    739 		$( event.target ).css( 'min-height', '' );
    740 		this.hoveredSidebar = null;
    741 	}
    742 };
    743 
    744 $( function(){ wpWidgets.init(); } );
    745 
    746 })(jQuery);
    747 
    748 /**
    749  * Removed in 5.5.0, needed for back-compatibility.
    750  *
    751  * @since 4.9.0
    752  * @deprecated 5.5.0
    753  *
    754  * @type {object}
    755 */
    756 wpWidgets.l10n = wpWidgets.l10n || {
    757 	save: '',
    758 	saved: '',
    759 	saveAlert: '',
    760 	widgetAdded: ''
    761 };
    762 
    763 wpWidgets.l10n = window.wp.deprecateL10nObject( 'wpWidgets.l10n', wpWidgets.l10n, '5.5.0' );