1 /*global ajaxurl, isRtl */ |
1 /*global ajaxurl, isRtl */ |
2 var wpWidgets; |
2 var wpWidgets; |
3 (function($) { |
3 (function($) { |
|
4 var $document = $( document ); |
4 |
5 |
5 wpWidgets = { |
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 /** |
|
15 * Translations. |
|
16 * |
|
17 * Exported from PHP in wp_default_scripts(). |
|
18 * |
|
19 * @var {object} |
|
20 */ |
|
21 l10n: { |
|
22 save: '{save}', |
|
23 saved: '{saved}', |
|
24 saveAlert: '{saveAlert}' |
|
25 }, |
|
26 |
|
27 /** |
|
28 * Lookup of which widgets have had change events triggered. |
|
29 * |
|
30 * @var {object} |
|
31 */ |
|
32 dirtyWidgets: {}, |
6 |
33 |
7 init : function() { |
34 init : function() { |
8 var rem, the_id, |
35 var rem, the_id, |
9 self = this, |
36 self = this, |
10 chooser = $('.widgets-chooser'), |
37 chooser = $('.widgets-chooser'), |
11 selectSidebar = chooser.find('.widgets-chooser-sidebars'), |
38 selectSidebar = chooser.find('.widgets-chooser-sidebars'), |
12 sidebars = $('div.widgets-sortables'), |
39 sidebars = $('div.widgets-sortables'), |
13 isRTL = !! ( 'undefined' !== typeof isRtl && isRtl ); |
40 isRTL = !! ( 'undefined' !== typeof isRtl && isRtl ); |
14 |
41 |
15 $('#widgets-right .sidebar-name').click( function() { |
42 // Handle the widgets containers in the right column. |
16 var $this = $(this), |
43 $( '#widgets-right .sidebar-name' ) |
17 $wrap = $this.closest('.widgets-holder-wrap'); |
44 /* |
18 |
45 * Toggle the widgets containers when clicked and update the toggle |
19 if ( $wrap.hasClass('closed') ) { |
46 * button `aria-expanded` attribute value. |
20 $wrap.removeClass('closed'); |
47 */ |
21 $this.parent().sortable('refresh'); |
48 .click( function() { |
22 } else { |
49 var $this = $( this ), |
23 $wrap.addClass('closed'); |
50 $wrap = $this.closest( '.widgets-holder-wrap '), |
24 } |
51 $toggle = $this.find( '.handlediv' ); |
25 }); |
52 |
26 |
53 if ( $wrap.hasClass( 'closed' ) ) { |
27 $('#widgets-left .sidebar-name').click( function() { |
54 $wrap.removeClass( 'closed' ); |
28 $(this).closest('.widgets-holder-wrap').toggleClass('closed'); |
55 $toggle.attr( 'aria-expanded', 'true' ); |
|
56 // Refresh the jQuery UI sortable items. |
|
57 $this.parent().sortable( 'refresh' ); |
|
58 } else { |
|
59 $wrap.addClass( 'closed' ); |
|
60 $toggle.attr( 'aria-expanded', 'false' ); |
|
61 } |
|
62 |
|
63 // Update the admin menu "sticky" state. |
|
64 $document.triggerHandler( 'wp-pin-menu' ); |
|
65 }) |
|
66 /* |
|
67 * Set the initial `aria-expanded` attribute value on the widgets |
|
68 * containers toggle button. The first one is expanded by default. |
|
69 */ |
|
70 .find( '.handlediv' ).each( function( index ) { |
|
71 if ( 0 === index ) { |
|
72 // jQuery equivalent of `continue` within an `each()` loop. |
|
73 return; |
|
74 } |
|
75 |
|
76 $( this ).attr( 'aria-expanded', 'false' ); |
|
77 }); |
|
78 |
|
79 // Show AYS dialog when there are unsaved widget changes. |
|
80 $( window ).on( 'beforeunload.widgets', function( event ) { |
|
81 var dirtyWidgetIds = [], unsavedWidgetsElements; |
|
82 $.each( self.dirtyWidgets, function( widgetId, dirty ) { |
|
83 if ( dirty ) { |
|
84 dirtyWidgetIds.push( widgetId ); |
|
85 } |
|
86 }); |
|
87 if ( 0 !== dirtyWidgetIds.length ) { |
|
88 unsavedWidgetsElements = $( '#widgets-right' ).find( '.widget' ).filter( function() { |
|
89 return -1 !== dirtyWidgetIds.indexOf( $( this ).prop( 'id' ).replace( /^widget-\d+_/, '' ) ); |
|
90 }); |
|
91 unsavedWidgetsElements.each( function() { |
|
92 if ( ! $( this ).hasClass( 'open' ) ) { |
|
93 $( this ).find( '.widget-title-action:first' ).click(); |
|
94 } |
|
95 }); |
|
96 |
|
97 // Bring the first unsaved widget into view and focus on the first tabbable field. |
|
98 unsavedWidgetsElements.first().each( function() { |
|
99 if ( this.scrollIntoViewIfNeeded ) { |
|
100 this.scrollIntoViewIfNeeded(); |
|
101 } else { |
|
102 this.scrollIntoView(); |
|
103 } |
|
104 $( this ).find( '.widget-inside :tabbable:first' ).focus(); |
|
105 } ); |
|
106 |
|
107 event.returnValue = wpWidgets.l10n.saveAlert; |
|
108 return event.returnValue; |
|
109 } |
|
110 }); |
|
111 |
|
112 // Handle the widgets containers in the left column. |
|
113 $( '#widgets-left .sidebar-name' ).click( function() { |
|
114 var $wrap = $( this ).closest( '.widgets-holder-wrap' ); |
|
115 |
|
116 $wrap |
|
117 .toggleClass( 'closed' ) |
|
118 .find( '.handlediv' ).attr( 'aria-expanded', ! $wrap.hasClass( 'closed' ) ); |
|
119 |
|
120 // Update the admin menu "sticky" state. |
|
121 $document.triggerHandler( 'wp-pin-menu' ); |
29 }); |
122 }); |
30 |
123 |
31 $(document.body).bind('click.widgets-toggle', function(e) { |
124 $(document.body).bind('click.widgets-toggle', function(e) { |
32 var target = $(e.target), |
125 var target = $(e.target), |
33 css = { 'z-index': 100 }, |
126 css = { 'z-index': 100 }, |
34 widget, inside, targetWidth, widgetWidth, margin; |
127 widget, inside, targetWidth, widgetWidth, margin, saveButton, widgetId, |
|
128 toggleBtn = target.closest( '.widget' ).find( '.widget-top button.widget-action' ); |
35 |
129 |
36 if ( target.parents('.widget-top').length && ! target.parents('#available-widgets').length ) { |
130 if ( target.parents('.widget-top').length && ! target.parents('#available-widgets').length ) { |
37 widget = target.closest('div.widget'); |
131 widget = target.closest('div.widget'); |
38 inside = widget.children('.widget-inside'); |
132 inside = widget.children('.widget-inside'); |
39 targetWidth = parseInt( widget.find('input.widget-width').val(), 10 ), |
133 targetWidth = parseInt( widget.find('input.widget-width').val(), 10 ); |
40 widgetWidth = widget.parent().width(); |
134 widgetWidth = widget.parent().width(); |
|
135 widgetId = inside.find( '.widget-id' ).val(); |
|
136 |
|
137 // Save button is initially disabled, but is enabled when a field is changed. |
|
138 if ( ! widget.data( 'dirty-state-initialized' ) ) { |
|
139 saveButton = inside.find( '.widget-control-save' ); |
|
140 saveButton.prop( 'disabled', true ).val( wpWidgets.l10n.saved ); |
|
141 inside.on( 'input change', function() { |
|
142 self.dirtyWidgets[ widgetId ] = true; |
|
143 widget.addClass( 'widget-dirty' ); |
|
144 saveButton.prop( 'disabled', false ).val( wpWidgets.l10n.save ); |
|
145 }); |
|
146 widget.data( 'dirty-state-initialized', true ); |
|
147 } |
41 |
148 |
42 if ( inside.is(':hidden') ) { |
149 if ( inside.is(':hidden') ) { |
43 if ( targetWidth > 250 && ( targetWidth + 30 > widgetWidth ) && widget.closest('div.widgets-sortables').length ) { |
150 if ( targetWidth > 250 && ( targetWidth + 30 > widgetWidth ) && widget.closest('div.widgets-sortables').length ) { |
44 if ( widget.closest('div.widget-liquid-right').length ) { |
151 if ( widget.closest('div.widget-liquid-right').length ) { |
45 margin = isRTL ? 'margin-right' : 'margin-left'; |
152 margin = isRTL ? 'margin-right' : 'margin-left'; |
66 wpWidgets.save( target.closest('div.widget'), 1, 1, 0 ); |
184 wpWidgets.save( target.closest('div.widget'), 1, 1, 0 ); |
67 e.preventDefault(); |
185 e.preventDefault(); |
68 } else if ( target.hasClass('widget-control-close') ) { |
186 } else if ( target.hasClass('widget-control-close') ) { |
69 widget = target.closest('div.widget'); |
187 widget = target.closest('div.widget'); |
70 widget.removeClass( 'open' ); |
188 widget.removeClass( 'open' ); |
|
189 toggleBtn.attr( 'aria-expanded', 'false' ); |
71 wpWidgets.close( widget ); |
190 wpWidgets.close( widget ); |
72 e.preventDefault(); |
191 e.preventDefault(); |
|
192 } else if ( target.attr( 'id' ) === 'inactive-widgets-control-remove' ) { |
|
193 wpWidgets.removeInactiveWidgets(); |
|
194 e.preventDefault(); |
73 } |
195 } |
74 }); |
196 }); |
75 |
197 |
76 sidebars.children('.widget').each( function() { |
198 sidebars.children('.widget').each( function() { |
77 var $this = $(this); |
199 var $this = $(this); |
78 |
200 |
79 wpWidgets.appendTitle( this ); |
201 wpWidgets.appendTitle( this ); |
80 |
202 |
81 if ( $this.find( 'p.widget-error' ).length ) { |
203 if ( $this.find( 'p.widget-error' ).length ) { |
82 $this.find( 'a.widget-action' ).trigger('click'); |
204 $this.find( '.widget-action' ).trigger( 'click' ).attr( 'aria-expanded', 'true' ); |
83 } |
205 } |
84 }); |
206 }); |
85 |
207 |
86 $('#widget-list').children('.widget').draggable({ |
208 $('#widget-list').children('.widget').draggable({ |
87 connectToSortable: 'div.widgets-sortables', |
209 connectToSortable: 'div.widgets-sortables', |
88 handle: '> .widget-top > .widget-title', |
210 handle: '> .widget-top > .widget-title', |
89 distance: 2, |
211 distance: 2, |
90 helper: 'clone', |
212 helper: 'clone', |
91 zIndex: 100, |
213 zIndex: 100, |
92 containment: 'document', |
214 containment: '#wpwrap', |
|
215 refreshPositions: true, |
93 start: function( event, ui ) { |
216 start: function( event, ui ) { |
94 var chooser = $(this).find('.widgets-chooser'); |
217 var chooser = $(this).find('.widgets-chooser'); |
95 |
218 |
96 ui.helper.find('div.widget-description').hide(); |
219 ui.helper.find('div.widget-description').hide(); |
97 the_id = this.id; |
220 the_id = this.id; |
111 |
234 |
112 rem = ''; |
235 rem = ''; |
113 } |
236 } |
114 }); |
237 }); |
115 |
238 |
|
239 /** |
|
240 * Opens and closes previously closed Sidebars when Widgets are dragged over/out of them. |
|
241 */ |
|
242 sidebars.droppable( { |
|
243 tolerance: 'intersect', |
|
244 |
|
245 /** |
|
246 * Open Sidebar when a Widget gets dragged over it. |
|
247 * |
|
248 * @param {object} event jQuery event object. |
|
249 */ |
|
250 over: function( event ) { |
|
251 var $wrap = $( event.target ).parent(); |
|
252 |
|
253 if ( wpWidgets.hoveredSidebar && ! $wrap.is( wpWidgets.hoveredSidebar ) ) { |
|
254 // Close the previous Sidebar as the Widget has been dragged onto another Sidebar. |
|
255 wpWidgets.closeSidebar( event ); |
|
256 } |
|
257 |
|
258 if ( $wrap.hasClass( 'closed' ) ) { |
|
259 wpWidgets.hoveredSidebar = $wrap; |
|
260 $wrap |
|
261 .removeClass( 'closed' ) |
|
262 .find( '.handlediv' ).attr( 'aria-expanded', 'true' ); |
|
263 } |
|
264 |
|
265 $( this ).sortable( 'refresh' ); |
|
266 }, |
|
267 |
|
268 /** |
|
269 * Close Sidebar when the Widget gets dragged out of it. |
|
270 * |
|
271 * @param {object} event jQuery event object. |
|
272 */ |
|
273 out: function( event ) { |
|
274 if ( wpWidgets.hoveredSidebar ) { |
|
275 wpWidgets.closeSidebar( event ); |
|
276 } |
|
277 } |
|
278 } ); |
|
279 |
116 sidebars.sortable({ |
280 sidebars.sortable({ |
117 placeholder: 'widget-placeholder', |
281 placeholder: 'widget-placeholder', |
118 items: '> .widget', |
282 items: '> .widget', |
119 handle: '> .widget-top > .widget-title', |
283 handle: '> .widget-top > .widget-title', |
120 cursor: 'move', |
284 cursor: 'move', |
121 distance: 2, |
285 distance: 2, |
122 containment: 'document', |
286 containment: '#wpwrap', |
|
287 tolerance: 'pointer', |
|
288 refreshPositions: true, |
123 start: function( event, ui ) { |
289 start: function( event, ui ) { |
124 var height, $this = $(this), |
290 var height, $this = $(this), |
125 $wrap = $this.parent(), |
291 $wrap = $this.parent(), |
126 inside = ui.item.children('.widget-inside'); |
292 inside = ui.item.children('.widget-inside'); |
127 |
293 |
128 if ( inside.css('display') === 'block' ) { |
294 if ( inside.css('display') === 'block' ) { |
|
295 ui.item.removeClass('open'); |
|
296 ui.item.find( '.widget-top button.widget-action' ).attr( 'aria-expanded', 'false' ); |
129 inside.hide(); |
297 inside.hide(); |
130 $(this).sortable('refreshPositions'); |
298 $(this).sortable('refreshPositions'); |
131 } |
299 } |
132 |
300 |
133 if ( ! $wrap.hasClass('closed') ) { |
301 if ( ! $wrap.hasClass('closed') ) { |
329 data['sidebars[' + $(this).attr('id') + ']'] = $(this).sortable('toArray').join(','); |
503 data['sidebars[' + $(this).attr('id') + ']'] = $(this).sortable('toArray').join(','); |
330 } |
504 } |
331 }); |
505 }); |
332 |
506 |
333 $.post( ajaxurl, data, function() { |
507 $.post( ajaxurl, data, function() { |
|
508 $( '#inactive-widgets-control-remove' ).prop( 'disabled' , ! $( '#wp_inactive_widgets .widget' ).length ); |
334 $( '.spinner' ).removeClass( 'is-active' ); |
509 $( '.spinner' ).removeClass( 'is-active' ); |
335 }); |
510 }); |
336 }, |
511 }, |
337 |
512 |
338 save : function( widget, del, animate, order ) { |
513 save : function( widget, del, animate, order ) { |
339 var sidebarId = widget.closest('div.widgets-sortables').attr('id'), |
514 var self = this, data, a, |
340 data = widget.find('form').serialize(), a; |
515 sidebarId = widget.closest( 'div.widgets-sortables' ).attr( 'id' ), |
|
516 form = widget.find( 'form' ), |
|
517 isAdd = widget.find( 'input.add_new' ).val(); |
|
518 |
|
519 if ( ! del && ! isAdd && form.prop( 'checkValidity' ) && ! form[0].checkValidity() ) { |
|
520 return; |
|
521 } |
|
522 |
|
523 data = form.serialize(); |
341 |
524 |
342 widget = $(widget); |
525 widget = $(widget); |
343 $( '.spinner', widget ).addClass( 'is-active' ); |
526 $( '.spinner', widget ).addClass( 'is-active' ); |
344 |
527 |
345 a = { |
528 a = { |
353 } |
536 } |
354 |
537 |
355 data += '&' + $.param(a); |
538 data += '&' + $.param(a); |
356 |
539 |
357 $.post( ajaxurl, data, function(r) { |
540 $.post( ajaxurl, data, function(r) { |
358 var id; |
541 var id = $('input.widget-id', widget).val(); |
359 |
542 |
360 if ( del ) { |
543 if ( del ) { |
361 if ( ! $('input.widget_number', widget).val() ) { |
544 if ( ! $('input.widget_number', widget).val() ) { |
362 id = $('input.widget-id', widget).val(); |
|
363 $('#available-widgets').find('input.widget-id').each(function(){ |
545 $('#available-widgets').find('input.widget-id').each(function(){ |
364 if ( $(this).val() === id ) { |
546 if ( $(this).val() === id ) { |
365 $(this).closest('div.widget').show(); |
547 $(this).closest('div.widget').show(); |
366 } |
548 } |
367 }); |
549 }); |
368 } |
550 } |
369 |
551 |
370 if ( animate ) { |
552 if ( animate ) { |
371 order = 0; |
553 order = 0; |
372 widget.slideUp('fast', function(){ |
554 widget.slideUp( 'fast', function() { |
373 $(this).remove(); |
555 $( this ).remove(); |
374 wpWidgets.saveOrder(); |
556 wpWidgets.saveOrder(); |
|
557 delete self.dirtyWidgets[ id ]; |
375 }); |
558 }); |
376 } else { |
559 } else { |
377 widget.remove(); |
560 widget.remove(); |
|
561 delete self.dirtyWidgets[ id ]; |
|
562 |
|
563 if ( sidebarId === 'wp_inactive_widgets' ) { |
|
564 $( '#inactive-widgets-control-remove' ).prop( 'disabled' , ! $( '#wp_inactive_widgets .widget' ).length ); |
|
565 } |
378 } |
566 } |
379 } else { |
567 } else { |
380 $( '.spinner' ).removeClass( 'is-active' ); |
568 $( '.spinner' ).removeClass( 'is-active' ); |
381 if ( r && r.length > 2 ) { |
569 if ( r && r.length > 2 ) { |
382 $( 'div.widget-content', widget ).html( r ); |
570 $( 'div.widget-content', widget ).html( r ); |
383 wpWidgets.appendTitle( widget ); |
571 wpWidgets.appendTitle( widget ); |
384 $( document ).trigger( 'widget-updated', [ widget ] ); |
572 |
385 } |
573 // Re-disable the save button. |
386 } |
574 widget.find( '.widget-control-save' ).prop( 'disabled', true ).val( wpWidgets.l10n.saved ); |
|
575 |
|
576 widget.removeClass( 'widget-dirty' ); |
|
577 |
|
578 // Clear the dirty flag from the widget. |
|
579 delete self.dirtyWidgets[ id ]; |
|
580 |
|
581 $document.trigger( 'widget-updated', [ widget ] ); |
|
582 |
|
583 if ( sidebarId === 'wp_inactive_widgets' ) { |
|
584 $( '#inactive-widgets-control-remove' ).prop( 'disabled' , ! $( '#wp_inactive_widgets .widget' ).length ); |
|
585 } |
|
586 } |
|
587 } |
|
588 |
387 if ( order ) { |
589 if ( order ) { |
388 wpWidgets.saveOrder(); |
590 wpWidgets.saveOrder(); |
389 } |
591 } |
390 }); |
592 }); |
|
593 }, |
|
594 |
|
595 removeInactiveWidgets : function() { |
|
596 var $element = $( '.remove-inactive-widgets' ), self = this, a, data; |
|
597 |
|
598 $( '.spinner', $element ).addClass( 'is-active' ); |
|
599 |
|
600 a = { |
|
601 action : 'delete-inactive-widgets', |
|
602 removeinactivewidgets : $( '#_wpnonce_remove_inactive_widgets' ).val() |
|
603 }; |
|
604 |
|
605 data = $.param( a ); |
|
606 |
|
607 $.post( ajaxurl, data, function() { |
|
608 $( '#wp_inactive_widgets .widget' ).each(function() { |
|
609 var $widget = $( this ); |
|
610 delete self.dirtyWidgets[ $widget.find( 'input.widget-id' ).val() ]; |
|
611 $widget.remove(); |
|
612 }); |
|
613 $( '#inactive-widgets-control-remove' ).prop( 'disabled', true ); |
|
614 $( '.spinner', $element ).removeClass( 'is-active' ); |
|
615 } ); |
391 }, |
616 }, |
392 |
617 |
393 appendTitle : function(widget) { |
618 appendTitle : function(widget) { |
394 var title = $('input[id*="-title"]', widget).val() || ''; |
619 var title = $('input[id*="-title"]', widget).val() || ''; |
395 |
620 |
434 } else if ( 'single' === add ) { |
662 } else if ( 'single' === add ) { |
435 widget.attr( 'id', 'new-' + widgetId ); |
663 widget.attr( 'id', 'new-' + widgetId ); |
436 $( '#' + widgetId ).hide(); |
664 $( '#' + widgetId ).hide(); |
437 } |
665 } |
438 |
666 |
439 // Open the widgets container |
667 // Open the widgets container. |
440 sidebar.closest( '.widgets-holder-wrap' ).removeClass('closed'); |
668 sidebar.closest( '.widgets-holder-wrap' ) |
|
669 .removeClass( 'closed' ) |
|
670 .find( '.handlediv' ).attr( 'aria-expanded', 'true' ); |
441 |
671 |
442 sidebar.append( widget ); |
672 sidebar.append( widget ); |
443 sidebar.sortable('refresh'); |
673 sidebar.sortable('refresh'); |
444 |
674 |
445 wpWidgets.save( widget, 0, 0, 1 ); |
675 wpWidgets.save( widget, 0, 0, 1 ); |
446 // No longer "new" widget |
676 // No longer "new" widget |
447 widget.find( 'input.add_new' ).val(''); |
677 widget.find( 'input.add_new' ).val(''); |
448 |
678 |
449 $( document ).trigger( 'widget-added', [ widget ] ); |
679 $document.trigger( 'widget-added', [ widget ] ); |
450 |
680 |
451 /* |
681 /* |
452 * Check if any part of the sidebar is visible in the viewport. If it is, don't scroll. |
682 * Check if any part of the sidebar is visible in the viewport. If it is, don't scroll. |
453 * Otherwise, scroll up to so the sidebar is in view. |
683 * Otherwise, scroll up to so the sidebar is in view. |
454 * |
684 * |