: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
if (this.clicked === 'setting') {
const editors=this._editors;
for (let i = editors.length - 1; i > -1; --i) {
this._initControl(editors[i].el, editors[i].data);
const id = '#' + this.dataset.id,
tabId = id.replace('#tb_options_', '');
let container = tabs.querySelector(id);
if (!container || container.parentNode !== tabs) {
container = this.getRootNode().querySelector(id);
if (!container || li.classList.contains('current')) {
const children = p.children,
containerChildren = container.parentNode.children;
for (let i = children.length - 1; i > -1; --i) {
children[i].classList.remove('current');
li.classList.add('current');
for (let i = containerChildren.length - 1; i > -1; --i) {
if (containerChildren[i].classList.contains('tb_tab')) {
containerChildren[i].style.display = 'none';
container.style.display = 'block';
Themify.trigger('tb_builder_tabsactive', [tabId, container]);
Themify.triggerEvent(container, 'tb_builder_tabsactive', {id: tabId});
_this.is_repeat = _this.is_sort =_this.component = _this.is_new = _this._is_ajax = _this.type = null;
_this._options = options;
_this.type = model.get('mod_name');
_this.component = model.type;
if (_this.component === 'module') {
_this._is_ajax = model.getPreviewType() === 'ajax';
_this.is_new = !!model.is_new;
_this.values = api.Helper.cloneObject(model.get('mod_settings'));
defaultTab = model.tab || 'setting';
_this._options.visibility??= true;
_this._options.animation??= true;
const top_bar = createDocumentFragment(),
container = createDocumentFragment(),
tabIcons = {styling: 'ti-brush', animation: 'ti-layers-alt', visibility: 'ti-eye'},
createTab = (index, options) => {
const fr = createDocumentFragment();
if (index === 'visibility' || index === 'animation') {
options = _this.getOptions(index);
} else if (index === 'styling' && api.LightBox.el.tfClass('tb_styling_tab_header')[0] === undefined) {
const div = createElement('','tb_styling_tab_header'),
globalStylesHTML = api.GS.globalStylesHTML();
div.appendChild(_this._getSwitcher());
div.appendChild(globalStylesHTML);
// generate html output from the options
fr.appendChild(_this.create(options));
if (index === 'styling') {
const reset = createElement('a',{href:'#',class:'reset-styling'}),
icon = createElement('i','tf_close');
reset.tfOn(_CLICK_, e => {
this.resetStyling(api.activeModel);
}).append(icon, createTextNode(i18n.reset_style));
if (api.isVisual && model) {
api.liveStylingInstance.module_rules = _this.styles;//by reference,will be fill when the option has been viewed
tabSwitch = function (e) {
const index = e.detail.id.replace('#tb_options_', '');
if (this.dataset.done === undefined) {
this.dataset.done = true;
this.appendChild(createTab(index, _this._options[index].options));
Themify.trigger('tb_editing_' + _this.type + '_' + index, api.LightBox.el);
for (let k in _this._options) {
let item=_this._options[k];
let tab_id = 'tb_options_' + k,
label = item.name !== undefined ? (i18n[item.name] || item.name) : i18n[k],
li = createElement('li'),
a = createElement('a',{href:'javascript:;','data-id':tab_id},label),
tooltip = createElement('span'),
wrapper = createElement('',{id:tab_id,class:'tb_tab tb_options_tab_wrapper tf_rel tf_box tf_w tf_hide'});
a.className = 'tb_tooltip';
tooltip.textContent = label;
a.appendChild(api.Helper.getIcon(tabIcons[k]));
if (defaultTab === k || defaultTab === undefined) {
li.className = 'current';
if (item.html !== undefined) {
wrapper.innerHTML = item.html;
wrapper.appendChild(createTab(k, item.options));
wrapper.style.display = 'block';
wrapper.dataset.done = true;
wrapper.tfOn('tb_builder_tabsactive', tabSwitch, {passive: true});
a.tfOn(_CLICK_, _this.switchTabs, {passive: true});
container.appendChild(wrapper);
const top = api.LightBox.el.tfClass('tb_options_tab')[0],
changeMode = (prevbreakpoint, breakpoint) => {
_this._updateStyles(prevbreakpoint, breakpoint);
top?.replaceChildren(top_bar);
Themify.on('themify_builder_lightbox_close', () => {
_this._radioChange = _this.afterRun = _this._bindings = _this._editors = [];
_this._stylesData = _this.settings = _this.styles = {};
_this._is_ajax = _this.is_repeat = _this.is_sort = _this.clicked = null;
if (typeof tinyMCE !== 'undefined') {
for (let i = tinymce.editors.length - 1; i > -1; --i) {
if (tinymce.editors[i].id !== 'content') {
tinyMCE.execCommand('mceRemoveEditor', true, tinymce.editors[i].id);
Themify.off('themify_builder_change_mode', changeMode);
_this.tabs.styleClicked = false;
model = _this.type = _this.component = _this.is_new = _this._options = _this=null;
.on('themify_builder_change_mode', changeMode);
Themify.trigger('tb_editing_' + _this.type + '_' + _this.clicked, api.LightBox.el);
* self.type is the module slug, trigger a separate event for all modules regardless of their slug
Themify.trigger('tb_editing_' + _this.component, api.LightBox.el);
getStyleVal(id, bp, vals) {
if (id !== undefined && id !== '' && api.activeModel !== null) {
if (vals === undefined) {
bp = api.activeBreakPoint;
if (bp === 'desktop' || this.clicked !== 'styling') {
if (vals !== null && vals[id] !== '') {
if (vals['breakpoint_' + bp] !== undefined && vals['breakpoint_' + bp][id] !== undefined && vals['breakpoint_' + bp][id] !== '') {
v = vals['breakpoint_' + bp][id];
const points = this.breakpointsReverse;
for (let i = points.indexOf(bp) + 1; i < points.length; ++i) {
if (points[i] !== 'desktop') {
if (vals['breakpoint_' + _bp] !== undefined && vals['breakpoint_' + _bp][id] !== undefined && vals['breakpoint_' + _bp][id] !== '') {
v = vals['breakpoint_' + _bp][id];
} else if (vals[id] !== undefined && vals[id] !== '') {
// Check for responsive disable
let binding_data = this._stylesData?.[id]?.binding;
if (binding_data?.[vals[id]]?.responsive?.disabled?.includes(id)) {
if ((v === undefined || v === '') && id.endsWith('_unit') && id.indexOf('lightbox_')!==0 && !id.includes('frame_')) {//because in the very old version, px wasn't saved and we can't detect after removing it was px value or not
//for columns it can be "value1,value2" where "value1" is value for v5, "value2" is for v7
else if (v !== undefined && v !== '' && api.activeModel.type === 'column' && v.toString().includes(',') && (id.indexOf('padding') === 0 || id.includes('margin'))) {
if (v[1] !== undefined && v[1] !== '') {
_updateStyles(prevbreakpoint, breakpoint) {
this.setStylingValues(prevbreakpoint);
const old_tab = this.clicked;
this.clicked = 'styling';
for (let k in this._stylesData) {
let el = this._stylesData[k],
if (type && type !== 'video' && type !== 'gallery' && type !== 'autocomplete' && type !== 'custom_css' && type !== 'builder' && el.is_responsive !== false) {
if (type === 'icon_radio') {
} else if (type === 'icon_checkbox') {
} else if (type === 'textarea' || type === 'icon' || type === 'hidden' || type === 'number') {
} else if (type === 'image') {
} else if (type === 'padding' || type === 'border_radius') {
} else if (type === 'frame') {
let v = this.getStyleVal(k);
this[type].update(k, v, this,prevbreakpoint,breakpoint);
if (el.binding !== undefined) {
let items = this.getEl(k),
res = items.tfClass('tfl-icon');
} else if (type === 'radio' || type === 'checkbox') {
res = items.tfTag('input');
for (let i = 0, len = res.length; i < len; ++i) {
this._binding(res[i], el, v);
//Disable responsive disable options
const disabled_options = api.LightBox.el.querySelectorAll('#tb_options_styling option.tb_responsive_disable');
for (let j = disabled_options.length - 1; j >= 0; j--) {
disabled_options[j].disabled = 'desktop' !== breakpoint;
setStylingValues(breakpoint) {
const data = api.Forms.serialize('tb_options_styling', true),
isDesktop = breakpoint === 'desktop';
if (isDesktop === false && this.values['breakpoint_' + breakpoint] === undefined) {
this.values['breakpoint_' + breakpoint] = {};
if (isDesktop === true) {
this.values[i] = data[i];
this.values['breakpoint_' + breakpoint][i] = data[i];
async resetStyling(model) {
const type = model.get('mod_name');
if (api.isGSPage === false && api.GS.activeGS === null) {
if (model.id === api.activeModel?.id) {
const field = this.getEl(api.GS.key);
if (field && field.value) {
const vals = field.value.split(' '),
bar = field.parentNode.querySelector('tb-gs');
for (let i = vals.length - 1; i > -1; --i) {
await api.GS.setGsStyle([], true, model);
let live = api.liveStylingInstance;
if (!live || live.el !== model.el) {
live = api.createStyleInstance();
live.init(true, false, model);
const prefix = live.prefix,
points = this.breakpointsReverse;
for (let i = points.length - 1; i > -1; --i) {
let stylesheet = ThemifyStyles.getSheet(points[i], api.GS.activeGS !== null),
rules = stylesheet.cssRules || stylesheet.rules;
for (let j = rules.length - 1; j > -1; --j) {
if (rules[j].selectorText.includes(prefix)) {
let css = rules[j].cssText.split('{')[1].split(';');
for (let k = css.length - 2; k > -1; --k) {
let prop = css[k].trim().split(': ')[0].trim();
if (rules[j].style[prop] !== undefined) {
rules[j].style[prop] = '';
if (model.type !== 'module') {
live.bindBackgroundMode('repeat', 'background_repeat');
live.bindBackgroundMode('repeat', 'b_r_h');
live.el.removeAttribute('data-tb_slider_videos');
live.el.querySelector(':scope>.tb_slider_videos')?.remove();
live.getComponentBgOverlay(type)?.remove();
const styleFields = ThemifyStyles.getStyleOptions(type),
values = model.id === api.activeModel?.id ? this.values : model.get('styling');
let key = i.includes('_color') ? 'color' : (i.includes('_style') ? 'style' : false),
if (i.indexOf('breakpoint_') === 0 || i === api.GS.key || styleFields[i] !== undefined || i.includes('_apply_all')) {
} else if (i.includes('_unit')) {//unit
key = i.replace(/_unit$/ig, '', '');
if (styleFields[key] !== undefined) {
} else if (i.includes('_w')) {//weight
key = i.replace(/_w$/ig, '', '');
if (styleFields[key] !== undefined && styleFields[key].type === 'font_select') {
} else if (key !== false) {
key = i.replace('_' + key, '_width');
if (styleFields[key] !== undefined && styleFields[key].type === 'border') {
if (model.id === api.activeModel?.id) {
container = this.getEl('tb_options_' + tabId);
for (let childs = container.children, i = childs.length - 1; i > -1; --i) {
if (!childs[i].classList.contains('tb_styling_tab_header')) {
container.removeAttribute('data-done');
Themify.triggerEvent(container, 'tb_builder_tabsactive', {id: tabId});
const content = createDocumentFragment();
if (data === undefined || data.length === 0) {
const info = createElement(),
infoText = createElement('p','',i18n.no_op_module);
info.appendChild(infoText);
content.appendChild(info);
if (data.type === 'tabs') {
content.appendChild(this.tabs.render(data, this));
if (item.hide === true || item.type === undefined || ('visibility' === this.clicked && 'row' === this.component && 'sticky_visibility' === item.id)) {
res = this[type].render(item, this);
if (res !== false && type !== 'separator' && type !== 'expand' && type !== 'group') {
let id = item.id || item.topId;
if (type !== 'tabs' && type !== 'multi' && type !== 'margin_opposity') {
if (this.clicked === 'styling') {
if (api.isVisual && item.prop !== undefined) {
this.styles[id] = api.Helper.cloneObject(item);
this._stylesData[id] = api.Helper.cloneObject(item);
else if (this.clicked === 'setting' && this.values !== null && this.values[id] !== undefined && this.is_repeat !== true) {
this.settings[id] = this.values[item.id];
if (item.units !== undefined && this.values[id + '_unit'] !== undefined) {
this.settings[id + '_unit'] = this.values[id + '_unit'];
if (type !== 'hook_content' && type !== 'slider' && type !== 'builder' && type !== 'tooltip' && type !== 'custom_css_id') {
fieldData={'data-type':type};
if (item.dc !== undefined && !item.dc) {
fieldCl += ' tb_disable_dc';
if (item.wrap_class !== undefined) {
fieldCl += ' ' + item.wrap_class;
if (type === 'toggle_switch') {
fieldCl += ' switch-wrapper';
} else if (type === 'slider') {
fieldCl += ' tb_slider_options';
} else if (type === 'message' && item.hideIf !== undefined && new Function('return ' + item.hideIf)) {
fieldCl += ' tb_hide_option';
} else if (item.required !== undefined && this.clicked === 'setting') {// validation rules
fieldData['data-validation'] = item.required.rule || 'not_empty';
let msg = item.required.message;
fieldData['data-error-msg'] = msg !== undefined ? (i18n[msg] || msg) : 'not_empty';
fieldCl += ' tb_must_validate';
if (this.clicked === 'styling' && item.is_responsive === false) {
fieldCl += ' tb_responsive_disable';
let txt = this._getTitle(item),
field = createElement('',fieldData);
let label = createElement('','tb_label',txt);
label.className += ' tb_empty_label';
if (item.help !== undefined && item.label !== '') {
label.className += ' contains-help';
label.appendChild(this.help(item.help));
field.appendChild(label);
let input = createElement('','tb_input');
field.appendChild(input);
content.appendChild(field);