: 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
el.classList.remove('tb_start_animate');
timer = el = owner = null;
this.tfOn('lostpointercapture pointerup', up, {passive: true,once: true})
.tfOn('pointermove', startDrag, {passive: true,once: true})
.tfOn('pointermove', draggableCallback, {passive: true})
.setPointerCapture(e.pointerId);
items = this.el.tfClass('tb_resizable');
for (let i = items.length - 1; i > -1; --i) {
items[i].tfOn('pointerdown', function(e) {
e.stopImmediatePropagation();
owner = this.ownerDocument,
el.style.willChange='transform,width,height';
const maxHeight = owner.documentElement.clientHeight * .9,
computed = getComputedStyle(el),
minWidth = parseInt(computed.getPropertyValue('min-width')),
maxWidth = parseInt(computed.getPropertyValue('max-width')),
axis = this.dataset.axis,
startH = ~~el.offsetHeight,
startW = ~~el.offsetWidth,
{clientX:resizeX,clientY:resizeY} = e,
ToolBar.el.classList.add('tb_start_animate');
el.classList.add('tb_start_animate');
owner.body.classList.add('tb_start_animate');
e.stopImmediatePropagation();
timer = requestAnimationFrame(() => {
const {clientX,clientY} = e,
matrix = new DOMMatrix(getComputedStyle(el).transform);
w = resizeX + startW - clientX;
if (w >= minWidth && w <= maxWidth) {
matrix.m41 += parseInt(el.style.width) - w;
el.style.width = w + 'px';
const h = axis === '-y' || axis === 'ne' || axis === 'nw' ? (resizeY + startH - clientY) : (startH + clientY - resizeY);
w = axis === 'sw' || axis === 'nw' ? (resizeX + startW - clientX) : (startW + clientX - resizeX);
if ((axis === 'se' || axis === 'x' || axis === 'sw' || axis === 'nw' || axis === 'ne') && w >= minWidth && w <= maxWidth) {
if (axis === 'sw' || axis === 'nw') {
matrix.m41 += parseInt(el.style.width) - w;
el.style.width = w + 'px';
if ((axis === 'se' || axis === 'y' || axis === '-y' || axis === 'sw' || axis === 'nw' || axis === 'ne') && h >= minHeight && h <= maxHeight) {
if (axis === '-y' || axis === 'nw' || axis === 'ne') {
matrix.m42 += parseInt(el.style.height) - h;
el.style.height = h + 'px';
el.style.transform = 'translate(' + matrix.m41 + 'px,' + matrix.m42 + 'px)';
if (axis !== 'y' && axis !== '-y') {
const current = self.getPanelClass(w);
if (activeCl !== current) {
el.classList.remove(activeCl);
self.setResponsiveTabs(current);
e.stopImmediatePropagation();
cancelAnimationFrame(timer);
this.tfOff('pointermove', _start, {passive: true,once: true})
.tfOff('pointermove', _resize, {passive: true})
.tfOff('lostpointercapture pointerup', _stop, {passive: true,once: true });
owner.body.classList.remove('tb_start_animate');
ToolBar.el.classList.remove('tb_start_animate');
el.classList.remove('tb_start_animate');
timer = activeCl = owner = el = null;
this.tfOn('pointermove', _start, {passive: true,once: true})
.tfOn('pointermove', _resize, {passive: true})
.tfOn('lostpointercapture pointerup', _stop, {passive: true,once: true })
.setPointerCapture(e.pointerId);
const input = this.el.tfClass('panel_search')[0];
const search = function(e) {
const el = this.tfClass('panel_search')[0],
parent = this.closest('.panel'),
target = parent.querySelector('.nav_tab .current').dataset.target,
s = e.type === 'reset' ? '' : el.value.trim();
if (target === 'panel_modules_wrap') {
items = parent.tfClass('module');
else if (target === 'panel_rows' && api.preDesignedRows) {
items = parent.tfClass('predesigned_row');
const dropdown=items[0].closest('.panel_tab').tfClass('dropdown_label')[0];
if(dropdown.dataset.active){
Themify.triggerEvent(dropdown.nextElementSibling.firstElementChild,_CLICK_);
else if (target === 'panel_library' && api.Library) {
items = parent.tfClass('library_item');
filter = items[0].closest('.panel_tab').querySelector('.library_tab .current').dataset.target;
const is_empty = s === '',
reg = !is_empty ? new RegExp(s, 'i') : false,
selector = isModule ? '.module_name' : (isLibrary ? '' : '.predesigned_title'),
for (let i = items.length - 1; i > -1; --i) {
let elm = selector === '' ? items[i] : items[i].querySelector(selector),
display = is_empty || reg.test(elm.textContent) ? '' : 'none';
if(filter && !items[i].classList.contains(filter)){
let parent = items[i].closest('.panel_category');
parent.parentNode.style.display = '';
items[i].style.display = display;
if (isModule===true && display==='') {
cats.add(items[i].parentNode);
parent.classList.toggle('panel_searching', !is_empty);
// Hide empty module accordions
items = parent.tfClass('panel_category');
for (let i = items.length - 1; i > -1; --i) {
items[i].parentNode.style.display=cats.has(items[i])?'':'none';
input.parentNode.tfOn('input reset', search, {
api.jsModuleLoaded().then(()=>{
if (_this.size === null) {
let storage = localStorage.getItem(_this.#storageKey);
storage = storage ? JSON.parse(storage) : {};
matrix = tr ? (new DOMMatrix(tr)) : null,
box = el.tfClass('panel_top')[0].getBoundingClientRect(),
wH = topWindow.innerHeight - box.height,
wW = topWindow.innerWidth,
storage = _this._getStorage();
width: parseInt(st.width),
height: parseInt(st.height)
if (obj.height <= 0 || isNaN(obj.height)) {
if (obj.width <= 0 || isNaN(obj.width)) {
obj = {...storage, ...obj};
if (obj.left < 0 || (obj.left+box.width) > wW) {
obj.left = (obj.left < 0 ? 0 : (wW - box.width));
if (obj.top < 0 || obj.top > wH) {
obj.top = (obj.top < 0 ? 0 : wH);
st.width = obj.width + 'px';
st.height = obj.height + 'px';
st.transform = 'translate(' + obj.left + 'px,' + obj.top + 'px)';
if (!api.isDocked && storage !== obj && Object.entries(obj).toString() !== Object.entries(storage).toString()) {
localStorage.setItem(_this.#storageKey, JSON.stringify(obj));
static _updateAddonsMessage(oldAddons){
title:'Themify Builder Pro',
'woocommerce-breadcrumb',
title:'Builder WooCommerce',
for(let [slug,name] of oldAddons){
if(addons[k].modules.includes(slug)){
addonsNames.add(addons[k].title);
body.appendChild(createElement('','tb_old_addons tf_abs_t tf_w tf_h'));
Themify.on('themify_builder_ready',()=>{
api.Builder.get().el?.classList.add('tb_old_addons');
},true,api.is_builder_ready);
this.el.classList.add('tb_old_addons');
ToolBar.el.classList.add('tb_old_addons');
TF_Notification.showHide('warning', i18n.update_addons.replaceAll('%addons%',[...addonsNames].join(', ')),10000);
static _updateBrowserMessage(){
let userAgent = navigator.userAgent.toLowerCase(),
if (userAgent.includes('firefox/')) {
else if (userAgent.includes('opr/')) {
else if (userAgent.includes('edg/')) {
else if (userAgent.includes('samsungbrowser/')) {
browser='Samsung Browser';
keyword='samsungbrowser';
else if (userAgent.includes('chrome/')) {
else if (userAgent.includes('safari/')) {
version=(userAgent.split('version/')[1] || userAgent.split('safari/')[1]).split(' ')[0];
keyword??=browser.toLowerCase();
version=userAgent.split(keyword+'/')[1]?.split(' ')[0] || '';
TF_Notification.showHide('warning', i18n.update_browser.replaceAll('%browser%',browser).replaceAll('%version%',version),10000);
api.SmallPanel = class extends api.MainPanel{
const root = doc.tfId('tb_small_panel_root'),
fr = root.firstElementChild,
styles = api.MainPanel.el.getRootNode().querySelectorAll('style,#tf_svg'),
fragment=createDocumentFragment();
if (fr) { // shadowrootmode="open" isn't support
mode: fr.getAttribute('shadowrootmode')
}).appendChild(fr.content);
for(let i=0,len=styles.length;i<len;++i){
if(styles[i].id!=='module_main_panel_style' && styles[i].id!=='tf_fonts_style'){
fragment.appendChild(styles[i].cloneNode(true));
root.shadowRoot.prepend(fragment);
if (!isFrontend && doc.querySelector('.edit-post-layout__content') !== null) {
doc.tfClass('.edit-post-layout__content').appendChild(root);
this.el = root.shadowRoot.tfId('small_panel');
if(!this.el.contains(e.target) && !this.el.getRootNode().host.contains(e.target)){
if(e.target.closest('.tb_column_btn_plus')){
e.stopImmediatePropagation();
if (topBodyCl.contains('tb_standalone_lightbox')) {
if(item.classList.contains('clicked')){
body.style.willChange='scroll-position';
if(this.el.childElementCount===0){
const menu = api.MainPanel.el.tfClass('nav_tab')[0].cloneNode(true),
container = api.MainPanel.el.tfClass('panel_container')[0].cloneNode(true),
fr = createDocumentFragment();
fr.append(menu, container);
const modules = container.tfClass('modules'),
predesign=container.tfClass('predesigned_row'),
tabs = this.el.tfClass('tb_compact_tabs'),
nav=this.el.tfClass('nav_tab');
for (let i = predesign.length - 1; i > -1; --i) {
for (let i = modules.length - 1; i > -1; --i) {
modules[i].style.display = '';
for (let i = tabs.length - 1; i > -1; --i) {
tabs[i].classList.remove('tb_compact_tabs');
for (let i = nav.length - 1; i > -1; --i) {
Themify.triggerEvent(nav[i].firstElementChild ,_CLICK_);
Themify.triggerEvent(this.el.tfClass('panel_search')[0],'input');
topWindowDoc.tfOn(_CLICK_,(e)=>{
if(!this.el.contains(e.target) && !this.el.getRootNode().host.contains(e.target)){
Themify.on('tfsmartresize',()=>{
}else if (!Themify.isTouch) {
this.el.tfClass('panel_search')[0].focus();
const hostCl=this.el.getRootNode().host.classList;
this.el.classList.toggle('tb_subrow_open', item.parentNode.closest('.sub_column') !== null);
item.classList.add('clicked');
ToolBar.el.classList.add('tb_panel_dropdown_openend');
api.MainPanel.el.classList.add('tb_panel_dropdown_openend');
hostCl.remove('tf_hide');
hostCl.remove('tf_hidden');
Themify.trigger('disableInline');
body.style.willChange='';
const host=this.el.getRootNode().host;
if(!host.classList.contains('tf_hide')){
host.classList.add('tf_hide');
ToolBar.el.classList.remove('tb_panel_dropdown_openend');
api.MainPanel.el.classList.remove('tb_panel_dropdown_openend');
const clicked=item || api.Builder.get().el.querySelector('.clicked.tb_column_btn_plus');
clicked.style.display='block';
{offsetWidth:w,offsetHeight:h} = el,
box = clicked.getBoundingClientRect(),
gutenContainer = !isFrontend ? doc.tfClass('edit-post-layout__content')[0] : null,
winW=doc.documentElement.clientWidth;
top = box.top + window.scrollY;
top += gutenContainer.scrollTop - 70;
left = gutenContainer.clientWidth / 2;