You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

444 line
12 KiB

  1. /**
  2. * Contains the postboxes logic, opening and closing postboxes, reordering and saving
  3. * the state and ordering to the database.
  4. *
  5. * @summary Contains postboxes logic
  6. *
  7. * @since 2.5.0
  8. * @requires jQuery
  9. */
  10. /* global ajaxurl, postBoxL10n */
  11. /**
  12. * This object contains all function to handle the behaviour of the post boxes. The post boxes are the boxes you see
  13. * around the content on the edit page.
  14. *
  15. * @since 2.7.0
  16. *
  17. * @namespace postboxes
  18. *
  19. * @type {Object}
  20. */
  21. var postboxes;
  22. (function($) {
  23. var $document = $( document );
  24. postboxes = {
  25. /**
  26. * @summary Handles a click on either the postbox heading or the postbox open/close icon.
  27. *
  28. * Opens or closes the postbox. Expects `this` to equal the clicked element.
  29. * Calls postboxes.pbshow if the postbox has been opened, calls postboxes.pbhide
  30. * if the postbox has been closed.
  31. *
  32. * @since 4.4.0
  33. * @memberof postboxes
  34. * @fires postboxes#postbox-toggled
  35. *
  36. * @returns {void}
  37. */
  38. handle_click : function () {
  39. var $el = $( this ),
  40. p = $el.parent( '.postbox' ),
  41. id = p.attr( 'id' ),
  42. ariaExpandedValue;
  43. if ( 'dashboard_browser_nag' === id ) {
  44. return;
  45. }
  46. p.toggleClass( 'closed' );
  47. ariaExpandedValue = ! p.hasClass( 'closed' );
  48. if ( $el.hasClass( 'handlediv' ) ) {
  49. // The handle button was clicked.
  50. $el.attr( 'aria-expanded', ariaExpandedValue );
  51. } else {
  52. // The handle heading was clicked.
  53. $el.closest( '.postbox' ).find( 'button.handlediv' )
  54. .attr( 'aria-expanded', ariaExpandedValue );
  55. }
  56. if ( postboxes.page !== 'press-this' ) {
  57. postboxes.save_state( postboxes.page );
  58. }
  59. if ( id ) {
  60. if ( !p.hasClass('closed') && $.isFunction( postboxes.pbshow ) ) {
  61. postboxes.pbshow( id );
  62. } else if ( p.hasClass('closed') && $.isFunction( postboxes.pbhide ) ) {
  63. postboxes.pbhide( id );
  64. }
  65. }
  66. /**
  67. * @summary Fires when a postbox has been opened or closed.
  68. *
  69. * Contains a jQuery object with the relevant postbox element.
  70. *
  71. * @since 4.0.0
  72. * @event postboxes#postbox-toggled
  73. * @type {Object}
  74. */
  75. $document.trigger( 'postbox-toggled', p );
  76. },
  77. /**
  78. * Adds event handlers to all postboxes and screen option on the current page.
  79. *
  80. * @since 2.7.0
  81. * @memberof postboxes
  82. *
  83. * @param {string} page The page we are currently on.
  84. * @param {Object} [args]
  85. * @param {Function} args.pbshow A callback that is called when a postbox opens.
  86. * @param {Function} args.pbhide A callback that is called when a postbox closes.
  87. * @returns {void}
  88. */
  89. add_postbox_toggles : function (page, args) {
  90. var $handles = $( '.postbox .hndle, .postbox .handlediv' );
  91. this.page = page;
  92. this.init( page, args );
  93. $handles.on( 'click.postboxes', this.handle_click );
  94. /**
  95. * @since 2.7.0
  96. */
  97. $('.postbox .hndle a').click( function(e) {
  98. e.stopPropagation();
  99. });
  100. /**
  101. * @summary Hides a postbox.
  102. *
  103. * Event handler for the postbox dismiss button. After clicking the button
  104. * the postbox will be hidden.
  105. *
  106. * @since 3.2.0
  107. *
  108. * @returns {void}
  109. */
  110. $( '.postbox a.dismiss' ).on( 'click.postboxes', function( e ) {
  111. var hide_id = $(this).parents('.postbox').attr('id') + '-hide';
  112. e.preventDefault();
  113. $( '#' + hide_id ).prop('checked', false).triggerHandler('click');
  114. });
  115. /**
  116. * @summary Hides the postbox element
  117. *
  118. * Event handler for the screen options checkboxes. When a checkbox is
  119. * clicked this function will hide or show the relevant postboxes.
  120. *
  121. * @since 2.7.0
  122. * @fires postboxes#postbox-toggled
  123. *
  124. * @returns {void}
  125. */
  126. $('.hide-postbox-tog').bind('click.postboxes', function() {
  127. var $el = $(this),
  128. boxId = $el.val(),
  129. $postbox = $( '#' + boxId );
  130. if ( $el.prop( 'checked' ) ) {
  131. $postbox.show();
  132. if ( $.isFunction( postboxes.pbshow ) ) {
  133. postboxes.pbshow( boxId );
  134. }
  135. } else {
  136. $postbox.hide();
  137. if ( $.isFunction( postboxes.pbhide ) ) {
  138. postboxes.pbhide( boxId );
  139. }
  140. }
  141. postboxes.save_state( page );
  142. postboxes._mark_area();
  143. /**
  144. * @since 4.0.0
  145. * @see postboxes.handle_click
  146. */
  147. $document.trigger( 'postbox-toggled', $postbox );
  148. });
  149. /**
  150. * @summary Changes the amount of columns based on the layout preferences.
  151. *
  152. * @since 2.8.0
  153. *
  154. * @returns {void}
  155. */
  156. $('.columns-prefs input[type="radio"]').bind('click.postboxes', function(){
  157. var n = parseInt($(this).val(), 10);
  158. if ( n ) {
  159. postboxes._pb_edit(n);
  160. postboxes.save_order( page );
  161. }
  162. });
  163. },
  164. /**
  165. * @summary Initializes all the postboxes, mainly their sortable behaviour.
  166. *
  167. * @since 2.7.0
  168. * @memberof postboxes
  169. *
  170. * @param {string} page The page we are currently on.
  171. * @param {Object} [args={}] The arguments for the postbox initializer.
  172. * @param {Function} args.pbshow A callback that is called when a postbox opens.
  173. * @param {Function} args.pbhide A callback that is called when a postbox
  174. * closes.
  175. *
  176. * @returns {void}
  177. */
  178. init : function(page, args) {
  179. var isMobile = $( document.body ).hasClass( 'mobile' ),
  180. $handleButtons = $( '.postbox .handlediv' );
  181. $.extend( this, args || {} );
  182. $('#wpbody-content').css('overflow','hidden');
  183. $('.meta-box-sortables').sortable({
  184. placeholder: 'sortable-placeholder',
  185. connectWith: '.meta-box-sortables',
  186. items: '.postbox',
  187. handle: '.hndle',
  188. cursor: 'move',
  189. delay: ( isMobile ? 200 : 0 ),
  190. distance: 2,
  191. tolerance: 'pointer',
  192. forcePlaceholderSize: true,
  193. helper: function( event, element ) {
  194. /* `helper: 'clone'` is equivalent to `return element.clone();`
  195. * Cloning a checked radio and then inserting that clone next to the original
  196. * radio unchecks the original radio (since only one of the two can be checked).
  197. * We get around this by renaming the helper's inputs' name attributes so that,
  198. * when the helper is inserted into the DOM for the sortable, no radios are
  199. * duplicated, and no original radio gets unchecked.
  200. */
  201. return element.clone()
  202. .find( ':input' )
  203. .attr( 'name', function( i, currentName ) {
  204. return 'sort_' + parseInt( Math.random() * 100000, 10 ).toString() + '_' + currentName;
  205. } )
  206. .end();
  207. },
  208. opacity: 0.65,
  209. stop: function() {
  210. var $el = $( this );
  211. if ( $el.find( '#dashboard_browser_nag' ).is( ':visible' ) && 'dashboard_browser_nag' != this.firstChild.id ) {
  212. $el.sortable('cancel');
  213. return;
  214. }
  215. postboxes.save_order(page);
  216. },
  217. receive: function(e,ui) {
  218. if ( 'dashboard_browser_nag' == ui.item[0].id )
  219. $(ui.sender).sortable('cancel');
  220. postboxes._mark_area();
  221. $document.trigger( 'postbox-moved', ui.item );
  222. }
  223. });
  224. if ( isMobile ) {
  225. $(document.body).bind('orientationchange.postboxes', function(){ postboxes._pb_change(); });
  226. this._pb_change();
  227. }
  228. this._mark_area();
  229. // Set the handle buttons `aria-expanded` attribute initial value on page load.
  230. $handleButtons.each( function () {
  231. var $el = $( this );
  232. $el.attr( 'aria-expanded', ! $el.parent( '.postbox' ).hasClass( 'closed' ) );
  233. });
  234. },
  235. /**
  236. * @summary Saves the state of the postboxes to the server.
  237. *
  238. * Saves the state of the postboxes to the server. It sends two lists, one with
  239. * all the closed postboxes, one with all the hidden postboxes.
  240. *
  241. * @since 2.7.0
  242. * @memberof postboxes
  243. *
  244. * @param {string} page The page we are currently on.
  245. * @returns {void}
  246. */
  247. save_state : function(page) {
  248. var closed, hidden;
  249. // Return on the nav-menus.php screen, see #35112.
  250. if ( 'nav-menus' === page ) {
  251. return;
  252. }
  253. closed = $( '.postbox' ).filter( '.closed' ).map( function() { return this.id; } ).get().join( ',' );
  254. hidden = $( '.postbox' ).filter( ':hidden' ).map( function() { return this.id; } ).get().join( ',' );
  255. $.post(ajaxurl, {
  256. action: 'closed-postboxes',
  257. closed: closed,
  258. hidden: hidden,
  259. closedpostboxesnonce: jQuery('#closedpostboxesnonce').val(),
  260. page: page
  261. });
  262. },
  263. /**
  264. * @summary Saves the order of the postboxes to the server.
  265. *
  266. * Saves the order of the postboxes to the server. Sends a list of all postboxes
  267. * inside a sortable area to the server.
  268. *
  269. * @since 2.8.0
  270. * @memberof postboxes
  271. *
  272. * @param {string} page The page we are currently on.
  273. * @returns {void}
  274. */
  275. save_order : function(page) {
  276. var postVars, page_columns = $('.columns-prefs input:checked').val() || 0;
  277. postVars = {
  278. action: 'meta-box-order',
  279. _ajax_nonce: $('#meta-box-order-nonce').val(),
  280. page_columns: page_columns,
  281. page: page
  282. };
  283. $('.meta-box-sortables').each( function() {
  284. postVars[ 'order[' + this.id.split( '-' )[0] + ']' ] = $( this ).sortable( 'toArray' ).join( ',' );
  285. } );
  286. $.post( ajaxurl, postVars );
  287. },
  288. /**
  289. * @summary Marks empty postbox areas.
  290. *
  291. * Adds a message to empty sortable areas on the dashboard page. Also adds a
  292. * border around the side area on the post edit screen if there are no postboxes
  293. * present.
  294. *
  295. * @since 3.3.0
  296. * @memberof postboxes
  297. * @access private
  298. *
  299. * @returns {void}
  300. */
  301. _mark_area : function() {
  302. var visible = $('div.postbox:visible').length, side = $('#post-body #side-sortables');
  303. $( '#dashboard-widgets .meta-box-sortables:visible' ).each( function() {
  304. var t = $(this);
  305. if ( visible == 1 || t.children('.postbox:visible').length ) {
  306. t.removeClass('empty-container');
  307. }
  308. else {
  309. t.addClass('empty-container');
  310. t.attr('data-emptyString', postBoxL10n.postBoxEmptyString);
  311. }
  312. });
  313. if ( side.length ) {
  314. if ( side.children('.postbox:visible').length )
  315. side.removeClass('empty-container');
  316. else if ( $('#postbox-container-1').css('width') == '280px' )
  317. side.addClass('empty-container');
  318. }
  319. },
  320. /**
  321. * @summary Changes the amount of columns on the post edit page.
  322. *
  323. * @since 3.3.0
  324. * @memberof postboxes
  325. * @fires postboxes#postboxes-columnchange
  326. * @access private
  327. *
  328. * @param {number} n The amount of columns to divide the post edit page in.
  329. * @returns {void}
  330. */
  331. _pb_edit : function(n) {
  332. var el = $('.metabox-holder').get(0);
  333. if ( el ) {
  334. el.className = el.className.replace(/columns-\d+/, 'columns-' + n);
  335. }
  336. /**
  337. * Fires when the amount of columns on the post edit page has been changed.
  338. *
  339. * @since 4.0.0
  340. * @event postboxes#postboxes-columnchange
  341. */
  342. $( document ).trigger( 'postboxes-columnchange' );
  343. },
  344. /**
  345. * @summary Changes the amount of columns the postboxes are in based on the
  346. * current orientation of the browser.
  347. *
  348. * @since 3.3.0
  349. * @memberof postboxes
  350. * @access private
  351. *
  352. * @returns {void}
  353. */
  354. _pb_change : function() {
  355. var check = $( 'label.columns-prefs-1 input[type="radio"]' );
  356. switch ( window.orientation ) {
  357. case 90:
  358. case -90:
  359. if ( !check.length || !check.is(':checked') )
  360. this._pb_edit(2);
  361. break;
  362. case 0:
  363. case 180:
  364. if ( $('#poststuff').length ) {
  365. this._pb_edit(1);
  366. } else {
  367. if ( !check.length || !check.is(':checked') )
  368. this._pb_edit(2);
  369. }
  370. break;
  371. }
  372. },
  373. /* Callbacks */
  374. /**
  375. * @since 2.7.0
  376. * @memberof postboxes
  377. * @access public
  378. * @property {Function|boolean} pbshow A callback that is called when a postbox
  379. * is opened.
  380. */
  381. pbshow : false,
  382. /**
  383. * @since 2.7.0
  384. * @memberof postboxes
  385. * @access public
  386. * @property {Function|boolean} pbhide A callback that is called when a postbox
  387. * is closed.
  388. */
  389. pbhide : false
  390. };
  391. }(jQuery));