: 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(events[ev].length===0){
this._events.set(id,events);
trigger(id, ev, ...args) {
const events = this._events.get(id),
if (events?.[ev]!==undefined) {
let _this = typeof id === 'string' ? this.get(id) : id;
for (let i = events[ev].length-1; i>-1;--i) {
let pr=events[ev][i].apply(_this, args);
if(pr instanceof Promise){
return Promise.all(proms).catch(()=>{});
const isFrontend=api.isFrontend,
async onResize(trigger) {
const events = $._data(window, 'events')?.resize;
for (let i = 0; i < events.length; ++i) {
if (events[i].handler !== undefined) {
this._onResizeEvents.add(events[i].handler);
const e = $.Event('resize', {
for (let handler of this._onResizeEvents) {
handler.apply(window, [e, $]);
Themify.triggerEvent(window,'resize')
.trigger('tfsmartresize', {w: Themify.w, h: Themify.h});
(new ResizeObserver(entries => {
Themify.trigger('documentsize');
if (api.activeBreakPoint !== 'desktop') {
timeout=setTimeout(() => {
const _body=entries[0].target;
cancelAnimationFrame(req);
req = requestAnimationFrame(() => {
topBody.style.height = _body.scrollHeight + 'px';
topBody.style.height = _body.scrollHeight + 'px';
findCssRule(rules, selector) {
selector = selector.replace(/\s*>\s*/g, '>').replace(/\,\s/g, ',').trim();
const isCondition=selector[0]==='@';
for (let i = rules.length - 1; i > -1; --i) {
if ((isCondition===true && rules[i].conditionText && rules[i].cssText.replace(/\s*>\s*/g, '>').replace(/\,\s/g, ',').trim().includes(selector)) || (isCondition===false && !rules[i].conditionText && selector === rules[i].selectorText.replace(/\s*>\s*/g, '>').replace(/\,\s/g, ',').trim())) {
const _COL_CLASSES_VALUES=api.getColClassValues();
for (let i = cl.length - 1; i > -1; --i) {
if (_COL_CLASSES_VALUES.includes(cl[i])) {
getRowSettings(base, type, saving) {
model_r = Registry.get(base.dataset.cid);
const sel=type==='subrow'?'module_subrow':type + '_inner',
inner = base.tfClass(sel)[0],
columns=api.Utils.getColumns(inner),
points = api.breakpointsReverse,
bpLength = points.length,
colPaddingsIds=['padding_top','padding_bottom','padding_left','padding_right','margin-bottom','margin-top'],
paddingLen=colPaddingsIds.length;
for (let i = 0; i < count; ++i) {
let model_c = Registry.get(columns[i].dataset.cid);
let modules = columns[i].tfClass('tb_holder')[0],
cl = this._filterClass(columns[i].classList),
if (cl !== '') { //backward compatibility
cols[index].grid_class = cl;
styling = api.Helper.cloneObject(model_c.get('styling'));
if (styling && Object.keys(styling).length > 0) {
if(isFrontend){//in admin part we can't use iframe to convert paddings that is why skipping
//we need always save padding/margin units as 2 "value1,value2"(when the unit is %) to detect the data is converted
for(let j=paddingLen-1;j>-1;--j){
let prop=colPaddingsIds[j];
if(styling[prop+'_unit']==='%' && styling[prop]!=='' && styling[prop]!==undefined && !styling[prop].toString().includes(',')){
styling[prop]=','+styling[prop];
for (let k = bpLength - 2; k>-1; --k) {
if(styling['breakpoint_'+points[k]]!==undefined){
let p=styling['breakpoint_'+points[k]][prop];
if(p!=='' && p!==undefined && !p.toString().includes(',') && ThemifyStyles.getStyleVal(prop+'_unit',styling,points[k])==='%'){
styling['breakpoint_'+points[k]][prop]=','+p;
model_c.constructor.builderSave(styling);
if(styling && Object.keys(styling).length>0){
cols[index].styling = styling;
if (modules !== undefined) {
modules = modules.children;
for (let j = 0; j < modules.length; ++j) {
let m = Registry.get(modules[j].dataset.cid),
mname=m?.get('mod_name');
styling = api.Helper.cloneObject(m.get('mod_settings'));
m.parseHtml(styling,saving);
m.constructor.builderSave(styling);
Themify.trigger( 'tb_save_component', {
if (styling && Object.keys(styling).length > 0) {
items[k].mod_settings = styling;
items[k] = this.getRowSettings(modules[j], 'subrow', saving);
cols[index].modules = items;
let sizes = {...model_r.get('sizes')};
for (let i = bpLength-1;i>-1;--i) {//make equal
size = sizes[bp + '_size'],
area = sizes[bp + '_area'],
colh = sizes[bp + '_auto_h'];
size=ThemifyStyles.getColSize(size,false);
if (size.includes(' ')) {
size = size.replace(/\s\s+/g, ' ').split(' ');
for (let j = size.length - 1; j > -1; --j) {
let fr = parseFloat(size[j].trim());
size[j] = size[j].replace(fr.toString(), parseFloat(fr.toFixed(5)).toString());
size = size.join(' ').replaceAll('0.', '.').trim();
sizes[bp + '_size']=size;
delete sizes[bp + '_area'];
area=area.replaceAll('col', '').replace(/\s\s+/g, ' ').trim();
sizes[bp + '_area']= area;
if(size && !size.includes(' ')){
let checkArea=model_r.getGridCss({size:size},bp);
if(checkArea['--area'] && checkArea['--area'].replaceAll('col', '').replace(/\s\s+/g, ' ').trim()===area){
delete sizes[bp + '_area'];
else if(!ThemifyStyles.getAreaValue(area)){
delete sizes[bp + '_area'];
sizes[bp + '_auto_h'] = parseInt(colh);
if (saving === true && sizes[bp + '_dir']!==undefined) {//backward
delete sizes[bp + '_dir'];
for (let i = 0; i < bpLength - 1; ++i) { //clean again duplicates
gutter = sizes[bp + '_gutter'],
colh = sizes[bp + '_auto_h'],
size = sizes[bp + '_size'],
align = sizes[bp + '_align'];
if (gutter || align || colh || size) {
for (let j = i + 1; j < bpLength; ++j) {
if (gutter && sizes[bp2 + '_gutter']) {
if (sizes[bp2 + '_gutter'] === gutter) {
delete sizes[bp + '_gutter'];
if (align && sizes[bp2 + '_align']) {
if (sizes[bp2 + '_align'] === align) {
delete sizes[bp + '_align'];
if (colh && sizes[bp2 + '_auto_h']) {
if (sizes[bp2 + '_auto_h'] === colh) {
delete sizes[bp + '_auto_h'];
if (size && sizes[bp2 + '_size']) {
if (saving === true && sizes[bp2 + '_size'] === size) {
delete sizes[bp + '_size'];
if (!gutter && !align && !colh && !size) {
if (sizes.desktop_area) {
for (let i = 0; i < count; ++i) {
if (area.join(' ') === sizes.desktop_area) {
delete sizes.desktop_area;
if ((sizes.mobile_dir !== undefined && (!sizes.desktop_dir || sizes.desktop_dir === sizes.tablet_landscape_dir) && sizes.tablet_dir === sizes.mobile_dir && sizes.tablet_landscape_dir === sizes.mobile_dir)) {
delete sizes.desktop_dir;
delete sizes.tablet_landscape_dir;
else if (sizes.desktop_dir === 'ltr') {
delete sizes.desktop_dir;
if (sizes.desktop_auto_h === -1) {
delete sizes.desktop_auto_h;
if (sizes.desktop_align === 'start' && api.isFullSection===false) {
delete sizes.desktop_align;
if (sizes.desktop_gutter === 'gutter') {
delete sizes.desktop_gutter;
if (sizes[i] === undefined || sizes[i] === '') {
option_data.sizes = sizes;
styling = api.Helper.cloneObject(model_r.get('styling'));
if(Object.keys(styling).length > 0){
model_r.constructor.builderSave(styling);
option_data.styling = styling;
if(api.Helper.isImageUrl(url) || url.includes('.mp4') || url.includes('.mpeg') || url.includes('.mp3')){
if(url.includes('themify.me')|| url.includes('themify.org')){
if(!url.includes(themifyBuilder.site_url)){
else if(url.includes(Themify.urlHost)){
if(Array.isArray(fields[i]) || typeof fields[i]==='object'){
let v=fields[i].toString().trim();
if(v.includes('<img ') || v.includes('<video ') || v.includes('<audio ')){
let tmp=createElement('template');
for(let allImages= tmp.content.querySelectorAll('img,video,audio'),j=allImages.length-1;j>-1;--j){
let {src,srcset}=allImages[j];
srcset=srcset?.split(' ') || [];
for(let k=srcset.length-1;k>-1;--k){
addImage(srcset[k].trim());
else if(v[0]==='[' && v.includes('path=')){
let m=v.match(/path.*?=.*?['"](.+?)['"]/igm);
m=m[0].split('path=')[1].replaceAll('"','').replace("'",'').split(',');
for(let j=m.length-1;j>-1;--j){
for(let cid of Registry.items.keys()){
getImages(v.get('styling'));
const domImages=api.Builder.get().el.tfTag('img');
for(let i=domImages.length-1;i>-1;--i){
let src=domImages[i].src,
srcset=domImages[i].srcset;
srcset=srcset?.split(' ') ||[];
for(let j=srcset.length-1;j>-1;--j){
addImage(srcset[j].trim());
images.set('themify',themifyImages);
images.set('local',localImages);
images.set('external',externalImages);
return type?images.get(type):images;
async importThemifyImages(images){
if(Themify.urlHost.includes('themify.me')){
images??=this.getAllImages('themify');
return new Promise(async (resolve,reject)=>{
await Themify.loadJs(Themify.url+'js/admin/import/import-images',!!window.TF_ImportImages);
const memory=~~themifyBuilder.memory || 64,
chunkSize=memory>=255?4:(memory>=120?3:(memory>60?2:1)),
res=await TF_ImportImages.init(images,themifyBuilder.nonce,i18n.uploading,chunkSize),
breakpoints=api.breakpointsReverse,
if(fields[i]===undefined){
if(Array.isArray(fields[i]) || typeof fields[i]==='object'){
let v=fields[i].toString().trim();
if(v.includes('<img ') || v.includes('<video ') || v.includes('<audio ')){
let tmp=createElement('template');
tmp.innerHTML='<div>'+v+'</div>';
let content=tmp.content.firstChild,
allImages= content.tfTag('img,video,audio');
for(let j=allImages.length-1;j>-1;--j){
let src=allImages[j].src;
if(img!==false && src.includes(k)){
allImages[j].outerHTML=img.html;
fields[i]=content.innerHTML;
else if( v[0]==='[' || !v.includes(' ')){
if(v[0]==='[' && v.includes('path=')){
v=v.replace('path=','ids=');
v=v.replaceAll(k,img.id);