/** * Admin JS file * * This file contains global admin functions * * @package Layers * @since Layers 1.0.0 * * Contents * 2 - Layers Custom Easing * 3 - Media Uploaders * 3.a - Image Remove Button * 3.b - Image Upload Button * 3.c - General File Remove Button * 3.d - General File Upload Button * 4 - Background Selectors * 5 - Color Selectors * 6 - Sortable Columns * 7 - Tabs * 8 - Design Controller toggles * 9 - Widget Focussing * 10 - Trigger input changes * 11 - Add Last Class to Design Bar Elements * 12 - Show/Hide linked elements * 13 - Init RTE Editors * 14 - Custom Widget Initialization Events * 15 - Intercom checkbox * 16 - Widget Peek/hide to preview changes * 17 - Customizer Control - Range Slider * 18 - Reset to Default * 19 - Linking from one section/panel to another. * 20 - Init Tip-Tip * 21 - Linking-UX * 22 - Force Customizer refresh if Widget exists that's not partial-widget-refresh * * Author: Obox Themes * Author URI: http://www.oboxthemes.com/ * License: GNU General Public License v2 or later * License URI: http://www.gnu.org/licenses/gpl-2.0.html */ jQuery(function($) { /** * 2 - Layers Custom Easing * * Extend jQuery easing with custom Layers easing function for UI animations - eg slideUp, slideDown */ // easeInOutQuad /*jQuery.extend( jQuery.easing, { layersEaseInOut: function (x, t, b, c, d) { if ((t/=d/2) < 1) return c/2*t*t + b; return -c/2 * ((--t)*(t-2) - 1) + b; }});*/ // easeInOutQuint jQuery.extend( jQuery.easing, { layersEaseInOut: function (x, t, b, c, d) { if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; return c/2*((t-=2)*t*t*t*t + 2) + b; }}); /** * 3 - Media Uploaders */ // 3.a - Image Remove Button var file_frame; $(document).on( 'click' , '.layers-image-container .layers-image-remove' , function(e){ e.preventDefault(); // "Hi Mom" $that = $(this); // Get the container $container = $that.closest( '.layers-image-container' ); $that.siblings('img').remove(); $container.removeClass( 'layers-has-image' ); $container.find('input').val('').layers_trigger_change(); $that.fadeOut(); return false; }); // 3.b - Image Upload Button $(document).on( 'click' , '.layers-image-upload-button' , function(e){ e.preventDefault(); // "Hi Mom" $that = $(this); // Get the container $container = $that.closest( '.layers-image-container' ); // If the media frame already exists, reopen it. if ( file_frame ) { file_frame.close(); } // Create the media frame. file_frame = wp.media.frames.file_frame = wp.media({ title: $that.data( 'title' ), button: { text: $that.data( 'button_text' ), }, multiple: false // Set to true to allow multiple files to be selected }); // When an image is selected, run a callback. file_frame.on( 'select', function() { // We set multiple to false so only get one image from the uploader attachment = file_frame.state().get('selection').first().toJSON(); // Remove any old image $container.find('img video').remove(); // Fade in Remove button $container.find('.layers-image-remove').fadeIn(); // Set attachment to the large/medium size if they're defined if( 'image' == attachment.type ) { if ( undefined !== attachment.sizes.medium ) { $attachment = attachment.sizes.medium; } else if( undefined !== attachment.sizes.large ) { $attachment = attachment.sizes.large; } else { $attachment = attachment; } // Create new image object var $image = $('').attr({ class : 'image-reveal', src : $attachment.url, height : $attachment.height, width : $attachment.width }); } else{ // Create new image object var $image = $('').attr({ class : 'image-reveal', src : attachment.icon }); } $container.children('.layers-image-display').eq(0).append( $image ); // Add 'Has Image' Class $container.addClass( 'layers-has-image' ); // Trigger change event $container.find('input').val( attachment.id ).layers_trigger_change(); return; }); // Finally, open the modal file_frame.open(); }); // 3.c - General File Remove Button $(document).on( 'click' , '.layers-file-remove' , function(e){ e.preventDefault(); // "Hi Mom" $that = $(this); $that.siblings('span').text(''); $that.siblings('input').val('').layers_trigger_change(); $that.closest( '.layers-file-container' ).removeClass( 'layers-has-file' ); $that.fadeOut(); return false; }); // 3.d - General File Upload Button $(document).on( 'click' , '.layers-regular-uploader' , function(e){ e.preventDefault(); // "Hi Mom" $that = $(this); // If the media frame already exists, reopen it. if ( file_frame ) { file_frame.close(); } // Create the media frame. file_frame = wp.media.frames.file_frame = wp.media({ title: $that.data( 'title' ), button: { text: $that.data( 'button_text' ), }, multiple: false // Set to true to allow multiple files to be selected }); // When an image is selected, run a callback. file_frame.on( 'select', function() { // We set multiple to false so only get one image from the uploader attachment = file_frame.state().get('selection').first().toJSON(); $that.closest( '.layers-file-container' ).addClass( 'layers-has-file' ); // Fade in Remove button $that.siblings('small').fadeIn(); // Add file name to the $that.siblings('span').text( attachment.filename ); // Trigger change event $that.siblings('input').val( attachment.id ).layers_trigger_change(); return; }); // Finally, open the modal file_frame.open(); }); /** * 4 -Background Selectors */ $(document).on( 'click', '.layers-background-selector li' , function(e){ e.preventDefault(); // "Hi Mom" $that = $(this); $type = $that.data('type'); $id = $that.data('id'); $index = $that.index(); // Our main containing div, we could use .parent() but what if we change the depth of this li? $elements = $( $id + '-controller' ).find( '.layers-controller-elements' ); // Change the input value $( $id + '-type' ).val( $type ).layers_trigger_change(); // Switch the selectors $that.addClass( 'active' ); $that.siblings().removeClass( 'active' ); // Switch the view $elements.find( '.layers-content' ).eq( $index ).addClass('section-active'); $elements.find( '.layers-content' ).eq( $index ).siblings().removeClass('section-active'); }); /** * 5 - Color Selectors */ if ( $('body.wp-customizer').length ) { /** * Customizer */ $( document ).on( 'layers-interface-init', function( e, element ){ layers_set_color_selector( $(element) ); }); } else{ /** * Not Customizer */ layers_set_color_selector( $('body') ); } function layers_set_color_selector( $element ){ $element.find('.layers-color-selector').each( function( index, element ) { var $color_input = $(this); // Initialize the individual color-picker $color_input.wpColorPicker({ change: function(event, ui){ if( 'undefined' !== typeof event ){ // Update the color input $( event.target ).val( ui.color.toString() ); // Debounce the color changes layers_debounce_color_input( $( event.target ) ); } }, clear: function(event) { if( 'undefined' !== typeof event && 'click' === event.type ){ // Ping a change to the main input - the value will be ''. $( $color_input ).layers_trigger_change(); } }, palettes: [ '#000000', '#FFFFFF', '#E2594E', '#F39C12', '#FFCD03', '#A2C661', '#009EEC', '#934F8C' ], }); }); } // Debounce function for color changing. var layers_debounce_color_input = _.debounce( function( element ){ $( element ).layers_trigger_change(); }, 400, false ); /** * 6 - Sortable Columns */ $( document ).on( 'layers-interface-init', function( e, element ){ layers_init_sortable_columns( $(element) ); }); function layers_init_sortable_columns( $element_s ){ // Bail if no sortable if( $.sortable == undefined ) return; $($element_s).find( '.layers-sortable').sortable({ placeholder: "layers-sortable-drop" }); } /** * 7 - Tabs */ $( document ).on( 'click' , '.l_admin-tabs li, .l_admin-tabs li a' , function(e){ e.preventDefault(); // "Hi Mom" $that = $(this); // Get the Tab Index $i = $that.index(); // Make this tab active $that.addClass( 'active' ).siblings().removeClass( 'active' ); // Get the nearest tab containers $tab_nav = $that.closest( '.l_admin-nav-tabs' ); $tab_container = $tab_nav.siblings('.l_admin-tab-content'); // Show/Hide tabs $tab_container.find( 'section.l_admin-tab-content' ).eq( $i ).addClass('l_admin-show').removeClass('l_admin-hide').slideDown().siblings( 'section.l_admin-tab-content' ).addClass('l_admin-hide').removeClass('l_admin-show').slideUp(); }); /** * 8 - Design Controller toggles */ // WIDGET - Design Bar Flyout Menus e.g. Layout, List Style, Advanced. var $menu_is_open = false; // Close any previously opened menu's. $( document ).on( 'click', function(e) { // Only ever do this if there is a previously opened item // is less taxing than searching the entire Customizer DOM // for open items every click in the customizer. if ( $menu_is_open ) { var $opened = $('.widget .layers-visuals-item.layers-active' ).not( $(e.target).parents('li.layers-visuals-item') ); if ( $opened.length ) { $opened.removeClass( 'layers-active' ); $menu_is_open = false; } } }); // Open the clicked menu. $( document ).on( 'click', '.widget ul.layers-visuals-wrapper > li.layers-visuals-item > a.layers-icon-wrapper', function(e){ e.preventDefault(); // "Hi Mom" $that = $(this); // Close Siblings $that.parent( 'li.layers-visuals-item' ).siblings().not( $that.parent() ).removeClass( 'layers-active' ); // Toggle active state $that.trigger( 'layers-design-bar-menu', $that ); // Deprecated event. $that.parent( 'li.layers-visuals-item' ).toggleClass( 'layers-active' ); $menu_is_open = ( $that.parent( 'li.layers-visuals-item' ).hasClass('layers-active') ); }); // WIDGET - Select Icon Group e.g. Text Align (left, right, center, justify). $( document ).on( 'mousedown' , '.layers-select-icons label.layers-icon-wrapper' , function(e){ // Cache elements. var $label = $(this); var $input = $('#' + $label.attr('for')); // Get input value var $value = $input.val(); // De-activate siblings $label.siblings( '.layers-icon-wrapper' ).removeClass( 'layers-active' ); // When the the whole flyout-menu is one Select Icon Group e.g. a widget's Layout (Boxed, Full-Width) // then set the parents Icon to what is being selected now - helpful to the user, it can be seen at a glance. $is_form_item = $label.closest( '.layers-form-item' ).siblings( '.layers-form-item' ).length; if ( 0 == $is_form_item ) { $label .closest( '.layers-pop-menu-wrapper' ) .siblings( '.layers-icon-wrapper' ) .find( 'span[class^="icon-"]' ) .attr( 'class', 'icon-' + $value ); } // Toggle active state $label.trigger( 'layers-design-bar-menu', $label ); if ( 'checkbox' == $input.attr('type') ) { // Input is a 'checkbox' when there's only one single button - so make it toggle on/off. if ( $label.hasClass( 'layers-active' ) ) $label.removeClass( 'layers-active' ); else $label.addClass( 'layers-active' ); } else { // Input is a 'radio' when there's multiple buttons - so make them behave like radio. $label.addClass( 'layers-active' ); } }); // CUSTOMIZE CONTROLS - Select Icon Group e.g. Header Width (Boxed, Full-Width) $( document ).on( 'click' , '[id^="layers-customize"] .layers-visuals-item' , function(e){ // "Hi Mom" $that = $(this); // Close siblings $that.siblings( '.layers-visuals-item' ).removeClass( 'layers-active' ); // Toggle active state $that.trigger( 'layers-design-bar-menu', $that ); $that.addClass( 'layers-active' ); }); $( document ).on( 'layers-design-bar-menu', '.layers-visuals-item', function( e, menu_item ){ $img = $(this).find( 'img[data-src]' ); $img.each(function(){ $(this).attr( 'src', $(this).data( 'src' ) ); }); }); // Set the correct element as checked on init - for elements that are added after page load. // eg javascript added elements like the repeater items. $( document ).on( 'layers-interface-init', function( e, element ){ $(element).find( '.layers-icon-group').each( function( j, element ) { $select_group = $(element); $select_item = $select_group.find('input[checked]'); $select_item_label = $select_item.parents('label'); $select_group.find('.layers-active').removeClass('layers-active'); $select_item_label.trigger('mousedown'); $select_item_label.addClass('layers-active'); }); }); /** * 9 - Widget Focussing */ $( document ).on( 'layers-widget-scroll' , '.widget' , function(e){ // "Hi Mom" $that = $(this); if( !$that.hasClass( 'expanded' ) ){ // Get the id of this widget $widget_id = $that.find( '.widget-id' ).val(); // Focus on the active widget layers_widget_focus( $widget_id ) } }); function layers_widget_focus( $widget_id ){ // Scroll to this widget $iframe = $( '#customize-preview iframe' ).contents(); $widget = $iframe.find( '#' + $widget_id ); // Check if the widget can be found - can't be found during widget-add if ( 0 < $widget.length ){ $iframe.find('html, body').animate( { scrollTop: $widget.offset().top }, { duration: 900, easing: 'layersEaseInOut' } ); } } /** * 10 - Trigger input changes */ $.fn.layers_trigger_change = function() { // Trigger 'change' and 'blur' to reset the customizer $changed = $(this).trigger("change").trigger("blur"); }; /** * 11 - Add Last Class to Elements */ $( document ).on( 'layers-interface-init', function( e, element ){ layers_init_add_last_class( $(element) ); }); function layers_init_add_last_class( $element_s ){ $element_s.find( '.layers-design-bar').each( function( j, element ) { var $design_bar = $(element); var $design_bar_li = $design_bar.children('ul').children('li'); if ( $design_bar.hasClass('layers-align-right') || $design_bar_li.length > 4 ) { $design_bar_li.eq(-1).addClass( 'layers-last' ); $design_bar_li.eq(-2).addClass( 'layers-last' ); } }); } /** * 12 - Show/Hide linked elements */ if ( $('body.wp-customizer').length ) { /** * Customizer */ $( document ).on( 'layers-interface-init', function( e, element ){ layers_init_show_if( $(element) ); }); } else { /** * Not Customizer */ layers_init_show_if( $('body') ); } function layers_init_show_if( $element_s ){ $element_s.find( '[data-show-if-selector]').each( function( j, element ) { var $this_element = $(element); var $compare_element = $( $this_element.attr( 'data-show-if-selector' ) ); // Apply show-if to the element once on startup. layers_apply_show_if( $this_element, $compare_element ); // Apply show-if to the element when this element is changed. /*$( document ).on( 'change', $compare_element, function(e){ layers_apply_show_if( $this_element, $compare_element ); });*/ $( $compare_element ).on( 'change', function(e){ layers_apply_show_if( $this_element, $compare_element ); }); }); } function layers_apply_show_if( $this_element, $compare_element ){ var $this_element_value = $this_element.data( 'show-if-value' ).toString().split(','); var $operator = $this_element.data( 'show-if-operator' ); // Get value based on the type of input being used. if ( $compare_element.attr('type') == 'checkbox' ) { // Checkbox $compare_element_value = ( $compare_element.is(':checked') ) ? 'true' : 'false' ; } else if ( 0 < $compare_element.closest( '.layers-select-icons' ).length && 0 < $compare_element.closest( 'fieldset.layers-post-meta' ).length ) { // Select icons $compare_element_value = $compare_element.parent().find('input:checked').val(); } else if ( $compare_element.hasClass( 'customize-control customize-control-layers-select-icons' ) ) { // Select icons $compare_element_value = $compare_element.find('input:checked').val(); } else { // All other inputs $compare_element_value = $compare_element.val(); } // Bail if there's no source element to reference. if ( 'undefined' === typeof( $compare_element_value ) || null === $compare_element_value ) { layers_show_if_display( 'hide', $this_element ); return false; } var $action = 'hide'; // Compare based on the chosen operator (default: ==) switch( $operator ) { case '!=': $.each( $this_element_value, function( index, val ) { if ( val.trim() != $compare_element_value.trim() ) $action = 'show'; }); break; case '!==': $.each( $this_element_value, function( index, val ) { if ( val.trim() !== $compare_element_value.trim() ) $action = 'show'; }); break; case '>': $.each( $this_element_value, function( index, val ) { if ( val.trim() > $compare_element_value.trim() ) $action = 'show'; }); break; case '<': $.each( $this_element_value, function( index, val ) { if ( val.trim() < $compare_element_value.trim() ) $action = 'show'; }); break; case '>=': $.each( $this_element_value, function( index, val ) { if ( val.trim() >= $compare_element_value.trim() ) $action = 'show'; }); break; case '<=': $.each( $this_element_value, function( index, val ) { if ( val.trim() <= $compare_element_value.trim() ) $action = 'show'; }); break; case '==': default: $.each( $this_element_value, function( index, val ) { if ( val.trim() == $compare_element_value.trim() ) $action = 'show'; }); break; } // Apply the result of the above compare. layers_show_if_display( $action, $this_element ); } function layers_show_if_display( $action, $element ) { // Calculate the reveal animation type. var animation_type = 'none'; // Get the right target element depending on what kind of component this is (is Customize Control or Design-Bar item) if ( $element.hasClass('l_option-customize-control') ){ // Target element is - Customize Control (entire control) $element = $element.parent('.customize-control'); animation_type = 'slideDown'; } else if ( $element.hasClass('layers-design-bar-form-item') ) { // Target element is - Design Bar (form-item) animation_type = 'slideDown'; } if ( 'hide' == $action ) { // Hide if( animation_type == 'slideDown' ){ $element.slideUp( { duration: 550, easing: 'layersEaseInOut', complete: function(){ $element.addClass( 'l_admin-hide' ); } } ); } else{ $element.addClass( 'l_admin-hide' ); } } else { // Show if( animation_type == 'slideDown' ){ $element.removeClass( 'l_admin-hide' ); $element.slideDown( { duration: 550, easing: 'layersEaseInOut' } ); } else{ $element.removeClass( 'l_admin-hide' ); } } } /** * 13 - Init RTE Editors */ $( document ).on( 'layers-interface-init', function( e, element ){ layers_init_editors( $(element) ); }); // Debugging // $editor_has_run_once = false; function layers_init_editors( $element_s ){ $element_s.find('.layers-rte').each( function( j, element ) { var $editor = $(element); // Bail if I'm already an RTE. if ( $editor.siblings( '.fr-box' ).length > 0 ) return true; // Debugging - init a simple froala once then bail. // if ( $editor_has_run_once ) return; // $editor.froalaEditor(); // $editor_has_run_once = true; // return false; // Default editor config. var $editor_config = { allowScript: true, allowStyle: true, convertMailAddresses: true, codeMirror: false, toolbarInline: false, initOnClick: false, imageEditButtons: [ 'removeImage' ], key: 'YWd1WDPTa1ZNRGe1OC1c1==', mediaManager: false, imagePaste: false, enter: $.FroalaEditor.ENTER_P, pastePlain: false, typingTimer: 1500, zIndex: 99, }; if ( $editor.data( 'allowed-buttons' ) ) { var allowed_buttons = $editor.data( 'allowed-buttons' ).split(','); $editor_config.toolbarButtons = allowed_buttons; $editor_config.toolbarButtonsMD = allowed_buttons; $editor_config.toolbarButtonsSM = allowed_buttons; $editor_config.toolbarButtonsXS = allowed_buttons; } if( $editor.data( 'allowed-tags' ) ) { if( '' !== $editor.data ){ $editor_config.htmlAllowedTags = $editor.data( 'allowed-tags' ).split(','); } } // Init editor. $editor.froalaEditor( $editor_config ); // Hide the toolbar at the start. $editor.froalaEditor('toolbar.hide'); // Editor events $editor .on('froalaEditor.contentChanged froalaEditor.input', function (e, editor) { $editor.layers_trigger_change(); }) .on('froalaEditor.focus', function (e, editor) { $editor.froalaEditor('toolbar.show'); }); }); } // Fix for 'clear formatting' button not working - invokes sending change to customizer prev $(document).on( 'click', '.fr-bttn[data-cmd="removeFormat"]', function(){ var $editor = $(this).closest('.layers-form-item').find('.layers-rte'); _.defer( function(arguments) { $editor.froalaEditor('blur'); $editor.froalaEditor('focus'); }); }); // Fix issue where Firefox performance slows down chronically while RTE's are still focussed by cursor. $(document).on( 'blur', '.fr-box .fr-element.fr-view', function(e){ // Cache sister textarea. $textarea = $(e.target).parents('.fr-box').siblings('textarea'); // Use near-instant timeout to make sure new element has time to get focus. setTimeout( function(){ // Cache newly focussed element. $newly_focussed_element = jQuery(':focus'); // Here is the fix: // If the next clicked element is a normal element (not a form field) // then Froala does not register the defocus of it's resource heavy // editor. So if the newly_focussed_element is not a form field then // we help by invisibly focussing Froala's hidden sister textarea which // releases the resource heavy Froala editor and returns performance // to it's normal state. if ( ! $newly_focussed_element.is('input') && ! $newly_focussed_element.is('textarea') && ! $newly_focussed_element.is('select') && ! $newly_focussed_element.parents().hasClass('fr-view') && ! $newly_focussed_element.hasClass('fr-view') ) { // Focus hidden sister textarea (show, FOCUS, then hide again - is needed for focus to trigger correctly). $textarea.show().focus().hide(); } }, 1 ); }); /** * 14 - Custom Widget Initialization Events */ /** * Trigger 'layers-interface-init' when: * 1. widget is focussed first time * 2. accordion element is added inside widget * to allow for just-in-time init instead of massive bulk init. */ $( document ).on( 'widget-added', function( e, widget ){ var $widget = $(widget); layers_expand_widget( $widget, e ); }); $( document ).on( 'expand collapse collapsed', '.customize-control-widget_form', function(e){ var $widget_li = $(this); var $widget = $widget_li.find( '.widget' ); if( 'expand' == e.type ){ // duplicate call to 'layers_expand_widget' in-case 'click' is not triggered // eg 'shift-click' on widget in customizer-preview. layers_expand_widget( $widget, e ); // Scroll only on expand. setTimeout(function() { $widget.trigger( 'layers-widget-scroll' ); }, 200 ); // Delay the removal of 'layers-loading' so it always displays for a definite length of time, // so the user is able to read it. setTimeout(function(){ $widget_li.removeClass( 'layers-loading' ); }, 1100 ); } else if( 'collapse' == e.type ){ $widget_li.removeClass('layers-focussed'); // Used for animation of the widget closing $widget_li.addClass('layers-collapsing'); } else if( 'collapsed' == e.type ){ $widget_li.removeClass('layers-collapsing'); } }); function layers_expand_widget( $widget, e ){ var $widget_li = $($widget).closest('.customize-control-widget_form'); // Instant user feedback $widget_li.addClass('layers-focussed'); // Instantly remove other classes on other widgets. $('.customize-control-widget_form.layers-focussed, .customize-control-widget_form.layers-loading').not( $widget_li ).removeClass('layers-focussed layers-loading'); // Handle the first time Init of a widget. if ( ! $widget_li.hasClass( 'layers-loading' ) && ! $widget_li.hasClass( 'layers-initialized' ) ){ $widget_li.addClass( 'layers-loading' ); $widget_li.addClass( 'layers-initialized' ); if ( 'widget-added' === e.type || 'click' === e.type ) { // If event is 'widget-added' it's our early invoked event so we can do things before all the WP things setTimeout(function(){ $( document ).trigger( 'layers-interface-init', $widget ); }, 50 ); } else { // If event is 'expand' it's a WP invoked event that we use as backup if the 'click' was not used. // eg 'shift-click' on widget in customizer-preview $( document ).trigger( 'layers-interface-init', $widget ); } } } /** * Trigger 'layers-interface-init' when: * 1. Accordion Panel/Section is expanded (opened) */ $( document ).on( 'expanded', '.control-section:not(.control-section-sidebar):not(#accordion-panel-widgets) ', function(e){ // Bail if we've a;ready initialized this. if ( $(this).hasClass('layers-initialized') ) return; // Add the 'initialized' class and trigger the event. $(this).addClass('layers-initialized'); $(document).trigger('layers-interface-init', $(this) ); }); /** * Trigger 'layers-widget-interface-init' when: * 1. Widget Accordion Panel is expanded (opened) */ $( document ).on( 'expanded', '.control-section#accordion-panel-widgets li.control-section-sidebar', function(e){ // Bail if we've a;ready initialized this. if ( $(this).hasClass('layers-initialized') ) return; // Add the 'initialized' class and trigger the event. $(this).addClass('layers-initialized'); $(document).trigger('layers-widget-interface-init', $(this) ); }); /** * 15 - Intercom checkbox */ $(document).on( 'change', '#layers-enable-intercom', function(e){ if( 'undefined' !== typeof Intercom ){ if( !$(this).prop('checked') ){ Intercom('shutdown'); } else if( 'undefined' !== typeof window.intercomSettings ){ Intercom('boot', window.intercomSettings ); } } }); /** * Duplicate Widgets. (disabled) */ /* $( document ).on( 'layers-interface-init', function( e, element ){ // Add the duplicate widget button to all the Layers Widget actions. $(element).find('.widget-control-actions .alignleft .widget-control-remove').after('Duplicate'); }); $( document ).on( 'click', '.layers-widget-duplicate-button', function( e, element ){ $button = $(this); $this_widget_form = $button.parents('.widget-inside'); $widget_panel_holder = $button.parents('.control-subsection.open'); $add_widgets_button = $widget_panel_holder.find('.add-new-widget'); // Get the widget type. $widget_id = $this_widget_form.find('[name="id_base"]').val(); $add_widgets_button.click(); $('#available-widgets-list').find('[id^="widget-tpl-'+ $widget_id +'"]').click(); }); */ /** * 16 - Widget Peek/hide to preview changes */ $( document ).on( 'layers-interface-init', function( e, element ){ // Add the peek buttons to all the Layers Widget actions. $(element).find('.widget-control-actions .alignleft').prepend(''); }); // Add the hover hiding of the Widget interface. $(document).on( 'mouseenter', '.layers-widget-peek-button', function(){ $(this).closest('.widget-inside').addClass('layers-peek-widget'); } ); $(document).on( 'mouseleave', '.layers-widget-peek-button', function(){ $(this).closest('.widget-inside').removeClass('layers-peek-widget'); } ); /** * 17 - Customizer Control - Range Slider */ $( document ).on( 'input change', '.layers-column input[type="range"]', function( e ){ // Push changes to the Number input. var $range_field = $(this); var $number_field = $(this).parent().parent().find('input[type="number"]'); if ( $range_field.attr( 'placeholder' ) && $range_field.attr( 'placeholder' ) == $range_field.val() ) { // If the range-slider is moved and there's a placeholder set // and the slider stops on the placeholder value then empty // the number field so nothing is applied. $number_field.val(''); $number_field.addClass( 'layers-range-disabled' ); } else { // Set the number value to equal this range. $number_field.val( $range_field.val() ); $number_field.removeClass( 'layers-range-disabled' ); } layers_debounce_range_input( $number_field ); }); $( document ).on( 'input change', '.layers-column input[type="number"]', function( e ){ // Push changes to the Range input. var $number_field = $(this); var $range_field = $(this).parent().parent().find('input[type="range"]'); if ( '' == $number_field.val() && $range_field.attr( 'placeholder' ) ) { // If number field is emptied and there's a placeholder set then // set the range slider so it reflects the placeholder too. $range_field.val( $range_field.attr( 'placeholder' ) ); } else { // Set the range to equal this number value. $range_field.val( $number_field.val() ); } }); var layers_debounce_range_input = _.debounce( function( element ){ $( element ).layers_trigger_change(); }, 550, false ); /** * 18 - Reset to Default */ $( document ).on( 'click', '.customize-control-default', function( e ){ var $refresh_button = $(this); var $control_holder = $refresh_button.closest('.customize-control'); var $default_value = $refresh_button.attr('data-default'); var $field = $control_holder.find('input, select, textarea'); if ( 'checkbox' == $field.eq(0).attr('type') ) { // Checkbox if ( ! $default_value || '' == $default_value ) $field.attr( 'checked', false ); else $field.attr( 'checked', true ); $field.eq(0).change(); } else { //else if ( $field.eq(0).is('input') ) { // Input's, Textarea $field.val( $default_value ); $field.eq(0).change(); } }); /** * 19 - Linking from one section/panel to another. * * Use class `customizer-link` and href `#target-panel-or-section-id` */ $( document ).on( 'click', '.customizer-link', function( e ){ $link = $(this); $related_accordion = $( $link.attr('href') ); // If there is a related panel ot section then open it. if ( $related_accordion.length ) { $related_accordion.children( 'h3.accordion-section-title' ).click(); } return false; }); /** * 20 - Init Tip-Tip */ if ( $('body.wp-customizer').length ) { /** * Customizer */ $( document ).on( 'layers-interface-init', function( e, element ){ init_tip_tip( $(element) ); }); } else{ /** * Not Customizer */ init_tip_tip( $( document ) ); } function init_tip_tip( $element_s ){ $element_s.find( '[data-tip]').each( function( j, element ) { // Tooltips $(element).layersTip({ 'attribute' : 'data-tip', 'fadeIn' : 300, 'fadeOut' : 300, 'delay' : 200, 'defaultPosition' : 'top', 'edgeOffset' : 3, 'maxWidth' : '300px' }); }); } /** * 21 - Linking-UX */ $( document ).on( 'layers-interface-init', function( e, element ){ layers_init_form_collections( $(element) ); }); function layers_init_form_collections( $element_s ){ /** * Get the link-type inputs and convert them to layersSlct2. */ $element_s.find( '.layers-widget-dynamic-linking-select').each( function( j, element ) { var initial_selection = { id : $(element).val(), text : $(element).attr( 'data-display-text' ), }; var placeholder = $(element).attr( 'placeholder' ); var related_type_select = $(element).parents('.layers-form-collection').find('[id$="-link_type"]'); $(element).layersSlct2({ ajax: { url: ajaxurl, dataType: 'json', quietMillis: 250, data: function(term, page) { return { action : 'layers_widget_linking_searches', link_type : related_type_select.val(), term : term, page : page, nonce : layers_admin_params.nonce_layers_widget_linking, }; }, results: function(data, params) { return { results: data.results, more: data.more, }; }, cache: true }, escapeMarkup: function(markup) { // let our custom formatter work return markup; }, initSelection: function(element, callback) { callback( initial_selection ); // Convert the value to a Name by doing reverse-lookup of the id. - Replaced this method with the ajax-free method above. /* var id = $(element).val(); if (id !== "") { jQuery.ajax({ type : 'post', dataType : 'json', url : ajaxurl, data : { action : 'layers_widget_linking_initial_selections', post_id : id, link_type : related_type_select.val(), nonce : layers_admin_params.nonce_layers_widget_linking, }, success: function( data ) { callback({ id: data.id, text: data.text }); } }); } */ }, formatSelection: function(data) { return data.text; }, containerCssClass: 'tpx-layersSlct2-container', dropdownCssClass: 'tpx-layersSlct2-drop', minimumInputLength: 1, width: '100%', }); $(element).on('change', function(e) { $(element).attr( 'data-display-text', e.added.text ).trigger('layers_init_linking'); }) }); /** * Dynamic updating of the Linking-UX heading. */ $element_s.find('.layers-form-collection').each( function( j, element ) { // Cache elements. var $collection_holder = $(element); var $collection_content = $collection_holder.find('.layers-form-collection-content'); var $collection_heading = $collection_holder.find('.layers-form-collection-header'); // Hide content part - like an accordion. $collection_content.hide(); // Update the heading on change of any input/select. $(element).find('select, input').on( 'change keyup layers_init_linking', function(){ // Get the link text. var link_text = $(element).find('[id$="-link_text"]').val(); // Get the link type. var link_type = $(element).find('[id$="-link_type"]').val(); // Get the link value. var link_input = $(element).find('[name$="link_type_' + link_type + ']"]'); link_value = ''; if ( 'custom' == link_type ) link_value = link_input.val(); else if ( 'post' == link_type ) link_value = link_input.attr('data-display-text'); // Compile the display content. var display_content = ''; if ( '' != link_text ) display_content += link_text + ' '; if ( '' != link_value ) display_content += '' + link_value + ' '; // If nothing is set then throw out   to hold the space. if ( '' == display_content ) display_content = ' '; $collection_heading.html( display_content ); }); // Ping an initial update at the start. $(element).find('select, input').eq(0).trigger('layers_init_linking'); }); } // Accordion-type panel of the Linking-UX $(document).on('click', '.layers-form-collection-header', function(){ /** * Show the current panel. */ $collection_holder = $(this).closest('.layers-form-collection'); $collection_content = $collection_holder.find('.layers-form-collection-content'); if ( $collection_holder.hasClass('closed') ) { $collection_holder.removeClass('closed'); $collection_content.slideDown({ easing: 'layersEaseInOut', duration: 250 }); } else{ $collection_holder.addClass('closed'); $collection_content.slideUp({ easing: 'layersEaseInOut', duration: 250 }); } /** * Hide the other panel (that are still showing) */ $other_collection_holders = $('.layers-form-collection:not(".closed")').not( $collection_holder ); $other_collection_contents = $other_collection_holders.find('.layers-form-collection-content'); $other_collection_holders.addClass('closed'); $other_collection_contents.slideUp({ easing: 'layersEaseInOut', duration: 250 }); }); /** * 22 - Force Customizer refresh if Widget exists that's not partial-widget-refresh. * * This is required because we don't use the `$args['before_widget'], $args['after_widget']` as our surrounding * tags on our widgets, as our framework needs full control of the attributes like `class`. We have solved this * in our internal widgets, but we cannot be sure that there aren't any 3rd party Layers based Widgets that * have not yet applied our fix. So in the case that there are non `customize_selective_refresh` enabled Widgets * then we will hard-refresh the customizer if the widgets are Reordered, Added, Deleted. Only on pages that have * Widgets that are not `customize_selective_refresh` enabled. */ $(document).on( 'layers-customizer-init', function(){ // Reorder Widgets. $('.accordion-section-content').on( 'sortupdate', function( event, ui ){ var $widget_li = ui.item; possibly_refresh_customizer( $widget_li ); }); // Add Widget. $( document ).on( 'widget-added', function( e, widget ){ var $widget = $(widget); possibly_refresh_customizer( $widget ); }); // Delete Widget. $('.accordion-section-content').on( 'click', '.widget-control-remove', function(e){ var $widget = $(this).closest('.customize-control-widget_form'); possibly_refresh_customizer( $widget ); }); }); function possibly_refresh_customizer( $widget ) { // Bail if old version of WP and SelectvieRfresh is not available yet. if ( undefined == wp.customize.Widgets.data.selectiveRefreshableWidgets ) return false; // Keep note if there are any non PWR widgets. var $all_partial_refresh_widget_enabled = true; if ( ! wp.customize.Widgets.data.selectiveRefreshableWidgets[ $widget.find('input[name=id_base]').val() ] ) { // The current widget clicked is not PWR so note this. $all_partial_refresh_widget_enabled = false; } else { // Loop through the widget on the same page as the one that was just interacted with. $widget.closest('.ui-sortable').find('input[name=id_base]').each(function(index, el){ // Get the widget type. var widget_type = $(el).val(); // Check if the current widget is PWR enabled. if ( ! wp.customize.Widgets.data.selectiveRefreshableWidgets[widget_type] ) { // If not PWR then note this. $all_partial_refresh_widget_enabled = false; } }); } // If there was a non PWR then refresh the Customizer Preview. if ( ! $all_partial_refresh_widget_enabled ) { // setTimeout delay so that the accordion is finished updating. setTimeout( function(){ wp.customize.previewer.refresh(); }, 600 ); } } });