first commit

This commit is contained in:
CHIEFSOFT\ameye
2023-12-28 16:20:07 -05:00
commit b114fdf4fa
5377 changed files with 1850677 additions and 0 deletions
@@ -0,0 +1,123 @@
//extends api.CZRModule
var BodyBGConstructor = BodyBGConstructor || {};
( function ( api, $, _ ) {
$.extend( BodyBGConstructor, {
initialize: function( id, options ) {
var module = this;
//extend the module with new template Selectors
// $.extend( module, {
// itemInputList : 'czr-module-bodybg-item-content'
// } );
//EXTEND THE DEFAULT CONSTRUCTORS FOR INPUT
module.inputConstructor = api.CZRInput.extend( module.CZRBodyBgInputMths || {} );
//EXTEND THE DEFAULT CONSTRUCTORS FOR MONOMODEL
module.itemConstructor = api.CZRItem.extend( module.CZBodyBgItemMths || {} );
//run the parent initialize
api.CZRModule.prototype.initialize.call( module, id, options );
},//initialize
CZRBodyBgInputMths : {
//////////////////////////////////////////////////
///SETUP SELECTS
//////////////////////////////////////////////////
//setup select on view_rendered|item_content_event_map
setupSelect : function() {
var input = this,
_id_param_map = {
'background-repeat' : 'bg_repeat_options',
'background-attachment' : 'bg_attachment_options',
'background-position' : 'bg_position_options'
},
item = input.input_parent,
serverParams = bodyBGModuleLocalized,
options = {},
module = input.module;
if ( ! _.has( _id_param_map, input.id ) )
return;
if ( _.isUndefined( serverParams ) || _.isUndefined( serverParams[ _id_param_map[input.id] ] ) )
return;
options = serverParams[ _id_param_map[input.id] ];
if ( _.isEmpty(options) )
return;
//generates the options
_.each( options, function( title, key ) {
var _attributes = {
value : key,
html: title
};
if ( key == input() || _.contains( input(), key ) )
$.extend( _attributes, { selected : "selected" } );
$( 'select[data-czrtype]', input.container ).append( $('<option>', _attributes) );
});
//fire czrSelect2
$( 'select[data-czrtype]', input.container ).czrSelect2();
}
},
CZBodyBgItemMths : {
//Fired if the item has been instantiated
//The item.callbacks are declared.
ready : function() {
var item = this;
item.inputCollection.bind( function( _col_ ) {
if ( ! _.isEmpty( _col_ ) && item.czr_Input && item.czr_Input.has( 'background-image' ) ) {
item.czr_Input('background-image').isReady.done( function( input_instance ) {
var set_visibilities = function( bg_val ) {
var is_bg_img_set = ! _.isEmpty( bg_val ) ||_.isNumber( bg_val);
_.each( ['background-repeat', 'background-attachment', 'background-position', 'background-size'], function( dep ) {
item.czr_Input(dep).container.toggle( is_bg_img_set || false );
});
};
set_visibilities( input_instance() );
//update the item model on 'background-image' change
item.bind('background-image:changed', function(){
set_visibilities( item.czr_Input('background-image')() );
});
});
}
});
api.CZRItem.prototype.ready.call( item );
},
}
});//$.extend
//provides a description of each module
//=> will determine :
//1) how to initialize the module model. If not crud, then the initial item(s) model shall be provided
//2) which js template(s) to use : if crud, the module template shall include the add new and pre-item elements.
// , if crud, the item shall be removable
//3) how to render : if multi item, the item content is rendered when user click on edit button.
// If not multi item, the single item content is rendered as soon as the item wrapper is rendered.
//4) some DOM behaviour. For example, a multi item shall be sortable.
api.czrModuleMap = api.czrModuleMap || {};
$.extend( api.czrModuleMap, {
czr_background : {
mthds : BodyBGConstructor,
crud : false,
multi_item : false,
name : 'Body Background',
has_mod_opt : false,
ready_on_section_expanded : true,
defaultItemModel : {
'background-color' : '#eaeaea',
'background-image' : '',
'background-repeat' : 'no-repeat',
'background-attachment' : 'fixed',
'background-position' : 'center center',
'background-size' : 'cover'
}
}
});
})( wp.customize , jQuery, _ );
@@ -0,0 +1,143 @@
<?php
function hu_register_body_bg_module( $args ) {
$defaults = array(
'setting_id' => '',
'base_url_path' => '',//PC_AC_BASE_URL/inc/czr-modules/social-links/
'version' => '',
'option_value' => array(), //<= will be used for the dynamic registration
'setting' => array(),
'control' => array(),
'section' => array(), //array( 'id' => '', 'label' => '' ),
'sanitize_callback' => '',
'validate_callback' => ''
);
$args = wp_parse_args( $args, $defaults );
if ( ! isset( $GLOBALS['czr_base_fmk_namespace'] ) ) {
error_log( __FUNCTION__ . ' => global czr_base_fmk not set' );
return;
}
$czrnamespace = $GLOBALS['czr_base_fmk_namespace'];
//czr_fn\czr_register_dynamic_module
$CZR_Fmk_Base_fn = $czrnamespace . 'CZR_Fmk_Base';
if ( ! function_exists( $CZR_Fmk_Base_fn) ) {
error_log( __FUNCTION__ . ' => Namespace problem => ' . $CZR_Fmk_Base_fn );
return;
}
$CZR_Fmk_Base_fn() -> czr_pre_register_dynamic_setting( array(
'setting_id' => $args['setting_id'],
'module_type' => 'czr_background',
'option_value' => ! is_array( $args['option_value'] ) ? array() : $args['option_value'],
'setting' => $args['setting'],
'section' => $args['section'],
'control' => $args['control']
));
// czr_fn\czr_register_dynamic_module()
$CZR_Fmk_Base_fn() -> czr_pre_register_dynamic_module( array(
'dynamic_registration' => true,
'module_type' => 'czr_background',
// 'sanitize_callback' => 'hu_sanitize_callback__czr_social_module',
// 'validate_callback' => 'hu_validate_callback__czr_social_module',
'customizer_assets' => array(
'control_js' => array(
// handle + params for wp_enqueue_script()
// @see https://developer.wordpress.org/reference/functions/wp_enqueue_script/
'czr-body-bg-module' => array(
'src' => sprintf(
'%1$s/assets/js/%2$s',
$args['base_url_path'],
'_3_2_body_background_module.js'
),
'deps' => array('customize-controls' , 'jquery', 'underscore'),
'ver' => ( defined('WP_DEBUG') && true === WP_DEBUG ) ? time() : $args['version'],
'in_footer' => true
)
),
'localized_control_js' => array(
'deps' => 'czr-customizer-fmk',
'global_var_name' => 'bodyBGModuleLocalized',
'params' => array(
//background repeat select options
'bg_repeat_options' => array(
'no-repeat' => __( 'No Repeat', 'hueman' ),
'repeat' => __( 'Repeat All', 'hueman' ),
'repeat-x' => __( 'Repeat Horizontally', 'hueman' ),
'repeat-y' => __( 'Repeat Vertically', 'hueman' ),
'inherit' => __( 'Inherit', 'hueman' ),
),
//background attachment select options
'bg_attachment_options' => array(
'fixed' => __( 'Fixed', 'hueman' ),
'scroll' => __( 'Scroll', 'hueman' ),
'inherit' => __( 'Inherit', 'hueman' ),
),
//background position select options
'bg_position_options' => array(
'left top' => __( 'Left Top', 'hueman' ),
'left center' => __( 'Left Center', 'hueman' ),
'left bottom' => __( 'Left Bottom', 'hueman' ),
'center top' => __( 'Center Top', 'hueman' ),
'center center' => __( 'Center Center', 'hueman' ),
'center bottom' => __( 'Center Bottom', 'hueman' ),
'right top' => __( 'Right Top', 'hueman' ),
'right center' => __( 'Right Center', 'hueman' ),
'right bottom' => __( 'Right Bottom', 'hueman' ),
)
)
)
),
'tmpl' => array(
'item-inputs' => array(
'background-color' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Color', 'hueman'),
'default' => '#eaeaea',
'width-100' => true
),//"#000000",
'background-image' => array(
'input_type' => 'upload',
'title' => __('Background Image', 'hueman'),
),
'background-repeat' => array(
'input_type' => 'select',
'title' => __('Repeat', 'hueman'),
),
'background-attachment' => array(
'input_type' => 'select',
'title' => __('Background attachment', 'hueman'),
),
'background-position' => array(
'input_type' => 'select',
'title' => __('Background position', 'hueman'),
),
'background-size' => array(
'input_type' => 'text',
'title' => __('Background size', 'hueman'),
'notice_after' => sprintf( '%1$s %2$s',
__('The background-size CSS property specifies the size of the background images. The size of the image can be fully constrained or only partially in order to preserve its intrinsic ratio.', 'hueman'),
sprintf(' %1$s %2$s.',
__('Learn more', 'hueman'),
sprintf('<a href="%1$s" target="_blank">%2$s</a>', esc_url('developer.mozilla.org/en-US/docs/Web/CSS/background-size'), __('here', 'hueman') )
)
)
)
)
)//tmpl
));
}//hu_register_body_bg_module()
@@ -0,0 +1,499 @@
//extends api.CZRDynModule
var CZRSocialModuleMths = CZRSocialModuleMths || {};
( function ( api, $, _ ) {
$.extend( CZRSocialModuleMths, {
initialize: function( id, options ) {
if ( _.isUndefined( window.socialModuleLocalized ) ) {
api.errorLog( 'social module => Missing localized js params socialModuleLocalized');
}
var module = this;
//run the parent initialize
api.CZRDynModule.prototype.initialize.call( module, id, options );
//extend the module with new template Selectors
$.extend( module, {
itemPreAddEl : '',/// 'czr-module-social-pre-add-view-content',
itemInputList : '',// 'czr-module-social-item-content',
modOptInputList : ''//czr-module-social-mod-opt'
} );
this.social_icons = [
'500px',
'adn',
'amazon',
'android',
'angellist',
'apple',
'behance',
'behance-square',
'bitbucket',
//'bitbucket-square', //<- removed in fa5
'black-tie',
'btc',
'buysellads',
'chrome',
'codepen',
'codiepie',
'connectdevelop',
'contao',
'dashcube',
'delicious',
'deviantart',
'digg',
'discord',
'dribbble',
'dropbox',
'drupal',
'edge',
'empire',
'envelope',
'envelope-o', //<- go with far envelope
'envelope-square',
'expeditedssl',
'facebook',
'facebook-f (alias)',
//'facebook-official', //<- removed in fa5
'facebook-square',
'firefox',
'flickr',
'flipboard',
'fonticons',
'fort-awesome',
'forumbee',
'foursquare',
'get-pocket',
'gg',
'gg-circle',
'git',
'github',
'github-alt',
'github-square',
'gitlab',
'git-square',
'google',
'google-plus',
//'google-plus-circle', //<- removed in fa5
//'google-plus-official', //<- removed in fa5
'google-plus-g', //<- added in fa5
'google-plus-square',
'google-wallet',
'gratipay',
'hacker-news',
'houzz',
'imdb',
'instagram',
'internet-explorer',
'ioxhost',
'joomla',
'jsfiddle',
'lastfm',
'lastfm-square',
'leanpub',
'line',
'linkedin',
//'linkedin-square', //<- removed in fa5
'linkedin-in', //<- added in fa5
'linux',
'mastodon',
'maxcdn',
//'meanpath', <- removed in fa5
'meetup',
'medium',
'mixcloud',
'map-marker',
'mobile',
'mobile-alt',//<- added in fa5
'modx',
'odnoklassniki',
'odnoklassniki-square',
'opencart',
'openid',
'opera',
'optin-monster',
'pagelines',
'patreon',
'paypal',
'phone',
'phone-square',
'pied-piper',
'pied-piper-alt',
'pinterest',
'pinterest-p',
'pinterest-square',
'product-hunt',
'qq',
'rebel',
'reddit',
'reddit-alien',
'reddit-square',
'renren',
'rss',
'rss-square',
'safari',
'scribd',
'sellsy',
'share-alt',
'share-alt-square',
'shirtsinbulk',
'simplybuilt',
'skyatlas',
'skype',
'slack',
'slideshare',
'sms',
'snapchat',
'soundcloud',
'spotify',
'stack-exchange',
'stack-overflow',
'steam',
'steam-square',
'stumbleupon',
'stumbleupon-circle',
'strava',
'telegram',
'tencent-weibo',
'tiktok',
'trello',
'tripadvisor',
'tumblr',
'tumblr-square',
'twitch',
'twitter',
'twitter-square',
'usb',
'viacoin',
'viber',
'vimeo',
'vimeo-square',
'vine',
'vk',
'weibo',
'weixin',
'whatsapp',
'wikipedia-w',
'windows',
'wordpress',
'xing',
'xing-square',
'yahoo',
'y-combinator',
'yelp',
'youtube',
//'youtube-play', //<- removed in fa5
'youtube-square'
];
//FA5 backward compatibility with FA4
//see https://github.com/presscustomizr/customizr/issues/1364
this.fa_solid_icons = [
'fa-envelope',
'fa-envelope-square',
'fa-map-marker',
'fa-mobile',
'fa-mobile-alt',
'fa-phone',
'fa-phone-square',
'fa-rss',
'fa-rss-square',
'fa-share-alt',
'fa-share-alt-square',
'fa-sms'
];
this.fa_icons_replacement = {
'fa-bitbucket-square' : 'fa-bitbucket',
'fa-facebook-official' : 'fa-facebook-f',
'fa-google-plus-circle' : 'fa-google-plus',
'fa-google-plus-official' : 'fa-google-plus',
'fa-linkedin-square' : 'fa-linkedin',
'fa-youtube-play' : 'fa-youtube'
};
this.defaultSocialColor = socialModuleLocalized.defaultSocialColor ? socialModuleLocalized.defaultSocialColor : 'rgb(90,90,90)';
this.defaultSocialSize = socialModuleLocalized.defaultSocialSize ? socialModuleLocalized.defaultSocialSize : 14;
//EXTEND THE DEFAULT CONSTRUCTORS FOR INPUT
module.inputConstructor = api.CZRInput.extend( module.CZRSocialsInputMths || {} );
//EXTEND THE DEFAULT CONSTRUCTORS FOR MONOMODEL
module.itemConstructor = api.CZRItem.extend( module.CZRSocialsItem || {} );
//declares a default ModOpt model
this.defaultModOptModel = {
is_mod_opt : true,
module_id : module.id,
'social-size' : module.defaultSocialSize
};
//declares a default model
this.defaultItemModel = {
id : '',
title : '' ,
'social-icon' : '',
'social-link' : '',
'social-color' : module.defaultSocialColor,
'social-target' : 1
};
//overrides the default success message
this.itemAddedMessage = socialModuleLocalized.i18n['New Social Link created ! Scroll down to edit it.'];
//fired ready :
//1) on section expansion
//2) or in the case of a module embedded in a regular control, if the module section is already opened => typically when skope is enabled
if ( _.has( api, 'czr_activeSectionId' ) && module.control.section() == api.czr_activeSectionId() && 'resolved' != module.isReady.state() ) {
module.ready();
}
api.section( module.control.section() ).expanded.bind(function(to) {
//set module ready on section expansion
if ( 'resolved' != module.isReady.state() ) {
module.ready();
}
});
module.isReady.then( function() {
if ( _.isUndefined( module.preItem ) )
return;
//specific update for the item preModel on social-icon change
module.preItem.bind( function( to, from ) {
if ( ! _.has(to, 'social-icon') )
return;
if ( _.isEqual( to['social-icon'], from['social-icon'] ) )
return;
module.updateItemModel( module.preItem, true );
});
});
},//initialize
//ACTIONS ON ICON CHANGE
//Fired on 'social-icon:changed'
//Don't fire in pre item case
//@item_instance an be the preItem or an already created item
updateItemModel : function( item_instance, is_preItem ) {
var item = item_instance,
module = this;
is_preItem = is_preItem || false;
//check if we are in the pre Item case => if so, the social-icon might be empty
if ( ! _.has( item(), 'social-icon') || _.isEmpty( item()['social-icon'] ) )
return;
var _new_model, _new_title, _new_color;
_new_model = $.extend( true, {}, item() );//always safer to deep clone ( alternative to _.clone() ) => we don't know how nested this object might be in the future
_new_title = this.getTitleFromIcon( _new_model['social-icon'] );
_new_color = module.defaultSocialColor;
if ( ! is_preItem && item.czr_Input.has( 'social-color' ) )
_new_color = item.czr_Input('social-color')();
//add text follow us... to the title
_new_title = [ socialModuleLocalized.i18n['Follow us on'], _new_title].join(' ');
if ( is_preItem ) {
_new_model = $.extend( _new_model, { title : _new_title, 'social-color' : _new_color } );
item.set( _new_model );
} else {
item.czr_Input('title').set( _new_title );
//item.czr_Input('social-link').set( '' );
if ( item.czr_Input('social-color') ) { //optional
item.czr_Input('social-color').set( _new_color );
}
}
},
/* Helpers */
getTitleFromIcon : function( icon ) {
return api.CZR_Helpers.capitalize( icon.replace('fa-', '').replace('envelope', 'email') );
},
getIconFromTitle : function( title ) {
return 'fa-' . title.toLowerCase().replace('envelope', 'email');
},
//from : https://stackoverflow.com/a/34560648
_strReplace : function( $f, $r, $s ) {
return $s.replace(new RegExp("(" + (typeof($f) == "string" ? $f.replace(/[.?*+^$[\]\\(){}|-]/g, "\\$&") : $f.map(function(i){return i.replace(/[.?*+^$[\]\\(){}|-]/g, "\\$&")}).join("|")) + ")", "g"), typeof($r) == "string" ? $r : typeof($f) == "string" ? $r[0] : function(i){ return $r[$f.indexOf(i)]});
},
buildFaIcon : function( value ) {
//FA5 backward compatibility with FA4
//see https://github.com/presscustomizr/customizr/issues/1364
//by default they're brands
var _fa_group = 'fab', //<- brand group by default
_icon_class = value.toLowerCase(),
solidIcons = this.fa_solid_icons,
iconsReplacement = this.fa_icons_replacement;
_icon_class = this._strReplace( _.keys( iconsReplacement ), _.values( iconsReplacement ),_icon_class);
//former -o icons => now part of the far (Regular) group
if ( _icon_class.match(/-o$/) ) {
_fa_group = 'far';
_icon_class = _icon_class.replace(/-o$/,'');
}
//solid icons
else if ( _.contains( solidIcons, _icon_class ) ) {
_fa_group = 'fas';
}
return _fa_group + ' ' +_icon_class;
},
CZRSocialsInputMths : {
setupSelect : function() {
var input = this,
item = input.input_parent,
module = input.module,
socialList = module.social_icons,
solidIcons = module.fa_solid_icons,
iconsReplacement = module.fa_icons_eplacement,
_model = item(),
//check if we are in the pre Item case => if so, the id is empty
is_preItem = _.isEmpty( _model.id );
//=> add the select text in the pre Item case
if ( is_preItem ) {
socialList = _.union( [ socialModuleLocalized.i18n['Select a social icon'] || 'Select a social icon' ], socialList );
}
//generates the options
_.each( socialList , function( icon_name, k ) {
icon_name = _.isEmpty( icon_name ) ? '' : icon_name;
// in the pre Item case the first select element is the notice "Select a social icon"
// doesn't need the fa-* class
var _value = ( is_preItem && 0 === k ) ? '' : 'fa-' + icon_name.toLowerCase(),
_attributes = {
value : _value,
html: module.getTitleFromIcon( icon_name )
};
if ( _value == _model['social-icon'] )
$.extend( _attributes, { selected : "selected" } );
$( 'select[data-czrtype="social-icon"]', input.container ).append( $('<option>', _attributes) );
});
function addIcon( state ) {
if (! state.id) { return state.text; }
//two spans here because we cannot wrap the social text into the social icon span as the solid FA5 font-weight is bold
var $state = $(
'<span class="' + module.buildFaIcon( state.element.value.toLowerCase() ) + '"></span><span class="social-name">&nbsp;&nbsp;' + state.text + '</span>'
);
return $state;
}
//fire select2
$( 'select[data-czrtype="social-icon"]', input.container ).czrSelect2( {
templateResult: addIcon,
templateSelection: addIcon
});
},
setupColorPicker : function( obj ) {
var input = this,
item = input.input_parent,
module = input.module,
$el = $( 'input[data-czrtype="social-color"]', input.container );
$el.iris( {
palettes: true,
hide:false,
defaultColor : module.defaultSocialColor || 'rgba(255,255,255,0.7)',
change : function( e, o ) {
//if the input val is not updated here, it's not detected right away.
//weird
//is there a "change complete" kind of event for iris ?
//hack to reset the color to default...@todo => use another color picker.
if ( _.has( o, 'color') && 16777215 == o.color._color )
$(this).val( module.defaultSocialColor || 'rgba(255,255,255,0.7)' );
else
$(this).val( o.color.toString() );
$(this).trigger('colorpickerchange').trigger('change');
}
});
//when the picker opens, it might be below the visible viewport.
//No built-in event available to react on this in the wpColorPicker unfortunately
$el.closest('div').on('click keydown', function() {
module._adjustScrollExpandedBlock( input.container );
});
}
},//CZRSocialsInputMths
CZRSocialsItem : {
//Fired if the item has been instantiated
//The item.callbacks are declared.
ready : function() {
var item = this;
api.CZRItem.prototype.ready.call( item );
//update the item model on social-icon change
item.bind('social-icon:changed', function(){
item.module.updateItemModel( item );
});
},
_buildTitle : function( title, icon, color ) {
var item = this,
module = item.module;
title = title || ( 'string' === typeof(icon) ? api.CZR_Helpers.capitalize( icon.replace( 'fa-', '') ) : '' );
title = api.CZR_Helpers.truncate(title, 20);
icon = icon || 'fa-' + module.social_icons[0];
color = color || module.defaultSocialColor;
return '<div><span class="' + module.buildFaIcon( icon ) + '" style="color:' + color + '"></span> ' + title + '</div>';
},
//overrides the default parent method by a custom one
//at this stage, the model passed in the obj is up to date
writeItemViewTitle : function( model ) {
var item = this,
module = item.module,
_model = model || item(),
_title = module.getTitleFromIcon( _model['social-icon'] );
$( '.' + module.control.css_attr.item_title , item.container ).html(
item._buildTitle( _title, _model['social-icon'], _model['social-color'] )
);
}
},//CZRSocialsItem
});//$.extend
})( wp.customize , jQuery, _ );
(function ( api, $ ) {
//provides a description of each module
//=> will determine :
//1) how to initialize the module model. If not crud, then the initial item(s) model shall be provided
//2) which js template(s) to use : if crud, the module template shall include the add new and pre-item elements.
// , if crud, the item shall be removable
//3) how to render : if multi item, the item content is rendered when user click on edit button.
// If not multi item, the single item content is rendered as soon as the item wrapper is rendered.
//4) some DOM behaviour. For example, a multi item shall be sortable.
api.czrModuleMap = api.czrModuleMap || {};
$.extend( api.czrModuleMap, {
czr_social_module : {
mthds : CZRSocialModuleMths,
crud : true,
name : 'Social Icons',
has_mod_opt : true
},
});
})( wp.customize, jQuery );
@@ -0,0 +1,219 @@
<?php
function hu_register_social_links_module( $args ) {
$defaults = array(
'setting_id' => '',
'base_url_path' => '',//PC_AC_BASE_URL/inc/czr-modules/social-links/
'version' => '',
'option_value' => array(), //<= will be used for the dynamic registration
'setting' => array(),
'control' => array(),
'section' => array(), //array( 'id' => '', 'label' => '' ),
'sanitize_callback' => '',
'validate_callback' => ''
);
$args = wp_parse_args( $args, $defaults );
if ( ! isset( $GLOBALS['czr_base_fmk_namespace'] ) ) {
error_log( __FUNCTION__ . ' => global czr_base_fmk not set' );
return;
}
$czrnamespace = $GLOBALS['czr_base_fmk_namespace'];
//czr_fn\czr_register_dynamic_module
$CZR_Fmk_Base_fn = $czrnamespace . 'CZR_Fmk_Base';
if ( ! function_exists( $CZR_Fmk_Base_fn) ) {
error_log( __FUNCTION__ . ' => Namespace problem => ' . $CZR_Fmk_Base_fn );
return;
}
$CZR_Fmk_Base_fn() -> czr_pre_register_dynamic_setting( array(
'setting_id' => $args['setting_id'],
'module_type' => 'czr_social_module',
'option_value' => ! is_array( $args['option_value'] ) ? array() : $args['option_value'],
'setting' => $args['setting'],
'section' => $args['section'],
'control' => $args['control']
));
// czr_fn\czr_register_dynamic_module()
$CZR_Fmk_Base_fn() -> czr_pre_register_dynamic_module( array(
'dynamic_registration' => true,
'module_type' => 'czr_social_module',
'sanitize_callback' => 'hu_sanitize_callback__czr_social_module',
//'validate_callback' => 'hu_validate_callback__czr_social_module',
'customizer_assets' => array(
'control_js' => array(
// handle + params for wp_enqueue_script()
// @see https://developer.wordpress.org/reference/functions/wp_enqueue_script/
'czr-social-links-module' => array(
'src' => sprintf(
'%1$s/assets/js/%2$s',
$args['base_url_path'],
'_2_7_socials_module.js'
),
'deps' => array('customize-controls' , 'jquery', 'underscore'),
'ver' => ( defined('WP_DEBUG') && true === WP_DEBUG ) ? time() : $args['version'],
'in_footer' => true
)
),
'localized_control_js' => array(
'deps' => 'czr-customizer-fmk',
'global_var_name' => 'socialModuleLocalized',
'params' => array(
//Social Module
'defaultSocialColor' => 'rgb(90,90,90)',
'defaultSocialSize' => 14,
'i18n' => array(
'Rss' => __('Rss', 'hueman'),
'Select a social icon' => __('Select a social icon', 'hueman'),
'Follow us on' => __('Follow us on', 'hueman'),
'Done !' => __('Done !', 'hueman'),
'New Social Link created ! Scroll down to edit it.' => __('New Social Link created ! Scroll down to edit it.', 'hueman'),
)
//option value for dynamic registration
)
)
),
'tmpl' => array(
'pre-item' => array(
'social-icon' => array(
'input_type' => 'select',
'title' => __('Select an icon', 'hueman')
),
'social-link' => array(
'input_type' => 'text',
'title' => __('Social link url', 'hueman'),
'notice_after' => __('Enter the full url of your social profile (must be valid url).', 'hueman'),
'placeholder' => __('http://...,mailto:...,...', 'hueman')
)
),
'mod-opt' => array(
'social-size' => array(
'input_type' => 'number',
'title' => __('Size in px', 'hueman'),
'step' => 1,
'min' => 5,
'transport' => 'postMessage'
)
),
'item-inputs' => array(
'social-icon' => array(
'input_type' => 'select',
'title' => __('Social icon', 'hueman')
),
'social-link' => array(
'input_type' => 'text',
'title' => __('Social link', 'hueman'),
'notice_after' => __('Enter the full url of your social profile (must be valid url).', 'hueman'),
'placeholder' => __('http://...,mailto:...,...', 'hueman')
),
'title' => array(
'input_type' => 'text',
'title' => __('Title', 'hueman'),
'notice_after' => __('This is the text displayed on mouse over.', 'hueman'),
),
'social-color' => array(
'input_type' => 'color',
'title' => sprintf( '%1$s <i>%2$s %3$s</i>', __('Icon color', 'hueman'), __('default:', 'hueman'), 'rgba(255,255,255,0.7)' ),
'notice_after' => __('Set a unique color for your icon.', 'hueman'),
'transport' => 'postMessage'
),
'social-target' => array(
'input_type' => 'nimblecheck',
'title' => __('Link target', 'hueman'),
'notice_after' => __('Check this option to open the link in a another tab of the browser.', 'hueman'),
'title_width' => 'width-80',
'input_width' => 'width-20',
)
)
)
));
}//ac_register_social_links_module()
/////////////////////////////////////////////////////////////////
// SANITIZATION
/***
* Social Module sanitization/validation
**/
function hu_sanitize_callback__czr_social_module( $socials ) {
// error_log( 'IN SANITIZATION CALLBACK' );
// error_log( print_r( $socials, true ));
if ( empty( $socials ) )
return array();
//sanitize urls and titles for the db
foreach ( $socials as $index => &$social ) {
if ( ! is_array( $social ) || ! array_key_exists( 'title', $social) )
continue;
$social['title'] = esc_attr( $social['title'] );
}
return $socials;
}
function hu_validate_callback__czr_social_module( $validity, $socials ) {
// error_log( 'IN VALIDATION CALLBACK' );
// error_log( print_r( $socials, true ));
$ids_malformed_url = array();
$malformed_message = __( 'An error occurred: malformed social links', 'hueman');
if ( empty( $socials ) )
return array();
//(
// [0] => Array
// (
// [is_mod_opt] => 1
// [module_id] => tc_social_links_czr_module
// [social-size] => 15
// )
// [1] => Array
// (
// [id] => czr_social_module_0
// [title] => Follow us on Renren
// [social-icon] => fa-renren
// [social-link] => http://customizr-dev.dev/feed/rss/
// [social-color] => #6d4c8e
// [social-target] => 1
// )
// )
//validate urls
foreach ( $socials as $index => $item_or_modopt ) {
if ( ! is_array( $item_or_modopt ) )
return new WP_Error( 'required', $malformed_message );
//should be an item or a mod opt
if ( ! array_key_exists( 'is_mod_opt', $item_or_modopt ) && ! array_key_exists( 'id', $item_or_modopt ) )
return new WP_Error( 'required', $malformed_message );
//if modopt case, skip
if ( array_key_exists( 'is_mod_opt', $item_or_modopt ) )
continue;
if ( $item_or_modopt['social-link'] != esc_url_raw( $item_or_modopt['social-link'] ) )
array_push( $ids_malformed_url, $item_or_modopt[ 'id' ] );
}
if ( empty( $ids_malformed_url) )
return null;
return new WP_Error( 'required', __( 'Please fill the social link inputs with a valid URLs', 'hueman' ), $ids_malformed_url );
}
@@ -0,0 +1,973 @@
//extends api.CZRDynModule
//globals widgetModuleLocalized, themeServerControlParams
var WidgetAreaConstructor = WidgetAreaConstructor || {};
( function ( api, $, _ ) {
$.extend( WidgetAreaConstructor, {
initialize: function( id, constructorOptions ) {
var module = this;
api.CZRDynModule.prototype.initialize.call( this, id, constructorOptions );
//extend the module with new template Selectors
$.extend( module, {
itemPreAddEl : 'czr-module-widgets-pre-add-view-content',
itemInputList : 'czr-module-widgets-item-input-list',
ruItemPart : 'czr-module-widgets-ru-item-part'
} );
//EXTEND THE DEFAULT CONSTRUCTORS FOR INPUT
module.inputConstructor = api.CZRInput.extend( module.CZRWZonesInputMths || {} );
//EXTEND THE DEFAULT CONSTRUCTORS FOR MONOMODEL
module.itemConstructor = api.CZRItem.extend( module.CZRWZonesItemConstructor || {} );
module.serverParams = widgetModuleLocalized || {};
//add a shortcut to the server side json properties
module.contexts = _.has( module.serverParams , 'sidebar_contexts') ? module.serverParams.sidebar_contexts : {};
//context match map
module.context_match_map = {
is_404 : '404',
is_category : 'archive-category',
is_home : 'home',
is_page : 'page',
is_search : 'search',
is_single : 'single'
};
module.locations = _.has( module.serverParams , 'sidebar_locations') ? module.serverParams.sidebar_locations : {};
//declares a default model
module.defaultItemModel = {
id : '',
title : widgetModuleLocalized.i18n.widgetZone,
contexts : _.without( _.keys(module.contexts), '_all_' ),//the server list of contexts is an object, we only need the keys, whitout _all_
locations : [ module.serverParams.defaultWidgetLocation ],
description : ''
};
//overrides the default success message
this.itemAddedMessage = widgetModuleLocalized.i18n.widgetZoneAdded;
//Observe and react to sidebar insights from the preview frame
// SIDEBAR INSIGHTS => stores and observes the sidebars and widgets settings sent by the preview */
if ( ! _.has( api, 'sidebar_insights' ) ) {
api.sidebar_insights = new api.Values();
api.sidebar_insights.create('candidates');//will store the sidebar candidates on preview refresh
api.sidebar_insights.create('actives');//will record the refreshed active list of active sidebars sent from the preview
api.sidebar_insights.create('inactives');
api.sidebar_insights.create('registered');
api.sidebar_insights.create('available_locations');
}
this.listenToSidebarInsights();
//React on 'houston-widget-settings'
//actives : data.renderedSidebars,
// inactives : _inactives,
// registered : _registered,
// candidates : _candidates,
// available_locations : data.availableWidgetLocations//built server side
api.czr_widgetZoneSettings = api.czr_widgetZoneSettings || new api.Value();
api.czr_widgetZoneSettings.bind( function( updated_data_sent_from_preview , from ) {
//console.log('REACT ON czr_widgetZoneSettings', updated_data_sent_from_preview , from );
module.isReady.then( function() {
_.each( updated_data_sent_from_preview, function( _data, _key ) {
api.sidebar_insights( _key ).set( _data );
});
});
});
//AVAILABLE LOCATIONS FOR THE PRE MODEL
//1) add an observable value to module.preItem to handle the alert visibility
module.preItem_location_alert_view_state = new api.Value( 'closed');
//2) add state listeners
module.preItem_location_alert_view_state.callbacks.add( function( to, from ) {
module._toggleLocationAlertExpansion( module.container, to );
});
//REACT ON ADD / REMOVE ITEMS
module.bind( 'item-added', function( model ) {
module.addWidgetSidebar( model );
});
module.bind( 'pre_item_api_remove' , function(model) {
module.removeWidgetSidebar( model );
});
//records the top margin value of the widgets panel on each expansion
var fixTopMargin = new api.Values();
fixTopMargin.create('fixed_for_current_session');
fixTopMargin.create('value');
api.section(module.serverParams.dynWidgetSection).fixTopMargin = fixTopMargin;
api.section(module.serverParams.dynWidgetSection).fixTopMargin('fixed_for_current_session').set(false);
//setup reactions on widget section expansion
//change the expanded behaviour for the widget zone section
//api.section(module.serverParams.dynWidgetSection).expanded.callbacks.add( function() { return module.widgetSectionReact.apply(module, arguments ); } );
//bind actions on widget panel expansion and widget zone section expansion
//Fire the module
api.panel('widgets').expanded.callbacks.add( function(to, from) {
module.widgetPanelReact();//setup some visual adjustments, must be ran each time panel is closed or expanded
//Fire the module if not done already
if ( 'resolved' == module.isReady.state() )
return;
module.ready();
});
},//initialize
//When the control is embedded on the page, this method is fired in api.CZRBaseModuleControl:ready()
//=> right after the module is instantiated.
//VERIFIED
ready : function() {
var module = this;
api.CZRDynModule.prototype.ready.call( module );
//add state listener on pre Item view
module.preItemExpanded.callbacks.add( function( to, from ) {
if ( ! to )
return;
//refresh the location list
module.preItem.czr_Input( 'locations' )._setupLocationSelect( true );//true for refresh
//refresh the location alert message
module.preItem.czr_Input( 'locations' ).mayBeDisplayModelAlert();
});
},
//overrides parent method
//adds the default widget zones in the items
//VERIFIED
initializeModuleModel : function( constructorOptions ) {
var module = this, dfd = $.Deferred();
constructorOptions.items = _.union( _.has( module.serverParams, 'default_zones' ) ? module.serverParams.default_zones : [], constructorOptions.items );
return dfd.resolve( constructorOptions ).promise();
},
});//$.extend()
})( wp.customize , jQuery, _ );//extends api.CZRDynModule
//globals widgetModuleLocalized, themeServerControlParams
var WidgetAreaConstructor = WidgetAreaConstructor || {};
( function ( api, $, _ ) {
$.extend( WidgetAreaConstructor, {
CZRWZonesInputMths : {
ready : function() {
var input = this;
input.bind('locations:changed', function(){
input.mayBeDisplayModelAlert();
});
api.CZRInput.prototype.ready.call( input);
},
//////////////////////////////////////////////////
///SETUP SELECTS
//////////////////////////////////////////////////
//setup select on view_rendered|item_content_event_map
setupSelect : function() {
var input = this;
if ( 'locations' == this.id )
this._setupLocationSelect();
if ( 'contexts' == this.id )
this._setupContextSelect();
},
//helper
_setupContextSelect : function() {
var input = this,
input_contexts = input(),
item = input.input_parent,
module = input.module;
//generates the contexts options
_.each( module.contexts, function( title, key ) {
var _attributes = {
value : key,
html: title
};
if ( key == input_contexts || _.contains( input_contexts, key ) )
$.extend( _attributes, { selected : "selected" } );
$( 'select[data-czrtype="contexts"]', input.container ).append( $('<option>', _attributes) );
});
//fire czrSelect2
$( 'select[data-czrtype="contexts"]', input.container ).czrSelect2();
},
//helper
//the refresh param is a bool
_setupLocationSelect : function(refresh ) {
var input = this,
input_locations = input(),
item = input.input_parent,
module = input.module,
available_locs = api.sidebar_insights('available_locations')();
//console.log('_setupLocationSelect', input(), module.locations );
//generates the locations options
//append them if not set yet
if ( ! $( 'select[data-czrtype="locations"]', input.container ).children().length ) {
_.each( module.locations, function( title, key ) {
var _attributes = {
value : key,
html: title
};
if ( key == input_locations || _.contains( input_locations, key ) )
$.extend( _attributes, { selected : "selected" } );
$( 'select[data-czrtype="locations"]', input.container ).append( $('<option>', _attributes) );
});
}//if
function setAvailability( state ) {
if (! state.id) { return state.text; }
if ( _.contains(available_locs, state.element.value) ) { return state.text; }
var $state = $(
'<span class="czr-unavailable-location fas fa-ban" title="' + widgetModuleLocalized.i18n.unavailableLocation + '">&nbsp;&nbsp;' + state.text + '</span>'
);
return $state;
}
if ( refresh ) {
$( 'select[data-czrtype="locations"]', input.container ).czrSelect2( 'destroy' );
}
//fire czrSelect2
$( 'select[data-czrtype="locations"]', input.container ).czrSelect2( {
templateResult: setAvailability,
templateSelection: setAvailability
});
},
//fired on view event map : 'locations:changed'
//@param obj { dom_el: $() , model : {} )
mayBeDisplayModelAlert : function() {
var input = this,
item = input.input_parent,
module = input.module;
//check if we are in the pre Item case => if so, the locations might be empty
if ( ! _.has( item(), 'locations') || _.isEmpty( item().locations ) )
return;
var _selected_locations = $('select[data-czrtype="locations"]', input.container ).val(),
available_locs = api.sidebar_insights('available_locations')(),
_unavailable = _.filter( _selected_locations, function( loc ) {
return ! _.contains(available_locs, loc);
});
//check if we are in the pre Item case => if so, the id is empty
if ( ! _.has( item(), 'id' ) || _.isEmpty( item().id ) ) {
module.preItem_location_alert_view_state.set( ! _.isEmpty( _unavailable ) ? 'expanded' : 'closed' );
} else {
item.czr_itemLocationAlert.set( ! _.isEmpty( _unavailable ) ? 'expanded' : 'closed' );
}
}
},//CZRWZonesInputMths
});//$.extend()
})( wp.customize , jQuery, _ );//extends api.CZRDynModule
//globals widgetModuleLocalized, themeServerControlParams
var WidgetAreaConstructor = WidgetAreaConstructor || {};
( function ( api, $, _ ) {
$.extend( WidgetAreaConstructor, {
CZRWZonesItemConstructor : {
initialize : function( id, options ) {
var item = this,
module = item.module;
//Add some observable values for this item
item.czr_itemLocationAlert = new api.Value();
api.CZRItem.prototype.initialize.call( item, null, options );
// filter the params of the ajax query used to get the item wrapper template
// because we need a ru ( not a read update delete ) template for builtins widget zones
// requestParams = {
// tmpl : 'rud-item-part',
// module_type: 'all_modules',
// nonce: api.settings.nonce.save//<= do we need to set a specific nonce to fetch the tmpls ?
// };
// this has been introduced in March 2018, after the introduction of the tmpl ajax fetching
// it does the same job that the overriden getTemplateEl() was doing.
// This filter is declared in item::renderItemWrapper()
item.bind( 'item-wrapper-tmpl-params-before-fetching', function( requestParams ) {
//force view-content type to ru-item-part if the model is a built-in (primary, secondary, footer-1, ...)
//=> user can't delete a built-in model.
requestParams.tmpl = ( _.has( item(), 'is_builtin' ) && item().is_builtin ) ? 'ruItemPart' : requestParams.tmpl;
return requestParams;
});
},
//extend parent setupview
itemWrapperViewSetup : function() {
var item = this,
module = item.module;
api.CZRItem.prototype.itemWrapperViewSetup.call(item);
/// ALERT FOR NOT AVAILABLE LOCATION
item.czr_itemLocationAlert.set('closed');
//add a state listener on expansion change
item.czr_itemLocationAlert.callbacks.add( function( to, from ) {
module._toggleLocationAlertExpansion( item.container , to );
});
//update item title
item.writeSubtitleInfos(item());
//this is fired just after the itemWrapperViewSetupApiListeners
//=> add a callback to refresh the availability status of the locations in the select location picker
//add a state listener on expansion change
item.viewState.callbacks.add( function( to, from ) {
if ( -1 == to.indexOf('expanded') )//can take the expanded_noscroll value !
return;
//don't try to invoke the input instances before the content is actually rendered
//=> there might be cases when the content rendering is debounced...
item.bind('contentRendered', function() {
//refresh the location list
item.czr_Input('locations')._setupLocationSelect( true );//true for refresh
//refresh the location alert message
item.czr_Input('locations').mayBeDisplayModelAlert();
});
});
},
//extend parent listener
itemReact : function(to, from) {
var item = this;
api.CZRItem.prototype.itemReact.call(item, to, from);
item.writeSubtitleInfos(to);
item.updateSectionTitle(to).setModelUpdateTimer();
},
//Fired in setupItemListeners. Reacts to model change.
//Write html informations under the title : location(s) and context(s)
writeSubtitleInfos : function(model) {
var item = this,
module = item.module,
_model = _.clone( model || item() ),
_locations = [],
_contexts = [],
_html = '';
if ( ! item.container.length )
return this;
//generate the locations and the contexts text from the json data if exists
_model.locations =_.isString(_model.locations) ? [_model.locations] : _model.locations;
_.each( _model.locations, function( loc ) {
if ( _.has( module.locations , loc ) )
_locations.push(module.locations[loc]);
else
_locations.push(loc);
}
);
//build the context list
_model.contexts =_.isString(_model.contexts) ? [_model.contexts] : _model.contexts;
//all contexts cases ?
if ( item._hasModelAllContexts( model ) ) {
_contexts.push(module.contexts._all_);
} else {
_.each( _model.contexts, function( con ) {
if ( _.has( module.contexts , con ) )
_contexts.push(module.contexts[con]);
else
_contexts.push(con);
}
);
}
//Translated strings
var _locationText = widgetModuleLocalized.i18n.locations,
_contextText = widgetModuleLocalized.i18n.contexts,
_notsetText = widgetModuleLocalized.i18n.notset;
_locations = _.isEmpty( _locations ) ? '<span style="font-weight: bold;">' + _notsetText + '</span>' : _locations.join(', ');
_contexts = _.isEmpty( _contexts ) ? '<span style="font-weight: bold;">' + _notsetText + '</span>' : _contexts.join(', ');
//write the description if builtin
//else, write the dynamic location
// if ( _.has(_model, 'description') && _.has(_model, 'is_builtin') )
// _html = _model.description + ' <strong>|</strong> <u>Contexts</u> : ' + _contexts;
// else
_html = '<u>' + _locationText + '</u> : ' + _locations + ' <strong>|</strong> <u>' + _contextText + '</u> : ' + _contexts;
if ( ! $('.czr-zone-infos', item.container ).length ) {
var $_zone_infos = $('<div/>', {
class : [ 'czr-zone-infos' , module.control.css_attr.item_sort_handle ].join(' '),
html : _html
});
$( '.' + module.control.css_attr.item_btns, item.container ).after($_zone_infos);
} else {
$('.czr-zone-infos', item.container ).html(_html);
}
return this;
},//writeSubtitleInfos
////Fired in setupItemListeners
updateSectionTitle : function(model) {
var _sidebar_id = 'sidebar-widgets-' + model.id,
_new_title = model.title;
//does this section exists ?
if ( ! api.section.has(_sidebar_id) )
return this;
//update the section title
$('.accordion-section-title', api.section(_sidebar_id).container ).text(_new_title);
//update the top title ( visible when inside the expanded section )
$('.customize-section-title h3', api.section(_sidebar_id).container ).html(
'<span class="customize-action">' + api.section(_sidebar_id).params.customizeAction + '</span>' + _new_title
);
// $('.customize-section-title h3', api.section(_sidebar_id).container )
// .append('<span>', {
// class: 'customize-section-back',
// html: api.section(_sidebar_id).params.customizeAction
// } )
// .append(_new_title);
//remove and re-instanciate
//=> works for the section but the controls are not activated anymore.
//Should be easy to fix but useless to go further here. Jquery does the job.
// var _params = _.clone( api.section(_sidebar_id).params );
// _params.title = _new_title;
// api.section(_sidebar_id).container.remove();
// api.section.remove(_sidebar_id);
// api.section.add( _sidebar_id, new api.sectionConstructor[_params.type]( _params.id ,{ params : _params } ) );
return this;
},
//fired on model_update
//Don't hammer the preview with too many refreshs
//2 seconds delay
setModelUpdateTimer : function() {
var item = this,
module = item.module;
clearTimeout( $.data(this, 'modelUpdateTimer') );
$.data(
this,
'modelUpdateTimer',
_.delay( function() {
//refresh preview
api.previewer.refresh();
} , 1000)
);//$.data
},
//@return bool
//takes the model unique id
_hasModelAllContexts : function( model ) {
var item = this,
module = item.module,
moduleContexts = _.keys(module.contexts);
model = model || this();
if ( ! _.has(model, 'contexts') )
return;
if ( _.contains( model.contexts, '_all_') )
return true;
//case when model does not have _all_ but all the others
return _.isEmpty( _.difference( _.without(moduleContexts, '_all_') , model.contexts ) );
},
//@param contexts = array of contexts
//api.czr_wpQueryInfos is refreshed on each preview refresh
_getMatchingContexts : function( defaults ) {
var module = this,
_current = api.czr_wpQueryInfos().conditional_tags || {},
_matched = _.filter( module.context_match_map, function( hu, wp ) { return true === _current[wp]; } );
return _.isEmpty( _matched ) ? defaults : _matched;
}
},//CZRWZonesItemConstructor
});//$.extend()
})( wp.customize , jQuery, _ );//extends api.CZRDynModule
//globals widgetModuleLocalized, themeServerControlParams
var WidgetAreaConstructor = WidgetAreaConstructor || {};
( function ( api, $, _ ) {
$.extend( WidgetAreaConstructor, {
/////////////////////////////////////////
/// ADD / REMOVE WIDGET ZONES
////////////////////////////////////////
//fired on model_added_by_user
//
//can also be called statically when a dynamic sidebar is added in the preview
//in this case the parameter are the sidebar data with id and name
addWidgetSidebar : function( model, sidebar_data ) {
if ( ! _.isObject(model) && _.isEmpty(sidebar_data) ) {
throw new Error('No valid input were provided to add a new Widget Zone.');
}
//ADD the new sidebar to the existing collection
//Clone the serverControlParams.defaultWidgetSidebar sidebar
var module = this,
_model = ! _.isEmpty(model) ? _.clone(model) : sidebar_data,
_new_sidebar = _.isEmpty(model) ? sidebar_data : $.extend(
_.clone( _.findWhere( api.Widgets.data.registeredSidebars, { id: module.serverParams.defaultWidgetSidebar } ) ),
{
name : _model.title,
id : _model.id
}
);
//Add it to the backbone collection
api.Widgets.registeredSidebars.add( _new_sidebar );
//test if added:
//api.Widgets.registeredSidebars('czr_sidebars_8');
//ADD the sidebar section
var _params = $.extend(
_.clone( api.section( "sidebar-widgets-" + module.serverParams.defaultWidgetSidebar ).params ),
{
id : "sidebar-widgets-" + _model.id,
instanceNumber: _.max(api.settings.sections, function(sec){ return sec.instanceNumber; }).instanceNumber + 1,
sidebarId: _new_sidebar.id,
title: _new_sidebar.name,
description : 'undefined' != typeof(sidebar_data) ? sidebar_data.description : api.section( "sidebar-widgets-" + module.serverParams.defaultWidgetSidebar ).params.description,
//always set the new priority to the maximum + 1 ( module.serverParams.dynWidgetSection is excluded from this calculation because it must always be at the bottom )
priority: _.max( _.omit( api.settings.sections, module.serverParams.dynWidgetSection), function(sec){ return sec.instanceNumber; }).priority + 1,
}
);
api.section.add( _params.id, new api.sectionConstructor[ _params.type ]( _params.id ,{ params : _params } ) );
//add it to the static collection of settings
api.settings.sections[ _params.id ] = _params.id;
//ADD A SETTING
//Clone the module.serverParams.defaultWidgetSidebar sidebar widget area setting
var _new_set_id = 'sidebars_widgets['+_model.id+']',
_new_set = $.extend(
_.clone( api.settings.settings['sidebars_widgets[' + module.serverParams.defaultWidgetSidebar + ']'] ),
{
value:[]
}
);
//add it to the static collection of settings
api.settings.settings[ _new_set_id ] = _new_set;
//instanciate it
api.create( _new_set_id, _new_set_id, _new_set.value, {
transport: _new_set.transport,
previewer: api.previewer,
dirty: false
} );
//ADD A CONTROL
var _cloned_control = $.extend(
_.clone( api.settings.controls['sidebars_widgets[' + module.serverParams.defaultWidgetSidebar + ']'] ),
{
settings : { default : _new_set_id }
}),
_new_control = {};
//replace serverControlParams.defaultWidgetSidebar by the new sidebar id
_.each( _cloned_control, function( param, key ) {
if ( 'string' == typeof(param) ) {
param = param.replace( module.serverParams.defaultWidgetSidebar , _model.id );
}
_new_control[key] = param;
});
//set the instance number (no sure if needed)
_new_control.instanceNumber = _.max(api.settings.controls, function(con){ return con.instanceNumber; }).instanceNumber + 1;
//add it to the static collection of controls
api.settings.controls[_new_set_id] = _new_control;
//instanciate it
api.control.add( _new_set_id, new api.controlConstructor[ _new_control.type ]( _new_set_id, {
params: _new_control,
previewer: api.previewer
} ) );
//say it to the control container
//only if we are in an instanciated object => because this method can be accessed statically
if ( _.has(this, 'container') )
this.container.trigger( 'widget_zone_created', { model : _model, section_id : "sidebar-widgets-" + _model.id , setting_id : _new_set_id });
},//addWidgetSidebar
//fired on "after_modelRemoved"
removeWidgetSidebar : function( model ) {
var module = this;
if ( ! _.isObject(model) || _.isEmpty(model) ) {
throw new Error('No valid data were provided to remove a Widget Zone.');
}
//Remove this sidebar from the backbone collection
api.Widgets.registeredSidebars.remove( model.id );
//remove the section from the api values and the DOM if exists
if ( api.section.has("sidebar-widgets-" + model.id) ) {
//Remove the section container from the DOM
api.section("sidebar-widgets-" + model.id).container.remove();
//Remove the sidebar section from the api
api.section.remove( "sidebar-widgets-" + model.id );
//Remove this section from the static collection
delete api.settings.sections[ "sidebar-widgets-" + model.id ];
}
//remove the setting from the api if exists
if ( api.has('sidebars_widgets['+model.id+']') ) {
//Remove this setting from the api
api.remove( 'sidebars_widgets['+model.id+']' );
//Remove this setting from the static collection
delete api.settings.settings['sidebars_widgets['+model.id+']'];
}
//remove the widget control of this sidebar from the api and the DOM if exists
if ( api.control.has('sidebars_widgets['+model.id+']') ) {
//Remove the control container from the DOM
api.control( 'sidebars_widgets['+model.id+']' ).container.remove();
//Remove this control from the api
api.control.remove( 'sidebars_widgets['+model.id+']' );
//Remove it to the static collection of controls
delete api.settings.controls['sidebars_widgets['+model.id+']'];
}
//refresh
var _refresh = function() {
api.previewer.refresh();
};
_refresh = _.debounce( _refresh, 500 );
$.when( _refresh() ).done( function() {
//say it
module.trigger( 'widget_zone_removed',
{
model : model,
section_id : "sidebar-widgets-" + model.id ,
setting_id : 'sidebars_widgets['+model.id+']'
}
);
});
},
});//$.extend()
})( wp.customize , jQuery, _ );
//extends api.CZRDynModule
//globals widgetModuleLocalized, themeServerControlParams
var WidgetAreaConstructor = WidgetAreaConstructor || {};
( function ( api, $, _ ) {
$.extend( WidgetAreaConstructor, {
/////////////////////////////////////////
/// SET EXPANSION CALLBACKS FOR WIDGET PANEL AND WIDGET ZONE CREATION SECTION
////////////////////////////////////////
//cb of : api.panel('widgets').expanded.callbacks.add
widgetPanelReact : function() {
var module = this;
//will be used for adjustments
var _top_margin = api.panel('widgets').container.find( '.control-panel-content' ).css('margin-top');
api.section(module.serverParams.dynWidgetSection).fixTopMargin('value').set( _top_margin );
var _section_content = api.section(module.serverParams.dynWidgetSection).container.find( '.accordion-section-content' ),
_panel_content = api.panel('widgets').container.find( '.control-panel-content' ),
_set_margins = function() {
_section_content.css( 'margin-top', '' );
_panel_content.css('margin-top', api.section(module.serverParams.dynWidgetSection).fixTopMargin('value')() );
};
// Fix the top margin after reflow.
api.bind( 'pane-contents-reflowed', _.debounce( function() {
_set_margins();
}, 150 ) );
//Close all views on widget panel expansion/clos
module.closeAllItems().closeRemoveDialogs();
//Close preItem dialog box if exists
if ( _.has( module, 'preItemExpanded' ) )
module.preItemExpanded.set(false);
},//widgetPanelReact()
//cb of api.section(module.serverParams.dynWidgetSection).expanded.callbacks
widgetSectionReact : function( to, from ) {
var module = this,
section = api.section(module.serverParams.dynWidgetSection),
container = section.container.closest( '.wp-full-overlay-sidebar-content' ),
content = section.container.find( '.accordion-section-content' ),
overlay = section.container.closest( '.wp-full-overlay' ),
backBtn = section.container.find( '.customize-section-back' ),
sectionTitle = section.container.find( '.accordion-section-title' ).first(),
headerActionsHeight = $( '#customize-header-actions' ).height(),
resizeContentHeight, expand, position, scroll;
if ( to ) {
overlay.removeClass( 'section-open' );
content.css( 'height', 'auto' );
//section.container.removeClass( 'open' );
sectionTitle.attr( 'tabindex', '0' );
content.css( 'margin-top', '' );
container.scrollTop( 0 );
}
module.closeAllItems().closeRemoveDialogs();
content.slideToggle();
}
});//$.extend()
})( wp.customize , jQuery, _ );
//extends api.CZRDynModule
//globals widgetModuleLocalized, themeServerControlParams
var WidgetAreaConstructor = WidgetAreaConstructor || {};
( function ( api, $, _ ) {
$.extend( WidgetAreaConstructor, {
/////////////////////////////////////////
/// LISTEN TO SIDEBAR INSIGHTS FROM THE PREVIEW FRAME
/// REACT TO THEM
////////////////////////////////////////
listenToSidebarInsights : function() {
var module = this;
//VISIBILITY BASED ON THE SIDEBAR INSIGHTS
api.sidebar_insights('registered').callbacks.add( function( _registered_zones ) {
var _current_collection = _.clone( module.itemCollection() );
_.each( _current_collection, function( _model ) {
if ( ! module.getViewEl(_model.id).length )
return;
module.getViewEl(_model.id).css('display' , _.contains( _registered_zones, _model.id ) ? 'block' : 'none' );
});
});
//OPACITY SIDEBAR INSIGHTS BASED
api.sidebar_insights('inactives').callbacks.add( function( _inactives_zones ) {
var _current_collection = _.clone( module.itemCollection() );
_.each( _current_collection, function( _model ) {
if ( ! module.getViewEl(_model.id).length )
return;
if ( _.contains( _inactives_zones, _model.id ) ) {
module.getViewEl( _model.id ).addClass('inactive');
if ( ! module.getViewEl( _model.id ).find('.czr-inactive-alert').length ) {
module.getViewEl( _model.id ).find('.czr-item-title').append(
$('<span/>', {class : "czr-inactive-alert", html : " [ " + widgetModuleLocalized.i18n.inactiveWidgetZone + " ]" })
);
}
}
else {
module.getViewEl( _model.id ).removeClass('inactive');
if ( module.getViewEl( _model.id ).find('.czr-inactive-alert').length )
module.getViewEl( _model.id ).find('.czr-inactive-alert').remove();
}
});
});
//WIDGET SIDEBAR CREATION BASED ON SIDEBAR INSIGHTS
//react to a new register candidate(s) on preview refresh
api.sidebar_insights('candidates').callbacks.add( function(_candidates) {
if ( ! _.isArray(_candidates) )
return;
_.each( _candidates, function( _sidebar ) {
if ( ! _.isObject(_sidebar) )
return;
//add this widget sidebar and the related setting and control.
//Only if not added already
if ( api.section.has("sidebar-widgets-" +_sidebar.id ) )
return;
//access the registration method statically
module.addWidgetSidebar( {}, _sidebar );
//activate it if so
if ( _.has( api.sidebar_insights('actives')(), _sidebar.id ) && api.section.has("sidebar-widgets-" +_sidebar.id ) )
api.section( "sidebar-widgets-" +_sidebar.id ).activate();
});
});
}//listenToSidebarInsights()
});//$.extend()
})( wp.customize , jQuery, _ );
//extends api.CZRDynModule
//globals widgetModuleLocalized, themeServerControlParams
var WidgetAreaConstructor = WidgetAreaConstructor || {};
( function ( api, $, _ ) {
$.extend( WidgetAreaConstructor, {
/////////////////////////////////////////
/// OVERRIDEN METHODS
////////////////////////////////////////
//fired in toggleItemExpansion()
//has to be overridden for the widget zones control because this control is embedded directly in a panel and not in a section
//therefore the module to animate the scrollTop is not the section container but $('.wp-full-overlay-sidebar-content')
_adjustScrollExpandedBlock : function( $_block_el, adjust ) {
if ( ! $_block_el.length )
return;
var module = this,
_currentScrollTopVal = $('.wp-full-overlay-sidebar-content').scrollTop(),
_scrollDownVal,
_adjust = adjust || 90;
setTimeout( function() {
if ( ( $_block_el.offset().top + $_block_el.height() + _adjust ) > $(window.top).height() ) {
_scrollDownVal = $_block_el.offset().top + $_block_el.height() + _adjust - $(window.top).height();
$('.wp-full-overlay-sidebar-content').animate({
scrollTop: _currentScrollTopVal + _scrollDownVal
}, 600);
}
}, 50);
},
//overrides the parent class default model getter
//=> add a dynamic title
getDefaultItemModel : function( id ) {
var module = this,
_current_collection = module.itemCollection(),
_default = _.clone( module.defaultItemModel ),
_default_contexts = _default.contexts;
return $.extend( _default, {
title : 'Widget Zone ' + ( _.size(_current_collection)*1 + 1 )
//contexts : module._getMatchingContexts( _default_contexts )
});
},
_toggleLocationAlertExpansion : function( $view, to ) {
var $_alert_el = $view.find('.czr-location-alert');
if ( ! $_alert_el.length ) {
var _html = [
'<span>' + widgetModuleLocalized.i18n.locationWarning + '</span>',
api.CZR_Helpers.getDocSearchLink( widgetModuleLocalized.i18n.locationWarning ),
].join('');
$_alert_el = $('<div/>', {
class:'czr-location-alert',
html:_html,
style:"display:none"
});
$('select[data-czrtype="locations"]', $view ).closest('div').after($_alert_el);
}
$_alert_el.toggle( 'expanded' == to);
}
});//$.extend()
})( wp.customize , jQuery, _ );
//globals widgetModuleLocalized, themeServerControlParams
/*****************************************************************************
* CAPTURE PREVIEW INFORMATIONS ON REFRESH + REACT TO THEM
*****************************************************************************/
(function (api, $, _) {
//Data are sent by the preview frame when the panel has sent the 'sync' or even better 'active' event
api.bind( 'ready', function() {
//observe widget settings changes
api.previewer.bind('houston-widget-settings', function(data) {
//console.log('control panel => ALORS ON HOUSTON- SETTINGS ? => ', data );
//get the difference
var _candidates = _.filter( data.registeredSidebars, function( sb ) {
return ! _.findWhere( _wpCustomizeWidgetsSettings.registeredSidebars, { id: sb.id } );
});
var _inactives = _.filter( data.registeredSidebars, function( sb ) {
return ! _.has( data.renderedSidebars, sb.id );
});
_inactives = _.map( _inactives, function(obj) {
return obj.id;
});
var _registered = _.map( data.registeredSidebars, function(obj) {
return obj.id;
});
//stores and update the widget zone settings
api.czr_widgetZoneSettings = api.czr_widgetZoneSettings || new api.Value();//will store all widget zones data sent by preview as an observable object
api.czr_widgetZoneSettings.set( {
actives : data.renderedSidebars,
inactives : _inactives,
registered : _registered,
candidates : _candidates,
available_locations : data.availableWidgetLocations//built server side
} );
});
});//api.bind('ready')
})( wp.customize , jQuery, _ );//extends api.CZRDynModule
//globals widgetModuleLocalized, themeServerControlParams
var WidgetAreaConstructor = WidgetAreaConstructor || {};
( function ( api, $, _ ) {
//provides a description of each module
//=> will determine :
//1) how to initialize the module model. If not crud, then the initial item(s) model shall be provided
//2) which js template(s) to use : if crud, the module template shall include the add new and pre-item elements.
// , if crud, the item shall be removable
//3) how to render : if multi item, the item content is rendered when user click on edit button.
// If not multi item, the single item content is rendered as soon as the item wrapper is rendered.
//4) some DOM behaviour. For example, a multi item shall be sortable.
api.czrModuleMap = api.czrModuleMap || {};
$.extend( api.czrModuleMap, {
czr_widget_areas_module : {
mthds : WidgetAreaConstructor,
crud : true,
multi_item : false,
name : 'Widget Areas',
has_mod_opt : false,
ready_on_section_expanded : true,
//defaultItemModel : {}
}
});
})( wp.customize , jQuery, _ );
@@ -0,0 +1,165 @@
//extends api.CZRDynModule
//globals widgetModuleLocalized, themeServerControlParams
var WidgetAreaConstructor = WidgetAreaConstructor || {};
( function ( api, $, _ ) {
$.extend( WidgetAreaConstructor, {
initialize: function( id, constructorOptions ) {
var module = this;
api.CZRDynModule.prototype.initialize.call( this, id, constructorOptions );
//extend the module with new template Selectors
$.extend( module, {
itemPreAddEl : 'czr-module-widgets-pre-add-view-content',
itemInputList : 'czr-module-widgets-item-input-list',
ruItemPart : 'czr-module-widgets-ru-item-part'
} );
//EXTEND THE DEFAULT CONSTRUCTORS FOR INPUT
module.inputConstructor = api.CZRInput.extend( module.CZRWZonesInputMths || {} );
//EXTEND THE DEFAULT CONSTRUCTORS FOR MONOMODEL
module.itemConstructor = api.CZRItem.extend( module.CZRWZonesItemConstructor || {} );
module.serverParams = widgetModuleLocalized || {};
//add a shortcut to the server side json properties
module.contexts = _.has( module.serverParams , 'sidebar_contexts') ? module.serverParams.sidebar_contexts : {};
//context match map
module.context_match_map = {
is_404 : '404',
is_category : 'archive-category',
is_home : 'home',
is_page : 'page',
is_search : 'search',
is_single : 'single'
};
module.locations = _.has( module.serverParams , 'sidebar_locations') ? module.serverParams.sidebar_locations : {};
//declares a default model
module.defaultItemModel = {
id : '',
title : widgetModuleLocalized.i18n.widgetZone,
contexts : _.without( _.keys(module.contexts), '_all_' ),//the server list of contexts is an object, we only need the keys, whitout _all_
locations : [ module.serverParams.defaultWidgetLocation ],
description : ''
};
//overrides the default success message
this.itemAddedMessage = widgetModuleLocalized.i18n.widgetZoneAdded;
//Observe and react to sidebar insights from the preview frame
// SIDEBAR INSIGHTS => stores and observes the sidebars and widgets settings sent by the preview */
if ( ! _.has( api, 'sidebar_insights' ) ) {
api.sidebar_insights = new api.Values();
api.sidebar_insights.create('candidates');//will store the sidebar candidates on preview refresh
api.sidebar_insights.create('actives');//will record the refreshed active list of active sidebars sent from the preview
api.sidebar_insights.create('inactives');
api.sidebar_insights.create('registered');
api.sidebar_insights.create('available_locations');
}
this.listenToSidebarInsights();
//React on 'houston-widget-settings'
//actives : data.renderedSidebars,
// inactives : _inactives,
// registered : _registered,
// candidates : _candidates,
// available_locations : data.availableWidgetLocations//built server side
api.czr_widgetZoneSettings = api.czr_widgetZoneSettings || new api.Value();
api.czr_widgetZoneSettings.bind( function( updated_data_sent_from_preview , from ) {
//console.log('REACT ON czr_widgetZoneSettings', updated_data_sent_from_preview , from );
module.isReady.then( function() {
_.each( updated_data_sent_from_preview, function( _data, _key ) {
api.sidebar_insights( _key ).set( _data );
});
});
});
//AVAILABLE LOCATIONS FOR THE PRE MODEL
//1) add an observable value to module.preItem to handle the alert visibility
module.preItem_location_alert_view_state = new api.Value( 'closed');
//2) add state listeners
module.preItem_location_alert_view_state.callbacks.add( function( to, from ) {
module._toggleLocationAlertExpansion( module.container, to );
});
//REACT ON ADD / REMOVE ITEMS
module.bind( 'item-added', function( model ) {
module.addWidgetSidebar( model );
});
module.bind( 'pre_item_api_remove' , function(model) {
module.removeWidgetSidebar( model );
});
//records the top margin value of the widgets panel on each expansion
var fixTopMargin = new api.Values();
fixTopMargin.create('fixed_for_current_session');
fixTopMargin.create('value');
api.section(module.serverParams.dynWidgetSection).fixTopMargin = fixTopMargin;
api.section(module.serverParams.dynWidgetSection).fixTopMargin('fixed_for_current_session').set(false);
//setup reactions on widget section expansion
//change the expanded behaviour for the widget zone section
//api.section(module.serverParams.dynWidgetSection).expanded.callbacks.add( function() { return module.widgetSectionReact.apply(module, arguments ); } );
//bind actions on widget panel expansion and widget zone section expansion
//Fire the module
api.panel('widgets').expanded.callbacks.add( function(to, from) {
module.widgetPanelReact();//setup some visual adjustments, must be ran each time panel is closed or expanded
//Fire the module if not done already
if ( 'resolved' == module.isReady.state() )
return;
module.ready();
});
},//initialize
//When the control is embedded on the page, this method is fired in api.CZRBaseModuleControl:ready()
//=> right after the module is instantiated.
//VERIFIED
ready : function() {
var module = this;
api.CZRDynModule.prototype.ready.call( module );
//add state listener on pre Item view
module.preItemExpanded.callbacks.add( function( to, from ) {
if ( ! to )
return;
//refresh the location list
module.preItem.czr_Input( 'locations' )._setupLocationSelect( true );//true for refresh
//refresh the location alert message
module.preItem.czr_Input( 'locations' ).mayBeDisplayModelAlert();
});
},
//overrides parent method
//adds the default widget zones in the items
//VERIFIED
initializeModuleModel : function( constructorOptions ) {
var module = this, dfd = $.Deferred();
constructorOptions.items = _.union( _.has( module.serverParams, 'default_zones' ) ? module.serverParams.default_zones : [], constructorOptions.items );
return dfd.resolve( constructorOptions ).promise();
},
});//$.extend()
})( wp.customize , jQuery, _ );
@@ -0,0 +1,130 @@
//extends api.CZRDynModule
//globals widgetModuleLocalized, themeServerControlParams
var WidgetAreaConstructor = WidgetAreaConstructor || {};
( function ( api, $, _ ) {
$.extend( WidgetAreaConstructor, {
CZRWZonesInputMths : {
ready : function() {
var input = this;
input.bind('locations:changed', function(){
input.mayBeDisplayModelAlert();
});
api.CZRInput.prototype.ready.call( input);
},
//////////////////////////////////////////////////
///SETUP SELECTS
//////////////////////////////////////////////////
//setup select on view_rendered|item_content_event_map
setupSelect : function() {
var input = this;
if ( 'locations' == this.id )
this._setupLocationSelect();
if ( 'contexts' == this.id )
this._setupContextSelect();
},
//helper
_setupContextSelect : function() {
var input = this,
input_contexts = input(),
item = input.input_parent,
module = input.module;
//generates the contexts options
_.each( module.contexts, function( title, key ) {
var _attributes = {
value : key,
html: title
};
if ( key == input_contexts || _.contains( input_contexts, key ) )
$.extend( _attributes, { selected : "selected" } );
$( 'select[data-czrtype="contexts"]', input.container ).append( $('<option>', _attributes) );
});
//fire czrSelect2
$( 'select[data-czrtype="contexts"]', input.container ).czrSelect2();
},
//helper
//the refresh param is a bool
_setupLocationSelect : function(refresh ) {
var input = this,
input_locations = input(),
item = input.input_parent,
module = input.module,
available_locs = api.sidebar_insights('available_locations')();
//console.log('_setupLocationSelect', input(), module.locations );
//generates the locations options
//append them if not set yet
if ( ! $( 'select[data-czrtype="locations"]', input.container ).children().length ) {
_.each( module.locations, function( title, key ) {
var _attributes = {
value : key,
html: title
};
if ( key == input_locations || _.contains( input_locations, key ) )
$.extend( _attributes, { selected : "selected" } );
$( 'select[data-czrtype="locations"]', input.container ).append( $('<option>', _attributes) );
});
}//if
function setAvailability( state ) {
if (! state.id) { return state.text; }
if ( _.contains(available_locs, state.element.value) ) { return state.text; }
var $state = $(
'<span class="czr-unavailable-location fas fa-ban" title="' + widgetModuleLocalized.i18n.unavailableLocation + '">&nbsp;&nbsp;' + state.text + '</span>'
);
return $state;
}
if ( refresh ) {
$( 'select[data-czrtype="locations"]', input.container ).czrSelect2( 'destroy' );
}
//fire czrSelect2
$( 'select[data-czrtype="locations"]', input.container ).czrSelect2( {
templateResult: setAvailability,
templateSelection: setAvailability
});
},
//fired on view event map : 'locations:changed'
//@param obj { dom_el: $() , model : {} )
mayBeDisplayModelAlert : function() {
var input = this,
item = input.input_parent,
module = input.module;
//check if we are in the pre Item case => if so, the locations might be empty
if ( ! _.has( item(), 'locations') || _.isEmpty( item().locations ) )
return;
var _selected_locations = $('select[data-czrtype="locations"]', input.container ).val(),
available_locs = api.sidebar_insights('available_locations')(),
_unavailable = _.filter( _selected_locations, function( loc ) {
return ! _.contains(available_locs, loc);
});
//check if we are in the pre Item case => if so, the id is empty
if ( ! _.has( item(), 'id' ) || _.isEmpty( item().id ) ) {
module.preItem_location_alert_view_state.set( ! _.isEmpty( _unavailable ) ? 'expanded' : 'closed' );
} else {
item.czr_itemLocationAlert.set( ! _.isEmpty( _unavailable ) ? 'expanded' : 'closed' );
}
}
},//CZRWZonesInputMths
});//$.extend()
})( wp.customize , jQuery, _ );
@@ -0,0 +1,239 @@
//extends api.CZRDynModule
//globals widgetModuleLocalized, themeServerControlParams
var WidgetAreaConstructor = WidgetAreaConstructor || {};
( function ( api, $, _ ) {
$.extend( WidgetAreaConstructor, {
CZRWZonesItemConstructor : {
initialize : function( id, options ) {
var item = this,
module = item.module;
//Add some observable values for this item
item.czr_itemLocationAlert = new api.Value();
api.CZRItem.prototype.initialize.call( item, null, options );
// filter the params of the ajax query used to get the item wrapper template
// because we need a ru ( not a read update delete ) template for builtins widget zones
// requestParams = {
// tmpl : 'rud-item-part',
// module_type: 'all_modules',
// nonce: api.settings.nonce.save//<= do we need to set a specific nonce to fetch the tmpls ?
// };
// this has been introduced in March 2018, after the introduction of the tmpl ajax fetching
// it does the same job that the overriden getTemplateEl() was doing.
// This filter is declared in item::renderItemWrapper()
item.bind( 'item-wrapper-tmpl-params-before-fetching', function( requestParams ) {
//force view-content type to ru-item-part if the model is a built-in (primary, secondary, footer-1, ...)
//=> user can't delete a built-in model.
requestParams.tmpl = ( _.has( item(), 'is_builtin' ) && item().is_builtin ) ? 'ruItemPart' : requestParams.tmpl;
return requestParams;
});
},
//extend parent setupview
itemWrapperViewSetup : function() {
var item = this,
module = item.module;
api.CZRItem.prototype.itemWrapperViewSetup.call(item);
/// ALERT FOR NOT AVAILABLE LOCATION
item.czr_itemLocationAlert.set('closed');
//add a state listener on expansion change
item.czr_itemLocationAlert.callbacks.add( function( to, from ) {
module._toggleLocationAlertExpansion( item.container , to );
});
//update item title
item.writeSubtitleInfos(item());
//this is fired just after the itemWrapperViewSetupApiListeners
//=> add a callback to refresh the availability status of the locations in the select location picker
//add a state listener on expansion change
item.viewState.callbacks.add( function( to, from ) {
if ( -1 == to.indexOf('expanded') )//can take the expanded_noscroll value !
return;
//don't try to invoke the input instances before the content is actually rendered
//=> there might be cases when the content rendering is debounced...
item.bind('contentRendered', function() {
//refresh the location list
item.czr_Input('locations')._setupLocationSelect( true );//true for refresh
//refresh the location alert message
item.czr_Input('locations').mayBeDisplayModelAlert();
});
});
},
//extend parent listener
itemReact : function(to, from) {
var item = this;
api.CZRItem.prototype.itemReact.call(item, to, from);
item.writeSubtitleInfos(to);
item.updateSectionTitle(to).setModelUpdateTimer();
},
//Fired in setupItemListeners. Reacts to model change.
//Write html informations under the title : location(s) and context(s)
writeSubtitleInfos : function(model) {
var item = this,
module = item.module,
_model = _.clone( model || item() ),
_locations = [],
_contexts = [],
_html = '';
if ( ! item.container.length )
return this;
//generate the locations and the contexts text from the json data if exists
_model.locations =_.isString(_model.locations) ? [_model.locations] : _model.locations;
_.each( _model.locations, function( loc ) {
if ( _.has( module.locations , loc ) )
_locations.push(module.locations[loc]);
else
_locations.push(loc);
}
);
//build the context list
_model.contexts =_.isString(_model.contexts) ? [_model.contexts] : _model.contexts;
//all contexts cases ?
if ( item._hasModelAllContexts( model ) ) {
_contexts.push(module.contexts._all_);
} else {
_.each( _model.contexts, function( con ) {
if ( _.has( module.contexts , con ) )
_contexts.push(module.contexts[con]);
else
_contexts.push(con);
}
);
}
//Translated strings
var _locationText = widgetModuleLocalized.i18n.locations,
_contextText = widgetModuleLocalized.i18n.contexts,
_notsetText = widgetModuleLocalized.i18n.notset;
_locations = _.isEmpty( _locations ) ? '<span style="font-weight: bold;">' + _notsetText + '</span>' : _locations.join(', ');
_contexts = _.isEmpty( _contexts ) ? '<span style="font-weight: bold;">' + _notsetText + '</span>' : _contexts.join(', ');
//write the description if builtin
//else, write the dynamic location
// if ( _.has(_model, 'description') && _.has(_model, 'is_builtin') )
// _html = _model.description + ' <strong>|</strong> <u>Contexts</u> : ' + _contexts;
// else
_html = '<u>' + _locationText + '</u> : ' + _locations + ' <strong>|</strong> <u>' + _contextText + '</u> : ' + _contexts;
if ( ! $('.czr-zone-infos', item.container ).length ) {
var $_zone_infos = $('<div/>', {
class : [ 'czr-zone-infos' , module.control.css_attr.item_sort_handle ].join(' '),
html : _html
});
$( '.' + module.control.css_attr.item_btns, item.container ).after($_zone_infos);
} else {
$('.czr-zone-infos', item.container ).html(_html);
}
return this;
},//writeSubtitleInfos
////Fired in setupItemListeners
updateSectionTitle : function(model) {
var _sidebar_id = 'sidebar-widgets-' + model.id,
_new_title = model.title;
//does this section exists ?
if ( ! api.section.has(_sidebar_id) )
return this;
//update the section title
$('.accordion-section-title', api.section(_sidebar_id).container ).text(_new_title);
//update the top title ( visible when inside the expanded section )
$('.customize-section-title h3', api.section(_sidebar_id).container ).html(
'<span class="customize-action">' + api.section(_sidebar_id).params.customizeAction + '</span>' + _new_title
);
// $('.customize-section-title h3', api.section(_sidebar_id).container )
// .append('<span>', {
// class: 'customize-section-back',
// html: api.section(_sidebar_id).params.customizeAction
// } )
// .append(_new_title);
//remove and re-instanciate
//=> works for the section but the controls are not activated anymore.
//Should be easy to fix but useless to go further here. Jquery does the job.
// var _params = _.clone( api.section(_sidebar_id).params );
// _params.title = _new_title;
// api.section(_sidebar_id).container.remove();
// api.section.remove(_sidebar_id);
// api.section.add( _sidebar_id, new api.sectionConstructor[_params.type]( _params.id ,{ params : _params } ) );
return this;
},
//fired on model_update
//Don't hammer the preview with too many refreshs
//2 seconds delay
setModelUpdateTimer : function() {
var item = this,
module = item.module;
clearTimeout( $.data(this, 'modelUpdateTimer') );
$.data(
this,
'modelUpdateTimer',
_.delay( function() {
//refresh preview
api.previewer.refresh();
} , 1000)
);//$.data
},
//@return bool
//takes the model unique id
_hasModelAllContexts : function( model ) {
var item = this,
module = item.module,
moduleContexts = _.keys(module.contexts);
model = model || this();
if ( ! _.has(model, 'contexts') )
return;
if ( _.contains( model.contexts, '_all_') )
return true;
//case when model does not have _all_ but all the others
return _.isEmpty( _.difference( _.without(moduleContexts, '_all_') , model.contexts ) );
},
//@param contexts = array of contexts
//api.czr_wpQueryInfos is refreshed on each preview refresh
_getMatchingContexts : function( defaults ) {
var module = this,
_current = api.czr_wpQueryInfos().conditional_tags || {},
_matched = _.filter( module.context_match_map, function( hu, wp ) { return true === _current[wp]; } );
return _.isEmpty( _matched ) ? defaults : _matched;
}
},//CZRWZonesItemConstructor
});//$.extend()
})( wp.customize , jQuery, _ );
@@ -0,0 +1,173 @@
//extends api.CZRDynModule
//globals widgetModuleLocalized, themeServerControlParams
var WidgetAreaConstructor = WidgetAreaConstructor || {};
( function ( api, $, _ ) {
$.extend( WidgetAreaConstructor, {
/////////////////////////////////////////
/// ADD / REMOVE WIDGET ZONES
////////////////////////////////////////
//fired on model_added_by_user
//
//can also be called statically when a dynamic sidebar is added in the preview
//in this case the parameter are the sidebar data with id and name
addWidgetSidebar : function( model, sidebar_data ) {
if ( ! _.isObject(model) && _.isEmpty(sidebar_data) ) {
throw new Error('No valid input were provided to add a new Widget Zone.');
}
//ADD the new sidebar to the existing collection
//Clone the serverControlParams.defaultWidgetSidebar sidebar
var module = this,
_model = ! _.isEmpty(model) ? _.clone(model) : sidebar_data,
_new_sidebar = _.isEmpty(model) ? sidebar_data : $.extend(
_.clone( _.findWhere( api.Widgets.data.registeredSidebars, { id: module.serverParams.defaultWidgetSidebar } ) ),
{
name : _model.title,
id : _model.id
}
);
//Add it to the backbone collection
api.Widgets.registeredSidebars.add( _new_sidebar );
//test if added:
//api.Widgets.registeredSidebars('czr_sidebars_8');
//ADD the sidebar section
var _params = $.extend(
_.clone( api.section( "sidebar-widgets-" + module.serverParams.defaultWidgetSidebar ).params ),
{
id : "sidebar-widgets-" + _model.id,
instanceNumber: _.max(api.settings.sections, function(sec){ return sec.instanceNumber; }).instanceNumber + 1,
sidebarId: _new_sidebar.id,
title: _new_sidebar.name,
description : 'undefined' != typeof(sidebar_data) ? sidebar_data.description : api.section( "sidebar-widgets-" + module.serverParams.defaultWidgetSidebar ).params.description,
//always set the new priority to the maximum + 1 ( module.serverParams.dynWidgetSection is excluded from this calculation because it must always be at the bottom )
priority: _.max( _.omit( api.settings.sections, module.serverParams.dynWidgetSection), function(sec){ return sec.instanceNumber; }).priority + 1,
}
);
api.section.add( _params.id, new api.sectionConstructor[ _params.type ]( _params.id ,{ params : _params } ) );
//add it to the static collection of settings
api.settings.sections[ _params.id ] = _params.id;
//ADD A SETTING
//Clone the module.serverParams.defaultWidgetSidebar sidebar widget area setting
var _new_set_id = 'sidebars_widgets['+_model.id+']',
_new_set = $.extend(
_.clone( api.settings.settings['sidebars_widgets[' + module.serverParams.defaultWidgetSidebar + ']'] ),
{
value:[]
}
);
//add it to the static collection of settings
api.settings.settings[ _new_set_id ] = _new_set;
//instanciate it
api.create( _new_set_id, _new_set_id, _new_set.value, {
transport: _new_set.transport,
previewer: api.previewer,
dirty: false
} );
//ADD A CONTROL
var _cloned_control = $.extend(
_.clone( api.settings.controls['sidebars_widgets[' + module.serverParams.defaultWidgetSidebar + ']'] ),
{
settings : { default : _new_set_id }
}),
_new_control = {};
//replace serverControlParams.defaultWidgetSidebar by the new sidebar id
_.each( _cloned_control, function( param, key ) {
if ( 'string' == typeof(param) ) {
param = param.replace( module.serverParams.defaultWidgetSidebar , _model.id );
}
_new_control[key] = param;
});
//set the instance number (no sure if needed)
_new_control.instanceNumber = _.max(api.settings.controls, function(con){ return con.instanceNumber; }).instanceNumber + 1;
//add it to the static collection of controls
api.settings.controls[_new_set_id] = _new_control;
//instanciate it
api.control.add( _new_set_id, new api.controlConstructor[ _new_control.type ]( _new_set_id, {
params: _new_control,
previewer: api.previewer
} ) );
//say it to the control container
//only if we are in an instanciated object => because this method can be accessed statically
if ( _.has(this, 'container') )
this.container.trigger( 'widget_zone_created', { model : _model, section_id : "sidebar-widgets-" + _model.id , setting_id : _new_set_id });
},//addWidgetSidebar
//fired on "after_modelRemoved"
removeWidgetSidebar : function( model ) {
var module = this;
if ( ! _.isObject(model) || _.isEmpty(model) ) {
throw new Error('No valid data were provided to remove a Widget Zone.');
}
//Remove this sidebar from the backbone collection
api.Widgets.registeredSidebars.remove( model.id );
//remove the section from the api values and the DOM if exists
if ( api.section.has("sidebar-widgets-" + model.id) ) {
//Remove the section container from the DOM
api.section("sidebar-widgets-" + model.id).container.remove();
//Remove the sidebar section from the api
api.section.remove( "sidebar-widgets-" + model.id );
//Remove this section from the static collection
delete api.settings.sections[ "sidebar-widgets-" + model.id ];
}
//remove the setting from the api if exists
if ( api.has('sidebars_widgets['+model.id+']') ) {
//Remove this setting from the api
api.remove( 'sidebars_widgets['+model.id+']' );
//Remove this setting from the static collection
delete api.settings.settings['sidebars_widgets['+model.id+']'];
}
//remove the widget control of this sidebar from the api and the DOM if exists
if ( api.control.has('sidebars_widgets['+model.id+']') ) {
//Remove the control container from the DOM
api.control( 'sidebars_widgets['+model.id+']' ).container.remove();
//Remove this control from the api
api.control.remove( 'sidebars_widgets['+model.id+']' );
//Remove it to the static collection of controls
delete api.settings.controls['sidebars_widgets['+model.id+']'];
}
//refresh
var _refresh = function() {
api.previewer.refresh();
};
_refresh = _.debounce( _refresh, 500 );
$.when( _refresh() ).done( function() {
//say it
module.trigger( 'widget_zone_removed',
{
model : model,
section_id : "sidebar-widgets-" + model.id ,
setting_id : 'sidebars_widgets['+model.id+']'
}
);
});
},
});//$.extend()
})( wp.customize , jQuery, _ );
@@ -0,0 +1,66 @@
//extends api.CZRDynModule
//globals widgetModuleLocalized, themeServerControlParams
var WidgetAreaConstructor = WidgetAreaConstructor || {};
( function ( api, $, _ ) {
$.extend( WidgetAreaConstructor, {
/////////////////////////////////////////
/// SET EXPANSION CALLBACKS FOR WIDGET PANEL AND WIDGET ZONE CREATION SECTION
////////////////////////////////////////
//cb of : api.panel('widgets').expanded.callbacks.add
widgetPanelReact : function() {
var module = this;
//will be used for adjustments
var _top_margin = api.panel('widgets').container.find( '.control-panel-content' ).css('margin-top');
api.section(module.serverParams.dynWidgetSection).fixTopMargin('value').set( _top_margin );
var _section_content = api.section(module.serverParams.dynWidgetSection).container.find( '.accordion-section-content' ),
_panel_content = api.panel('widgets').container.find( '.control-panel-content' ),
_set_margins = function() {
_section_content.css( 'margin-top', '' );
_panel_content.css('margin-top', api.section(module.serverParams.dynWidgetSection).fixTopMargin('value')() );
};
// Fix the top margin after reflow.
api.bind( 'pane-contents-reflowed', _.debounce( function() {
_set_margins();
}, 150 ) );
//Close all views on widget panel expansion/clos
module.closeAllItems().closeRemoveDialogs();
//Close preItem dialog box if exists
if ( _.has( module, 'preItemExpanded' ) )
module.preItemExpanded.set(false);
},//widgetPanelReact()
//cb of api.section(module.serverParams.dynWidgetSection).expanded.callbacks
widgetSectionReact : function( to, from ) {
var module = this,
section = api.section(module.serverParams.dynWidgetSection),
container = section.container.closest( '.wp-full-overlay-sidebar-content' ),
content = section.container.find( '.accordion-section-content' ),
overlay = section.container.closest( '.wp-full-overlay' ),
backBtn = section.container.find( '.customize-section-back' ),
sectionTitle = section.container.find( '.accordion-section-title' ).first(),
headerActionsHeight = $( '#customize-header-actions' ).height(),
resizeContentHeight, expand, position, scroll;
if ( to ) {
overlay.removeClass( 'section-open' );
content.css( 'height', 'auto' );
//section.container.removeClass( 'open' );
sectionTitle.attr( 'tabindex', '0' );
content.css( 'margin-top', '' );
container.scrollTop( 0 );
}
module.closeAllItems().closeRemoveDialogs();
content.slideToggle();
}
});//$.extend()
})( wp.customize , jQuery, _ );
@@ -0,0 +1,72 @@
//extends api.CZRDynModule
//globals widgetModuleLocalized, themeServerControlParams
var WidgetAreaConstructor = WidgetAreaConstructor || {};
( function ( api, $, _ ) {
$.extend( WidgetAreaConstructor, {
/////////////////////////////////////////
/// LISTEN TO SIDEBAR INSIGHTS FROM THE PREVIEW FRAME
/// REACT TO THEM
////////////////////////////////////////
listenToSidebarInsights : function() {
var module = this;
//VISIBILITY BASED ON THE SIDEBAR INSIGHTS
api.sidebar_insights('registered').callbacks.add( function( _registered_zones ) {
var _current_collection = _.clone( module.itemCollection() );
_.each( _current_collection, function( _model ) {
if ( ! module.getViewEl(_model.id).length )
return;
module.getViewEl(_model.id).css('display' , _.contains( _registered_zones, _model.id ) ? 'block' : 'none' );
});
});
//OPACITY SIDEBAR INSIGHTS BASED
api.sidebar_insights('inactives').callbacks.add( function( _inactives_zones ) {
var _current_collection = _.clone( module.itemCollection() );
_.each( _current_collection, function( _model ) {
if ( ! module.getViewEl(_model.id).length )
return;
if ( _.contains( _inactives_zones, _model.id ) ) {
module.getViewEl( _model.id ).addClass('inactive');
if ( ! module.getViewEl( _model.id ).find('.czr-inactive-alert').length ) {
module.getViewEl( _model.id ).find('.czr-item-title').append(
$('<span/>', {class : "czr-inactive-alert", html : " [ " + widgetModuleLocalized.i18n.inactiveWidgetZone + " ]" })
);
}
}
else {
module.getViewEl( _model.id ).removeClass('inactive');
if ( module.getViewEl( _model.id ).find('.czr-inactive-alert').length )
module.getViewEl( _model.id ).find('.czr-inactive-alert').remove();
}
});
});
//WIDGET SIDEBAR CREATION BASED ON SIDEBAR INSIGHTS
//react to a new register candidate(s) on preview refresh
api.sidebar_insights('candidates').callbacks.add( function(_candidates) {
if ( ! _.isArray(_candidates) )
return;
_.each( _candidates, function( _sidebar ) {
if ( ! _.isObject(_sidebar) )
return;
//add this widget sidebar and the related setting and control.
//Only if not added already
if ( api.section.has("sidebar-widgets-" +_sidebar.id ) )
return;
//access the registration method statically
module.addWidgetSidebar( {}, _sidebar );
//activate it if so
if ( _.has( api.sidebar_insights('actives')(), _sidebar.id ) && api.section.has("sidebar-widgets-" +_sidebar.id ) )
api.section( "sidebar-widgets-" +_sidebar.id ).activate();
});
});
}//listenToSidebarInsights()
});//$.extend()
})( wp.customize , jQuery, _ );
@@ -0,0 +1,66 @@
//extends api.CZRDynModule
//globals widgetModuleLocalized, themeServerControlParams
var WidgetAreaConstructor = WidgetAreaConstructor || {};
( function ( api, $, _ ) {
$.extend( WidgetAreaConstructor, {
/////////////////////////////////////////
/// OVERRIDEN METHODS
////////////////////////////////////////
//fired in toggleItemExpansion()
//has to be overridden for the widget zones control because this control is embedded directly in a panel and not in a section
//therefore the module to animate the scrollTop is not the section container but $('.wp-full-overlay-sidebar-content')
_adjustScrollExpandedBlock : function( $_block_el, adjust ) {
if ( ! $_block_el.length )
return;
var module = this,
_currentScrollTopVal = $('.wp-full-overlay-sidebar-content').scrollTop(),
_scrollDownVal,
_adjust = adjust || 90;
setTimeout( function() {
if ( ( $_block_el.offset().top + $_block_el.height() + _adjust ) > $(window.top).height() ) {
_scrollDownVal = $_block_el.offset().top + $_block_el.height() + _adjust - $(window.top).height();
$('.wp-full-overlay-sidebar-content').animate({
scrollTop: _currentScrollTopVal + _scrollDownVal
}, 600);
}
}, 50);
},
//overrides the parent class default model getter
//=> add a dynamic title
getDefaultItemModel : function( id ) {
var module = this,
_current_collection = module.itemCollection(),
_default = _.clone( module.defaultItemModel ),
_default_contexts = _default.contexts;
return $.extend( _default, {
title : 'Widget Zone ' + ( _.size(_current_collection)*1 + 1 )
//contexts : module._getMatchingContexts( _default_contexts )
});
},
_toggleLocationAlertExpansion : function( $view, to ) {
var $_alert_el = $view.find('.czr-location-alert');
if ( ! $_alert_el.length ) {
var _html = [
'<span>' + widgetModuleLocalized.i18n.locationWarning + '</span>',
api.CZR_Helpers.getDocSearchLink( widgetModuleLocalized.i18n.locationWarning ),
].join('');
$_alert_el = $('<div/>', {
class:'czr-location-alert',
html:_html,
style:"display:none"
});
$('select[data-czrtype="locations"]', $view ).closest('div').after($_alert_el);
}
$_alert_el.toggle( 'expanded' == to);
}
});//$.extend()
})( wp.customize , jQuery, _ );
@@ -0,0 +1,41 @@
//globals widgetModuleLocalized, themeServerControlParams
/*****************************************************************************
* CAPTURE PREVIEW INFORMATIONS ON REFRESH + REACT TO THEM
*****************************************************************************/
(function (api, $, _) {
//Data are sent by the preview frame when the panel has sent the 'sync' or even better 'active' event
api.bind( 'ready', function() {
//observe widget settings changes
api.previewer.bind('houston-widget-settings', function(data) {
//console.log('control panel => ALORS ON HOUSTON- SETTINGS ? => ', data );
//get the difference
var _candidates = _.filter( data.registeredSidebars, function( sb ) {
return ! _.findWhere( _wpCustomizeWidgetsSettings.registeredSidebars, { id: sb.id } );
});
var _inactives = _.filter( data.registeredSidebars, function( sb ) {
return ! _.has( data.renderedSidebars, sb.id );
});
_inactives = _.map( _inactives, function(obj) {
return obj.id;
});
var _registered = _.map( data.registeredSidebars, function(obj) {
return obj.id;
});
//stores and update the widget zone settings
api.czr_widgetZoneSettings = api.czr_widgetZoneSettings || new api.Value();//will store all widget zones data sent by preview as an observable object
api.czr_widgetZoneSettings.set( {
actives : data.renderedSidebars,
inactives : _inactives,
registered : _registered,
candidates : _candidates,
available_locations : data.availableWidgetLocations//built server side
} );
});
});//api.bind('ready')
})( wp.customize , jQuery, _ );
@@ -0,0 +1,25 @@
//extends api.CZRDynModule
//globals widgetModuleLocalized, themeServerControlParams
var WidgetAreaConstructor = WidgetAreaConstructor || {};
( function ( api, $, _ ) {
//provides a description of each module
//=> will determine :
//1) how to initialize the module model. If not crud, then the initial item(s) model shall be provided
//2) which js template(s) to use : if crud, the module template shall include the add new and pre-item elements.
// , if crud, the item shall be removable
//3) how to render : if multi item, the item content is rendered when user click on edit button.
// If not multi item, the single item content is rendered as soon as the item wrapper is rendered.
//4) some DOM behaviour. For example, a multi item shall be sortable.
api.czrModuleMap = api.czrModuleMap || {};
$.extend( api.czrModuleMap, {
czr_widget_areas_module : {
mthds : WidgetAreaConstructor,
crud : true,
multi_item : false,
name : 'Widget Areas',
has_mod_opt : false,
ready_on_section_expanded : true,
//defaultItemModel : {}
}
});
})( wp.customize , jQuery, _ );
@@ -0,0 +1,269 @@
<?php
function hu_register_widget_zones_module( $args ) {
$defaults = array(
'setting_id' => '',
'base_url_path' => '',//PC_AC_BASE_URL/inc/czr-modules/social-links/
'version' => '',
'option_value' => array(), //<= will be used for the dynamic registration
'setting' => array(),
'control' => array(),
'section' => array(), //array( 'id' => '', 'label' => '' ),
'sanitize_callback' => '',
'validate_callback' => ''
);
$args = wp_parse_args( $args, $defaults );
if ( ! isset( $GLOBALS['czr_base_fmk_namespace'] ) ) {
error_log( __FUNCTION__ . ' => global czr_base_fmk not set' );
return;
}
$czrnamespace = $GLOBALS['czr_base_fmk_namespace'];
//czr_fn\czr_register_dynamic_module
$CZR_Fmk_Base_fn = $czrnamespace . 'CZR_Fmk_Base';
if ( ! function_exists( $CZR_Fmk_Base_fn) ) {
error_log( __FUNCTION__ . ' => Namespace problem => ' . $CZR_Fmk_Base_fn );
return;
}
$CZR_Fmk_Base_fn() -> czr_pre_register_dynamic_setting( array(
'setting_id' => $args['setting_id'],
'module_type' => 'czr_widget_areas_module',
'option_value' => ! is_array( $args['option_value'] ) ? array() : $args['option_value'],
'setting' => $args['setting'],
'section' => $args['section'],
'control' => $args['control']
));
// czr_fn\czr_register_dynamic_module()
$CZR_Fmk_Base_fn() -> czr_pre_register_dynamic_module( array(
'dynamic_registration' => true,
'module_type' => 'czr_widget_areas_module',
// 'sanitize_callback' => 'hu_sanitize_callback__czr_social_module',
// 'validate_callback' => 'hu_validate_callback__czr_social_module',
'customizer_assets' => array(
'control_js' => array(
// handle + params for wp_enqueue_script()
// @see https://developer.wordpress.org/reference/functions/wp_enqueue_script/
'czr-widget-areas-module' => array(
'src' => sprintf(
'%1$s/assets/js/%2$s',
$args['base_url_path'],
'_2_6_widget_areas_module.js'
),
'deps' => array('customize-controls' , 'jquery', 'underscore'),
'ver' => ( defined('WP_DEBUG') && true === WP_DEBUG ) ? time() : $args['version'],
'in_footer' => true
)
),
'localized_control_js' => array(
'deps' => 'czr-customizer-fmk',
'global_var_name' => 'widgetModuleLocalized',
'params' => array(
//Widget Area Module
'dynWidgetSection' => HU_DYN_WIDGETS_SECTION,
'defaultWidgetSidebar' => 'primary',//the one that will be cloned. Specific to each themes
'defaultWidgetLocation' => 's1',//Specific to each themes
'sidebar_contexts' => hu_get_contexts_list(),
'sidebar_locations' => hu_get_widget_el_locations(),
'i18n' => array(
'widgetZone' => __('Widget Zone', 'hueman'),
'widgetZoneAdded' => __('New Widget Zone created ! Scroll down to edit it.', 'hueman'),
'inactiveWidgetZone' => __('Inactive in current context/location', 'hueman'),
'unavailableLocation' => __('Unavailable location. Some settings must be changed.', 'hueman'),
'locationWarning' => __('A selected location is not available with the current settings.', 'hueman'),
'locations' => __('Location(s)', 'hueman'),
'contexts' => __('Context(s)', 'hueman'),
'notset' => __('Not set', 'hueman'),
)
//'default_zones' => hu_get_widget_el_default_zones()
)
)
),
'tmpl' => array()//tmpl
));
}//hu_register_widget_zones_module()
/* ------------------------------------------------------------------------- *
* Various Helpers
/* ------------------------------------------------------------------------- */
function hu_get_widget_el_locations() {
$_default_locations = hu_get_builtin_widget_zones_location();
//generates the locations for json
$locations = array();
foreach ($_default_locations as $_id => $data ) {
$_k = key($data);
$locations[$_k] = $data[$_k];
}
return $locations;
}
function hu_get_widget_el_default_zones() {
//generates the default widget zone for json
$default_zones = array();
foreach ( hu_get_default_widget_zones() as $_zone_id => $_data ) {
//get the default location
$_loc = isset($_default_locations[$_zone_id]) ? key($_default_locations[$_zone_id]) : '';
$default_zones[] = array(
'id' => $_data['id'],
'title' => $_data['name'],
'contexts' => array('_all_'),
'locations' => array($_loc),
'is_builtin' => true,
'description' => $_data['description']
);
}
return $default_zones;
}
/*****************************************************************************
* PREVIEW SCRIPT
*****************************************************************************/
//exports some wp_query informations. Updated on each preview refresh.
add_action( 'customize_preview_init' , 'hu_schedule_preview_script_printing', 20 );
function hu_schedule_preview_script_printing() {
add_action( 'wp_footer', 'hu_print_widget_module_script', 1000 );
}
function hu_print_widget_module_script() {
?>
<script id="hu-widget-zones-module">
//global _wpWidgetCustomizerPreviewSettings
( function( api, $, _ ) {
api.bind( 'preview-ready', function() {
api.preview.bind( 'active', function() {
api.preview.send( 'houston-widget-settings',
_.extend( _wpWidgetCustomizerPreviewSettings,
{
availableWidgetLocations : _.values( api.settings.availableWidgetLocations )
}
)
);//send()
});
});
})( wp.customize, jQuery, _ );
</script>
<?php
}
/*****************************************************************************
* PRINT TEMPLATES
*****************************************************************************/
//print the pre add view content
add_action( 'customize_controls_print_footer_scripts', 'hu_print_widget_areas_templates' , 1 );
function hu_print_widget_areas_templates() {
if ( ! isset( $GLOBALS['czr_base_fmk_namespace'] ) ) {
error_log( __FUNCTION__ . ' => global czr_base_fmk not set' );
return;
}
$czrnamespace = $GLOBALS['czr_base_fmk_namespace'];
//czr_fn\czr_register_dynamic_module
$CZR_Fmk_Base_fn = $czrnamespace . 'CZR_Fmk_Base';
if ( ! function_exists( $CZR_Fmk_Base_fn) ) {
error_log( __FUNCTION__ . ' => Namespace problem => ' . $CZR_Fmk_Base_fn );
return;
}
$css_attr = $CZR_Fmk_Base_fn()->czr_css_attr;
?>
<script type="text/html" id="tmpl-czr-module-widgets-pre-add-view-content">
<div class="<?php echo $css_attr['sub_set_wrapper']; ?>" data-input-type="text">
<div class="customize-control-title"><?php _e('Name', 'hueman'); ?></div>
<div class="czr-input">
<input data-czrtype="title" type="text" value="" placeholder="<?php _e('Give it a name', 'hueman'); ?>"></input>
</div>
<span class="czr-notice"><?php _e('Personalizing the name of the widget zone will help you identify it.', 'hueman'); ?></span>
</div>
<div class="<?php echo $css_attr['sub_set_wrapper']; ?> width-100" data-input-type="select">
<div class="customize-control-title"><?php _e('Location(s)', 'hueman'); ?></div>
<span class="czr-notice"><?php _e('Select the pre-defined location(s) in which you will embed this widget zone.', 'hueman'); ?></span>
<div class="czr-input">
<select data-czrtype="locations" class="js-example-basic-multiple" multiple="multiple"></select>
</div>
</div>
</script>
<?php
//print template for built-in models like primary, secondary, footer-1, etc...
//REDUCED VIEW TEMPLATE
//no remove button
//no remove alert wrapper
?>
<script type="text/html" id="tmpl-czr-module-widgets-ru-item-part">
<div class="<?php echo $css_attr['item_header']; ?> czr-builtin-model">
<div class="<?php echo $css_attr['item_title']; ?> <?php echo $css_attr['item_sort_handle']; ?>"><h4>{{ data.title }}</h4></div>
<div class="<?php echo $css_attr['item_btns']; ?>"><a title="<?php _e('Edit', 'hueman'); ?>" href="javascript:void(0);" class="fas fa-pencil-alt <?php echo $css_attr['edit_view_btn']; ?>"></a></div>
</div>
</script>
<?php
//the following template is a "sub view"
//it's rendered :
//1) on customizer start, depending on what is fetched from the db
//2) dynamically when designing from the customizer
//data looks like : { id : 'sidebar-one', title : 'A Title One' }
?>
<script type="text/html" id="tmpl-czr-module-widgets-item-input-list">
<div class="<?php echo $css_attr['sub_set_wrapper']; ?>" data-input-type="text">
<div class="customize-control-title"><?php _e('id', 'hueman'); ?></div>
<input data-czrtype="id" type="hidden"></input>
<span><?php _e('unique id', 'hueman')?> : {{ data.id }}</span>
</div>
<div class="<?php echo $css_attr['sub_set_wrapper']; ?>" data-input-type="text">
<div class="customize-control-title"><?php _e('Name', 'hueman'); ?></div>
<div class="czr-input">
<input data-czrtype="title" type="text" value="{{ data.title }}" placeholder="<?php _e('Enter a name', 'hueman'); ?>"></input>
</div>
<span class="czr-notice"><?php _e('Personalizing the name of the widget zone will help you identify it.', 'hueman'); ?></span>
</div>
<div class="<?php echo $css_attr['sub_set_wrapper']; ?> width-100" data-input-type="select">
<div class="customize-control-title"><?php _e('Location(s)', 'hueman'); ?></div>
<span class="czr-notice"><?php _e('Select the pre-defined location(s) in which you will embed this widget zone.', 'hueman'); ?></span>
<div class="czr-input">
<select data-czrtype="locations" class="js-example-basic-multiple" multiple="multiple"></select>
</div>
</div>
<div class="<?php echo $css_attr['sub_set_wrapper']; ?> width-100" data-input-type="select">
<div class="customize-control-title"><?php _e('Context(s)', 'hueman'); ?></div>
<span class="czr-notice"><?php _e('Pick the context(s) where this widget area will be displayed.', 'hueman'); ?></span>
<div class="czr-input">
<select data-czrtype="contexts" class="js-example-basic-multiple" multiple="multiple"></select>
</div>
</div>
</script>
<?php
}