]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-admin/js/widgets.js
WordPress 4.3
[autoinstalls/wordpress.git] / wp-admin / js / widgets.js
1 /*global ajaxurl, isRtl */
2 var wpWidgets;
3 (function($) {
4         var $document = $( document );
5
6 wpWidgets = {
7         /**
8          * A closed Sidebar that gets a Widget dragged over it.
9          *
10          * @var element|null
11          */
12         hoveredSidebar: null,
13
14         init : function() {
15                 var rem, the_id,
16                         self = this,
17                         chooser = $('.widgets-chooser'),
18                         selectSidebar = chooser.find('.widgets-chooser-sidebars'),
19                         sidebars = $('div.widgets-sortables'),
20                         isRTL = !! ( 'undefined' !== typeof isRtl && isRtl );
21
22                 $('#widgets-right .sidebar-name').click( function() {
23                         var $this = $(this),
24                                 $wrap = $this.closest('.widgets-holder-wrap');
25
26                         if ( $wrap.hasClass('closed') ) {
27                                 $wrap.removeClass('closed');
28                                 $this.parent().sortable('refresh');
29                         } else {
30                                 $wrap.addClass('closed');
31                         }
32
33                         $document.triggerHandler( 'wp-pin-menu' );
34                 });
35
36                 $('#widgets-left .sidebar-name').click( function() {
37                         $(this).closest('.widgets-holder-wrap').toggleClass('closed');
38                         $document.triggerHandler( 'wp-pin-menu' );
39                 });
40
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;
45
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();
51
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';
56                                                 } else {
57                                                         margin = isRTL ? 'margin-left' : 'margin-right';
58                                                 }
59
60                                                 css[ margin ] = widgetWidth - ( targetWidth + 30 ) + 'px';
61                                                 widget.css( css );
62                                         }
63                                         widget.addClass( 'open' );
64                                         inside.slideDown('fast');
65                                 } else {
66                                         inside.slideUp('fast', function() {
67                                                 widget.attr( 'style', '' );
68                                                 widget.removeClass( 'open' );
69                                         });
70                                 }
71                                 e.preventDefault();
72                         } else if ( target.hasClass('widget-control-save') ) {
73                                 wpWidgets.save( target.closest('div.widget'), 0, 1, 0 );
74                                 e.preventDefault();
75                         } else if ( target.hasClass('widget-control-remove') ) {
76                                 wpWidgets.save( target.closest('div.widget'), 1, 1, 0 );
77                                 e.preventDefault();
78                         } else if ( target.hasClass('widget-control-close') ) {
79                                 widget = target.closest('div.widget');
80                                 widget.removeClass( 'open' );
81                                 wpWidgets.close( widget );
82                                 e.preventDefault();
83                         }
84                 });
85
86                 sidebars.children('.widget').each( function() {
87                         var $this = $(this);
88
89                         wpWidgets.appendTitle( this );
90
91                         if ( $this.find( 'p.widget-error' ).length ) {
92                                 $this.find( 'a.widget-action' ).trigger('click');
93                         }
94                 });
95
96                 $('#widget-list').children('.widget').draggable({
97                         connectToSortable: 'div.widgets-sortables',
98                         handle: '> .widget-top > .widget-title',
99                         distance: 2,
100                         helper: 'clone',
101                         zIndex: 100,
102                         containment: '#wpwrap',
103                         refreshPositions: true,
104                         start: function( event, ui ) {
105                                 var chooser = $(this).find('.widgets-chooser');
106
107                                 ui.helper.find('div.widget-description').hide();
108                                 the_id = this.id;
109
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();
116                                 }
117                         },
118                         stop: function() {
119                                 if ( rem ) {
120                                         $(rem).hide();
121                                 }
122
123                                 rem = '';
124                         }
125                 });
126
127                 /**
128                  * Opens and closes previously closed Sidebars when Widgets are dragged over/out of them.
129                  */
130                 sidebars.droppable( {
131                         tolerance: 'intersect',
132
133                         /**
134                          * Open Sidebar when a Widget gets dragged over it.
135                          *
136                          * @param event
137                          */
138                         over: function( event ) {
139                                 var $wrap = $( event.target ).parent();
140
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 );
144                                 }
145
146                                 if ( $wrap.hasClass( 'closed' ) ) {
147                                         wpWidgets.hoveredSidebar = $wrap;
148                                         $wrap.removeClass( 'closed' );
149                                 }
150
151                                 $( this ).sortable( 'refresh' );
152                         },
153
154                         /**
155                          * Close Sidebar when the Widget gets dragged out of it.
156                          *
157                          * @param event
158                          */
159                         out: function( event ) {
160                                 if ( wpWidgets.hoveredSidebar ) {
161                                         wpWidgets.closeSidebar( event );
162                                 }
163                         }
164                 } );
165
166                 sidebars.sortable({
167                         placeholder: 'widget-placeholder',
168                         items: '> .widget',
169                         handle: '> .widget-top > .widget-title',
170                         cursor: 'move',
171                         distance: 2,
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');
179
180                                 if ( inside.css('display') === 'block' ) {
181                                         inside.hide();
182                                         $(this).sortable('refreshPositions');
183                                 }
184
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' );
190                                 }
191                         },
192
193                         stop: function( event, ui ) {
194                                 var addNew, widgetNumber, $sidebar, $children, child, item,
195                                         $widget = ui.item,
196                                         id = the_id;
197
198                                 // Reset the var to hold a previously closed sidebar.
199                                 wpWidgets.hoveredSidebar = null;
200
201                                 if ( $widget.hasClass('deleting') ) {
202                                         wpWidgets.save( $widget, 1, 0, 1 ); // delete widget
203                                         $widget.remove();
204                                         return;
205                                 }
206
207                                 addNew = $widget.find('input.add_new').val();
208                                 widgetNumber = $widget.find('input.multi_number').val();
209
210                                 $widget.attr( 'style', '' ).removeClass('ui-draggable');
211                                 the_id = '';
212
213                                 if ( addNew ) {
214                                         if ( 'multi' === addNew ) {
215                                                 $widget.html(
216                                                         $widget.html().replace( /<[^<>]+>/g, function( tag ) {
217                                                                 return tag.replace( /__i__|%i%/g, widgetNumber );
218                                                         })
219                                                 );
220
221                                                 $widget.attr( 'id', id.replace( '__i__', widgetNumber ) );
222                                                 widgetNumber++;
223
224                                                 $( 'div#' + id ).find( 'input.multi_number' ).val( widgetNumber );
225                                         } else if ( 'single' === addNew ) {
226                                                 $widget.attr( 'id', 'new-' + id );
227                                                 rem = 'div#' + id;
228                                         }
229
230                                         wpWidgets.save( $widget, 0, 0, 1 );
231                                         $widget.find('input.add_new').val('');
232                                         $document.trigger( 'widget-added', [ $widget ] );
233                                 }
234
235                                 $sidebar = $widget.parent();
236
237                                 if ( $sidebar.parent().hasClass('closed') ) {
238                                         $sidebar.parent().removeClass('closed');
239                                         $children = $sidebar.children('.widget');
240
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);
245
246                                                 if ( child.id && item.id && child.id !== item.id ) {
247                                                         $( child ).before( $widget );
248                                                 }
249                                         }
250                                 }
251
252                                 if ( addNew ) {
253                                         $widget.find( 'a.widget-action' ).trigger('click');
254                                 } else {
255                                         wpWidgets.saveOrder( $sidebar.attr('id') );
256                                 }
257                         },
258
259                         activate: function() {
260                                 $(this).parent().addClass( 'widget-hover' );
261                         },
262
263                         deactivate: function() {
264                                 // Remove all min-height added on "start"
265                                 $(this).css( 'min-height', '' ).parent().removeClass( 'widget-hover' );
266                         },
267
268                         receive: function( event, ui ) {
269                                 var $sender = $( ui.sender );
270
271                                 // Don't add more widgets to orphaned sidebars
272                                 if ( this.id.indexOf('orphaned_widgets') > -1 ) {
273                                         $sender.sortable('cancel');
274                                         return;
275                                 }
276
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(); } );
280                                 }
281                         }
282                 }).sortable( 'option', 'connectWith', 'div.widgets-sortables' );
283
284                 $('#available-widgets').droppable({
285                         tolerance: 'pointer',
286                         accept: function(o){
287                                 return $(o).parent().attr('id') !== 'widget-list';
288                         },
289                         drop: function(e,ui) {
290                                 ui.draggable.addClass('deleting');
291                                 $('#removing-widget').hide().children('span').empty();
292                         },
293                         over: function(e,ui) {
294                                 ui.draggable.addClass('deleting');
295                                 $('div.widget-placeholder').hide();
296
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() );
300                                 }
301                         },
302                         out: function(e,ui) {
303                                 ui.draggable.removeClass('deleting');
304                                 $('div.widget-placeholder').show();
305                                 $('#removing-widget').hide().children('span').empty();
306                         }
307                 });
308
309                 // Area Chooser
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 ) );
315
316                         if ( index === 0 ) {
317                                 li.addClass( 'widgets-chooser-selected' );
318                         }
319
320                         selectSidebar.append( li );
321                         li.data( 'sidebarId', id );
322                 });
323
324                 $( '#available-widgets .widget .widget-title' ).on( 'click.widgets-chooser', function() {
325                         var $widget = $(this).closest( '.widget' );
326
327                         if ( $widget.hasClass( 'widget-in-question' ) || $( '#widgets-left' ).hasClass( 'chooser' ) ) {
328                                 self.closeChooser();
329                         } else {
330                                 // Open the chooser
331                                 self.clearWidgetSelection();
332                                 $( '#widgets-left' ).addClass( 'chooser' );
333                                 $widget.addClass( 'widget-in-question' ).children( '.widget-description' ).after( chooser );
334
335                                 chooser.slideDown( 300, function() {
336                                         selectSidebar.find('.widgets-chooser-selected').focus();
337                                 });
338
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' );
342                                 } );
343                         }
344                 });
345
346                 // Add event handlers
347                 chooser.on( 'click.widgets-chooser', function( event ) {
348                         var $target = $( event.target );
349
350                         if ( $target.hasClass('button-primary') ) {
351                                 self.addWidget( chooser );
352                                 self.closeChooser();
353                         } else if ( $target.hasClass('button-secondary') ) {
354                                 self.closeChooser();
355                         }
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
360                                         self.closeChooser();
361                                 } else {
362                                         self.addWidget( chooser );
363                                         self.closeChooser();
364                                 }
365                         } else if ( event.which === $.ui.keyCode.ESCAPE ) {
366                                 self.closeChooser();
367                         }
368                 });
369         },
370
371         saveOrder : function( sidebarId ) {
372                 var data = {
373                         action: 'widgets-order',
374                         savewidgets: $('#_wpnonce_widgets').val(),
375                         sidebars: []
376                 };
377
378                 if ( sidebarId ) {
379                         $( '#' + sidebarId ).find( '.spinner:first' ).addClass( 'is-active' );
380                 }
381
382                 $('div.widgets-sortables').each( function() {
383                         if ( $(this).sortable ) {
384                                 data['sidebars[' + $(this).attr('id') + ']'] = $(this).sortable('toArray').join(',');
385                         }
386                 });
387
388                 $.post( ajaxurl, data, function() {
389                         $( '.spinner' ).removeClass( 'is-active' );
390                 });
391         },
392
393         save : function( widget, del, animate, order ) {
394                 var sidebarId = widget.closest('div.widgets-sortables').attr('id'),
395                         data = widget.find('form').serialize(), a;
396
397                 widget = $(widget);
398                 $( '.spinner', widget ).addClass( 'is-active' );
399
400                 a = {
401                         action: 'save-widget',
402                         savewidgets: $('#_wpnonce_widgets').val(),
403                         sidebar: sidebarId
404                 };
405
406                 if ( del ) {
407                         a.delete_widget = 1;
408                 }
409
410                 data += '&' + $.param(a);
411
412                 $.post( ajaxurl, data, function(r) {
413                         var id;
414
415                         if ( del ) {
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();
421                                                 }
422                                         });
423                                 }
424
425                                 if ( animate ) {
426                                         order = 0;
427                                         widget.slideUp('fast', function(){
428                                                 $(this).remove();
429                                                 wpWidgets.saveOrder();
430                                         });
431                                 } else {
432                                         widget.remove();
433                                 }
434                         } else {
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 ] );
440                                 }
441                         }
442                         if ( order ) {
443                                 wpWidgets.saveOrder();
444                         }
445                 });
446         },
447
448         appendTitle : function(widget) {
449                 var title = $('input[id*="-title"]', widget).val() || '';
450
451                 if ( title ) {
452                         title = ': ' + title.replace(/<[^<>]+>/g, '').replace(/</g, '&lt;').replace(/>/g, '&gt;');
453                 }
454
455                 $(widget).children('.widget-top').children('.widget-title').children()
456                                 .children('.in-widget-title').html(title);
457
458         },
459
460         close : function(widget) {
461                 widget.children('.widget-inside').slideUp('fast', function() {
462                         widget.attr( 'style', '' );
463                 });
464         },
465
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 );
470
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();
475
476                 // Remove the cloned chooser from the widget
477                 widget.find('.widgets-chooser').remove();
478
479                 if ( 'multi' === add ) {
480                         widget.html(
481                                 widget.html().replace( /<[^<>]+>/g, function(m) {
482                                         return m.replace( /__i__|%i%/g, n );
483                                 })
484                         );
485
486                         widget.attr( 'id', widgetId.replace( '__i__', n ) );
487                         n++;
488                         $( '#' + widgetId ).find('input.multi_number').val(n);
489                 } else if ( 'single' === add ) {
490                         widget.attr( 'id', 'new-' + widgetId );
491                         $( '#' + widgetId ).hide();
492                 }
493
494                 // Open the widgets container
495                 sidebar.closest( '.widgets-holder-wrap' ).removeClass('closed');
496
497                 sidebar.append( widget );
498                 sidebar.sortable('refresh');
499
500                 wpWidgets.save( widget, 0, 0, 1 );
501                 // No longer "new" widget
502                 widget.find( 'input.add_new' ).val('');
503
504                 $document.trigger( 'widget-added', [ widget ] );
505
506                 /*
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.
509                  *
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.
512                  */
513                 viewportTop = $(window).scrollTop();
514                 viewportBottom = viewportTop + $(window).height();
515                 sidebarBounds = sidebar.offset();
516
517                 sidebarBounds.bottom = sidebarBounds.top + sidebar.outerHeight();
518
519                 if ( viewportTop > sidebarBounds.bottom || viewportBottom < sidebarBounds.top ) {
520                         $( 'html, body' ).animate({
521                                 scrollTop: sidebarBounds.top - 130
522                         }, 200 );
523                 }
524
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');
529                 }, 250 );
530         },
531
532         closeChooser: function() {
533                 var self = this;
534
535                 $( '.widgets-chooser' ).slideUp( 200, function() {
536                         $( '#wpbody-content' ).append( this );
537                         self.clearWidgetSelection();
538                 });
539         },
540
541         clearWidgetSelection: function() {
542                 $( '#widgets-left' ).removeClass( 'chooser' );
543                 $( '.widget-in-question' ).removeClass( 'widget-in-question' );
544         },
545
546         /**
547          * Closes a Sidebar that was previously closed, but opened by dragging a Widget over it.
548          *
549          * Used when a Widget gets dragged in/out of the Sidebar and never dropped.
550          *
551          * @param sidebar
552          */
553         closeSidebar: function( sidebar ) {
554                 this.hoveredSidebar.addClass( 'closed' );
555                 $( sidebar.target ).css( 'min-height', '' );
556                 this.hoveredSidebar = null;
557         }
558 };
559
560 $document.ready( function(){ wpWidgets.init(); } );
561
562 })(jQuery);