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 );
86 sidebars.children('.widget').each( function() {
89 wpWidgets.appendTitle( this );
91 if ( $this.find( 'p.widget-error' ).length ) {
92 $this.find( 'a.widget-action' ).trigger('click');
96 $('#widget-list').children('.widget').draggable({
97 connectToSortable: 'div.widgets-sortables',
98 handle: '> .widget-top > .widget-title',
102 containment: '#wpwrap',
103 refreshPositions: true,
104 start: function( event, ui ) {
105 var chooser = $(this).find('.widgets-chooser');
107 ui.helper.find('div.widget-description').hide();
110 if ( chooser.length ) {
111 // Hide the chooser and move it out of the widget
112 $( '#wpbody-content' ).append( chooser.hide() );
113 // Delete the cloned chooser from the drag helper
114 ui.helper.find('.widgets-chooser').remove();
115 self.clearWidgetSelection();
128 * Opens and closes previously closed Sidebars when Widgets are dragged over/out of them.
130 sidebars.droppable( {
131 tolerance: 'intersect',
134 * Open Sidebar when a Widget gets dragged over it.
138 over: function( event ) {
139 var $wrap = $( event.target ).parent();
141 if ( wpWidgets.hoveredSidebar && ! $wrap.is( wpWidgets.hoveredSidebar ) ) {
142 // Close the previous Sidebar as the Widget has been dragged onto another Sidebar.
143 wpWidgets.closeSidebar( event );
146 if ( $wrap.hasClass( 'closed' ) ) {
147 wpWidgets.hoveredSidebar = $wrap;
148 $wrap.removeClass( 'closed' );
151 $( this ).sortable( 'refresh' );
155 * Close Sidebar when the Widget gets dragged out of it.
159 out: function( event ) {
160 if ( wpWidgets.hoveredSidebar ) {
161 wpWidgets.closeSidebar( event );
167 placeholder: 'widget-placeholder',
169 handle: '> .widget-top > .widget-title',
172 containment: '#wpwrap',
173 tolerance: 'pointer',
174 refreshPositions: true,
175 start: function( event, ui ) {
176 var height, $this = $(this),
177 $wrap = $this.parent(),
178 inside = ui.item.children('.widget-inside');
180 if ( inside.css('display') === 'block' ) {
182 $(this).sortable('refreshPositions');
185 if ( ! $wrap.hasClass('closed') ) {
186 // Lock all open sidebars min-height when starting to drag.
187 // Prevents jumping when dragging a widget from an open sidebar to a closed sidebar below.
188 height = ui.item.hasClass('ui-draggable') ? $this.height() : 1 + $this.height();
189 $this.css( 'min-height', height + 'px' );
193 stop: function( event, ui ) {
194 var addNew, widgetNumber, $sidebar, $children, child, item,
198 // Reset the var to hold a previously closed sidebar.
199 wpWidgets.hoveredSidebar = null;
201 if ( $widget.hasClass('deleting') ) {
202 wpWidgets.save( $widget, 1, 0, 1 ); // delete widget
207 addNew = $widget.find('input.add_new').val();
208 widgetNumber = $widget.find('input.multi_number').val();
210 $widget.attr( 'style', '' ).removeClass('ui-draggable');
214 if ( 'multi' === addNew ) {
216 $widget.html().replace( /<[^<>]+>/g, function( tag ) {
217 return tag.replace( /__i__|%i%/g, widgetNumber );
221 $widget.attr( 'id', id.replace( '__i__', widgetNumber ) );
224 $( 'div#' + id ).find( 'input.multi_number' ).val( widgetNumber );
225 } else if ( 'single' === addNew ) {
226 $widget.attr( 'id', 'new-' + id );
230 wpWidgets.save( $widget, 0, 0, 1 );
231 $widget.find('input.add_new').val('');
232 $document.trigger( 'widget-added', [ $widget ] );
235 $sidebar = $widget.parent();
237 if ( $sidebar.parent().hasClass('closed') ) {
238 $sidebar.parent().removeClass('closed');
239 $children = $sidebar.children('.widget');
241 // Make sure the dropped widget is at the top
242 if ( $children.length > 1 ) {
243 child = $children.get(0);
244 item = $widget.get(0);
246 if ( child.id && item.id && child.id !== item.id ) {
247 $( child ).before( $widget );
253 $widget.find( 'a.widget-action' ).trigger('click');
255 wpWidgets.saveOrder( $sidebar.attr('id') );
259 activate: function() {
260 $(this).parent().addClass( 'widget-hover' );
263 deactivate: function() {
264 // Remove all min-height added on "start"
265 $(this).css( 'min-height', '' ).parent().removeClass( 'widget-hover' );
268 receive: function( event, ui ) {
269 var $sender = $( ui.sender );
271 // Don't add more widgets to orphaned sidebars
272 if ( this.id.indexOf('orphaned_widgets') > -1 ) {
273 $sender.sortable('cancel');
277 // If the last widget was moved out of an orphaned sidebar, close and remove it.
278 if ( $sender.attr('id').indexOf('orphaned_widgets') > -1 && ! $sender.children('.widget').length ) {
279 $sender.parents('.orphan-sidebar').slideUp( 400, function(){ $(this).remove(); } );
282 }).sortable( 'option', 'connectWith', 'div.widgets-sortables' );
284 $('#available-widgets').droppable({
285 tolerance: 'pointer',
287 return $(o).parent().attr('id') !== 'widget-list';
289 drop: function(e,ui) {
290 ui.draggable.addClass('deleting');
291 $('#removing-widget').hide().children('span').empty();
293 over: function(e,ui) {
294 ui.draggable.addClass('deleting');
295 $('div.widget-placeholder').hide();
297 if ( ui.draggable.hasClass('ui-sortable-helper') ) {
298 $('#removing-widget').show().children('span')
299 .html( ui.draggable.find('div.widget-title').children('h4').html() );
302 out: function(e,ui) {
303 ui.draggable.removeClass('deleting');
304 $('div.widget-placeholder').show();
305 $('#removing-widget').hide().children('span').empty();
310 $( '#widgets-right .widgets-holder-wrap' ).each( function( index, element ) {
311 var $element = $( element ),
312 name = $element.find( '.sidebar-name h3' ).text(),
313 id = $element.find( '.widgets-sortables' ).attr( 'id' ),
314 li = $('<li tabindex="0">').text( $.trim( name ) );
317 li.addClass( 'widgets-chooser-selected' );
320 selectSidebar.append( li );
321 li.data( 'sidebarId', id );
324 $( '#available-widgets .widget .widget-title' ).on( 'click.widgets-chooser', function() {
325 var $widget = $(this).closest( '.widget' );
327 if ( $widget.hasClass( 'widget-in-question' ) || $( '#widgets-left' ).hasClass( 'chooser' ) ) {
331 self.clearWidgetSelection();
332 $( '#widgets-left' ).addClass( 'chooser' );
333 $widget.addClass( 'widget-in-question' ).children( '.widget-description' ).after( chooser );
335 chooser.slideDown( 300, function() {
336 selectSidebar.find('.widgets-chooser-selected').focus();
339 selectSidebar.find( 'li' ).on( 'focusin.widgets-chooser', function() {
340 selectSidebar.find('.widgets-chooser-selected').removeClass( 'widgets-chooser-selected' );
341 $(this).addClass( 'widgets-chooser-selected' );
346 // Add event handlers
347 chooser.on( 'click.widgets-chooser', function( event ) {
348 var $target = $( event.target );
350 if ( $target.hasClass('button-primary') ) {
351 self.addWidget( chooser );
353 } else if ( $target.hasClass('button-secondary') ) {
356 }).on( 'keyup.widgets-chooser', function( event ) {
357 if ( event.which === $.ui.keyCode.ENTER ) {
358 if ( $( event.target ).hasClass('button-secondary') ) {
359 // Close instead of adding when pressing Enter on the Cancel button
362 self.addWidget( chooser );
365 } else if ( event.which === $.ui.keyCode.ESCAPE ) {
371 saveOrder : function( sidebarId ) {
373 action: 'widgets-order',
374 savewidgets: $('#_wpnonce_widgets').val(),
379 $( '#' + sidebarId ).find( '.spinner:first' ).addClass( 'is-active' );
382 $('div.widgets-sortables').each( function() {
383 if ( $(this).sortable ) {
384 data['sidebars[' + $(this).attr('id') + ']'] = $(this).sortable('toArray').join(',');
388 $.post( ajaxurl, data, function() {
389 $( '.spinner' ).removeClass( 'is-active' );
393 save : function( widget, del, animate, order ) {
394 var sidebarId = widget.closest('div.widgets-sortables').attr('id'),
395 data = widget.find('form').serialize(), a;
398 $( '.spinner', widget ).addClass( 'is-active' );
401 action: 'save-widget',
402 savewidgets: $('#_wpnonce_widgets').val(),
410 data += '&' + $.param(a);
412 $.post( ajaxurl, data, function(r) {
416 if ( ! $('input.widget_number', widget).val() ) {
417 id = $('input.widget-id', widget).val();
418 $('#available-widgets').find('input.widget-id').each(function(){
419 if ( $(this).val() === id ) {
420 $(this).closest('div.widget').show();
427 widget.slideUp('fast', function(){
429 wpWidgets.saveOrder();
435 $( '.spinner' ).removeClass( 'is-active' );
436 if ( r && r.length > 2 ) {
437 $( 'div.widget-content', widget ).html( r );
438 wpWidgets.appendTitle( widget );
439 $document.trigger( 'widget-updated', [ widget ] );
443 wpWidgets.saveOrder();
448 appendTitle : function(widget) {
449 var title = $('input[id*="-title"]', widget).val() || '';
452 title = ': ' + title.replace(/<[^<>]+>/g, '').replace(/</g, '<').replace(/>/g, '>');
455 $(widget).children('.widget-top').children('.widget-title').children()
456 .children('.in-widget-title').html(title);
460 close : function(widget) {
461 widget.children('.widget-inside').slideUp('fast', function() {
462 widget.attr( 'style', '' );
466 addWidget: function( chooser ) {
467 var widget, widgetId, add, n, viewportTop, viewportBottom, sidebarBounds,
468 sidebarId = chooser.find( '.widgets-chooser-selected' ).data('sidebarId'),
469 sidebar = $( '#' + sidebarId );
471 widget = $('#available-widgets').find('.widget-in-question').clone();
472 widgetId = widget.attr('id');
473 add = widget.find( 'input.add_new' ).val();
474 n = widget.find( 'input.multi_number' ).val();
476 // Remove the cloned chooser from the widget
477 widget.find('.widgets-chooser').remove();
479 if ( 'multi' === add ) {
481 widget.html().replace( /<[^<>]+>/g, function(m) {
482 return m.replace( /__i__|%i%/g, n );
486 widget.attr( 'id', widgetId.replace( '__i__', n ) );
488 $( '#' + widgetId ).find('input.multi_number').val(n);
489 } else if ( 'single' === add ) {
490 widget.attr( 'id', 'new-' + widgetId );
491 $( '#' + widgetId ).hide();
494 // Open the widgets container
495 sidebar.closest( '.widgets-holder-wrap' ).removeClass('closed');
497 sidebar.append( widget );
498 sidebar.sortable('refresh');
500 wpWidgets.save( widget, 0, 0, 1 );
501 // No longer "new" widget
502 widget.find( 'input.add_new' ).val('');
504 $document.trigger( 'widget-added', [ widget ] );
507 * Check if any part of the sidebar is visible in the viewport. If it is, don't scroll.
508 * Otherwise, scroll up to so the sidebar is in view.
510 * We do this by comparing the top and bottom, of the sidebar so see if they are within
511 * the bounds of the viewport.
513 viewportTop = $(window).scrollTop();
514 viewportBottom = viewportTop + $(window).height();
515 sidebarBounds = sidebar.offset();
517 sidebarBounds.bottom = sidebarBounds.top + sidebar.outerHeight();
519 if ( viewportTop > sidebarBounds.bottom || viewportBottom < sidebarBounds.top ) {
520 $( 'html, body' ).animate({
521 scrollTop: sidebarBounds.top - 130
525 window.setTimeout( function() {
526 // Cannot use a callback in the animation above as it fires twice,
527 // have to queue this "by hand".
528 widget.find( '.widget-title' ).trigger('click');
532 closeChooser: function() {
535 $( '.widgets-chooser' ).slideUp( 200, function() {
536 $( '#wpbody-content' ).append( this );
537 self.clearWidgetSelection();
541 clearWidgetSelection: function() {
542 $( '#widgets-left' ).removeClass( 'chooser' );
543 $( '.widget-in-question' ).removeClass( 'widget-in-question' );
547 * Closes a Sidebar that was previously closed, but opened by dragging a Widget over it.
549 * Used when a Widget gets dragged in/out of the Sidebar and never dropped.
553 closeSidebar: function( sidebar ) {
554 this.hoveredSidebar.addClass( 'closed' );
555 $( sidebar.target ).css( 'min-height', '' );
556 this.hoveredSidebar = null;
560 $document.ready( function(){ wpWidgets.init(); } );