1 /*global ajaxurl, isRtl */
4 var $document = $( document );
8 * A closed Sidebar that gets a Widget dragged over it.
17 chooser = $('.widgets-chooser'),
18 selectSidebar = chooser.find('.widgets-chooser-sidebars'),
19 sidebars = $('div.widgets-sortables'),
20 isRTL = !! ( 'undefined' !== typeof isRtl && isRtl );
22 $('#widgets-right .sidebar-name').click( function() {
24 $wrap = $this.closest('.widgets-holder-wrap');
26 if ( $wrap.hasClass('closed') ) {
27 $wrap.removeClass('closed');
28 $this.parent().sortable('refresh');
30 $wrap.addClass('closed');
33 $document.triggerHandler( 'wp-pin-menu' );
36 $('#widgets-left .sidebar-name').click( function() {
37 $(this).closest('.widgets-holder-wrap').toggleClass('closed');
38 $document.triggerHandler( 'wp-pin-menu' );
41 $(document.body).bind('click.widgets-toggle', function(e) {
42 var target = $(e.target),
43 css = { 'z-index': 100 },
44 widget, inside, targetWidth, widgetWidth, margin;
46 if ( target.parents('.widget-top').length && ! target.parents('#available-widgets').length ) {
47 widget = target.closest('div.widget');
48 inside = widget.children('.widget-inside');
49 targetWidth = parseInt( widget.find('input.widget-width').val(), 10 ),
50 widgetWidth = widget.parent().width();
52 if ( inside.is(':hidden') ) {
53 if ( targetWidth > 250 && ( targetWidth + 30 > widgetWidth ) && widget.closest('div.widgets-sortables').length ) {
54 if ( widget.closest('div.widget-liquid-right').length ) {
55 margin = isRTL ? 'margin-right' : 'margin-left';
57 margin = isRTL ? 'margin-left' : 'margin-right';
60 css[ margin ] = widgetWidth - ( targetWidth + 30 ) + 'px';
63 widget.addClass( 'open' );
64 inside.slideDown('fast');
66 inside.slideUp('fast', function() {
67 widget.attr( 'style', '' );
68 widget.removeClass( 'open' );
72 } else if ( target.hasClass('widget-control-save') ) {
73 wpWidgets.save( target.closest('div.widget'), 0, 1, 0 );
75 } else if ( target.hasClass('widget-control-remove') ) {
76 wpWidgets.save( target.closest('div.widget'), 1, 1, 0 );
78 } else if ( target.hasClass('widget-control-close') ) {
79 widget = target.closest('div.widget');
80 widget.removeClass( 'open' );
81 wpWidgets.close( widget );
83 } else if ( target.attr( 'id' ) === 'inactive-widgets-control-remove' ) {
84 wpWidgets.removeInactiveWidgets();
89 sidebars.children('.widget').each( function() {
92 wpWidgets.appendTitle( this );
94 if ( $this.find( 'p.widget-error' ).length ) {
95 $this.find( 'a.widget-action' ).trigger('click');
99 $('#widget-list').children('.widget').draggable({
100 connectToSortable: 'div.widgets-sortables',
101 handle: '> .widget-top > .widget-title',
105 containment: '#wpwrap',
106 refreshPositions: true,
107 start: function( event, ui ) {
108 var chooser = $(this).find('.widgets-chooser');
110 ui.helper.find('div.widget-description').hide();
113 if ( chooser.length ) {
114 // Hide the chooser and move it out of the widget
115 $( '#wpbody-content' ).append( chooser.hide() );
116 // Delete the cloned chooser from the drag helper
117 ui.helper.find('.widgets-chooser').remove();
118 self.clearWidgetSelection();
131 * Opens and closes previously closed Sidebars when Widgets are dragged over/out of them.
133 sidebars.droppable( {
134 tolerance: 'intersect',
137 * Open Sidebar when a Widget gets dragged over it.
141 over: function( event ) {
142 var $wrap = $( event.target ).parent();
144 if ( wpWidgets.hoveredSidebar && ! $wrap.is( wpWidgets.hoveredSidebar ) ) {
145 // Close the previous Sidebar as the Widget has been dragged onto another Sidebar.
146 wpWidgets.closeSidebar( event );
149 if ( $wrap.hasClass( 'closed' ) ) {
150 wpWidgets.hoveredSidebar = $wrap;
151 $wrap.removeClass( 'closed' );
154 $( this ).sortable( 'refresh' );
158 * Close Sidebar when the Widget gets dragged out of it.
162 out: function( event ) {
163 if ( wpWidgets.hoveredSidebar ) {
164 wpWidgets.closeSidebar( event );
170 placeholder: 'widget-placeholder',
172 handle: '> .widget-top > .widget-title',
175 containment: '#wpwrap',
176 tolerance: 'pointer',
177 refreshPositions: true,
178 start: function( event, ui ) {
179 var height, $this = $(this),
180 $wrap = $this.parent(),
181 inside = ui.item.children('.widget-inside');
183 if ( inside.css('display') === 'block' ) {
184 ui.item.removeClass('open');
186 $(this).sortable('refreshPositions');
189 if ( ! $wrap.hasClass('closed') ) {
190 // Lock all open sidebars min-height when starting to drag.
191 // Prevents jumping when dragging a widget from an open sidebar to a closed sidebar below.
192 height = ui.item.hasClass('ui-draggable') ? $this.height() : 1 + $this.height();
193 $this.css( 'min-height', height + 'px' );
197 stop: function( event, ui ) {
198 var addNew, widgetNumber, $sidebar, $children, child, item,
202 // Reset the var to hold a previously closed sidebar.
203 wpWidgets.hoveredSidebar = null;
205 if ( $widget.hasClass('deleting') ) {
206 wpWidgets.save( $widget, 1, 0, 1 ); // delete widget
211 addNew = $widget.find('input.add_new').val();
212 widgetNumber = $widget.find('input.multi_number').val();
214 $widget.attr( 'style', '' ).removeClass('ui-draggable');
218 if ( 'multi' === addNew ) {
220 $widget.html().replace( /<[^<>]+>/g, function( tag ) {
221 return tag.replace( /__i__|%i%/g, widgetNumber );
225 $widget.attr( 'id', id.replace( '__i__', widgetNumber ) );
228 $( 'div#' + id ).find( 'input.multi_number' ).val( widgetNumber );
229 } else if ( 'single' === addNew ) {
230 $widget.attr( 'id', 'new-' + id );
234 wpWidgets.save( $widget, 0, 0, 1 );
235 $widget.find('input.add_new').val('');
236 $document.trigger( 'widget-added', [ $widget ] );
239 $sidebar = $widget.parent();
241 if ( $sidebar.parent().hasClass('closed') ) {
242 $sidebar.parent().removeClass('closed');
243 $children = $sidebar.children('.widget');
245 // Make sure the dropped widget is at the top
246 if ( $children.length > 1 ) {
247 child = $children.get(0);
248 item = $widget.get(0);
250 if ( child.id && item.id && child.id !== item.id ) {
251 $( child ).before( $widget );
257 $widget.find( 'a.widget-action' ).trigger('click');
259 wpWidgets.saveOrder( $sidebar.attr('id') );
263 activate: function() {
264 $(this).parent().addClass( 'widget-hover' );
267 deactivate: function() {
268 // Remove all min-height added on "start"
269 $(this).css( 'min-height', '' ).parent().removeClass( 'widget-hover' );
272 receive: function( event, ui ) {
273 var $sender = $( ui.sender );
275 // Don't add more widgets to orphaned sidebars
276 if ( this.id.indexOf('orphaned_widgets') > -1 ) {
277 $sender.sortable('cancel');
281 // If the last widget was moved out of an orphaned sidebar, close and remove it.
282 if ( $sender.attr('id').indexOf('orphaned_widgets') > -1 && ! $sender.children('.widget').length ) {
283 $sender.parents('.orphan-sidebar').slideUp( 400, function(){ $(this).remove(); } );
286 }).sortable( 'option', 'connectWith', 'div.widgets-sortables' );
288 $('#available-widgets').droppable({
289 tolerance: 'pointer',
291 return $(o).parent().attr('id') !== 'widget-list';
293 drop: function(e,ui) {
294 ui.draggable.addClass('deleting');
295 $('#removing-widget').hide().children('span').empty();
297 over: function(e,ui) {
298 ui.draggable.addClass('deleting');
299 $('div.widget-placeholder').hide();
301 if ( ui.draggable.hasClass('ui-sortable-helper') ) {
302 $('#removing-widget').show().children('span')
303 .html( ui.draggable.find( 'div.widget-title' ).children( 'h3' ).html() );
306 out: function(e,ui) {
307 ui.draggable.removeClass('deleting');
308 $('div.widget-placeholder').show();
309 $('#removing-widget').hide().children('span').empty();
314 $( '#widgets-right .widgets-holder-wrap' ).each( function( index, element ) {
315 var $element = $( element ),
316 name = $element.find( '.sidebar-name h2' ).text(),
317 id = $element.find( '.widgets-sortables' ).attr( 'id' ),
318 li = $('<li tabindex="0">').text( $.trim( name ) );
321 li.addClass( 'widgets-chooser-selected' );
324 selectSidebar.append( li );
325 li.data( 'sidebarId', id );
328 $( '#available-widgets .widget .widget-title' ).on( 'click.widgets-chooser', function() {
329 var $widget = $(this).closest( '.widget' );
331 if ( $widget.hasClass( 'widget-in-question' ) || $( '#widgets-left' ).hasClass( 'chooser' ) ) {
335 self.clearWidgetSelection();
336 $( '#widgets-left' ).addClass( 'chooser' );
337 $widget.addClass( 'widget-in-question' ).children( '.widget-description' ).after( chooser );
339 chooser.slideDown( 300, function() {
340 selectSidebar.find('.widgets-chooser-selected').focus();
343 selectSidebar.find( 'li' ).on( 'focusin.widgets-chooser', function() {
344 selectSidebar.find('.widgets-chooser-selected').removeClass( 'widgets-chooser-selected' );
345 $(this).addClass( 'widgets-chooser-selected' );
350 // Add event handlers
351 chooser.on( 'click.widgets-chooser', function( event ) {
352 var $target = $( event.target );
354 if ( $target.hasClass('button-primary') ) {
355 self.addWidget( chooser );
357 } else if ( $target.hasClass('button-secondary') ) {
360 }).on( 'keyup.widgets-chooser', function( event ) {
361 if ( event.which === $.ui.keyCode.ENTER ) {
362 if ( $( event.target ).hasClass('button-secondary') ) {
363 // Close instead of adding when pressing Enter on the Cancel button
366 self.addWidget( chooser );
369 } else if ( event.which === $.ui.keyCode.ESCAPE ) {
375 saveOrder : function( sidebarId ) {
377 action: 'widgets-order',
378 savewidgets: $('#_wpnonce_widgets').val(),
383 $( '#' + sidebarId ).find( '.spinner:first' ).addClass( 'is-active' );
386 $('div.widgets-sortables').each( function() {
387 if ( $(this).sortable ) {
388 data['sidebars[' + $(this).attr('id') + ']'] = $(this).sortable('toArray').join(',');
392 $.post( ajaxurl, data, function() {
393 $( '#inactive-widgets-control-remove' ).prop( 'disabled' , ! $( '#wp_inactive_widgets .widget' ).length );
394 $( '.spinner' ).removeClass( 'is-active' );
398 save : function( widget, del, animate, order ) {
399 var sidebarId = widget.closest('div.widgets-sortables').attr('id'),
400 data = widget.find('form').serialize(), a;
403 $( '.spinner', widget ).addClass( 'is-active' );
406 action: 'save-widget',
407 savewidgets: $('#_wpnonce_widgets').val(),
415 data += '&' + $.param(a);
417 $.post( ajaxurl, data, function(r) {
421 if ( ! $('input.widget_number', widget).val() ) {
422 id = $('input.widget-id', widget).val();
423 $('#available-widgets').find('input.widget-id').each(function(){
424 if ( $(this).val() === id ) {
425 $(this).closest('div.widget').show();
432 widget.slideUp('fast', function(){
434 wpWidgets.saveOrder();
439 if ( sidebarId === 'wp_inactive_widgets' ) {
440 $( '#inactive-widgets-control-remove' ).prop( 'disabled' , ! $( '#wp_inactive_widgets .widget' ).length );
444 $( '.spinner' ).removeClass( 'is-active' );
445 if ( r && r.length > 2 ) {
446 $( 'div.widget-content', widget ).html( r );
447 wpWidgets.appendTitle( widget );
448 $document.trigger( 'widget-updated', [ widget ] );
450 if ( sidebarId === 'wp_inactive_widgets' ) {
451 $( '#inactive-widgets-control-remove' ).prop( 'disabled' , ! $( '#wp_inactive_widgets .widget' ).length );
457 wpWidgets.saveOrder();
462 removeInactiveWidgets : function() {
463 var $element = $( '.remove-inactive-widgets' ), a, data;
465 $( '.spinner', $element ).addClass( 'is-active' );
468 action : 'delete-inactive-widgets',
469 removeinactivewidgets : $( '#_wpnonce_remove_inactive_widgets' ).val()
474 $.post( ajaxurl, data, function() {
475 $( '#wp_inactive_widgets .widget' ).remove();
476 $( '#inactive-widgets-control-remove' ).prop( 'disabled' , true );
477 $( '.spinner', $element ).removeClass( 'is-active' );
481 appendTitle : function(widget) {
482 var title = $('input[id*="-title"]', widget).val() || '';
485 title = ': ' + title.replace(/<[^<>]+>/g, '').replace(/</g, '<').replace(/>/g, '>');
488 $(widget).children('.widget-top').children('.widget-title').children()
489 .children('.in-widget-title').html(title);
493 close : function(widget) {
494 widget.children('.widget-inside').slideUp('fast', function() {
495 widget.attr( 'style', '' );
499 addWidget: function( chooser ) {
500 var widget, widgetId, add, n, viewportTop, viewportBottom, sidebarBounds,
501 sidebarId = chooser.find( '.widgets-chooser-selected' ).data('sidebarId'),
502 sidebar = $( '#' + sidebarId );
504 widget = $('#available-widgets').find('.widget-in-question').clone();
505 widgetId = widget.attr('id');
506 add = widget.find( 'input.add_new' ).val();
507 n = widget.find( 'input.multi_number' ).val();
509 // Remove the cloned chooser from the widget
510 widget.find('.widgets-chooser').remove();
512 if ( 'multi' === add ) {
514 widget.html().replace( /<[^<>]+>/g, function(m) {
515 return m.replace( /__i__|%i%/g, n );
519 widget.attr( 'id', widgetId.replace( '__i__', n ) );
521 $( '#' + widgetId ).find('input.multi_number').val(n);
522 } else if ( 'single' === add ) {
523 widget.attr( 'id', 'new-' + widgetId );
524 $( '#' + widgetId ).hide();
527 // Open the widgets container
528 sidebar.closest( '.widgets-holder-wrap' ).removeClass('closed');
530 sidebar.append( widget );
531 sidebar.sortable('refresh');
533 wpWidgets.save( widget, 0, 0, 1 );
534 // No longer "new" widget
535 widget.find( 'input.add_new' ).val('');
537 $document.trigger( 'widget-added', [ widget ] );
540 * Check if any part of the sidebar is visible in the viewport. If it is, don't scroll.
541 * Otherwise, scroll up to so the sidebar is in view.
543 * We do this by comparing the top and bottom, of the sidebar so see if they are within
544 * the bounds of the viewport.
546 viewportTop = $(window).scrollTop();
547 viewportBottom = viewportTop + $(window).height();
548 sidebarBounds = sidebar.offset();
550 sidebarBounds.bottom = sidebarBounds.top + sidebar.outerHeight();
552 if ( viewportTop > sidebarBounds.bottom || viewportBottom < sidebarBounds.top ) {
553 $( 'html, body' ).animate({
554 scrollTop: sidebarBounds.top - 130
558 window.setTimeout( function() {
559 // Cannot use a callback in the animation above as it fires twice,
560 // have to queue this "by hand".
561 widget.find( '.widget-title' ).trigger('click');
565 closeChooser: function() {
568 $( '.widgets-chooser' ).slideUp( 200, function() {
569 $( '#wpbody-content' ).append( this );
570 self.clearWidgetSelection();
574 clearWidgetSelection: function() {
575 $( '#widgets-left' ).removeClass( 'chooser' );
576 $( '.widget-in-question' ).removeClass( 'widget-in-question' );
580 * Closes a Sidebar that was previously closed, but opened by dragging a Widget over it.
582 * Used when a Widget gets dragged in/out of the Sidebar and never dropped.
586 closeSidebar: function( sidebar ) {
587 this.hoveredSidebar.addClass( 'closed' );
588 $( sidebar.target ).css( 'min-height', '' );
589 this.hoveredSidebar = null;
593 $document.ready( function(){ wpWidgets.init(); } );