PluginProbe ʕ •ᴥ•ʔ
Custom Sidebars – Dynamic Sidebar Classic Widget Area Manager / 3.1.3
Custom Sidebars – Dynamic Sidebar Classic Widget Area Manager v3.1.3
trunk 2.1.2.0 3.0.0.0 3.0.0.1 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.7.1 3.0.8 3.0.8.1 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.3 3.31 3.32 3.35 3.36 3.37 3.38
custom-sidebars / assets / js / cs.js
custom-sidebars / assets / js Last commit date
cs-cloning.js 8 years ago cs-cloning.min.js 8 years ago cs-visibility.js 8 years ago cs-visibility.min.js 8 years ago cs.js 8 years ago cs.min.js 8 years ago
cs.js
1602 lines
1 /*! Custom Sidebars - v3.1.3
2 * https://premium.wpmudev.org/project/custom-sidebars-pro/
3 * Copyright (c) 2018; * Licensed GPLv2+ */
4 /*global window:false */
5 /*global console:false */
6 /*global document:false */
7 /*global wp:false */
8 /*global wpmUi:false */
9 /*global csSidebars:false */
10 /*global csSidebarsData:false */
11
12 /**
13 * CsSidebar class
14 *
15 * This adds new functionality to each sidebar.
16 *
17 * Note: Before the first CsSidebar object is created the class csSidebars below
18 * must be initialized!
19 */
20
21
22 /*
23 * http://blog.stevenlevithan.com/archives/faster-trim-javascript
24 */
25 function trim( str ) {
26 str = str.replace(/^\s\s*/, '');
27 for (var i = str.length - 1; i >= 0; i--) {
28 if (/\S/.test(str.charAt(i))) {
29 str = str.substring(0, i + 1);
30 break;
31 }
32 }
33 return str;
34 }
35
36 function CsSidebar(id, type) {
37 var editbar;
38
39 /**
40 * Replace % to fix bug http://wordpress.org/support/topic/in-wp-35-sidebars-are-not-collapsable-anymore?replies=16#post-3990447
41 * We'll use this.id to select and the original id for html
42 *
43 * @since 1.2
44 */
45 this.id = id.split('%').join('\\%');
46
47 /**
48 * Either 'custom' or 'theme'
49 *
50 * @since 2.0
51 */
52 this.type = type;
53
54 this.sb = jQuery('#' + this.id);
55 this.widgets = '';
56 this.name = trim(this.sb.find('.sidebar-name h2').text());
57 this.description = trim(this.sb.find('.sidebar-description').text());
58
59 // Add one of two editbars to each sidebar.
60 if ( type === 'custom' ) {
61 editbar = window.csSidebars.extras.find('.cs-custom-sidebar').clone();
62 } else {
63 editbar = window.csSidebars.extras.find('.cs-theme-sidebar').clone();
64 }
65
66 this.sb.parent().append(editbar);
67
68 // Customize the links and label-tags.
69 editbar.find('label').each(function(){
70 var me = jQuery( this );
71 window.csSidebars.addIdToLabel( me, id );
72 });
73 }
74
75 /**
76 * Returns the sidebar ID.
77 *
78 * @since 2.0
79 */
80 CsSidebar.prototype.getID = function() {
81 return this.id.split('\\').join('');
82 };
83
84
85 /**
86 * =============================================================================
87 *
88 *
89 * csSidebars class
90 *
91 * This is the collection of all CsSidebar objects.
92 */
93 window.csSidebars = null;
94
95 (function($){
96 window.csSidebars = {
97 /**
98 * List of all CsSidebar objects.
99 * @type array
100 */
101 sidebars: [],
102
103 /**
104 * This is the same prefix as defined in class-custom-sidebars.php
105 * @type string
106 */
107 sidebar_prefix: 'cs-',
108
109 /**
110 * Form for the edit-sidebar popup.
111 * @type jQuery object
112 */
113 edit_form: null,
114
115 /**
116 * Form for the delete-sidebar popup.
117 * @type jQuery object
118 */
119 delete_form: null,
120
121 /**
122 * Form for the export/import popup.
123 * @type jQuery object
124 */
125 export_form: null,
126
127 /**
128 * Form for the location popup.
129 * @type jQuery object
130 */
131 location_form: null,
132
133 /**
134 * Shortcut to '#widgets-right'
135 * @type jQuery object
136 */
137 right: null,
138
139 /**
140 * Shortcut to '#cs-widgets-extra'
141 * @type jQuery object
142 */
143 extras: null,
144
145 /**
146 * Stores the callback functions associated with the toolbar actions.
147 * @see csSidebars.handleAction()
148 * @see csSidebars.registerAction()
149 * @type Object
150 */
151 action_handlers: {},
152
153
154 /*====================================*\
155 ========================================
156 == ==
157 == INITIALIZATION ==
158 == ==
159 ========================================
160 \*====================================*/
161
162 init: function(){
163 if ( 'undefined' === typeof( csSidebarsData ) ) {
164 // Inside theme customizer we load the JS but have no widget-data.
165 return;
166 }
167
168 csSidebars
169 .initControls()
170 .initTopTools()
171 .initSidebars()
172 .initToolbars()
173 .initColumns();
174 },
175
176 /**
177 * =====================================================================
178 * Initialize DOM and find jQuery objects
179 *
180 * @since 1.0.0
181 */
182 initControls: function(){
183 csSidebars.right = jQuery( '#widgets-right' );
184 csSidebars.extras = jQuery( '#cs-widgets-extra' );
185
186 if ( null === csSidebars.edit_form ) {
187 csSidebars.edit_form = csSidebars.extras.find( '.cs-editor' ).clone();
188 csSidebars.extras.find( '.cs-editor' ).remove();
189 }
190
191 if ( null === csSidebars.delete_form ) {
192 csSidebars.delete_form = csSidebars.extras.find( '.cs-delete' ).clone();
193 csSidebars.extras.find( '.cs-delete' ).remove();
194 }
195
196 if ( null === csSidebars.export_form ) {
197 csSidebars.export_form = csSidebars.extras.find( '.cs-export' ).clone();
198 csSidebars.extras.find( '.cs-export' ).remove();
199 }
200
201 if ( null === csSidebars.location_form ) {
202 csSidebars.location_form = csSidebars.extras.find( '.cs-location' ).clone();
203 csSidebars.extras.find( '.cs-location' ).remove();
204 }
205
206 jQuery('#cs-title-options')
207 .detach()
208 .prependTo( csSidebars.right );
209
210 return csSidebars;
211 },
212
213 /**
214 * =====================================================================
215 * Arrange sidebars in left/right columns.
216 * Left column: Custom sidebars. Right column: Theme sidebars.
217 *
218 * @since 2.0
219 */
220 initColumns: function() {
221 var col1 = csSidebars.right.find( '.sidebars-column-1' ),
222 col2 = csSidebars.right.find( '.sidebars-column-2' ),
223 title = jQuery( '<div class="cs-title"><h2></h2></div>' ),
224 sidebars = csSidebars.right.find( '.widgets-holder-wrap' );
225
226 if ( ! col2.length ) {
227 col2 = jQuery( '<div class="sidebars-column-2"></div>' );
228 col2.appendTo( csSidebars.right );
229 }
230
231 function toggle_sort() {
232 var me = jQuery( this ),
233 col = me.closest( '.sidebars-column-1, .sidebars-column-2' ),
234 dir = col.data( 'sort-dir' );
235
236 dir = ('asc' === dir ? 'desc' : 'asc');
237 csSidebars.sort_sidebars( col, dir );
238 }
239
240 title
241 .find( 'h2' )
242 .append( '<span class="cs-title-val"></span><i class="cs-icon dashicons dashicons-sort"></i>' )
243 .css({'cursor': 'pointer'});
244
245 title
246 .clone()
247 .prependTo( col1 )
248 .click( toggle_sort )
249 .find('.cs-title-val')
250 .text( csSidebarsData.custom_sidebars );
251
252 title
253 .clone()
254 .prependTo( col2 )
255 .click( toggle_sort )
256 .find( '.cs-title-val' )
257 .text( csSidebarsData.theme_sidebars );
258
259 col1 = jQuery( '<div class="inner"></div>' ).appendTo( col1 );
260 col2 = jQuery( '<div class="inner"></div>' ).appendTo( col2 );
261
262 sidebars.each(function check_sidebar() {
263 var me = jQuery( this ),
264 sbar = me.find( '.widgets-sortables' );
265
266 if ( csSidebars.isCustomSidebar( sbar) ) {
267 me.appendTo( col1 );
268 } else {
269 me.appendTo( col2 );
270 }
271 });
272 },
273
274 /**
275 * =====================================================================
276 * Initialization function, creates a CsSidebar object for each sidebar.
277 *
278 * @since 1.0.0
279 */
280 initSidebars: function(){
281 csSidebars.right.find('.widgets-sortables').each(function() {
282 var key, sb,
283 state = false,
284 me = jQuery( this ),
285 id = me.attr('id');
286
287 if ( me.data( 'cs-init' ) === true ) { return; }
288 me.data( 'cs-init', true );
289
290 if ( csSidebars.isCustomSidebar( this ) ) {
291 sb = csSidebars.add( id, 'custom' );
292 } else {
293 sb = csSidebars.add( id, 'theme' );
294
295 // Set correct "replaceable" flag for the toolbar.
296 for ( key in csSidebarsData.replaceable ) {
297 if ( ! csSidebarsData.replaceable.hasOwnProperty( key ) ) {
298 continue;
299 }
300 if ( csSidebarsData.replaceable[key] === id ) {
301 state = true;
302 break;
303 }
304 }
305
306 csSidebars.setReplaceable( sb, state, false );
307 }
308 });
309 return csSidebars;
310 },
311
312 /**
313 * =====================================================================
314 * Initialize the top toolbar, above the sidebar list.
315 *
316 * @since 1.0.0
317 */
318 initTopTools: function() {
319 var btn_create = jQuery( '.btn-create-sidebar' ),
320 btn_export = jQuery( '.btn-export' ),
321 topbar = jQuery( '.cs-options' ),
322 txt_filter = jQuery( '<input type="search" class="cs-filter" />' ),
323 data = {};
324
325 // Button: Add new sidebar.
326 btn_create.click(function() {
327 data.id = '';
328 data.title = csSidebarsData.title_new;
329 data.button = csSidebarsData.btn_new;
330 data.description = '';
331 data.name = '';
332
333 csSidebars.showEditor( data );
334 });
335
336 // Button: Export sidebars.
337 btn_export.click( csSidebars.showExport );
338
339 // Add Sidebar filter.
340 txt_filter
341 .appendTo( topbar )
342 .attr( 'placeholder', csSidebarsData.filter )
343 .keyup( csSidebars.filter_sidebars )
344 .on( 'search', csSidebars.filter_sidebars );
345
346 return csSidebars;
347 },
348
349 /**
350 * =====================================================================
351 * Hook up all the functions in the sidebar toolbar.
352 * Toolbar is in the bottom of each sidebar.
353 *
354 * @since 1.0.0
355 */
356 initToolbars: function() {
357 function tool_action( ev ) {
358 var me = jQuery( ev.target ).closest( '.cs-tool' ),
359 action = me.data( 'action' ),
360 id = csSidebars.getIdFromEditbar( me ),
361 sb = csSidebars.find( id );
362
363 // Return value False means: Execute the default click handler.
364 return ! csSidebars.handleAction( action, sb );
365 }
366
367 csSidebars.registerAction( 'edit', csSidebars.showEditor );
368 csSidebars.registerAction( 'location', csSidebars.showLocations );
369 csSidebars.registerAction( 'delete', csSidebars.showRemove );
370 csSidebars.registerAction( 'replaceable', csSidebars.setReplaceable );
371
372 csSidebars.right.on('click', '.cs-tool', tool_action);
373
374 return csSidebars;
375 },
376
377 /**
378 * Triggers the callback function for the specified toolbar action.
379 *
380 * @since 2.0
381 */
382 handleAction: function( action, sb ) {
383 if ( 'function' === typeof csSidebars.action_handlers[ action ] ) {
384 return !! csSidebars.action_handlers[ action ]( sb );
385 }
386 return false;
387 },
388
389 /**
390 * Registers a new callback function that is triggered when the
391 * associated toolbar icon is clicked.
392 *
393 * @since 2.0
394 */
395 registerAction: function( action, callback ) {
396 csSidebars.action_handlers[ action ] = callback;
397 },
398
399 /**
400 * Displays a error notification that something has gone wrong.
401 *
402 * @since 2.0
403 * @param mixed details Ajax response string/object.
404 */
405 showAjaxError: function( details ) {
406 var msg = {};
407
408 msg.message = csSidebarsData.ajax_error;
409 msg.details = details;
410 msg.parent = '#widgets-right';
411 msg.insert_after = '#cs-title-options';
412 msg.id = 'editor';
413 msg.type = 'err';
414
415 wpmUi.message( msg );
416 },
417
418 /**
419 * Sorts the sidebars in the specified column
420 *
421 * @since 2.0.9.7
422 * @param jQuery col Sidebar container/column.
423 * @param string dir "asc|desc"
424 */
425 sort_sidebars: function( col, dir ) {
426 var sidebars = col.find( '.widgets-holder-wrap' ),
427 icon = col.find( '.cs-title .cs-icon' );
428
429 sidebars.sortElements(function( a, b ) {
430 var val_a = jQuery(a).find('.sidebar-name h2').text(),
431 val_b = jQuery(b).find('.sidebar-name h2').text();
432
433 if ( dir === 'asc' ) {
434 return val_a > val_b ? 1 : -1;
435 } else {
436 return val_a < val_b ? 1 : -1;
437 }
438 });
439
440 // Change the indicator.
441 col.data( 'sort-dir', dir );
442 if ( 'asc' === dir ) {
443 icon
444 .removeClass( 'dashicons-arrow-down dashicons-sort' )
445 .addClass( 'dashicons-arrow-up' );
446 } else {
447 icon
448 .removeClass( 'dashicons-arrow-up dashicons-sort' )
449 .addClass( 'dashicons-arrow-down' );
450 }
451 },
452
453 /**
454 * Filters the sidebars by title
455 *
456 * @since 2.0.9.7
457 */
458 filter_sidebars: function( ev ) {
459 var query = jQuery( 'input.cs-filter' ).val().toLowerCase(),
460 all = csSidebars.right.find( '.widgets-holder-wrap' );
461
462 all.each(function(){
463 var sb = jQuery( this ),
464 title = sb.find( '.sidebar-name h2' ).text();
465
466 if ( title.toLowerCase().indexOf( query ) !== -1 ) {
467 sb.show();
468 } else {
469 sb.hide();
470 }
471 });
472 jQuery( window ).trigger( 'cs-resize' );
473 },
474
475
476 /*============================*\
477 ================================
478 == ==
479 == EDITOR ==
480 == ==
481 ================================
482 \*============================*/
483
484 /**
485 * =====================================================================
486 * Show the editor for a custom sidebar as a popup window.
487 *
488 * @since 2.0
489 * @param Object data Data describing the popup window.
490 * - id .. ID of the sidebar (text).
491 * - name .. Value of field "name".
492 * - description .. Value of field "description".
493 * - title .. Text for the window title.
494 * - button .. Caption of the save button.
495 *
496 * or a CsSidebar object.
497 */
498 showEditor: function( data ) {
499 var popup = null,
500 ajax = null;
501
502 if ( data instanceof CsSidebar ) {
503 data = {
504 id: data.getID(),
505 title: csSidebarsData.title_edit.replace( '[Sidebar]', data.name ),
506 button: csSidebarsData.btn_edit
507 };
508 }
509
510 // Hide the "extra" fields
511 function hide_extras() {
512 popup.$().removeClass( 'csb-has-more' );
513 popup.size( 782, 215 );
514 }
515
516 // Show the "extra" fields
517 function show_extras() {
518 popup.$().addClass( 'csb-has-more' );
519 popup.size( 782, 545 );
520 }
521
522 // Toggle the "extra" fields based on the checkbox state.
523 function toggle_extras() {
524 if ( jQuery( this ).prop( 'checked' ) ) {
525 show_extras();
526 } else {
527 hide_extras();
528 }
529 }
530
531 // Populates the input fields in the editor with given data.
532 function set_values( data, okay, xhr ) {
533 popup.loading( false );
534
535 // Ignore error responses from Ajax.
536 if ( ! data ) {
537 return false;
538 }
539
540 if ( ! okay ) {
541 popup.destroy();
542 csSidebars.showAjaxError( data );
543 return false;
544 }
545
546 if ( data.sidebar ) {
547 data = data.sidebar;
548 }
549
550 // Populate known fields.
551 if ( data.id ) {
552 popup.$().find( '#csb-id' ).val( data.id );
553 }
554 if ( data.name ) {
555 popup.$().find( '#csb-name' ).val( data.name );
556 }
557 if ( data.description ) {
558 popup.$().find( '#csb-description' ).val( data.description );
559 }
560 if ( data.before_title ) {
561 popup.$().find( '#csb-before-title' ).val( data.before_title );
562 }
563 if ( data.after_title ) {
564 popup.$().find( '#csb-after-title' ).val( data.after_title );
565 }
566 if ( data.before_widget ) {
567 popup.$().find( '#csb-before-widget' ).val( data.before_widget );
568 }
569 if ( data.after_widget ) {
570 popup.$().find( '#csb-after-widget' ).val( data.after_widget );
571 }
572 if ( data.button ) {
573 popup.$().find( '.btn-save' ).text( data.button );
574 }
575 if ( data.advance ) {
576 popup.$().find( '#csb-more' ).prop( 'checked', true );
577 show_extras();
578 }
579 }
580
581 // Close popup after ajax request
582 function handle_done_save( resp, okay, xhr ) {
583 var msg = {}, sb;
584
585 popup.loading( false );
586 popup.destroy();
587
588 msg.message = resp.message;
589 // msg.details = resp;
590 msg.parent = '#widgets-right';
591 msg.insert_after = '#cs-title-options';
592 msg.id = 'editor';
593
594 if ( okay ) {
595 if ( 'update' === resp.action ) {
596 // Update the name/description of the sidebar.
597 sb = csSidebars.find( resp.data.id );
598 csSidebars.updateSidebar( sb, resp.data );
599 } else if ( 'insert' === resp.action ) {
600 // Insert a brand new sidebar container.
601 csSidebars.insertSidebar( resp.data );
602 $('.cs-wrap .custom-sidebars-add-new').detach();
603 }
604 } else {
605 msg.type = 'err';
606 }
607 wpmUi.message( msg );
608 }
609
610 // Submit the data via ajax.
611 function save_data() {
612 var form = popup.$().find( 'form' );
613 if ( 0 < popup.$('#csb-more:checked').length ) {
614 jQuery('<input>').attr({
615 type: 'hidden',
616 value: 'show',
617 name: 'advance'
618 }).appendTo(form);
619 }
620 // Start loading-animation.
621 popup.loading( true );
622 ajax.reset()
623 .data( form )
624 .ondone( handle_done_save )
625 .load_json();
626 return false;
627 }
628
629 // Show the EDITOR popup.
630 popup = wpmUi.popup()
631 .modal( true )
632 .title( data.title )
633 .onshow( hide_extras )
634 .content( csSidebars.edit_form );
635
636 hide_extras();
637 set_values( data, true, null );
638
639 // Create new ajax object to get sidebar details.
640 ajax = wpmUi.ajax( null, 'cs-ajax' );
641 if ( data.id ) {
642 popup.loading( true );
643 ajax.reset()
644 .data({
645 'do': 'get',
646 'sb': data.id,
647 '_wpnonce': csSidebarsData._wpnonce_get
648 })
649 .ondone( set_values )
650 .load_json();
651 }
652
653 popup.show();
654 popup.$().find( '#csb-name' ).focus();
655
656 /**
657 * handle enter key on new sidebar name
658 */
659 popup.$().on( 'keypress', '#csb-name', function(e) {
660 if ( 13 === e.keyCode ) {
661 if ( 0 < $(this).val().length ) {
662 $('#csb-description').focus();
663 }
664 }
665 });
666
667 /**
668 * handle enter key on new sidebar description
669 */
670 popup.$().on( 'keypress', '#csb-description', function(e) {
671 if ( 13 === e.keyCode ) {
672 popup.$('.btn-save').click();
673 }
674 });
675
676 // Add event hooks to the editor.
677 popup.$().on( 'click', '#csb-more', toggle_extras );
678 popup.$().on( 'click', '.btn-save', save_data );
679 popup.$().on( 'click', '.btn-cancel', popup.destroy );
680
681 return true;
682 },
683
684 /**
685 * Update the name/description of an existing sidebar container.
686 *
687 * @since 1.0.0
688 */
689 updateSidebar: function( sb, data ) {
690 // Update the title.
691 sb.sb
692 .find( '.sidebar-name h2' )
693 .text( data.name );
694
695 // Update description.
696 sb.sb
697 .find( '.sidebar-description' )
698 .html( '<p class="description"></p>' )
699 .find( '.description' )
700 .text( data.description );
701
702 return csSidebars;
703 },
704
705 /**
706 * Insert a brand new sidebar container.
707 *
708 * @since 1.0.0
709 */
710 insertSidebar: function( data ) {
711 var box = jQuery( '<div class="widgets-holder-wrap"></div>' ),
712 inner = jQuery( '<div class="widgets-sortables ui-sortable"></div>' ),
713 name = jQuery( '<div class="sidebar-name"><div class="sidebar-name-arrow"><br></div><h2></h2></div>' ),
714 desc = jQuery( '<div class="sidebar-description"></div>' ),
715 col = csSidebars.right.find( '.sidebars-column-1 > .inner:first' );
716
717 // Set sidebar specific values.
718 inner.attr( 'id', data.id );
719
720 name
721 .find( 'h2' )
722 .text( data.name );
723
724 desc
725 .html( '<p class="description"></p>' )
726 .find( '.description' )
727 .text( data.description );
728
729 // Assemble the new sidebar box in correct order.
730 name.appendTo( inner );
731 desc.appendTo( inner );
732 inner.appendTo( box );
733
734 // Display the new sidebar on screen.
735 box.prependTo( col );
736
737 // Remove hooks added by wpWidgets.init()
738 jQuery( '#widgets-right .sidebar-name' ).unbind( 'click' );
739 jQuery( '#widgets-left .sidebar-name' ).unbind( 'click' );
740 jQuery( document.body ).unbind('click.widgets-toggle');
741 jQuery('.widgets-chooser')
742 .off( 'click.widgets-chooser' )
743 .off( 'keyup.widgets-chooser' );
744 jQuery( '#available-widgets .widget .widget-title' ).off( 'click.widgets-chooser' );
745 jQuery( '.widgets-chooser-sidebars' ).empty();
746
747 // Re-Init the page using wpWidgets.init()
748 window.wpWidgets.init();
749
750 // Add the plugin toolbar to the new sidebar.
751 csSidebars.initSidebars();
752
753 return csSidebars;
754 },
755
756
757 /*============================*\
758 ================================
759 == ==
760 == EXPORT ==
761 == ==
762 ================================
763 \*============================*/
764
765 /**
766 * Shows a popup window with the export/import form.
767 *
768 * @since 2.0
769 */
770 showExport: function() {
771 var popup = null,
772 ajax = null;
773
774 // Download export file.
775 function do_export( ev ) {
776 var form = jQuery( this ).closest( 'form' );
777
778 ajax.reset()
779 .data( form )
780 .load_http();
781
782 popup.destroy();
783
784 ev.preventDefault();
785 return false;
786 }
787
788 // Ajax handler after import file was uploaded.
789 function handle_done_upload( resp, okay, xhr ) {
790 var msg = {};
791 popup.loading( false );
792
793 if ( okay ) {
794 popup
795 .size( 900, 600 )
796 .content( resp.html );
797 } else {
798 msg.message = resp.message;
799 // msg.details = resp;
800 msg.parent = popup.$().find( '.wpmui-wnd-content' );
801 msg.insert_after = false;
802 msg.id = 'export';
803 //Change msg.class to msg['class']. Reserved words not allowed as unquoted properties in older version of javascript
804 msg['class'] = 'wpmui-wnd-err';
805 msg.type = 'err';
806 wpmUi.message( msg );
807 }
808 }
809
810 // Upload the import file.
811 function do_upload( ev ) {
812 var form = jQuery( this ).closest( 'form' );
813
814 popup.loading( true );
815 ajax.reset()
816 .data( form )
817 .ondone( handle_done_upload )
818 .load_json( 'cs-ajax' );
819
820 ev.preventDefault();
821 return false;
822 }
823
824 // Import preview: Toggle widgets
825 function toggle_widgets() {
826 var me = jQuery( this ),
827 checked = me.prop( 'checked' ),
828 items = popup.$().find( '.column-widgets, .import-widgets' );
829
830 if ( checked ) {
831 items.show();
832 } else {
833 items.hide();
834 }
835 }
836
837 // Import preview: Cancel.
838 function show_overview() {
839 popup.size( 782, 480 );
840 popup.content( csSidebars.export_form );
841 }
842
843 // Import preview: Import the data.
844 function do_import() {
845 var form = popup.$().find( '.frm-import' );
846
847 popup.loading( true );
848
849 ajax.reset()
850 .data( form )
851 .load_http( '_self' );
852 }
853
854 // Show the EXPORT popup.
855 popup = wpmUi.popup()
856 .modal( true )
857 .size( 782, 480 )
858 .title( csSidebarsData.title_export )
859 .content( csSidebars.export_form )
860 .show();
861
862 ajax = wpmUi.ajax( null, 'cs-ajax' );
863
864 // Events for the Import / Export view.
865 popup.$().on( 'submit', '.frm-export', do_export );
866 popup.$().on( 'submit', '.frm-preview-import', do_upload );
867
868 // Events for the Import preview.
869 popup.$().on( 'change', '#import-widgets', toggle_widgets );
870 popup.$().on( 'click', '.btn-cancel', show_overview );
871 popup.$().on( 'click', '.btn-import', do_import );
872
873 return true;
874 },
875
876
877 /*============================*\
878 ================================
879 == ==
880 == REMOVE ==
881 == ==
882 ================================
883 \*============================*/
884
885 /**
886 * =====================================================================
887 * Ask for confirmation before deleting a sidebar
888 */
889 showRemove: function( sb ) {
890 var popup = null,
891 ajax = null,
892 id = sb.getID(),
893 name = sb.name;
894
895 // Insert the sidebar name into the delete message.
896 function insert_name( el ) {
897 el.find('.name').text( name );
898 }
899
900 // Closes the delete confirmation.
901 function close_popup() {
902 popup.loading( false );
903 popup.destroy();
904 }
905
906 // Handle response of the delete ajax-call.
907 function handle_done( resp, okay, xhr ) {
908 var msg = {};
909
910 popup.loading( false );
911 popup.destroy();
912
913 msg.message = resp.message;
914 // msg.details = resp;
915 msg.parent = '#widgets-right';
916 msg.insert_after = '#cs-title-options';
917 msg.id = 'editor';
918
919 if ( okay ) {
920 // Remove the Sidebar from the page.
921 csSidebars.right
922 .find('#' + id)
923 .closest('.widgets-holder-wrap')
924 .remove();
925
926 // Remove object from internal collection.
927 csSidebars.remove( id );
928
929 // show "Create a custom sidebar to get started." if it is
930 // needed.
931 if ( "delete" === resp.action ) {
932 window.csSidebars.showGetStartedBox();
933 }
934 } else {
935 msg.type = 'err';
936 }
937
938 wpmUi.message( msg );
939 }
940
941 // Deletes the sidebar and closes the confirmation popup.
942 function delete_sidebar() {
943 popup.loading( true );
944 ajax.reset()
945 .data({
946 'do': 'delete',
947 'sb': id,
948 '_wpnonce': $('#_wp_nonce_cs_delete_sidebar').val()
949 })
950 .ondone( handle_done )
951 .load_json();
952 }
953
954 // Show the REMOVE popup.
955 popup = wpmUi.popup()
956 .modal( true )
957 .size( 560, 160 )
958 .title( csSidebarsData.title_delete )
959 .content( csSidebars.delete_form )
960 .onshow( insert_name )
961 .show();
962
963 // Create new ajax object.
964 ajax = wpmUi.ajax( null, 'cs-ajax' );
965
966 popup.$().on( 'click', '.btn-cancel', close_popup );
967 popup.$().on( 'click', '.btn-delete', delete_sidebar );
968
969 return true;
970 },
971
972
973 /*==============================*\
974 ==================================
975 == ==
976 == LOCATION ==
977 == ==
978 ==================================
979 \*==============================*/
980
981 /**
982 * =====================================================================
983 * Show popup to assign sidebar to default categories.
984 *
985 * @since 2.0
986 */
987 showLocations: function( sb ){
988 var popup = null,
989 ajax = null,
990 form = null,
991 id = sb.getID();
992
993 // Display the location data after it was loaded by ajax.
994 function handle_done_load( resp, okay, xhr ) {
995 var theme_sb, opt, name, msg = {}; // Only used in error case.
996
997 popup.loading( false );
998
999 if ( ! okay ) {
1000 popup.destroy();
1001 csSidebars.showAjaxError( resp );
1002 return;
1003 }
1004
1005 // Display the sidebar name.
1006 popup.$().find( '.sb-name' ).text( resp.sidebar.name );
1007 var sb_id = resp.sidebar.id;
1008
1009 /**
1010 * hide message
1011 */
1012 popup.$().find('.message.no-sidebars').hide();
1013
1014 /**
1015 * Count sidebars
1016 */
1017 var visible_sidebars = 0;
1018
1019 // Only show settings for replaceable sidebars
1020 var sidebars = popup.$().find( '.cs-replaceable' );
1021 sidebars.hide();
1022 resp.replaceable = wpmUi.obj( resp.replaceable );
1023 for ( var key0 in resp.replaceable ) {
1024 if ( ! resp.replaceable.hasOwnProperty( key0 ) ) {
1025 continue;
1026 }
1027 sidebars.filter( '.' + resp.replaceable[key0] ).show();
1028 visible_sidebars++;
1029 }
1030
1031 /**
1032 * no visible_sidebars - show information about it
1033 */
1034 if ( 0 === visible_sidebars ) {
1035 popup.$().find( '.wpmui-box, .message, .button-primary' ).hide();
1036 popup.$().find('.message.no-sidebars').show().parent().addClass('notice notice-error').removeClass('hidden');
1037 }
1038
1039 // Add a new option to the replacement list.
1040 function _add_option( item, lists, key ) {
1041 var opt = jQuery( '<option></option>' );
1042 opt.attr( 'value', key ).text( item.name );
1043 lists.append( opt );
1044 }
1045
1046 // Check if the current sidebar is a replacement in the list.
1047 function _select_option( replacement, sidebar, key, lists ) {
1048 var row = lists
1049 .closest( '.cs-replaceable' )
1050 .filter('.' + sidebar),
1051 option = row
1052 .find( 'option[value="' + key + '"]' ),
1053 group = row.find( 'optgroup.used' ),
1054 check = row.find( '.detail-toggle' );
1055
1056 if ( replacement === sb_id ) {
1057 option.prop( 'selected', true );
1058 if ( true !== check.prop( 'checked' ) ) {
1059 check.prop( 'checked', true );
1060 row.addClass( 'open' );
1061
1062 // Upgrade the select list with chosen.
1063 wpmUi.upgrade_multiselect( row );
1064 }
1065 } else {
1066 if ( ! group.length ) {
1067 group = jQuery( '<optgroup class="used">' )
1068 .attr( 'label', row.data( 'lbl-used' ) )
1069 .appendTo( row.find( '.details select' ) );
1070 }
1071 option.detach().appendTo( group );
1072 }
1073 }
1074
1075 // ----- Category ----------------------------------------------
1076 // Refresh list for single categories and category archives.
1077 var lst_cat = popup.$().find( '.cs-datalist.cs-cat' );
1078 var lst_act = popup.$().find( '.cs-datalist.cs-arc-cat' );
1079 var data_cat = resp.categories;
1080 lst_act.empty();
1081 lst_cat.empty();
1082 // Add the options
1083 for ( var key1 in data_cat ) {
1084 _add_option( data_cat[ key1 ], lst_act, key1 );
1085 _add_option( data_cat[ key1 ], lst_cat, key1 );
1086 }
1087
1088 // Select options
1089 for ( var key2 in data_cat ) {
1090 if ( data_cat[ key2 ].single ) {
1091 for ( theme_sb in data_cat[ key2 ].single ) {
1092 _select_option(
1093 data_cat[ key2 ].single[ theme_sb ],
1094 theme_sb,
1095 key2,
1096 lst_cat
1097 );
1098 }
1099 }
1100 if ( data_cat[ key2 ].archive ) {
1101 for ( theme_sb in data_cat[ key2 ].archive ) {
1102 _select_option(
1103 data_cat[ key2 ].archive[ theme_sb ],
1104 theme_sb,
1105 key2,
1106 lst_act
1107 );
1108 }
1109 }
1110 }
1111
1112 // ----- Post Type ---------------------------------------------
1113 // Refresh list for single posttypes.
1114 var lst_pst = popup.$().find( '.cs-datalist.cs-pt' );
1115 var data_pst = resp.posttypes;
1116 lst_pst.empty();
1117 // Add the options
1118 for ( var key3 in data_pst ) {
1119 opt = jQuery( '<option></option>' );
1120 name = data_pst[ key3 ].name;
1121 opt.attr( 'value', key3 ).text( name );
1122 lst_pst.append( opt );
1123 }
1124
1125 // Select options
1126 for ( var key4 in data_pst ) {
1127 if ( data_pst[ key4 ].single ) {
1128 for ( theme_sb in data_pst[ key4 ].single ) {
1129 _select_option(
1130 data_pst[ key4 ].single[ theme_sb ],
1131 theme_sb,
1132 key4,
1133 lst_pst
1134 );
1135 }
1136 }
1137 }
1138
1139 // ----- Archives ----------------------------------------------
1140 // Refresh list for archive types.
1141 var lst_arc = popup.$().find( '.cs-datalist.cs-arc' );
1142 var data_arc = resp.archives;
1143 lst_arc.empty();
1144 // Add the options
1145 for ( var key5 in data_arc ) {
1146 opt = jQuery( '<option></option>' );
1147 name = data_arc[ key5 ].name;
1148 opt.attr( 'value', key5 ).text( name );
1149 lst_arc.append( opt );
1150 }
1151
1152 // Select options
1153 for ( var key6 in data_arc ) {
1154 if ( data_arc[ key6 ].archive ) {
1155 for ( theme_sb in data_arc[ key6 ].archive ) {
1156 _select_option(
1157 data_arc[ key6 ].archive[ theme_sb ],
1158 theme_sb,
1159 key6,
1160 lst_arc
1161 );
1162 }
1163 }
1164 }
1165
1166 // ----- Authors ----------------------------------------------
1167 // Refresh list for authors.
1168 var lst_aut = popup.$().find( '.cs-datalist.cs-arc-aut' );
1169 var data_aut = resp.authors;
1170 lst_aut.empty();
1171 // Add the options
1172 for ( var key7 in data_aut ) {
1173 opt = jQuery( '<option></option>' );
1174 name = data_aut[ key7 ].name;
1175
1176 opt.attr( 'value', key7 ).text( name );
1177 lst_aut.append( opt );
1178 }
1179
1180 // Select options
1181 for ( var key8 in data_aut ) {
1182 if ( data_aut[ key8 ].archive ) {
1183 for ( theme_sb in data_aut[ key8 ].archive ) {
1184 _select_option(
1185 data_aut[ key8 ].archive[ theme_sb ],
1186 theme_sb,
1187 key8,
1188 lst_aut
1189 );
1190 }
1191 }
1192 }
1193
1194 // ----- 3rd part plugins ----------------------------------------------
1195 var lst_3rd = popup.$().find( '.cs-3rd-part .cs-datalist' );
1196 lst_3rd.each( function() {
1197 var data_3rd = resp[$(this).data('id')];
1198 $(this).empty();
1199 // Add the options
1200 for ( var key9 in data_3rd ) {
1201 opt = jQuery( '<option></option>' );
1202 name = data_3rd[ key9 ].name;
1203 opt.attr( 'value', key9 ).text( name );
1204 $(this).append( opt );
1205 }
1206 // Select options
1207 for ( var key10 in data_3rd ) {
1208 if ( data_3rd[ key10 ].archive ) {
1209 for ( theme_sb in data_3rd[ key10 ].archive ) {
1210 _select_option(
1211 data_3rd[ key10 ].archive[ theme_sb ],
1212 theme_sb,
1213 key10,
1214 $(this)
1215 );
1216 }
1217 }
1218 }
1219 });
1220
1221 } // end: handle_done_load()
1222
1223 // User clicks on "replace <sidebar> for <category>" checkbox.
1224 function toggle_details( ev ) {
1225 var inp = jQuery( this ),
1226 row = inp.closest( '.cs-replaceable' ),
1227 sel = row.find( 'select' );
1228
1229 if ( inp.prop( 'checked' ) ) {
1230 row.addClass( 'open' );
1231
1232 // Upgrade the select list with chosen.
1233 wpmUi.upgrade_multiselect( row );
1234
1235 // Tell the select list to render the contents again.
1236 sel.trigger( 'change.select2' );
1237 } else {
1238 row.removeClass( 'open' );
1239
1240 // Remove all selected options.
1241 sel.val( [] );
1242 }
1243 }
1244
1245 // After saving data via ajax is done.
1246 function handle_done_save( resp, okay, xhr ) {
1247 var msg = {};
1248
1249 popup.loading( false );
1250 popup.destroy();
1251
1252 msg.message = resp.message;
1253 // msg.details = resp;
1254 msg.parent = '#widgets-right';
1255 msg.insert_after = '#cs-title-options';
1256 msg.id = 'editor';
1257
1258 if ( ! okay ) {
1259 msg.type = 'err';
1260 }
1261
1262 wpmUi.message( msg );
1263 }
1264
1265 // Submit the data and close the popup.
1266 function save_data() {
1267 popup.loading( true );
1268 ajax.reset()
1269 .data( form )
1270 .ondone( handle_done_save )
1271 .load_json();
1272 }
1273
1274 // Show the LOCATION popup.
1275 popup = wpmUi.popup()
1276 .modal( true )
1277 .size( 782, 560 )
1278 .title( csSidebarsData.title_location )
1279 .content( csSidebars.location_form )
1280 .show();
1281
1282 popup.loading( true );
1283 form = popup.$().find( '.frm-location' );
1284 form.find( '.sb-id' ).val( id );
1285
1286 // Initialize ajax object.
1287 ajax = wpmUi.ajax( null, 'cs-ajax' );
1288 ajax.reset()
1289 .data({
1290 'do': 'get-location',
1291 'sb': id
1292 })
1293 .ondone( handle_done_load )
1294 .load_json();
1295
1296 // Attach events.
1297 popup.$().on( 'click', '.detail-toggle', toggle_details );
1298 popup.$().on( 'click', '.btn-save', save_data );
1299 popup.$().on( 'click', '.btn-cancel', popup.destroy );
1300
1301 return true;
1302 },
1303
1304 /*======================================*\
1305 ==========================================
1306 == ==
1307 == REPLACEABLE FLAG ==
1308 == ==
1309 ==========================================
1310 \*======================================*/
1311
1312 /**
1313 * =====================================================================
1314 * Change the replaceable flag
1315 *
1316 * @since 1.0.0
1317 */
1318 setReplaceable: function( sb, state, do_ajax ) {
1319 var ajax,
1320 theme_sb = csSidebars.right.find( '.sidebars-column-2 .widgets-holder-wrap' ),
1321 the_bar = jQuery( sb.sb ).closest( '.widgets-holder-wrap' ),
1322 chk = the_bar.find( '.cs-toolbar .chk-replaceable' ),
1323 marker = the_bar.find( '.replace-marker' ),
1324 btn_replaceable = the_bar.find( '.cs-toolbar .btn-replaceable' );
1325
1326 // After changing a sidebars "replaceable" flag.
1327 function handle_done_replaceable( resp, okay, xhr ) {
1328 // Adjust the "replaceable" flag to match the data returned by the ajax request.
1329 if ( resp instanceof Object && typeof resp.replaceable === 'object' ) {
1330 csSidebarsData.replaceable = wpmUi.obj( resp.replaceable );
1331
1332 theme_sb.find( '.widgets-sortables' ).each(function() {
1333 var _state = false,
1334 _me = jQuery( this ),
1335 _id = _me.attr( 'id' ),
1336 _sb = csSidebars.find( _id );
1337
1338 for ( var key in csSidebarsData.replaceable ) {
1339 if ( ! csSidebarsData.replaceable.hasOwnProperty( key ) ) {
1340 continue;
1341 }
1342 if ( csSidebarsData.replaceable[key] === _id ) {
1343 _state = true;
1344 break;
1345 }
1346 }
1347 csSidebars.setReplaceable( _sb, _state, false );
1348 });
1349 }
1350
1351 // Enable the checkboxes again after the ajax request is handled.
1352 theme_sb.find( '.cs-toolbar .chk-replaceable' ).prop( 'disabled', false );
1353 theme_sb.find( '.cs-toolbar .btn-replaceable' ).removeClass( 'wpmui-loading' );
1354 }
1355
1356 if ( undefined === state ) { state = chk.prop( 'checked' ); }
1357 if ( undefined === do_ajax ) { do_ajax = true; }
1358
1359 if ( chk.data( 'active' ) === state ) {
1360 return false;
1361 }
1362 chk.data( 'active', state );
1363 chk.prop( 'checked', state );
1364
1365 if ( state ) {
1366 if ( ! marker.length ) {
1367 jQuery( '<div></div>' )
1368 .appendTo( the_bar )
1369 .attr( 'data-label', csSidebarsData.lbl_replaceable )
1370 .addClass( 'replace-marker' );
1371 }
1372 the_bar.addClass( 'replaceable' );
1373 } else {
1374 marker.remove();
1375 the_bar.removeClass( 'replaceable' );
1376 }
1377
1378 if ( do_ajax ) {
1379 // Disable the checkbox until ajax request is done.
1380 theme_sb.find( '.cs-toolbar .chk-replaceable' ).prop( 'disabled', true );
1381 theme_sb.find( '.cs-toolbar .btn-replaceable' ).addClass( 'wpmui-loading' );
1382
1383 ajax = wpmUi.ajax( null, 'cs-ajax' );
1384 ajax.reset()
1385 .data({
1386 'do': 'replaceable',
1387 'state': state,
1388 'sb': sb.getID()
1389 })
1390 .ondone( handle_done_replaceable )
1391 .load_json();
1392 }
1393
1394 /**
1395 * This function is called by csSidebars.handleAction. Return value
1396 * False means that the default click event should be executed after
1397 * this function was called.
1398 */
1399 return false;
1400 },
1401
1402 /*=============================*\
1403 =================================
1404 == ==
1405 == HELPERS ==
1406 == ==
1407 =================================
1408 \*=============================*/
1409
1410 /**
1411 * =====================================================================
1412 * Find the specified CsSidebar object.
1413 *
1414 * @since 1.0.0
1415 */
1416 find: function(id){
1417 return csSidebars.sidebars[id];
1418 },
1419
1420 /**
1421 * =====================================================================
1422 * Create a new CsSidebar object.
1423 *
1424 * @since 1.0.0
1425 */
1426 add: function(id, type){
1427 csSidebars.sidebars[id] = new CsSidebar(id, type);
1428 return csSidebars.sidebars[id];
1429 },
1430
1431 /**
1432 * =====================================================================
1433 * Removes a new CsSidebar object.
1434 *
1435 * @since 2.0
1436 */
1437 remove: function(id){
1438 delete csSidebars.sidebars[id];
1439 },
1440
1441 /**
1442 * =====================================================================
1443 * Returns true when the specified ID is recognized as a sidebar
1444 * that was created by the custom sidebars plugin.
1445 *
1446 * @since 2.0
1447 */
1448 isCustomSidebar: function( el ) {
1449 var id = jQuery( el ).attr('id'),
1450 prefix = id.substr(0, csSidebars.sidebar_prefix.length);
1451
1452 return prefix === csSidebars.sidebar_prefix;
1453 },
1454
1455 /**
1456 * =====================================================================
1457 * Append the specified sidebar ID to the label and input element.
1458 *
1459 * @since 2.0
1460 */
1461 addIdToLabel: function( $obj, id ){
1462 if ( true !== $obj.data( 'label-done' ) ) {
1463 var prefix = $obj.attr('for');
1464 $obj.attr( 'for', prefix + id );
1465 $obj.find( '.has-label' ).attr( 'id', prefix + id );
1466 $obj.data( 'label-done', true );
1467 }
1468 },
1469
1470 /**
1471 * =====================================================================
1472 * Returns the sidebar ID based on the sidebar DOM object.
1473 *
1474 * @since 2.0
1475 * @param jQuery $obj Any DOM object inside the Sidebar HTML structure.
1476 * @return string The sidebar ID
1477 */
1478 getIdFromEditbar: function( $obj ){
1479 var wrapper = $obj.closest( '.widgets-holder-wrap' ),
1480 sb = wrapper.find( '.widgets-sortables:first' ),
1481 id = sb.attr( 'id' );
1482 return id;
1483 },
1484
1485 /**
1486 * =====================================================================
1487 * Show "Create a custom sidebar to get started." box.
1488 *
1489 * @since 3.0.4
1490 */
1491 showGetStartedBox: function() {
1492 if ( 0 === $(".sidebars-column-1 .inner .widgets-holder-wrap").length ) {
1493 var template = wp.template('custom-sidebars-new');
1494 $(".sidebars-column-1 .inner").before( template() );
1495 $(".custom-sidebars-add-new").on( "click", function() {
1496 $( "button.btn-create-sidebar" ).click();
1497 });
1498 }
1499 }
1500 };
1501
1502 jQuery(function($){
1503 $('#csfooter').hide();
1504 if ( $('#widgets-right').length > 0 ) {
1505 csSidebars.init();
1506 }
1507 $('.defaultsContainer').hide();
1508
1509 $( '#widgets-right .widgets-sortables' ).on( "sort", function(event, ui) {
1510 var topx = $('#widgets-right').top;
1511 ui.position.top = - $('#widgets-right').css('top');
1512 });
1513 });
1514 /**
1515 * add new sidebar placeholder
1516 */
1517 jQuery(document).ready( function($) {
1518 window.setTimeout( function() {
1519 window.csSidebars.showGetStartedBox();
1520 }, 1000);
1521 });
1522 })(jQuery);
1523
1524 /**
1525 * jQuery.fn.sortElements
1526 * --------------
1527 * @param Function comparator:
1528 * Exactly the same behaviour as [1,2,3].sort(comparator)
1529 *
1530 * @param Function getSortable
1531 * A function that should return the element that is
1532 * to be sorted. The comparator will run on the
1533 * current collection, but you may want the actual
1534 * resulting sort to occur on a parent or another
1535 * associated element.
1536 *
1537 * E.g. $('td').sortElements(comparator, function(){
1538 * return this.parentNode;
1539 * })
1540 *
1541 * The <td>'s parent (<tr>) will be sorted instead
1542 * of the <td> itself.
1543 *
1544 * @see http://james.padolsey.com/javascript/sorting-elements-with-jquery/
1545 */
1546 jQuery.fn.sortElements = (function(){
1547 var sort = [].sort;
1548 return function(comparator, getSortable) {
1549 getSortable = getSortable || function(){return this;};
1550 var placements = this.map(function(){
1551 var sortElement = getSortable.call(this),
1552 parentNode = sortElement.parentNode,
1553 // Since the element itself will change position, we have
1554 // to have some way of storing its original position in
1555 // the DOM. The easiest way is to have a 'flag' node:
1556 nextSibling = parentNode.insertBefore(
1557 document.createTextNode(''),
1558 sortElement.nextSibling
1559 );
1560 return function() {
1561 if (parentNode === this) {
1562 throw new Error(
1563 "You can't sort elements if any one is a descendant of another."
1564 );
1565 }
1566 // Insert before flag:
1567 parentNode.insertBefore(this, nextSibling);
1568 // Remove flag:
1569 parentNode.removeChild(nextSibling);
1570 };
1571 });
1572 return sort.call(this, comparator).each(function(i){
1573 placements[i].call(getSortable.call(this));
1574 });
1575 };
1576 })();
1577
1578
1579 /*global console:false */
1580 /*global document:false */
1581 /*global ajaxurl:false */
1582
1583 /**
1584 * Handle "Custom sidebars configuration is allowed for:" option on
1585 * widgets screen options.
1586 */
1587 (function($){
1588 jQuery(document).ready( function($) {
1589 $('#screen-options-wrap .cs-roles input[type=checkbox]').on( 'change', function() {
1590 var data = {
1591 'action': 'custom_sidebars_metabox_roles',
1592 '_wpnonce': $('#custom_sidebars_metabox_roles').val(),
1593 'fields': {}
1594 };
1595 $('#screen-options-wrap .cs-roles input[type=checkbox]').each( function() {
1596 data.fields[$(this).val()] = this.checked;
1597 });
1598 $.post( ajaxurl, data );
1599 });
1600 });
1601 })(jQuery);
1602