PluginProbe ʕ •ᴥ•ʔ
Custom Sidebars – Dynamic Sidebar Classic Widget Area Manager / 3.0.7.1
Custom Sidebars – Dynamic Sidebar Classic Widget Area Manager v3.0.7.1
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 / js / cs.js
custom-sidebars / js Last commit date
cs-cloning.js 9 years ago cs-cloning.min.js 9 years ago cs-visibility.js 9 years ago cs-visibility.min.js 9 years ago cs.js 9 years ago cs.min.js 9 years ago
cs.js
1561 lines
1 /*! Custom Sidebars - v3.0.7.1
2 * https://premium.wpmudev.org/project/custom-sidebars-pro/
3 * Copyright (c) 2017; * 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 }
576
577 // Close popup after ajax request
578 function handle_done_save( resp, okay, xhr ) {
579 var msg = {}, sb;
580
581 popup.loading( false );
582 popup.destroy();
583
584 msg.message = resp.message;
585 // msg.details = resp;
586 msg.parent = '#widgets-right';
587 msg.insert_after = '#cs-title-options';
588 msg.id = 'editor';
589
590 if ( okay ) {
591 if ( 'update' === resp.action ) {
592 // Update the name/description of the sidebar.
593 sb = csSidebars.find( resp.data.id );
594 csSidebars.updateSidebar( sb, resp.data );
595 } else if ( 'insert' === resp.action ) {
596 // Insert a brand new sidebar container.
597 csSidebars.insertSidebar( resp.data );
598 $('.cs-wrap .custom-sidebars-add-new').detach();
599 }
600 } else {
601 msg.type = 'err';
602 }
603 wpmUi.message( msg );
604 }
605
606 // Submit the data via ajax.
607 function save_data() {
608 var form = popup.$().find( 'form' );
609
610 // Start loading-animation.
611 popup.loading( true );
612
613 ajax.reset()
614 .data( form )
615 .ondone( handle_done_save )
616 .load_json();
617
618 return false;
619 }
620
621 // Show the EDITOR popup.
622 popup = wpmUi.popup()
623 .modal( true )
624 .title( data.title )
625 .onshow( hide_extras )
626 .content( csSidebars.edit_form );
627
628 hide_extras();
629 set_values( data, true, null );
630
631 // Create new ajax object to get sidebar details.
632 ajax = wpmUi.ajax( null, 'cs-ajax' );
633 if ( data.id ) {
634 popup.loading( true );
635 ajax.reset()
636 .data({
637 'do': 'get',
638 'sb': data.id
639 })
640 .ondone( set_values )
641 .load_json();
642 }
643
644 popup.show();
645 popup.$().find( '#csb-name' ).focus();
646
647 /**
648 * handle enter key on new sidebar name
649 */
650 popup.$().on( 'keypress', '#csb-name', function(e) {
651 if ( 13 === e.keyCode ) {
652 if ( 0 < $(this).val().length ) {
653 $('#csb-description').focus();
654 }
655 }
656 });
657
658 /**
659 * handle enter key on new sidebar description
660 */
661 popup.$().on( 'keypress', '#csb-description', function(e) {
662 if ( 13 === e.keyCode ) {
663 popup.$('.btn-save').click();
664 }
665 });
666
667 // Add event hooks to the editor.
668 popup.$().on( 'click', '#csb-more', toggle_extras );
669 popup.$().on( 'click', '.btn-save', save_data );
670 popup.$().on( 'click', '.btn-cancel', popup.destroy );
671
672 return true;
673 },
674
675 /**
676 * Update the name/description of an existing sidebar container.
677 *
678 * @since 1.0.0
679 */
680 updateSidebar: function( sb, data ) {
681 // Update the title.
682 sb.sb
683 .find( '.sidebar-name h2' )
684 .text( data.name );
685
686 // Update description.
687 sb.sb
688 .find( '.sidebar-description' )
689 .html( '<p class="description"></p>' )
690 .find( '.description' )
691 .text( data.description );
692
693 return csSidebars;
694 },
695
696 /**
697 * Insert a brand new sidebar container.
698 *
699 * @since 1.0.0
700 */
701 insertSidebar: function( data ) {
702 var box = jQuery( '<div class="widgets-holder-wrap"></div>' ),
703 inner = jQuery( '<div class="widgets-sortables ui-sortable"></div>' ),
704 name = jQuery( '<div class="sidebar-name"><div class="sidebar-name-arrow"><br></div><h2></h2></div>' ),
705 desc = jQuery( '<div class="sidebar-description"></div>' ),
706 col = csSidebars.right.find( '.sidebars-column-1 > .inner:first' );
707
708 // Set sidebar specific values.
709 inner.attr( 'id', data.id );
710
711 name
712 .find( 'h2' )
713 .text( data.name );
714
715 desc
716 .html( '<p class="description"></p>' )
717 .find( '.description' )
718 .text( data.description );
719
720 // Assemble the new sidebar box in correct order.
721 name.appendTo( inner );
722 desc.appendTo( inner );
723 inner.appendTo( box );
724
725 // Display the new sidebar on screen.
726 box.prependTo( col );
727
728 // Remove hooks added by wpWidgets.init()
729 jQuery( '#widgets-right .sidebar-name' ).unbind( 'click' );
730 jQuery( '#widgets-left .sidebar-name' ).unbind( 'click' );
731 jQuery( document.body ).unbind('click.widgets-toggle');
732 jQuery('.widgets-chooser')
733 .off( 'click.widgets-chooser' )
734 .off( 'keyup.widgets-chooser' );
735 jQuery( '#available-widgets .widget .widget-title' ).off( 'click.widgets-chooser' );
736 jQuery( '.widgets-chooser-sidebars' ).empty();
737
738 // Re-Init the page using wpWidgets.init()
739 window.wpWidgets.init();
740
741 // Add the plugin toolbar to the new sidebar.
742 csSidebars.initSidebars();
743
744 return csSidebars;
745 },
746
747
748 /*============================*\
749 ================================
750 == ==
751 == EXPORT ==
752 == ==
753 ================================
754 \*============================*/
755
756 /**
757 * Shows a popup window with the export/import form.
758 *
759 * @since 2.0
760 */
761 showExport: function() {
762 var popup = null,
763 ajax = null;
764
765 // Download export file.
766 function do_export( ev ) {
767 var form = jQuery( this ).closest( 'form' );
768
769 ajax.reset()
770 .data( form )
771 .load_http();
772
773 popup.destroy();
774
775 ev.preventDefault();
776 return false;
777 }
778
779 // Ajax handler after import file was uploaded.
780 function handle_done_upload( resp, okay, xhr ) {
781 var msg = {};
782 popup.loading( false );
783
784 if ( okay ) {
785 popup
786 .size( 900, 600 )
787 .content( resp.html );
788 } else {
789 msg.message = resp.message;
790 // msg.details = resp;
791 msg.parent = popup.$().find( '.wpmui-wnd-content' );
792 msg.insert_after = false;
793 msg.id = 'export';
794 //Change msg.class to msg['class']. Reserved words not allowed as unquoted properties in older version of javascript
795 msg['class'] = 'wpmui-wnd-err';
796 msg.type = 'err';
797 wpmUi.message( msg );
798 }
799 }
800
801 // Upload the import file.
802 function do_upload( ev ) {
803 var form = jQuery( this ).closest( 'form' );
804
805 popup.loading( true );
806 ajax.reset()
807 .data( form )
808 .ondone( handle_done_upload )
809 .load_json( 'cs-ajax' );
810
811 ev.preventDefault();
812 return false;
813 }
814
815 // Import preview: Toggle widgets
816 function toggle_widgets() {
817 var me = jQuery( this ),
818 checked = me.prop( 'checked' ),
819 items = popup.$().find( '.column-widgets, .import-widgets' );
820
821 if ( checked ) {
822 items.show();
823 } else {
824 items.hide();
825 }
826 }
827
828 // Import preview: Cancel.
829 function show_overview() {
830 popup.size( 782, 480 );
831 popup.content( csSidebars.export_form );
832 }
833
834 // Import preview: Import the data.
835 function do_import() {
836 var form = popup.$().find( '.frm-import' );
837
838 popup.loading( true );
839
840 ajax.reset()
841 .data( form )
842 .load_http( '_self' );
843 }
844
845 // Show the EXPORT popup.
846 popup = wpmUi.popup()
847 .modal( true )
848 .size( 782, 480 )
849 .title( csSidebarsData.title_export )
850 .content( csSidebars.export_form )
851 .show();
852
853 ajax = wpmUi.ajax( null, 'cs-ajax' );
854
855 // Events for the Import / Export view.
856 popup.$().on( 'submit', '.frm-export', do_export );
857 popup.$().on( 'submit', '.frm-preview-import', do_upload );
858
859 // Events for the Import preview.
860 popup.$().on( 'change', '#import-widgets', toggle_widgets );
861 popup.$().on( 'click', '.btn-cancel', show_overview );
862 popup.$().on( 'click', '.btn-import', do_import );
863
864 return true;
865 },
866
867
868 /*============================*\
869 ================================
870 == ==
871 == REMOVE ==
872 == ==
873 ================================
874 \*============================*/
875
876 /**
877 * =====================================================================
878 * Ask for confirmation before deleting a sidebar
879 */
880 showRemove: function( sb ) {
881 var popup = null,
882 ajax = null,
883 id = sb.getID(),
884 name = sb.name;
885
886 // Insert the sidebar name into the delete message.
887 function insert_name( el ) {
888 el.find('.name').text( name );
889 }
890
891 // Closes the delete confirmation.
892 function close_popup() {
893 popup.loading( false );
894 popup.destroy();
895 }
896
897 // Handle response of the delete ajax-call.
898 function handle_done( resp, okay, xhr ) {
899 var msg = {};
900
901 popup.loading( false );
902 popup.destroy();
903
904 msg.message = resp.message;
905 // msg.details = resp;
906 msg.parent = '#widgets-right';
907 msg.insert_after = '#cs-title-options';
908 msg.id = 'editor';
909
910 if ( okay ) {
911 // Remove the Sidebar from the page.
912 csSidebars.right
913 .find('#' + id)
914 .closest('.widgets-holder-wrap')
915 .remove();
916
917 // Remove object from internal collection.
918 csSidebars.remove( id );
919
920 // show "Create a custom sidebar to get started." if it is
921 // needed.
922 if ( "delete" === resp.action ) {
923 window.csSidebars.showGetStartedBox();
924 }
925 } else {
926 msg.type = 'err';
927 }
928
929 wpmUi.message( msg );
930 }
931
932 // Deletes the sidebar and closes the confirmation popup.
933 function delete_sidebar() {
934 popup.loading( true );
935
936 ajax.reset()
937 .data({
938 'do': 'delete',
939 'sb': id
940 })
941 .ondone( handle_done )
942 .load_json();
943 }
944
945 // Show the REMOVE popup.
946 popup = wpmUi.popup()
947 .modal( true )
948 .size( 560, 160 )
949 .title( csSidebarsData.title_delete )
950 .content( csSidebars.delete_form )
951 .onshow( insert_name )
952 .show();
953
954 // Create new ajax object.
955 ajax = wpmUi.ajax( null, 'cs-ajax' );
956
957 popup.$().on( 'click', '.btn-cancel', close_popup );
958 popup.$().on( 'click', '.btn-delete', delete_sidebar );
959
960 return true;
961 },
962
963
964 /*==============================*\
965 ==================================
966 == ==
967 == LOCATION ==
968 == ==
969 ==================================
970 \*==============================*/
971
972 /**
973 * =====================================================================
974 * Show popup to assign sidebar to default categories.
975 *
976 * @since 2.0
977 */
978 showLocations: function( sb ){
979 var popup = null,
980 ajax = null,
981 form = null,
982 id = sb.getID();
983
984 // Display the location data after it was loaded by ajax.
985 function handle_done_load( resp, okay, xhr ) {
986 var theme_sb, opt, name, msg = {}; // Only used in error case.
987
988 popup.loading( false );
989
990 if ( ! okay ) {
991 popup.destroy();
992 csSidebars.showAjaxError( resp );
993 return;
994 }
995
996 // Display the sidebar name.
997 popup.$().find( '.sb-name' ).text( resp.sidebar.name );
998 var sb_id = resp.sidebar.id;
999
1000 /**
1001 * hide message
1002 */
1003 popup.$().find('.message.no-sidebars').hide();
1004
1005 /**
1006 * Count sidebars
1007 */
1008 var visible_sidebars = 0;
1009
1010 // Only show settings for replaceable sidebars
1011 var sidebars = popup.$().find( '.cs-replaceable' );
1012 sidebars.hide();
1013 resp.replaceable = wpmUi.obj( resp.replaceable );
1014 for ( var key0 in resp.replaceable ) {
1015 if ( ! resp.replaceable.hasOwnProperty( key0 ) ) {
1016 continue;
1017 }
1018 sidebars.filter( '.' + resp.replaceable[key0] ).show();
1019 visible_sidebars++;
1020 }
1021
1022 /**
1023 * no visible_sidebars - show information about it
1024 */
1025 if ( 0 === visible_sidebars ) {
1026 popup.$().find( '.wpmui-box, .message, .button-primary' ).hide();
1027 popup.$().find('.message.no-sidebars').show().parent().addClass('notice notice-error').removeClass('hidden');
1028 }
1029
1030 // Add a new option to the replacement list.
1031 function _add_option( item, lists, key ) {
1032 var opt = jQuery( '<option></option>' );
1033 opt.attr( 'value', key ).text( item.name );
1034 lists.append( opt );
1035 }
1036
1037 // Check if the current sidebar is a replacement in the list.
1038 function _select_option( replacement, sidebar, key, lists ) {
1039 var row = lists
1040 .closest( '.cs-replaceable' )
1041 .filter('.' + sidebar),
1042 option = row
1043 .find( 'option[value="' + key + '"]' ),
1044 group = row.find( 'optgroup.used' ),
1045 check = row.find( '.detail-toggle' );
1046
1047 if ( replacement === sb_id ) {
1048 option.prop( 'selected', true );
1049 if ( true !== check.prop( 'checked' ) ) {
1050 check.prop( 'checked', true );
1051 row.addClass( 'open' );
1052
1053 // Upgrade the select list with chosen.
1054 wpmUi.upgrade_multiselect( row );
1055 }
1056 } else {
1057 if ( ! group.length ) {
1058 group = jQuery( '<optgroup class="used">' )
1059 .attr( 'label', row.data( 'lbl-used' ) )
1060 .appendTo( row.find( '.details select' ) );
1061 }
1062 option.detach().appendTo( group );
1063 }
1064 }
1065
1066 // ----- Category ----------------------------------------------
1067 // Refresh list for single categories and category archives.
1068 var lst_cat = popup.$().find( '.cs-datalist.cs-cat' );
1069 var lst_act = popup.$().find( '.cs-datalist.cs-arc-cat' );
1070 var data_cat = resp.categories;
1071 lst_act.empty();
1072 lst_cat.empty();
1073 // Add the options
1074 for ( var key1 in data_cat ) {
1075 _add_option( data_cat[ key1 ], lst_act, key1 );
1076 _add_option( data_cat[ key1 ], lst_cat, key1 );
1077 }
1078
1079 // Select options
1080 for ( var key2 in data_cat ) {
1081 if ( data_cat[ key2 ].single ) {
1082 for ( theme_sb in data_cat[ key2 ].single ) {
1083 _select_option(
1084 data_cat[ key2 ].single[ theme_sb ],
1085 theme_sb,
1086 key2,
1087 lst_cat
1088 );
1089 }
1090 }
1091 if ( data_cat[ key2 ].archive ) {
1092 for ( theme_sb in data_cat[ key2 ].archive ) {
1093 _select_option(
1094 data_cat[ key2 ].archive[ theme_sb ],
1095 theme_sb,
1096 key2,
1097 lst_act
1098 );
1099 }
1100 }
1101 }
1102
1103 // ----- Post Type ---------------------------------------------
1104 // Refresh list for single posttypes.
1105 var lst_pst = popup.$().find( '.cs-datalist.cs-pt' );
1106 var data_pst = resp.posttypes;
1107 lst_pst.empty();
1108 // Add the options
1109 for ( var key3 in data_pst ) {
1110 opt = jQuery( '<option></option>' );
1111 name = data_pst[ key3 ].name;
1112
1113 opt.attr( 'value', key3 ).text( name );
1114 lst_pst.append( opt );
1115 }
1116
1117 // Select options
1118 for ( var key4 in data_pst ) {
1119 if ( data_pst[ key4 ].single ) {
1120 for ( theme_sb in data_pst[ key4 ].single ) {
1121 _select_option(
1122 data_pst[ key4 ].single[ theme_sb ],
1123 theme_sb,
1124 key4,
1125 lst_pst
1126 );
1127 }
1128 }
1129 }
1130
1131 // ----- Archives ----------------------------------------------
1132 // Refresh list for archive types.
1133 var lst_arc = popup.$().find( '.cs-datalist.cs-arc' );
1134 var data_arc = resp.archives;
1135 lst_arc.empty();
1136 // Add the options
1137 for ( var key5 in data_arc ) {
1138 opt = jQuery( '<option></option>' );
1139 name = data_arc[ key5 ].name;
1140
1141 opt.attr( 'value', key5 ).text( name );
1142 lst_arc.append( opt );
1143 }
1144
1145 // Select options
1146 for ( var key6 in data_arc ) {
1147 if ( data_arc[ key6 ].archive ) {
1148 for ( theme_sb in data_arc[ key6 ].archive ) {
1149 _select_option(
1150 data_arc[ key6 ].archive[ theme_sb ],
1151 theme_sb,
1152 key6,
1153 lst_arc
1154 );
1155 }
1156 }
1157 }
1158
1159 // ----- Authors ----------------------------------------------
1160 // Refresh list for authors.
1161 var lst_aut = popup.$().find( '.cs-datalist.cs-arc-aut' );
1162 var data_aut = resp.authors;
1163 lst_aut.empty();
1164 // Add the options
1165 for ( var key7 in data_aut ) {
1166 opt = jQuery( '<option></option>' );
1167 name = data_aut[ key7 ].name;
1168
1169 opt.attr( 'value', key7 ).text( name );
1170 lst_aut.append( opt );
1171 }
1172
1173 // Select options
1174 for ( var key8 in data_aut ) {
1175 if ( data_aut[ key8 ].archive ) {
1176 for ( theme_sb in data_aut[ key8 ].archive ) {
1177 _select_option(
1178 data_aut[ key8 ].archive[ theme_sb ],
1179 theme_sb,
1180 key8,
1181 lst_aut
1182 );
1183 }
1184 }
1185 }
1186
1187 } // end: handle_done_load()
1188
1189 // User clicks on "replace <sidebar> for <category>" checkbox.
1190 function toggle_details( ev ) {
1191 var inp = jQuery( this ),
1192 row = inp.closest( '.cs-replaceable' ),
1193 sel = row.find( 'select' );
1194
1195 if ( inp.prop( 'checked' ) ) {
1196 row.addClass( 'open' );
1197
1198 // Upgrade the select list with chosen.
1199 wpmUi.upgrade_multiselect( row );
1200
1201 // Tell the select list to render the contents again.
1202 sel.trigger( 'change.select2' );
1203 } else {
1204 row.removeClass( 'open' );
1205
1206 // Remove all selected options.
1207 sel.val( [] );
1208 }
1209 }
1210
1211 // After saving data via ajax is done.
1212 function handle_done_save( resp, okay, xhr ) {
1213 var msg = {};
1214
1215 popup.loading( false );
1216 popup.destroy();
1217
1218 msg.message = resp.message;
1219 // msg.details = resp;
1220 msg.parent = '#widgets-right';
1221 msg.insert_after = '#cs-title-options';
1222 msg.id = 'editor';
1223
1224 if ( ! okay ) {
1225 msg.type = 'err';
1226 }
1227
1228 wpmUi.message( msg );
1229 }
1230
1231 // Submit the data and close the popup.
1232 function save_data() {
1233 popup.loading( true );
1234
1235 ajax.reset()
1236 .data( form )
1237 .ondone( handle_done_save )
1238 .load_json();
1239 }
1240
1241 // Show the LOCATION popup.
1242 popup = wpmUi.popup()
1243 .modal( true )
1244 .size( 782, 560 )
1245 .title( csSidebarsData.title_location )
1246 .content( csSidebars.location_form )
1247 .show();
1248
1249 popup.loading( true );
1250 form = popup.$().find( '.frm-location' );
1251 form.find( '.sb-id' ).val( id );
1252
1253 // Initialize ajax object.
1254 ajax = wpmUi.ajax( null, 'cs-ajax' );
1255 ajax.reset()
1256 .data({
1257 'do': 'get-location',
1258 'sb': id
1259 })
1260 .ondone( handle_done_load )
1261 .load_json();
1262
1263 // Attach events.
1264 popup.$().on( 'click', '.detail-toggle', toggle_details );
1265 popup.$().on( 'click', '.btn-save', save_data );
1266 popup.$().on( 'click', '.btn-cancel', popup.destroy );
1267
1268 return true;
1269 },
1270
1271 /*======================================*\
1272 ==========================================
1273 == ==
1274 == REPLACEABLE FLAG ==
1275 == ==
1276 ==========================================
1277 \*======================================*/
1278
1279 /**
1280 * =====================================================================
1281 * Change the replaceable flag
1282 *
1283 * @since 1.0.0
1284 */
1285 setReplaceable: function( sb, state, do_ajax ) {
1286 var ajax,
1287 theme_sb = csSidebars.right.find( '.sidebars-column-2 .widgets-holder-wrap' ),
1288 the_bar = jQuery( sb.sb ).closest( '.widgets-holder-wrap' ),
1289 chk = the_bar.find( '.cs-toolbar .chk-replaceable' ),
1290 marker = the_bar.find( '.replace-marker' ),
1291 btn_replaceable = the_bar.find( '.cs-toolbar .btn-replaceable' );
1292
1293 // After changing a sidebars "replaceable" flag.
1294 function handle_done_replaceable( resp, okay, xhr ) {
1295 // Adjust the "replaceable" flag to match the data returned by the ajax request.
1296 if ( resp instanceof Object && typeof resp.replaceable === 'object' ) {
1297 csSidebarsData.replaceable = wpmUi.obj( resp.replaceable );
1298
1299 theme_sb.find( '.widgets-sortables' ).each(function() {
1300 var _state = false,
1301 _me = jQuery( this ),
1302 _id = _me.attr( 'id' ),
1303 _sb = csSidebars.find( _id );
1304
1305 for ( var key in csSidebarsData.replaceable ) {
1306 if ( ! csSidebarsData.replaceable.hasOwnProperty( key ) ) {
1307 continue;
1308 }
1309 if ( csSidebarsData.replaceable[key] === _id ) {
1310 _state = true;
1311 break;
1312 }
1313 }
1314 csSidebars.setReplaceable( _sb, _state, false );
1315 });
1316 }
1317
1318 // Enable the checkboxes again after the ajax request is handled.
1319 theme_sb.find( '.cs-toolbar .chk-replaceable' ).prop( 'disabled', false );
1320 theme_sb.find( '.cs-toolbar .btn-replaceable' ).removeClass( 'wpmui-loading' );
1321 }
1322
1323 if ( undefined === state ) { state = chk.prop( 'checked' ); }
1324 if ( undefined === do_ajax ) { do_ajax = true; }
1325
1326 if ( chk.data( 'active' ) === state ) {
1327 return false;
1328 }
1329 chk.data( 'active', state );
1330 chk.prop( 'checked', state );
1331
1332 if ( state ) {
1333 if ( ! marker.length ) {
1334 jQuery( '<div></div>' )
1335 .appendTo( the_bar )
1336 .attr( 'data-label', csSidebarsData.lbl_replaceable )
1337 .addClass( 'replace-marker' );
1338 }
1339 the_bar.addClass( 'replaceable' );
1340 } else {
1341 marker.remove();
1342 the_bar.removeClass( 'replaceable' );
1343 }
1344
1345 if ( do_ajax ) {
1346 // Disable the checkbox until ajax request is done.
1347 theme_sb.find( '.cs-toolbar .chk-replaceable' ).prop( 'disabled', true );
1348 theme_sb.find( '.cs-toolbar .btn-replaceable' ).addClass( 'wpmui-loading' );
1349
1350 ajax = wpmUi.ajax( null, 'cs-ajax' );
1351 ajax.reset()
1352 .data({
1353 'do': 'replaceable',
1354 'state': state,
1355 'sb': sb.getID()
1356 })
1357 .ondone( handle_done_replaceable )
1358 .load_json();
1359 }
1360
1361 /**
1362 * This function is called by csSidebars.handleAction. Return value
1363 * False means that the default click event should be executed after
1364 * this function was called.
1365 */
1366 return false;
1367 },
1368
1369
1370 /*=============================*\
1371 =================================
1372 == ==
1373 == HELPERS ==
1374 == ==
1375 =================================
1376 \*=============================*/
1377
1378
1379 /**
1380 * =====================================================================
1381 * Find the specified CsSidebar object.
1382 *
1383 * @since 1.0.0
1384 */
1385 find: function(id){
1386 return csSidebars.sidebars[id];
1387 },
1388
1389 /**
1390 * =====================================================================
1391 * Create a new CsSidebar object.
1392 *
1393 * @since 1.0.0
1394 */
1395 add: function(id, type){
1396 csSidebars.sidebars[id] = new CsSidebar(id, type);
1397 return csSidebars.sidebars[id];
1398 },
1399
1400 /**
1401 * =====================================================================
1402 * Removes a new CsSidebar object.
1403 *
1404 * @since 2.0
1405 */
1406 remove: function(id){
1407 delete csSidebars.sidebars[id];
1408 },
1409
1410 /**
1411 * =====================================================================
1412 * Returns true when the specified ID is recognized as a sidebar
1413 * that was created by the custom sidebars plugin.
1414 *
1415 * @since 2.0
1416 */
1417 isCustomSidebar: function( el ) {
1418 var id = jQuery( el ).attr('id'),
1419 prefix = id.substr(0, csSidebars.sidebar_prefix.length);
1420
1421 return prefix === csSidebars.sidebar_prefix;
1422 },
1423
1424 /**
1425 * =====================================================================
1426 * Append the specified sidebar ID to the label and input element.
1427 *
1428 * @since 2.0
1429 */
1430 addIdToLabel: function( $obj, id ){
1431 if ( true !== $obj.data( 'label-done' ) ) {
1432 var prefix = $obj.attr('for');
1433 $obj.attr( 'for', prefix + id );
1434 $obj.find( '.has-label' ).attr( 'id', prefix + id );
1435 $obj.data( 'label-done', true );
1436 }
1437 },
1438
1439 /**
1440 * =====================================================================
1441 * Returns the sidebar ID based on the sidebar DOM object.
1442 *
1443 * @since 2.0
1444 * @param jQuery $obj Any DOM object inside the Sidebar HTML structure.
1445 * @return string The sidebar ID
1446 */
1447 getIdFromEditbar: function( $obj ){
1448 var wrapper = $obj.closest( '.widgets-holder-wrap' ),
1449 sb = wrapper.find( '.widgets-sortables:first' ),
1450 id = sb.attr( 'id' );
1451 return id;
1452 },
1453
1454 /**
1455 * =====================================================================
1456 * Show "Create a custom sidebar to get started." box.
1457 *
1458 * @since 3.0.4
1459 */
1460 showGetStartedBox: function() {
1461 if ( 0 === $(".sidebars-column-1 .inner .widgets-holder-wrap").length ) {
1462 var template = wp.template('custom-sidebars-new');
1463 $(".sidebars-column-1 .inner").before( template() );
1464 $(".custom-sidebars-add-new").on( "click", function() {
1465 $( "button.btn-create-sidebar" ).click();
1466 });
1467 }
1468 }
1469 };
1470
1471 jQuery(function($){
1472 $('#csfooter').hide();
1473 if ( $('#widgets-right').length > 0 ) {
1474 csSidebars.init();
1475 }
1476 $('.defaultsContainer').hide();
1477
1478 $( '#widgets-right .widgets-sortables' ).on( "sort", function(event, ui) {
1479 var topx = $('#widgets-right').top;
1480 ui.position.top = - $('#widgets-right').css('top');
1481 });
1482 });
1483 /**
1484 * add new sidebar placeholder
1485 */
1486 jQuery(document).ready( function($) {
1487 window.setTimeout( function() {
1488 window.csSidebars.showGetStartedBox();
1489 }, 1000);
1490 });
1491 })(jQuery);
1492
1493 /**
1494 * jQuery.fn.sortElements
1495 * --------------
1496 * @param Function comparator:
1497 * Exactly the same behaviour as [1,2,3].sort(comparator)
1498 *
1499 * @param Function getSortable
1500 * A function that should return the element that is
1501 * to be sorted. The comparator will run on the
1502 * current collection, but you may want the actual
1503 * resulting sort to occur on a parent or another
1504 * associated element.
1505 *
1506 * E.g. $('td').sortElements(comparator, function(){
1507 * return this.parentNode;
1508 * })
1509 *
1510 * The <td>'s parent (<tr>) will be sorted instead
1511 * of the <td> itself.
1512 *
1513 * @see http://james.padolsey.com/javascript/sorting-elements-with-jquery/
1514 */
1515 jQuery.fn.sortElements = (function(){
1516
1517 var sort = [].sort;
1518
1519 return function(comparator, getSortable) {
1520
1521 getSortable = getSortable || function(){return this;};
1522
1523 var placements = this.map(function(){
1524
1525 var sortElement = getSortable.call(this),
1526 parentNode = sortElement.parentNode,
1527
1528 // Since the element itself will change position, we have
1529 // to have some way of storing its original position in
1530 // the DOM. The easiest way is to have a 'flag' node:
1531 nextSibling = parentNode.insertBefore(
1532 document.createTextNode(''),
1533 sortElement.nextSibling
1534 );
1535
1536 return function() {
1537
1538 if (parentNode === this) {
1539 throw new Error(
1540 "You can't sort elements if any one is a descendant of another."
1541 );
1542 }
1543
1544 // Insert before flag:
1545 parentNode.insertBefore(this, nextSibling);
1546 // Remove flag:
1547 parentNode.removeChild(nextSibling);
1548
1549 };
1550
1551 });
1552
1553 return sort.call(this, comparator).each(function(i){
1554 placements[i].call(getSortable.call(this));
1555 });
1556
1557 };
1558
1559 })();
1560
1561