2 * Contains the postboxes logic, opening and closing postboxes, reordering and saving
3 * the state and ordering to the database.
5 * @summary Contains postboxes logic
11 /* global ajaxurl, postBoxL10n */
14 * This object contains all function to handle the behaviour of the post boxes. The post boxes are the boxes you see
15 * around the content on the edit page.
19 * @namespace postboxes
26 var $document = $( document );
31 * @summary Handles a click on either the postbox heading or the postbox open/close icon.
33 * Opens or closes the postbox. Expects `this` to equal the clicked element.
34 * Calls postboxes.pbshow if the postbox has been opened, calls postboxes.pbhide
35 * if the postbox has been closed.
39 * @fires postboxes#postbox-toggled
43 handle_click : function () {
45 p = $el.parent( '.postbox' ),
49 if ( 'dashboard_browser_nag' === id ) {
53 p.toggleClass( 'closed' );
55 ariaExpandedValue = ! p.hasClass( 'closed' );
57 if ( $el.hasClass( 'handlediv' ) ) {
58 // The handle button was clicked.
59 $el.attr( 'aria-expanded', ariaExpandedValue );
61 // The handle heading was clicked.
62 $el.closest( '.postbox' ).find( 'button.handlediv' )
63 .attr( 'aria-expanded', ariaExpandedValue );
66 if ( postboxes.page !== 'press-this' ) {
67 postboxes.save_state( postboxes.page );
71 if ( !p.hasClass('closed') && $.isFunction( postboxes.pbshow ) ) {
72 postboxes.pbshow( id );
73 } else if ( p.hasClass('closed') && $.isFunction( postboxes.pbhide ) ) {
74 postboxes.pbhide( id );
79 * @summary Fires when a postbox has been opened or closed.
81 * Contains a jQuery object with the relevant postbox element.
84 * @event postboxes#postbox-toggled
87 $document.trigger( 'postbox-toggled', p );
91 * Adds event handlers to all postboxes and screen option on the current page.
96 * @param {string} page The page we are currently on.
97 * @param {Object} [args]
98 * @param {Function} args.pbshow A callback that is called when a postbox opens.
99 * @param {Function} args.pbhide A callback that is called when a postbox closes.
102 add_postbox_toggles : function (page, args) {
103 var $handles = $( '.postbox .hndle, .postbox .handlediv' );
106 this.init( page, args );
108 $handles.on( 'click.postboxes', this.handle_click );
113 $('.postbox .hndle a').click( function(e) {
118 * @summary Hides a postbox.
120 * Event handler for the postbox dismiss button. After clicking the button
121 * the postbox will be hidden.
127 $( '.postbox a.dismiss' ).on( 'click.postboxes', function( e ) {
128 var hide_id = $(this).parents('.postbox').attr('id') + '-hide';
130 $( '#' + hide_id ).prop('checked', false).triggerHandler('click');
134 * @summary Hides the postbox element
136 * Event handler for the screen options checkboxes. When a checkbox is
137 * clicked this function will hide or show the relevant postboxes.
140 * @fires postboxes#postbox-toggled
144 $('.hide-postbox-tog').bind('click.postboxes', function() {
147 $postbox = $( '#' + boxId );
149 if ( $el.prop( 'checked' ) ) {
151 if ( $.isFunction( postboxes.pbshow ) ) {
152 postboxes.pbshow( boxId );
156 if ( $.isFunction( postboxes.pbhide ) ) {
157 postboxes.pbhide( boxId );
161 postboxes.save_state( page );
162 postboxes._mark_area();
166 * @see postboxes.handle_click
168 $document.trigger( 'postbox-toggled', $postbox );
172 * @summary Changes the amount of columns based on the layout preferences.
178 $('.columns-prefs input[type="radio"]').bind('click.postboxes', function(){
179 var n = parseInt($(this).val(), 10);
182 postboxes._pb_edit(n);
183 postboxes.save_order( page );
189 * @summary Initializes all the postboxes, mainly their sortable behaviour.
192 * @memberof postboxes
194 * @param {string} page The page we are currently on.
195 * @param {Object} [args={}] The arguments for the postbox initializer.
196 * @param {Function} args.pbshow A callback that is called when a postbox opens.
197 * @param {Function} args.pbhide A callback that is called when a postbox
202 init : function(page, args) {
203 var isMobile = $( document.body ).hasClass( 'mobile' ),
204 $handleButtons = $( '.postbox .handlediv' );
206 $.extend( this, args || {} );
207 $('#wpbody-content').css('overflow','hidden');
208 $('.meta-box-sortables').sortable({
209 placeholder: 'sortable-placeholder',
210 connectWith: '.meta-box-sortables',
214 delay: ( isMobile ? 200 : 0 ),
216 tolerance: 'pointer',
217 forcePlaceholderSize: true,
218 helper: function( event, element ) {
219 /* `helper: 'clone'` is equivalent to `return element.clone();`
220 * Cloning a checked radio and then inserting that clone next to the original
221 * radio unchecks the original radio (since only one of the two can be checked).
222 * We get around this by renaming the helper's inputs' name attributes so that,
223 * when the helper is inserted into the DOM for the sortable, no radios are
224 * duplicated, and no original radio gets unchecked.
226 return element.clone()
228 .attr( 'name', function( i, currentName ) {
229 return 'sort_' + parseInt( Math.random() * 100000, 10 ).toString() + '_' + currentName;
237 if ( $el.find( '#dashboard_browser_nag' ).is( ':visible' ) && 'dashboard_browser_nag' != this.firstChild.id ) {
238 $el.sortable('cancel');
242 postboxes.save_order(page);
244 receive: function(e,ui) {
245 if ( 'dashboard_browser_nag' == ui.item[0].id )
246 $(ui.sender).sortable('cancel');
248 postboxes._mark_area();
249 $document.trigger( 'postbox-moved', ui.item );
254 $(document.body).bind('orientationchange.postboxes', function(){ postboxes._pb_change(); });
260 // Set the handle buttons `aria-expanded` attribute initial value on page load.
261 $handleButtons.each( function () {
263 $el.attr( 'aria-expanded', ! $el.parent( '.postbox' ).hasClass( 'closed' ) );
268 * @summary Saves the state of the postboxes to the server.
270 * Saves the state of the postboxes to the server. It sends two lists, one with
271 * all the closed postboxes, one with all the hidden postboxes.
274 * @memberof postboxes
276 * @param {string} page The page we are currently on.
279 save_state : function(page) {
282 // Return on the nav-menus.php screen, see #35112.
283 if ( 'nav-menus' === page ) {
287 closed = $( '.postbox' ).filter( '.closed' ).map( function() { return this.id; } ).get().join( ',' );
288 hidden = $( '.postbox' ).filter( ':hidden' ).map( function() { return this.id; } ).get().join( ',' );
291 action: 'closed-postboxes',
294 closedpostboxesnonce: jQuery('#closedpostboxesnonce').val(),
300 * @summary Saves the order of the postboxes to the server.
302 * Saves the order of the postboxes to the server. Sends a list of all postboxes
303 * inside a sortable area to the server.
306 * @memberof postboxes
308 * @param {string} page The page we are currently on.
311 save_order : function(page) {
312 var postVars, page_columns = $('.columns-prefs input:checked').val() || 0;
315 action: 'meta-box-order',
316 _ajax_nonce: $('#meta-box-order-nonce').val(),
317 page_columns: page_columns,
321 $('.meta-box-sortables').each( function() {
322 postVars[ 'order[' + this.id.split( '-' )[0] + ']' ] = $( this ).sortable( 'toArray' ).join( ',' );
325 $.post( ajaxurl, postVars );
329 * @summary Marks empty postbox areas.
331 * Adds a message to empty sortable areas on the dashboard page. Also adds a
332 * border around the side area on the post edit screen if there are no postboxes
336 * @memberof postboxes
341 _mark_area : function() {
342 var visible = $('div.postbox:visible').length, side = $('#post-body #side-sortables');
344 $( '#dashboard-widgets .meta-box-sortables:visible' ).each( function() {
347 if ( visible == 1 || t.children('.postbox:visible').length ) {
348 t.removeClass('empty-container');
351 t.addClass('empty-container');
352 t.attr('data-emptyString', postBoxL10n.postBoxEmptyString);
357 if ( side.children('.postbox:visible').length )
358 side.removeClass('empty-container');
359 else if ( $('#postbox-container-1').css('width') == '280px' )
360 side.addClass('empty-container');
365 * @summary Changes the amount of columns on the post edit page.
368 * @memberof postboxes
369 * @fires postboxes#postboxes-columnchange
372 * @param {number} n The amount of columns to divide the post edit page in.
375 _pb_edit : function(n) {
376 var el = $('.metabox-holder').get(0);
379 el.className = el.className.replace(/columns-\d+/, 'columns-' + n);
383 * Fires when the amount of columns on the post edit page has been changed.
386 * @event postboxes#postboxes-columnchange
388 $( document ).trigger( 'postboxes-columnchange' );
392 * @summary Changes the amount of columns the postboxes are in based on the
393 * current orientation of the browser.
396 * @memberof postboxes
401 _pb_change : function() {
402 var check = $( 'label.columns-prefs-1 input[type="radio"]' );
404 switch ( window.orientation ) {
407 if ( !check.length || !check.is(':checked') )
412 if ( $('#poststuff').length ) {
415 if ( !check.length || !check.is(':checked') )
426 * @memberof postboxes
428 * @property {Function|boolean} pbshow A callback that is called when a postbox
435 * @memberof postboxes
437 * @property {Function|boolean} pbhide A callback that is called when a postbox