(function() {
const middleware = {};
middleware.config = {
enabled: true,
checkoutLoginRequired: false
};
middleware.server_vars = {};
middleware.server_vars = {"domain":"https:\/\/gateway.aws.payl8r.com\/middleware"};
Object.assign(middleware.config, middleware.server_vars);
// Who needs jquery..? - Helper function sections
middleware.select = selector => [...document.querySelectorAll(selector)];
middleware.selectOne = selector => document.querySelector(selector);
middleware.exists = selector => document.querySelector(selector) !== null;
middleware.create = (html, tag = 'div') => {
var frag = document.createDocumentFragment(),
temp = document.createElement(tag);
temp.innerHTML = html;
while (temp.firstChild) {
frag.appendChild(temp.firstChild);
}
return frag;
};
middleware.prepend = (node, element, index = 0) => node.insertBefore(element, node.childNodes[index]);
middleware.append = (node, element) => node.insertBefore(element, node.childNodes[node.childNodes.length - 1]);
middleware.insert = (node, element, nth = null) => nth == null ? middleware.append(node, element) : middleware.prepend(node, element, nth);
middleware.insertCss = (css) => middleware.append(middleware.selectOne('body'), middleware.create(``));
middleware.propsToAttributes = (props) => Object.keys(props).filter(prop => prop !== "content").map(key => `${key}="${props[key]}"`).join(' ');
middleware.onClick = (selector, fn) => document.addEventListener('click', event => event.target.matches(selector) && fn(event));
middleware.show = (selector) => middleware.selectOne(selector).style.display = 'block';
middleware.hide = (selector) => middleware.selectOne(selector).style.display = 'none';
middleware.remove = (selector) => middleware.selectOne(selector) == null ? null : middleware.selectOne(selector).parentNode.removeChild(middleware.selectOne(selector));
middleware.waitUntil = (condition_closure, tick = 100) => new Promise((resolve, reject) => {
if(condition_closure()) {
return resolve();
}
return setTimeout(() => middleware.waitUntil(condition_closure, tick).then(resolve), tick);
});
// Ajax / XHR
middleware.serializeData = data =>
Object.keys(data).reduce((form, key) => {
form.append(key, typeof data[key] == 'object' ? JSON.stringify(data[key]) : data[key]);
return form;
}, new FormData());
middleware.post = (endpoint, data) =>
fetch(endpoint, {
method: 'POST',
mode: 'cors',
// body: JSON.stringify(data)
body: middleware.serializeData(data)
});
middleware.getCustomerId = () => ((typeof ShopifyAnalytics !== 'undefined' && ShopifyAnalytics.meta && ShopifyAnalytics.meta.page && ShopifyAnalytics.meta.page.customerId) ? ShopifyAnalytics.meta.page.customerId : null);
// Middleware tools
middleware.getCart = () =>
new Promise((resolve, reject) => {
fetch(`${window.location.origin}/cart.js`)
.then(response => response.json())
.then(resolve);
});
middleware.clearCart = () =>
new Promise((resolve, reject) => {
fetch(`${window.location.origin}/clear.js`)
.then(response => response.json())
.then(resolve => resolve);
});
middleware.getShippingOptions = () => new Promise((resolve, reject) => {
fetch(`${window.location.origin}/cart/shipping_rates.json?shipping_address%5Bzip%5D=m98dq&shipping_address%5Bcountry%5D=United+Kingdom`)
.then(response => response.json())
.then(response => response.shipping_rates)
.then(resolve);
});
middleware.selectShippingOption = () => middleware.getShippingOptions().then(rates => new Promise((resolve, reject) => {
// If there are no rates available - resolve with nothing
if(rates == null || rates.length == 0) {
resolve(null);
return;
}
const options = rates.map(option => ({
name: option.name,
code: option.code,
price: option.price,
price_display: `£${option.price}`
}));
middleware.modal('Select Shipping Option', options.map((_, i) => `${_.name} - ${_.price_display}`)).then(modalIndex => {
resolve(rates[modalIndex]);
});
}));
//
middleware.forceLogin = () => new Promise((resolve, reject) => {
if(middleware.config.checkoutLoginRequired && middleware.getCustomerId() == null) {
return middleware.modal('You need to be logged in in order to checkout with payl8r', ['Log in']).then(modalIndex => {
window.location.href = '/account/login?return_url=%2Fcart';
reject();
});
}
return resolve();
});
middleware.submitCartToMiddleware = () =>
new Promise((resolve, reject) => {
middleware.initialiseModal();
middleware.getCart().then(cart => {
middleware.forceLogin().then(() => {
middleware.selectShippingOption().then(shippingOption => {
middleware.post(`${middleware.config.domain}/v1/checkout`, {
cart_json: JSON.stringify(cart),
shipping_json: JSON.stringify(shippingOption),
customerId: middleware.getCustomerId()
})
.then(response => response.json())
.then(response => {
resolve(response);
});
});
});
});
});
middleware.modalStyle = ``;
middleware.initialiseModal = () => {
middleware.append(middleware.selectOne('body'), middleware.create(`
`));
};
middleware.updateModal = (label = null, content = null) => {
if(label !== null) {
middleware.remove('#payl8r_modal_label');
middleware.append(middleware.selectOne('#payl8r_modal_wrapper .payl8r_modal'), middleware.create(`${label}
`));
}
if(content !== null) {
middleware.remove('#payl8r_modal_content');
middleware.append(middleware.selectOne('#payl8r_modal_wrapper .payl8r_modal'), middleware.create(`${content}
`));
}
}
middleware.modalClose = () => middleware.remove('#payl8r_modal_wrapper');
middleware.modal = (label = '', options = []) => new Promise((resolve, reject) => {
middleware.updateModal(label, options.map((option, index) => `${option}
`).join(''));
middleware.onClick('#payl8r_modal_wrapper', () => middleware.modalClose());
middleware.onClick('.payl8r_modal_option', (e) => {
let result = e.target.dataset.index;
middleware.updateModal('Hang on while we are getting your order ready', '');
middleware.remove('#payl8r_modal_content');
resolve(result);
});
});
// Checkout button logic
middleware.checkout = {
html: `
`,
style: `
@import url("https://payl8r.com/frontend/css/checkout-button-style.css");
#payl8r-middleware-checkout {
float: right;
clear: none;
width: 225px;
margin-left: 8px;
font-size: 1em !important;
}`,
buttonNth: -1,
parentSelector: '.cart__submit-controls',
shouldDisplay: () => window.location.pathname == '/cart',
initialise: () => {
// Only add checkout button at the /cart
if(!middleware.checkout.shouldDisplay()) {
return;
}
// Add loading behaviour
middleware.insertCss(middleware.checkout.style);
// Insert the checkout button - once parent selector is available - as it might get added dynamically
middleware
.waitUntil(() => middleware.exists(middleware.checkout.parentSelector))
.then(() => {
const submitArea = middleware.selectOne(middleware.checkout.parentSelector);
const checkoutButton = middleware.create(middleware.checkout.html);
// Insert button
middleware.insert(submitArea, checkoutButton, middleware.checkout.buttonNth);
// Add the event handler
middleware.onClick(`#payl8r-middleware-checkout, #payl8r-middleware-checkout *`, (event) => middleware.checkout.checkoutClicked(event));
});
},
checkoutClicked: (event = null) => {
if(event) {
event.preventDefault();
}
middleware.submitCartToMiddleware().then(response => {
if(response.success) {
// window.open(response.redirect_url, '_open');
window.location.href = response.redirect_url;
middleware.modalClose();
return;
} else {
middleware.updateModal(response.result);
}
})
}
};
// Calculator section
middleware.calculator = {
html : ``,
nth: -1,
style: ``,
scriptAttributes: {
src: 'https://assets.payl8r.com/js/pl-calculator-light-app.js',
id: 'pl-calculator-light-app-script',
'data-theme': 'spread',
},
parentSelector: `.price__regular`,
shouldDisplay: () => (
window.ShopifyAnalytics !== null &&
window.ShopifyAnalytics.meta !== null &&
window.ShopifyAnalytics.meta.page !== null &&
window.ShopifyAnalytics.meta.page.pageType == 'product'),
priceGetter: () => parseFloat(middleware.selectOne(middleware.calculator.priceSelector).textContent.match(/[\d\.\,]+/g)[0].replace(',', '')),
getUsername: () =>
new Promise ( ( resolve, reject) => {
middleware.post(`${middleware.config.domain}/v1/getUsername`, {
})
.then(response => response.json())
.then(response => {
resolve(response);
});
}),
priceSelector: '.price-item.price-item--regular',
priceChangeDomSelector: '.price-item.price-item--regular',
minValueVisible: 50,
initialise: () => {
// Only display on product pages
if(!middleware.calculator.shouldDisplay()) {
return;
}
// Add style
middleware.insertCss(middleware.calculator.style);
// Calculator init
document.plCalcPrice = {Price: middleware.calculator.priceGetter()};
// Username init
middleware.calculator.getUsername().then(response => {
document.plGetUsername = {Username: response.username};
});
middleware
.waitUntil(() => middleware.exists(middleware.calculator.parentSelector))
.then(() => {
// Initialize button
middleware.insert(
middleware.selectOne(middleware.calculator.parentSelector),
middleware.create(middleware.calculator.html),
middleware.calculator.nth
);
// Initialise script
let script = document.createElement( 'script' );
Object
.keys(middleware.calculator.scriptAttributes)
.forEach(key => script.setAttribute(key, middleware.calculator.scriptAttributes[key]));
script.onload = () => setTimeout(middleware.calculator.refreshPrice(), 250);
middleware.append(middleware.selectOne('body'), script);
// Initialise price change detection
middleware.calculator.priceUpdateDetectionLogic();
});
},
refreshPrice: () => {
document.plCalcPrice.Price = middleware.calculator.priceGetter();
if(document.plCalcPrice.rerender) {
document.plCalcPrice.rerender(document.plCalcPrice.Price);
}
// Show calculator on min value
middleware.show('#pl-calculator-light-app');
if(middleware.calculator.priceGetter() < middleware.calculator.minValueVisible) {
middleware.hide('#pl-calculator-light-app');
}
},
priceUpdateDetectionLogic: () => {
let MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
let priceDom = document.querySelector(middleware.calculator.priceChangeDomSelector);
(new MutationObserver(() => middleware.calculator.refreshPrice()))
.observe(
priceDom, {
attributes: true,
childList: true,
characterData: true,
subtree: true
}
);
}
}
middleware.checkoutPage = {
getCurrentPageElement: (selector) => fetch(window.location.href)
.then(response => response.text())
.then(body => {
let x = document.createElement('html');
x.innerHTML = body;
return x;
})
.then(_ => [..._.querySelectorAll(selector)]),
script: ``,
html: ``,
style: `@import url("https://payl8r.com/frontend/css/checkout-button-style.css");
#payl8r-middleware-checkout {
float: right;
clear: none;
width: 225px;
margin-left: 8px;
font-size: 1em !important;
}`,
buttonNth: 3,
parentSelector: `.step__footer`,
shouldDisplay: () =>
window.ShopifyAnalytics &&
window.ShopifyAnalytics.meta &&
window.ShopifyAnalytics.meta.page &&
window.ShopifyAnalytics.meta.page.path &&
(
window.ShopifyAnalytics.meta.page.path == "/checkout/contact_information" ||
window.ShopifyAnalytics.meta.page.path == "/checkout/shipping" ||
window.ShopifyAnalytics.meta.page.path == "/checkout/payment"
),
initialise: () => {
if(middleware.checkoutPage.shouldDisplay()) {
const submitArea = middleware.selectOne(middleware.checkoutPage.parentSelector);
const checkoutButton = middleware.create(middleware.checkoutPage.html);
// Insert last of at nth position if specified
middleware.insert(submitArea, checkoutButton, middleware.checkout.buttonNth);
// Insert css
middleware.insertCss(middleware.checkoutPage.style);
// Add the event handler
middleware.onClick(`#payl8r-middleware-checkout, #payl8r-middleware-checkout *`, middleware.checkoutPage.clicked);
}
},
clicked: (event = null) => {
middleware.post(`${middleware.config.domain}/v1/checkout`, {
checkout_token: Shopify.Checkout.token
})
}
}
// Only available within checkout page
middleware.checkoutPage.details = {
selectorDiscount: '.reduction-code__text',
selectorEmail: '.review-block__content bdo[dir=ltr]',
selectorAddress: '.address.address--tight',
getDiscount: () => new Promise((resolve, reject) => {
middleware.checkoutPage.getCurrentPageElement(middleware.checkoutPage.discounts.selector)
.then(nodes => {
if(nodes.length > 0) {
return resolve(Array.from(new Set(nodes.map(x => x.innerText))));
}
return resolve([]);
});
}),
getEmail: () => new Promise((resolve, reject) => {
middleware.checkoutPage.getCurrentPageElement(middleware.checkoutPage.contactInformation.selectorEmail)
.then(nodes => {
return (nodes.length > 0) ? resolve(nodes.map(x => x.innerText).join('')) : resolve(null);
});
}),
getAddress: () => new Promise((resolve, reject) => {
middleware.checkoutPage.getCurrentPageElement(middleware.checkoutPage.contactInformation.selectorAddress)
.then(nodes => {
return (nodes.length > 0) ? resolve(nodes.map(x => x.innerText).join('').trim().split(', ')) : resolve(null);
});
})
};
middleware.config.enabled = false; // No retailer found, domains searched:
//
if(middleware.config.enabled) {
// If login is not set to required or customer id available
middleware.checkout.initialise();
middleware.calculator.initialise();
// middleware.checkoutPage.initialise();
}
// Expose middleware to frontend
window.payl8r_middleware = middleware;
document.payl8r_middleware = middleware;
})();