: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* This file contain abstraction class to create module object.
* Themify_Builder_Component_Module class should be used as main class and
* create any child extend class for module.
* @package Themify_Builder
* @subpackage Themify_Builder/classes
defined('ABSPATH') || exit;
* The abstract class for Module.
* Abstraction class to initialize module object, don't initialize
* this class directly but please create child class of it.
* @package Themify_Builder
* @subpackage Themify_Builder/classes
class Themify_Builder_Component_Module {
public $name = ''; //deprecated
public $slug = ''; //deprecated
public $category = null; //deprecated
private static $assets = array();
public static $isFirstModule = false;
public static $disable_inline_edit = false;
public static function get_module_class(string $slug):string{
$name=\explode('-',$slug);
$className= \implode('_',$items);
if(!class_exists('\\'.$className,false)){//bakward
$className= 'TB_Image_Pro_Module';
elseif($slug==='typewriter'){
$className= 'TB_Typewriter';
$className= 'TB_Statistics_Module';
elseif($slug==='readtime'){
$className= 'TB_Read_Time_Module';
elseif($slug==='ab-image'){
$className= 'TB_AB_Image_Module';
public static function get_js_css():array {
public static function get_module_icon():string {
public static function get_module_name():string {
public static function is_available():bool{
public static function get_json_file():array {
public static function get_styles_json():array {
$loadFiles=apply_filters('tb_json_files', []);
$res=[THEMIFY_BUILDER_URI.'/json/style.json?ver='.THEMIFY_VERSION];
foreach($loadFiles as $file){
$res[]=$file['f'].'?ver='.$file['v'];
$modules=self::load_modules();
foreach ($modules as $id=>$module) {
$file=$module::get_json_file();
if ( ! themify_is_dev_mode() && count( $files ) > 2 ) {
$jsonName=$jsonDir.crc32(implode('',$keys)).'.json';
$jsonF=THEMIFY_BUILDER_DIR. $jsonName;
foreach($files as $file){
$content=$hasError===false?Themify_Filesystem::get_file_content($file['f']):null;
$content=json_decode($content,true);
$jsonData=array_merge($jsonData, $content);
if($hasError===false && empty($content)){
$fallback[]=$file['f'].'?ver='.$file['v'];
$tmpF=THEMIFY_BUILDER_DIR.$jsonDir. uniqid('tmp_');
$jsonData=json_encode($jsonData);
if(empty($jsonData) || !file_put_contents($tmpF, $jsonData) || !rename($tmpF,$jsonF)){
$res=array_merge($res,$fallback);
$res[]=THEMIFY_BUILDER_URI.$jsonName.'?ver='.THEMIFY_VERSION;
foreach($files as $file){
$res[]=$file['f'].'?ver='.$file['v'];
public static function add_inline_edit_icon() {
public static function get_checkbox_data(string $setting):string {
return \implode(' ', \explode('|', $setting));
* Return only value setting
public static function get_param_value(string $string):string {
return \explode('|', $string)[0];
* Retrieve builder templates
* @param string $template_path
* @param string $default_path
public static function retrieve_template(string $template_name,$args = array(),string $template_path = '', string $default_path = '', bool $echo = true) {
self::get_template($template_name, $args, $template_path, $default_path);
* @param string $template_path
* @param string $default_path
protected static function get_template(string $template_name, &$args = array(),string $template_path = '',string $default_path = '') {
$key = $template_path . $template_name;
if (!isset($paths[$key])) {
$paths[$key] = self::locate_template($template_name, $template_path, $default_path);
if (isset($paths[$key]) && $paths[$key] !== '') {
global $ThemifyBuilder;//deprecated
* Locate a template and return the path for inclusion.
* This is the load order:
* yourtheme / $template_path / $template_name
* $default_path / $template_name
public static function locate_template(string $template_name,string $template_path = '',string $default_path = ''):string {
static $theme_dir = null;
static $child_dir = null;
$DS = DIRECTORY_SEPARATOR;
if ($theme_dir === null) {
$builderDir = $DS . 'themify-builder' . $DS;
$theme_dir = get_template_directory() . $builderDir;
if (!\is_dir($theme_dir)) {
$child_dir = get_stylesheet_directory() . $builderDir;
if (!\is_dir($child_dir)) {
if ($theme_dir !== false || $child_dir !== null || $child_dir !== false) {
if ($child_dir !== null && $child_dir !== false) {
$templates[] = $child_dir;
if ($theme_dir !== false) {
$templates[] = $theme_dir;
foreach ($templates as $dir) {//is theme file
if (\is_file($dir . $template_name)) {
$template = $dir . $template_name;
if ($template_path === '') {
$modulesPath = \Themify_Builder_Model::get_directory_path();
if (\strpos($template_name, 'template-') === 0) {
$module = str_replace(array('template-', '.php'), '', $template_name);
if (isset($modulesPath[$module])) {
$template = pathinfo($modulesPath[$module], PATHINFO_DIRNAME) . $DS . 'templates' . $DS . $template_name;
$dir = rtrim(THEMIFY_BUILDER_TEMPLATES_DIR, $DS) . $DS;
if (\is_file($dir . $template_name)) {
$template = $dir . $template_name;
foreach ($modulesPath as $m) {//backward
$dir = pathinfo($m, PATHINFO_DIRNAME) . $DS . 'templates' . $DS . $template_name;
$template = $default_path . $template_name;
if (\is_file($template)) {
$template = \rtrim($template_path, $DS) . $DS . $template_name;
return apply_filters('themify_builder_locate_template', $template, $template_name, $template_path);
* Sticky Element props attributes
* @param array $fields_args
* @param string $mod_name
* @param string $module_ID
public static function sticky_element_props(array &$props,array $fields_args) {
if (!empty($fields_args['stick_at_check']) || !empty($fields_args['stick_at_check_t']) || !empty($fields_args['stick_at_check_tl']) || !empty($fields_args['stick_at_check_m'])) {
static $is_sticky = null;
if ($is_sticky === null) {
$is_sticky = \Themify_Builder_Model::is_sticky_scroll_active();
if ($is_sticky !== false) {
$_arr = array('d', 'tl', 't', 'm');
$key = $v === 'd' ? '' : '_' . $v;
if (($key === '' && !empty($fields_args['stick_at_check'])) || ($key !== '' && isset($fields_args['stick_at_check' . $key]) && $fields_args['stick_at_check' . $key] !== '')) {
if ($key !== '' && $fields_args['stick_at_check' . $key] !== '1') {
if (isset($fields_args['stick_at_position' . $key]) && $fields_args['stick_at_position' . $key] === 'bottom') {
$settings[$v]['stick'] = array();
$settings[$v]['stick']['p'] = $fields_args['stick_at_position' . $key];
if (!empty($fields_args['stick_at_pos_val' . $key])) {
if (!isset($settings[$v]['stick'])) {
$settings[$v] = array('stick' => array());
$settings[$v]['stick']['v'] = $fields_args['stick_at_pos_val' . $key];
if (isset($fields_args['stick_at_pos_val_unit' . $key]) && $fields_args['stick_at_pos_val_unit' . $key] !== 'px') {
$settings[$v]['stick']['u'] = $fields_args['stick_at_pos_val_unit' . $key];
if (!empty($fields_args['unstick_when_check' . $key])) {
if (isset($fields_args['unstick_when_element' . $key]) && $fields_args['unstick_when_element' . $key] !== 'builder_end') {
if (isset($fields_args['unstick_when_condition' . $key]) && $fields_args['unstick_when_condition' . $key] !== 'hits') {
$unstick['r'] = $fields_args['unstick_when_condition' . $key];
$unstick['type'] = $fields_args['unstick_when_element' . $key];
if ($unstick['type'] === 'row' && isset($fields_args['unstick_when_el_row_id' . $key]) && $fields_args['unstick_when_el_row_id' . $key] !== 'row') {
$unstick['el'] = $fields_args['unstick_when_el_row_id' . $key];
} elseif ($unstick['type'] === 'module' && !empty($fields_args['unstick_when_el_mod_id' . $key])) {
$unstick['el'] = $fields_args['unstick_when_el_mod_id' . $key];
if (isset($fields_args['unstick_when_pos' . $key]) && $fields_args['unstick_when_pos' . $key] !== 'this') {
$unstick['cur'] = $fields_args['unstick_when_pos' . $key];
if (!empty($fields_args['unstick_when_pos_val' . $key])) {
$unstick['v'] = $fields_args['unstick_when_pos_val' . $key];
if (isset($fields_args['unstick_when_pos_val_unit' . $key]) && $fields_args['unstick_when_pos_val_unit' . $key] !== 'px') {
$unstick['u'] = $fields_args['unstick_when_pos_val_unit' . $key];
$unstick['type'] = 'builder';
$settings[$v]['unstick'] = $unstick;
$props['data-sticky-active'] = \json_encode($settings);
if ($is_sticky !== 'done') {
\Themify_Enqueue_Assets::addPrefetchJs(THEMIFY_BUILDER_JS_MODULES . 'sticky.js', THEMIFY_VERSION);
}//Add custom attributes html5 data to module container div to show parallax options.
elseif (Themify_Builder::$frontedit_active === false && (!empty($fields_args['motion_effects']) || !empty($fields_args['custom_parallax_scroll_speed']) )) {
$is_lax = \Themify_Builder_Model::is_scroll_effect_active();
$has_lax = false; /* validate Lax settings */
// Check settings from Floating tab to apply them to Lax library
if (!empty($fields_args['custom_parallax_scroll_speed'])) {
$props['data-parallax-element-speed'] = $fields_args['custom_parallax_scroll_speed'];
$speed = self::map_animation_speed($fields_args['custom_parallax_scroll_speed']);
if (!isset($fields_args['custom_parallax_scroll_reverse']) || $fields_args['custom_parallax_scroll_reverse'] === '|') {
$props['data-lax-translate-y'] = 'vh 1,0 ' . $speed;
if (!empty($fields_args['custom_parallax_scroll_fade']) && $fields_args['custom_parallax_scroll_fade'] !== '|') {
$props['data-lax-opacity'] = 'vh 1,0 0';
// Add motion effects from Motion tab
$effects=isset($fields_args['motion_effects'])?$fields_args['motion_effects']:array();
if (!isset($effects['t'])) {
$props['data-lax-optimize'] = 'true';
if (!empty($effects['v']['val']['v_dir'])) {
$v_speed = isset($effects['v']['val']['v_speed']) ? $effects['v']['val']['v_speed'] : 1;
$v_speed = self::map_animation_speed($v_speed);
$viewport = isset($effects['v']['val']['v_vp']) ? explode(',', $effects['v']['val']['v_vp']) : array(0, 100);
$bottom = 1 - ( (int) $viewport[0] / 100 );
$top = 1 - ( (int) $viewport[1] / 100 );
$props['data-lax-translate-y'] = $effects['v']['val']['v_dir'] === 'up' ? '(vh*' . $bottom . ') 0,(vh*' . $top . ') -' . $v_speed : '(vh*' . $bottom . ') 0,(vh*' . $top . ') ' . $v_speed;
if (!empty($effects['h']['val']['h_dir'])) {
$h_speed = isset($effects['h']['val']['h_speed']) ? $effects['h']['val']['h_speed'] : 9;
$h_speed=self::map_animation_speed($h_speed);
$viewport = isset($effects['h']['val']['h_vp']) ? explode(',', $effects['h']['val']['h_vp']) : array(0, 100);
$bottom = 1 - ( (int) $viewport[0] / 100 );
$top = 1 - ( (int) $viewport[1] / 100 );
$props['data-lax-translate-x'] = $effects['h']['val']['h_dir'] === 'toleft' ? '(vh*' . $bottom . ') 0,(vh*' . $top . ') -' . $h_speed : '(vh*' . $bottom . ') 0,(vh*' . $top . ') ' . $h_speed;
if (!empty($effects['t']['val']['t_dir'])) {
$viewport = isset($effects['t']['val']['t_vp']) ? explode(',', $effects['t']['val']['t_vp']) : array(0, 100);
$bottom = 1 - ( (int) $viewport[0] / 100 );
$top = 1 - ( (int) $viewport[1] / 100 );
$center = ( $bottom - ( ( $bottom - $top ) / 2 ) );
if ($effects['t']['val']['t_dir'] === 'fadein') {
$props['data-lax-opacity'] = '(vh*' . $bottom . ') 0,(vh*' . $top . ') 1';
} elseif ($effects['t']['val']['t_dir'] === 'fadeout') {
$props['data-lax-opacity'] = '(vh*' . $bottom . ') 1,(vh*' . $top . ') 0';
} elseif ($effects['t']['val']['t_dir'] === 'fadeoutin') {
$props['data-lax-opacity'] = '(vh*' . $bottom . ') 1,(vh*' . $center . ') 0,(vh*' . $top . ') 1';
} elseif ($effects['t']['val']['t_dir'] === 'fadeinout') {
$props['data-lax-opacity'] = '(vh*' . $bottom . ') 0,(vh*' . $center . ') 1,(vh*' . $top . ') 0';
} elseif (!isset($fields_args['animation_effect_delay'])) {
unset($props['data-lax-opacity'], $props['data-lax-optimize']);
if (!empty($effects['b']['val']['b_dir'])) {
$b_level = isset($effects['b']['val']['b_level']) ? $effects['b']['val']['b_level']: 5;
$b_level=self::map_animation_speed($b_level, 'blur');
$viewport = isset($effects['b']['val']['b_vp']) ? explode(',', $effects['b']['val']['b_vp']) : array(0, 100);
$bottom = 1 - ( (int) $viewport[0] / 100 );
$top = 1 - ( (int) $viewport[1] / 100 );
$props['data-lax-blur'] = $effects['b']['val']['b_dir'] === 'fadein' ? '(vh*' . $bottom . ') ' . $b_level . ',(vh*' . $top . ') 0' : '(vh*' . $bottom . ') 0,(vh*' . $top . ') ' . $b_level;
if (!empty($effects['r']['val']['r_dir'])) {
$viewport = isset($effects['r']['val']['r_vp']) ? explode(',', $effects['r']['val']['r_vp']) : array(0, 100);
$rotates = isset($effects['r']['val']['r_num']) ? (float) $effects['r']['val']['r_num'] * 360 : 360;
$bottom = 1 - ( (int) $viewport[0] / 100 );
$top = 1 - ( (int) $viewport[1] / 100 );
$props['data-lax-rotate'] = $effects['r']['val']['r_dir'] === 'toleft' ? '(vh*' . $bottom . ') 0,(vh*' . $top . ') -' . $rotates : '(vh*' . $bottom . ') 0,(vh*' . $top . ') ' . $rotates;
if (isset($effects['r']['val']['r_origin'])) {
$props['data-box-position'] = self::map_transform_origin($effects['r']['val']['r_origin']);
if (!empty($effects['s']['val']['s_dir'])) {
$viewport = isset($effects['s']['val']['s_vp']) ? explode(',', $effects['s']['val']['s_vp']) : array(0, 100);
$ratio = isset($effects['s']['val']['s_ratio']) ? (float) $effects['s']['val']['s_ratio'] : 3;
$bottom = 1 - ( (int) $viewport[0] / 100 );
$top = 1 - ( (int) $viewport[1] / 100 );
$props['data-lax-scale'] = $effects['s']['val']['s_dir'] === 'up' ? '(vh*' . $bottom . ') 1,(vh*' . $top . ') ' . $ratio : '(vh*' . $bottom . ') 1,(vh*' . $top . ') ' . number_format(1 / $ratio, 3);
if (isset($effects['s']['val']['s_origin'])) {
$props['data-box-position'] = self::map_transform_origin($effects['s']['val']['s_origin']);