var PC=PC||{};
PC.fe=PC.fe||{};
PC.shareYourDesign={};
!(function($, _){
'use strict';
wp.hooks.addAction('PC.fe.start', 'mkl/syd/share', function(configurator_view){
PC.shareYourDesign.modal=new modalView();
configurator_view.$('.share-your-design').on('click', PC.shareYourDesign.modal.toggle.bind(PC.shareYourDesign.modal) )
});
wp.hooks.addAction('PC.fe.start', 'mkl/syd/share', function (){
const urlParams=new URLSearchParams(window.location.search);
const sharedConfig=urlParams.get('shared_configuration');
if(sharedConfig){
const decoded=PC.fe.get_config_from_code(sharedConfig);
if(decoded&&Array.isArray(decoded) ){
console.log('Restoring shared configuration:', decoded);
PC_config.config.load_config_content=decoded;
}else{
console.warn('Invalid shared configuration string.');
}}
}, 40);
wp.hooks.addAction('PC.fe.syd.modal.open', 'mkl/syd/share', function(syd_modal){
if(PC.shareYourDesign.modal&&PC.shareYourDesign.modal) PC.shareYourDesign.modal.close(null, { skip_restore_focus: true });
});
var modalView=Backbone.View.extend({
tagName: 'div',
className: 'share-your-design--container',
template: wp.template('mkl-save-the-design--link-modal'),
isOpened: false,
_shareTokenExpiresAt: null,
_refresh_share_token_seq: 0,
initialize: function(){
this.insertTarget=PC.fe.modal.$('.share-your-design-modal-container');
if(! this.insertTarget.length){
this.insertTarget=$('.share-your-design-modal-container');
}
this.insertTarget=wp.hooks.applyFilters('syd_share_modal_insert_target', this.insertTarget);
if(! this.insertTarget.length){
console.warn('Share your design: No location was found to insert the view...');
return;
}
this.render();
wp.hooks.doAction('PC.fe.syd.share.modal.init', this);
return this;
},
events: {
'click .syd-action--email-url': 'send_email',
'submit .save-form': 'send_email',
'click .syd-share-send-another': 'on_share_send_another',
'click .syd-social-links a': 'on_social_media_share',
'click .close-share-design': 'close',
},
focusable_selector: 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])',
render: function(){
this.$el.append(this.template());
this.insertTarget.append(this.$el);
this.$message=this.$('.message');
if(window.tippy){
var btns=this.$('.syd-social-links a');
btns.each(function(index, btn){
var tooltip_options={
content: $(btn).find('span').html(),
allowHTML: true,
placement: 'top',
zIndex: 100001
};
if(tooltip_options.content&&tooltip_options.content.length){
$(btn).addClass('icon-only');
tippy(btn, tooltip_options);
}});
}},
announce: function(message, assertive){
if(! message) return;
var $live=this.$('.syd-live-region').first();
if(! $live.length) return;
$live.attr('role', assertive ? 'alert':'status');
$live.text('');
setTimeout(function(){
$live.text(message);
}, 15);
},
get_focusable: function(){
return this.$('.mkl-pc-modal').find(this.focusable_selector).filter(':visible');
},
get_initial_focus_target: function(){
var $dialog=this.$('.mkl-pc-modal').first();
if(! $dialog.length) return this.$el;
var $firstInput=this.$('.save-form input:visible:not([disabled])').first();
if($firstInput.length) return $firstInput;
var $focusable=this.get_focusable();
if($focusable.length) return $focusable.first();
return $dialog;
},
handle_keydown: function(e){
if(! this.isOpened) return;
if(e.key==='Escape'){
e.preventDefault();
e.stopPropagation();
if(e.stopImmediatePropagation){
e.stopImmediatePropagation();
}
this.close();
return;
}
if(e.key!=='Tab') return;
var $focusable=this.get_focusable();
if(! $focusable.length) return;
var first=$focusable[0];
var last=$focusable[ $focusable.length - 1 ];
var activeEl=document.activeElement;
var insideDialog=this.$('.mkl-pc-modal').first().has(activeEl).length > 0||this.$('.mkl-pc-modal').first().is(activeEl);
if(! insideDialog){
e.preventDefault();
(e.shiftKey ? last:first).focus();
return;
}
if(e.shiftKey&&activeEl===first){
e.preventDefault();
last.focus();
}else if(! e.shiftKey&&activeEl===last){
e.preventDefault();
first.focus();
}},
toggle: function(e){
if(e) e.preventDefault();
if(this.isOpened){
this.close();
}else{
this.open();
}},
open: function(e){
this.isOpened=true;
this.trigger_el=$(document.activeElement);
this._shareCaptchaPollTimer=null;
if(PC.fe.saveYourDesign.modal&&PC.fe.saveYourDesign.modal.opened) PC.fe.saveYourDesign.modal.close(null, { skip_restore_focus: true });
this.insertTarget.addClass('opened');
$('body').addClass('syd-share-modal-opened');
var $dialog=this.$('.mkl-pc-modal').first();
$dialog.attr('aria-hidden', 'false');
$dialog.removeAttr('inert');
$(document).off('keydown.syd-share-modal-' + this.cid).on('keydown.syd-share-modal-' + this.cid, this.handle_keydown.bind(this) );
var $target=this.get_initial_focus_target();
if($target&&$target.length){
setTimeout(function(){
$target.trigger('focus');
}, 0);
}
const side=wp.hooks.applyFilters('syd_share_modal_side', 'left');
const offset=this.$el.offset();
const parent_offset=PC.fe.modal.$el.offset();
this.$el.css('--container-offset', Math.round(offset.left - parent_offset.left) + 'px');
this.$el.css('--container-width', Math.round(PC.fe.modal.$el.outerWidth()) + 'px');
var token_padding_ms=2000;
var token_ok=this._shareActionToken&&this._shareTokenExpiresAt &&
Date.now() < this._shareTokenExpiresAt - token_padding_ms;
if(! token_ok){
this.resetShareCaptchaWidgets();
}
this.maybeMountShareTurnstile();
this.bindCaptchaListeners();
if(! this.needs_share_captcha()){
this.refreshShareActionToken();
}else{
var $send_btn=this.$('.syd-action--email-url').first();
if($send_btn.length){
$send_btn.prop('disabled', false);
}}
},
needs_share_captcha: function(){
return Boolean(window.PC_SYD_SHARE&&window.PC_SYD_SHARE.captcha_enabled);
},
bindCaptchaListeners: function(){
var view=this;
if(! window.PC_SYD_SHARE||! window.PC_SYD_SHARE.captcha_enabled){
return;
}
this.$('.save-form')
.off('input.sydCaptcha change.sydCaptcha')
.on('input.sydCaptcha change.sydCaptcha',
'textarea[name=\"cf-turnstile-response\"], input[name=\"cf-turnstile-response\"], textarea[name=\"h-captcha-response\"], input[name=\"h-captcha-response\"]',
function(){
view.refreshShareActionToken();
}
);
},
maybeMountShareTurnstile: function(){
var view=this;
if(! window.PC_SYD_SHARE||! window.PC_SYD_SHARE.captcha_enabled){
return;
}
if(( window.PC_SYD_SHARE.captcha_provider||'turnstile')!=='turnstile'){
return;
}
var $container=this.$('.syd-share-captcha .cf-turnstile').first();
if(! $container.length){
return;
}
var el=$container[0];
if(el.getAttribute('data-cf-turnstile-widget-id') ){
return;
}
var attempts=0;
function try_render(){
if(! window.turnstile||typeof window.turnstile.render!=='function'){
attempts +=1;
if(attempts < 50){
setTimeout(try_render, 100);
}
return;
}
try {
window.turnstile.render(el, {
callback: function(){
view.refreshShareActionToken();
},
});
} catch(err){
if(window.console&&console.warn){
console.warn('Share Turnstile: render failed', err);
}}
}
try_render();
},
getCaptchaPayload: function(){
if(! window.PC_SYD_SHARE||! window.PC_SYD_SHARE.captcha_enabled){
return {};}
var provider=window.PC_SYD_SHARE.captcha_provider||'turnstile';
if('turnstile'===provider){
var cf=this.$('textarea[name=\"cf-turnstile-response\"], input[name=\"cf-turnstile-response\"]').first().val();
return { 'cf-turnstile-response':(cf||'').toString() };}
if('hcaptcha'===provider){
var hc=this.$('textarea[name=\"h-captcha-response\"], input[name=\"h-captcha-response\"]').first().val();
return { 'h-captcha-response':(hc||'').toString() };}
return {};},
set_share_token_loading: function(on){
var $wrap=this.$('.save-form--container').first();
if(! $wrap.length){
return;
}
var $anim=$wrap.find('.save-btns-busy-animation--container').first();
var preparing=(window.PC_SYD_SHARE&&window.PC_SYD_SHARE.token_preparing)
? window.PC_SYD_SHARE.token_preparing
: 'Preparing secure session…';
if(on){
$wrap.addClass('syd-minting-action-token');
$wrap.attr('aria-busy', 'true');
if($anim.length){
$anim.attr('aria-label', preparing);
}}else{
$wrap.removeClass('syd-minting-action-token');
$wrap.removeAttr('aria-busy');
if($anim.length){
$anim.removeAttr('aria-label');
}}
},
resetShareCaptchaWidgets: function(){
var provider=(window.PC_SYD_SHARE&&window.PC_SYD_SHARE.captcha_provider) ? window.PC_SYD_SHARE.captcha_provider:'';
var $wrap=this.$('.syd-share-captcha');
if(! $wrap.length){
return;
}
if('hcaptcha'===provider&&typeof window.hcaptcha!=='undefined'&&typeof window.hcaptcha.reset==='function'){
var reset_done=false;
$wrap.find('h-captcha').each(function(){
var wid=typeof this.widgetId!=='undefined' ? this.widgetId:null;
if(typeof wid==='number'){
try {
window.hcaptcha.reset(wid);
reset_done=true;
} catch(err){
}}
});
if(! reset_done){
$wrap.find('textarea[name="h-captcha-response"], input[name="h-captcha-response"]').val('');
}}
if('turnstile'===provider&&typeof window.turnstile!=='undefined'){
$wrap.find('.cf-turnstile').each(function(){
var host=this;
var wid=host.getAttribute('data-cf-turnstile-widget-id');
if(wid){
try {
if(typeof window.turnstile.remove==='function'){
window.turnstile.remove(wid);
}else if(typeof window.turnstile.reset==='function'){
window.turnstile.reset(wid);
}} catch(err2){
}}
host.innerHTML='';
host.removeAttribute('data-cf-turnstile-widget-id');
});
}},
refreshShareActionToken: function(){
var view=this;
var $btn=this.$('.syd-action--email-url').first();
if(! $btn.length){
return;
}
var failMsg=(window.PC_SYD_SHARE&&window.PC_SYD_SHARE.token_prepare_failed) ? window.PC_SYD_SHARE.token_prepare_failed:'Could not prepare the sharing form.';
var url=(typeof PC_config!=='undefined'&&PC_config.frontend_action_token_url) ? PC_config.frontend_action_token_url:'';
var token_padding_ms=2000;
if(this._shareActionToken &&
this._shareTokenExpiresAt &&
Date.now() < this._shareTokenExpiresAt - token_padding_ms
){
$btn.prop('disabled', false);
this.$message.html('').removeClass('success error');
this.set_share_token_loading(false);
return;
}
if(! url){
$btn.prop('disabled', true);
this.$message.html('').removeClass('success').addClass('error').html(failMsg);
this.announce(failMsg, true);
this.set_share_token_loading(false);
return;
}
this.$message.html('').removeClass('success error');
var productId=('variable'===PC.fe.product_type) ? PC.fe.parent_product:PC.fe.active_product;
var captchaPayload=this.getCaptchaPayload();
if(this.needs_share_captcha()){
var provider=window.PC_SYD_SHARE.captcha_provider||'turnstile';
var responseKey=('hcaptcha'===provider) ? 'h-captcha-response':'cf-turnstile-response';
if(! captchaPayload[ responseKey ]){
$btn.prop('disabled', false);
return;
}}
$btn.prop('disabled', true);
var fetch_seq=++this._refresh_share_token_seq;
this.set_share_token_loading(true);
var fetch_headers={ 'Content-Type': 'application/json' };
if(typeof PC_config!=='undefined'&&PC_config.rest_nonce){
fetch_headers['X-WP-Nonce']=PC_config.rest_nonce;
}
fetch(url, {
method: 'POST',
credentials: 'same-origin',
headers: fetch_headers,
body: JSON.stringify(Object.assign({ purpose: 'syd_share_send', product_id: productId }, captchaPayload) ),
})
.then(function(res){
return res.json().then(function(body){
if(! res.ok){
var msg=(body&&body.message) ? body.message:failMsg;
throw new Error(msg);
}
return body;
});
})
.then(function(data){
if(! data||! data.token){
throw new Error(failMsg);
}
view._shareActionToken=data.token;
view._shareTokenExpiresAt=Date.now() +(parseInt(data.expires_in, 10)||300) * 1000;
$btn.prop('disabled', false);
})
.catch(function(err){
view._shareActionToken=null;
view._shareTokenExpiresAt=null;
view.resetShareCaptchaWidgets();
view.maybeMountShareTurnstile();
$btn.prop('disabled', ! view.needs_share_captcha());
var msg=(err&&err.message) ? err.message:failMsg;
view.$message.addClass('error').html(msg);
view.announce(msg, true);
})
.then(function(){
if(fetch_seq!==view._refresh_share_token_seq){
return;
}
view.set_share_token_loading(false);
});
},
close: function(e, options){
options=options||{};
if(e&&e.preventDefault) e.preventDefault();
if(this._shareCaptchaPollTimer){
clearTimeout(this._shareCaptchaPollTimer);
this._shareCaptchaPollTimer=null;
}
this._refresh_share_token_seq++;
this.set_share_token_loading(false);
this.$el.removeClass('sent-email sending-email');
this.$message.html('').removeClass('success error');
this.isOpened=false;
this.insertTarget.removeClass('opened');
$('body').removeClass('syd-share-modal-opened');
var $dialog=this.$('.mkl-pc-modal').first();
$dialog.attr('aria-hidden', 'true');
$dialog.attr('inert', 'inert');
$(document).off('keydown.syd-share-modal-' + this.cid);
if(! options.skip_restore_focus&&this.trigger_el&&this.trigger_el.length){
this.trigger_el.trigger('focus');
}},
get_url: function(){
const configuration_code=PC.fe.get_config_code(true);
var configuration_link=new URL(window.location.href);
configuration_link.searchParams.delete('shared_configuration');
configuration_link.searchParams.append('shared_configuration', configuration_code);
configuration_link.searchParams.delete('load_config_from_cart');
configuration_link.searchParams.delete('edit_config_from_cart');
if(! configuration_link.searchParams.has('open_configurator') ) configuration_link.searchParams.append('open_configurator', 1);
return configuration_link.href
},
on_share_send_another: function(e){
if(e&&e.preventDefault){
e.preventDefault();
}
var form=this.$('.save-form');
this.$el.removeClass('sent-email');
this.$message.html('').removeClass('success error');
if(form.length&&form[0]){
form[0].reset();
}
this._shareActionToken=null;
this._shareTokenExpiresAt=null;
this._refresh_share_token_seq++;
this.resetShareCaptchaWidgets();
this.bindCaptchaListeners();
this.maybeMountShareTurnstile();
if(! this.needs_share_captcha()){
this.refreshShareActionToken();
}else{
var $send=this.$('.syd-action--email-url').first();
if($send.length){
$send.prop('disabled', false);
}}
var $first_field=form.find('input:visible:not([disabled]), textarea:visible:not([disabled])').first();
if($first_field.length){
setTimeout(function(){
$first_field.trigger('focus');
}, 0);
}},
send_email: function(e){
if(e) e.preventDefault();
const form=this.$('.save-form');
var skip_share_ajax_field=function(key){
if(key==='h-captcha-response'||key==='g-recaptcha-response'||key==='cf-turnstile-response'){
return true;
}
if(key==='hcaptcha-widget-id'||key==='hcap_hp_sig'){
return true;
}
if(/^hcap_hp_/.test(key) ){
return true;
}
return false;
};
var form_data_raw=new FormData(form[0]);
let form_data=new FormData();
form_data_raw.forEach(function(value, key){
if(! skip_share_ajax_field(key) ){
form_data.append(key, value);
}});
var $submitButton=this.$('.syd-action--email-url').first();
this.$message.html('').removeClass('success error');
var sessionMsg=(window.PC_SYD_SHARE&&window.PC_SYD_SHARE.session_expired) ? window.PC_SYD_SHARE.session_expired:'Session expired.';
var captchaRequiredGateMsg=(window.PC_SYD_SHARE&&window.PC_SYD_SHARE.captcha_required) ? window.PC_SYD_SHARE.captcha_required:'Please complete the captcha to continue.';
if(! this._shareActionToken){
var gateMsg=sessionMsg;
if(window.PC_SYD_SHARE&&window.PC_SYD_SHARE.captcha_enabled){
var cp=this.getCaptchaPayload();
var gateProvider=window.PC_SYD_SHARE.captcha_provider||'turnstile';
var gateKey=('hcaptcha'===gateProvider) ? 'h-captcha-response':'cf-turnstile-response';
if(! cp[ gateKey ]){
gateMsg=captchaRequiredGateMsg;
}}
this.$message.addClass('error').html(gateMsg);
this.announce(gateMsg, true);
this.refreshShareActionToken();
return;
}
if(! form[0].checkValidity()){
form[0].reportValidity();
return;
}
var $el=this.$el;
$submitButton.prop('disabled', true);
var configuration_link=this.get_url();
if('variable'==PC.fe.product_type){
form_data.append('product_id', PC.fe.parent_product);
form_data.append('variation_id', PC.fe.active_product);
}else{
form_data.append('product_id', PC.fe.active_product);
form_data.append('variation_id', 0);
}
form_data.append('configuration_link', configuration_link);
form_data.append('configuration_code', PC.fe.get_config_code());
form_data.append('configuration', PC.fe.save_data.save());
form_data.append('action', 'mkl_share_config');
form_data.append('mkl_pc_action_token', this._shareActionToken);
this.$el.addClass('sending-email');
this.$('.save-form').attr('aria-busy', 'true');
this.announce('Sending configuration email.');
wp.ajax.send({
data: Object.fromEntries(form_data.entries()),
success: _.bind(function(response){
$el.removeClass('sending-email');
$el.addClass('sent-email');
form[0].reset();
var send_another_label=(window.PC_SYD_SHARE&&window.PC_SYD_SHARE.send_another_email)
? window.PC_SYD_SHARE.send_another_email
: 'Send another email';
var $success_wrap=$('<div class="syd-share-success">');
$('<div class="syd-share-success__message">').html(response.message).appendTo($success_wrap);
var $another_btn=$('<button type="button" class="button syd-share-send-another">')
.text(send_another_label);
$('<p class="syd-share-success__actions">').append($another_btn).appendTo($success_wrap);
this.$message.addClass('success').empty().append($success_wrap);
this.announce(response.message);
setTimeout(function(){
$another_btn.trigger('focus');
}, 0);
this.$('.save-form').attr('aria-busy', 'false');
this._shareActionToken=null;
this._shareTokenExpiresAt=null;
this.resetShareCaptchaWidgets();
$submitButton.prop('disabled', true);
}, this),
error: _.bind(function(data){
var err_raw=data&&data.error ? data.error:'';
var err_parts=Array.isArray(err_raw) ? err_raw:[ err_raw ];
var err_joined=err_parts.map(function(s){
return String(s);
}).join(' ');
var token_dead=/session has expired|please try again/i.test(err_joined);
if(token_dead){
this._shareActionToken=null;
this._shareTokenExpiresAt=null;
this.resetShareCaptchaWidgets();
this.refreshShareActionToken();
}
$submitButton.prop('disabled', false);
this.$el.removeClass('sending-email');
this.$message.addClass('error');
var errors=Array.isArray(err_raw) ? err_raw:[ err_raw ];
this.$message.html('<ul>' + errors.map(single_error=> '<li>'+ single_error + '</li>').join(' ') + '</ul>');
this.$message.find('a').prop('target', '_blank');
this.announce(errors.join('. '), true);
this.$('.save-form').attr('aria-busy', 'false');
}, this),
})
},
on_social_media_share: function(e){
var sm=$(e.currentTarget).data('sm');
var url=this.get_url();
if('copy'===sm){
e.preventDefault();
if(url&&navigator.clipboard){
navigator.clipboard.writeText(url)
.then(()=> {
alert('Configuration URL copied to clipboard');
})
.catch(err=> {
alert('Error in copying url: ');
});
}}
e.currentTarget.href=e.currentTarget.href.replace('{url}', encodeURIComponent(url) ).replace('{share_text}', encodeURIComponent(PC.fe.currentProductData.product_info.title) );
}});
})(jQuery, PC._us||window._);