: 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
content.appendChild(res);
} else if (res !== false) {
content.appendChild(res);
const items = data.options,
tabs_container = createDocumentFragment(),
nav = createElement('ul', 'tb_tab_nav tf_scrollbar'),
isStylingTab=self.clicked === 'styling',
stickyWraper = (isStylingTab && this.styleClicked === false && self.component === 'module') ? createElement('','tb_styling_tab_nav') : null,
tabs = createElement('','tb_tabs tf_rel tf_w'),
isRadio = data.isRadio !== undefined;
tabs.className += ' ' + data.class;
this.styleClicked = true;
v = self.getStyleVal(data.id)??data.default;
nav.className += ' tb_radio_wrap';
if (self.is_repeat === true) {
nav.className += self.is_sort === true ? ' tb_lb_sort_child' : ' tb_lb_option_child';
nav.dataset.inputId = data.id;
nav.className += ' tb_lb_option';
if(isStylingTab && api.isVisual){
self.styles[data.id] = {prop: data.prop, selector: data.selector};
id = item.href || ('tb_' + this.click + '_' + k),
li = createElement('li'),
a = createElement(isRadio === true?'label':'a',{'data-id':id,class:(item.class || '')}),
div = createElement('',{id:id,class: 'tb_tab tf_hide'}),
opt = item.options || item;
a.textContent = i18n[item.label]??item.label??i18n[k];
if (item.icon !== undefined) {
a.appendChild(api.Helper.getIcon(item.icon));
if (item.title !== undefined) {
let input = createElement('input',{type:'radio',class:'tb_radio_tab_input',name:data.id,value:k});
if (v === k || v === 'tb_' + k) {
a.className = 'tb_radio_tab_label';
a.tfOn('change', self.switchTabs, {passive: true})
if(isStylingTab && api.isVisual){
self._initControl(input, {...data,type:'radio'});
a.tfOn(_CLICK_, self.switchTabs, {passive: true})
if (first === null || v === k || v === 'tb_' + k) {
li.className = 'current';
div.appendChild(self.create(opt));
div.style.display = 'block';
div.tfOn('tb_builder_tabsactive', function () {
this.appendChild(self.create(opt));
}, {once: true, passive: true});
tabs_container.appendChild(div);
if (stickyWraper !== null && self._options?.styling?.options?.type==='tabs') {
stickyWraper.appendChild(nav);
tabs.appendChild(stickyWraper);
tabs.appendChild(tabs_container);
setTimeout(self.callbacks.bind(self), 5);
const wr = createElement(),
f = createDocumentFragment(),
options = self.create(data.options);
if (data.label !== undefined) {
label.textContent = i18n[data.label] || data.label;
if (data.wrap_class !== undefined) {
wr.className = data.wrap_class;
wr.classList.add('tb_field_group');
wr.classList.add(data.id);
if (data.display === 'accordion') {
wr.classList.add('tb_field_group_acc');
const content = createElement('','tf_hide tb_field_group_content'),
icon = api.Helper.getIcon('ti-angle-up');
label.className = 'tb_style_toggle tb_closed';
content.appendChild(options);
label.tfOn(_CLICK_, () => {
$(content).slideToggle();
label.classList.toggle('tb_closed');
wr.append(label, content);
if (data.label !== undefined) {
label.className = 'tb_label';
if (data.help !== undefined && data.label !== '') {
label.className += +' contains-help';
label.appendChild(this.help(data.help));
const fr = createDocumentFragment(),
wrapper = createElement('',{class:'tb_row_js_wrapper tf_rel tb_lb_option',id:data.id}),
add_new = createElement('button',{class:'add_new tf_plus_icon tf_icon_btn tf_rel',type:'button'},i18n[ data.new_row ] || i18n.new_row),
origRepeat = self.is_repeat === true;
if (data.wrap_class !== undefined) {
wrapper.className += ' ' + data.wrap_class;
if (self.values[data.id] !== undefined) {
const values = self.values[data.id].slice(),
orig = api.Helper.cloneObject(self.values);
for (let i = 0; i < values.length; ++i) {
self.values = values[i] || {};
wrapper.appendChild(this._builderFields(data, self));
wrapper.appendChild(this._builderFields(data, self));
wrapper.appendChild(add_new);
_this._sortable(wrapper, self);
add_new.tfOn(_CLICK_, function (e) {
const isRepeat = self.is_repeat === true,
orig = api.Helper.cloneObject(self.values),
container = this.parentNode;
self.values = api.activeModel.getPreviewSettings?(api.activeModel.getPreviewSettings()?.[container.id]?.[0] || {}):{};
const item = _this._builderFields(data, self);
api.activeModel.addRow?.(item);
Themify.trigger('tb_repeatable_add_new', item);
Themify.triggerEvent(container, 'added');
self.control.preview(container, null, {repeat: true});
self.is_repeat = isRepeat;
self.is_repeat = origRepeat;
_builderFields(data, self) {
repeat = createElement('','tb_repeatable_field'),
top = createElement('','tb_repeatable_field_top tf_rel tf_box tf_textl'),
menu = createElement('', 'row_menu'),
icon = createElement('',{class:'menu_icon',tabindex:-1}),
ul = createElement('ul','tb_down'),
_duplicate = createElement('li','tb_duplicate_row',i18n.duplicate),
_delete = createElement('li','tb_delete_row tf_close',i18n.delete),
toggle = createElement('','tb_arrow tb_toggle_row'),
content = createElement('','tb_repeatable_field_content'),
up = createElement('',{class:'tb_arrow tb_up_row',title:i18n.up}),
down = createElement('',{class:'tb_arrow tb_down_row',title:i18n.down});
content.appendChild(self.create(data.options));
ul.append(_duplicate, _delete);
top.append(menu, up, down, toggle);
repeat.append(top, content);
top.tfOn(_CLICK_, function (e) {
repeatContainer = this.parentNode;
if (cl.contains('tb_delete_row')) {
const p = target.closest('.tb_row_js_wrapper');
_this._delete(target, p);
self.control.preview(p, null, {repeat: true});
else if (cl.contains('tb_duplicate_row')) {
const orig = api.Helper.cloneObject(self.values),
isRepeat = self.is_repeat === true;
self.values = api.Forms.serialize(repeatContainer, true, true);
api.activeModel.duplicateRow?.(self.values, orig, repeatContainer);
const item = _this._builderFields(data, self);
repeatContainer.after(item);
Themify.triggerEvent(repeatContainer.parentNode, 'duplicate');
Themify.trigger('tb_repeatable_duplicate', item);
self.control.preview(repeatContainer, null, {repeat: true});
self.is_repeat = isRepeat;
} else if (cl.contains('tb_toggle_row')) {
} else if (cl.contains('tb_arrow')) {
_this._move(target, self);
el.tfOn('pointerdown', function (e) {
if (e.button === 0 && e.target.classList.contains('tb_repeatable_field_top')) {
e.stopImmediatePropagation();
doc = this.ownerDocument,
item = e.target.closest('.tb_repeatable_field'),
const scrollDrag = y => {
if (y >= viewMax || y <= viewMin) {
if (isWorking === false) {
const k = ~~(viewMax / 10);
scrollbar.scrollTop += y <= viewMin ? (-1) * k : k;
timeout = setTimeout(() => {
requestAnimationFrame(() => {
e.stopImmediatePropagation();
timer = requestAnimationFrame(() => {
const {clientX:x,clientY:y} = e,
moveTo = e.type === 'mousemove' ? e.target : doc.elementFromPoint(x, y),
clientY = y - holderHeight - parentNode.getBoundingClientRect().top;
if (clientY > 0 && clientY < parentHeight) {
holder.style.transform = 'translateY(' + clientY + 'px)';
if (y >= viewMax || y <= viewMin) {
if (moveTo !== item && moveTo?.classList.contains('tb_repeatable_field')) {
const side = y > prevY ? 'bottom' : 'top';
if (dir !== side || theLast !== moveTo) {
side === 'bottom' ? moveTo.after(item) : moveTo.before(item);
topBodyCl.add('tb_start_animate', 'tb_move_drag');
parentNode = item.parentNode;
parentNode.classList.add('tb_sort_start');
if (typeof tinyMCE !== 'undefined') {
const items = parentNode.tfClass('tb_lb_wp_editor');
for (let i = items.length - 1; i > -1; --i) {
editors[id] = tinymce.get(id).getContent();
tinyMCE.execCommand('mceRemoveEditor', false, id);
index = Themify.convert(parentNode.children).indexOf(item);
if (!item.classList.contains('collapsed')) {
item.tfClass('tb_repeatable_field_content')[0].style.display = 'none';
item.classList.add('collapsed');
holder = item.cloneNode(true);
holder.tfClass('tb_repeatable_field_content')[0].remove();
scrollbar = item.closest('.tf_scrollbar');
holder.className += ' tb_sort_handler';
item.classList.add('tb_current_sort');
holderHeight = holder.getBoundingClientRect().height / 2;
parentHeight = parentNode.offsetHeight;
const box = scrollbar.getBoundingClientRect();
viewMax = box.bottom - 40;
this.tfOff('pointermove', start, {passive: true, once: true})
.tfOff('pointermove', move, {passive: true})
.tfOff('lostpointercapture pointerup', up, {passive: true, once: true});
e.stopImmediatePropagation();
cancelAnimationFrame(timer);
if (typeof tinyMCE !== 'undefined') {
for (let id in editors) {
tinyMCE.execCommand('mceAddEditor', false, id);
tinymce.get(id).setContent(editors[id]);
item.classList.remove('tb_current_sort');
item.classList.remove('collapsed');
item.tfClass('tb_repeatable_field_content')[0].style.display = '';
const newIndex = Themify.convert(parentNode.children).indexOf(item);
if (index !== newIndex) {
api.activeModel.sortRow?.(item, index, newIndex);
requestAnimationFrame(() => {
if (index !== newIndex) {
self.control.preview(parentNode, null, {repeat: true});
Themify.triggerEvent(parentNode, 'sortable');
parentNode.classList.remove('tb_sort_start');
topBodyCl.remove('tb_start_animate', 'tb_move_drag');
theLast = holder = toggleCollapse = dir = index = prevY = editors = scrollbar = doc = parentHeight = holderHeight = isWorking = viewMin = viewMax = item = timer = timeout = null;
this.tfOn('lostpointercapture pointerup', up, {passive: true, once: true})
.tfOn('pointermove', start, {passive: true, once: true})
.tfOn('pointermove', move, {passive: true})
.setPointerCapture(e.pointerId);
const r = el.closest('.tb_repeatable_field'),
dir = el.classList.contains('tb_up_row') ? 'top' : 'bottom',
next = dir === 'bottom' ? r.nextElementSibling : r.previousElementSibling,
index = Themify.convert(p.children).indexOf(r);
dir === 'bottom' ? next.after(r) : next.before(r);
api.activeModel.sortRow?.(r, index, Themify.convert(p.children).indexOf(r));
requestAnimationFrame(() => {
p.closest('.tf_scrollbar').scroll({
top: r.offsetTop + p.offsetTop + 10
self.control.preview(p, null, {repeat: true});
Themify.triggerEvent(p, 'sortable');
$(el).closest('.tb_repeatable_field').toggleClass('collapsed').find('.tb_repeatable_field_content').slideToggle();
const item = el.closest('.tb_repeatable_field');
api.activeModel.deleteRow?.(item, p);
Themify.trigger('tb_repeatable_delete', item);
Themify.triggerEvent(p, 'delete');
_expand(item, data, self) {
item.tfOn(_CLICK_, function (e) {
let wrap = this.tfClass('tb_accordion_fields_options')[0];
if (wrap === undefined) {
wrap =createElement('','tb_toggleable_fields_options tb_accordion_fields_options tf_w');
wrap.style.display = 'none';
const pid = this.parentNode.closest('.tb_accordion_fields').id,
if (self.values[pid]?.[id]?.val !== undefined) {
orig = api.Helper.cloneObject(self.values);
self.values = self.values[pid][id].val;
wrap.appendChild(self.create(data.options));
self.is_repeat = orig = null;
} else if (wrap.contains(e.target)) {
if (this.classList.contains('tb_closed')) {
$(wrap).slideDown(function () {
this.parentNode.classList.remove('tb_closed');
$(wrap).slideUp(function () {
this.parentNode.classList.add('tb_closed');
_resetMotion(item, self) {
if (self.values.motion_effects !== undefined) {
for (let prop in self.values.motion_effects) {
self.values.motion_effects[prop].val = {[prop + '_dir']: ''};
const tab = item.parentNode.querySelector('#motion_effects'),
select = tab.tfTag('select'),
boxes = tab.tfClass('tb_position_box_wrapper'),
sliders = tab.tfClass('tb_slider_wrapper'),
ranges = tab.tfClass('tb_range');
for (let i = select.length - 1; i > -1; --i) {
select[i].selectedIndex = 0;
Themify.triggerEvent(select[i], 'change');
for (let i = ranges.length - 1; i > -1; --i) {
ranges[i].value = ranges[i].min ??'';
for (let i = boxes.length - 1; i > -1; --i) {
boxes[i].tfTag('input')[0].value = '50,50';
boxes[i].tfClass('tb_position_box_handle')[0].removeAttribute('style');
for (let i = sliders.length - 1; i > -1; --i) {