$('[name="ShippingCountryCode"], [name="BillingCountryCode"]').change(function(){
var countrycode = $(this).val();
if(ScnStore.IsCISCountry(countrycode) && !ScnStore.IsCISWebssite()) {
$('[name="ShippingCountryCode"]').val('');
$('[name="BillingCountryCode"]').val('');
ScnStore.ShowRUMessage(countrycode);
}
});
/**/
;(function(factory){
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else if (typeof exports !== 'undefined') {
module.exports = factory(require('jquery'));
} else {
factory(jQuery);
}
})(function($){
// Prevent console.log() from breaking IE and other old browsers
if ( ! window.console ) console = { log: function(){} };
/*
* The constructor function for ScnStore
*/
function ScnStore(element, settings) {
this.defaults = {
logLevel: 'error',
locale: 'en',
prodLang: 'en',
currency: 'USD',
miniCart: {
Container: '',
AjaxUrl: '',
Enabled: false
},
checkoutUrl: 'checkout.action',
successUrl: 'thank-you.action',
orderForm: {}, // Defaults to empty
cardTypes: {
visa: '^4',
amex: '^3[47]',
mastercard: '^5[1-5]\d{2}|^2(?:2(?:2[1-9]|[3-9]\d)|[3-6]\d\d|7(?:[01]\d|20))',
discover: '^6(?:011|5)',
jcb: '^(?:2131|1800)',
dinersclub: '^3(?:0[0-5]|[68])'
}
};
// We create a new property to hold our default settings after they
// have been merged with user supplied settings
this.settings = $.extend({},this,this.defaults,settings);
// This object holds values that will change as the plugin operates
this.initials = {
lastCart: {},
orderForm: {},
isPageLoad: true,
countryList: {},
currency: this.settings.currency
};
// Attaches the properties of this.initials as properties of ScnStore
$.extend(this,this.initials);
// Here we'll hold a reference to the html tag of the document
this.$el = $('html');
// We'll call our initiator function to get things rolling!
this.init();
}
/**
* Called once per instance
* Calls starter methods and associate the '.zippy-carousel' class
* @params void
* @returns void
*/
ScnStore.prototype.init = function(){
// Add a class to the body so we can add styles
this.$el.addClass('scnstore');
// Pull in the logger library so we can have good logging that doesn't
// break browsers and preserves stack traces (loglevel.js)
this.log = window.log.noConflict();
this.log.setLevel(this.settings.logLevel);
// Bind any events to make stuff happen on the real
this.activate();
// Register the events. Let the user click stuff to make things happen
this.events();
// Activate the mini cart if it's enabled
if (this.settings.miniCart.Enabled) {
this.MiniCart.init();
}
// Tell the app that the page loaded (should be last)
this._pageLoaded();
};
/**
* Activates the initial store stuff when the page loads
* @params void
* @returns void
*
*/
ScnStore.prototype.activate = function(){
// Update the cart contents as soon as we get to the page.
this.CartContents();
// Load in the order form data if any is set
this._loadOrderForm();
// Set any defaults for page load
this._initPaymentForm();
};
/**
* Associate event handlers to events
* For all events we'll add them in here.
* @params void
* @returns void
*
*/
ScnStore.prototype.events = function(){
// It's like that and like this and like that and uh...
var that = this;
// Find all the AddToCart buttons and attach a listener to each one
$('body').on('click', '.scnstore-atc', function(){
var $el = $(this);
var prodId = $el.data('store-product-id');
var qty = ($el.data('store-qty'))? $(this).data('store-qty'): 1;
that._failIfUndefined(prodId,
'data-store-product-id not set on the element!');
that._logger('Add To Cart click detected', 'info');
that._logger('Data on click: '+prodId+' with qty:'+qty);
// Add a loading class to the element
// (Remove it when the response comes back)
$el.addClass('loading');
that.AddToCart(prodId, qty, $el);
});
// Remove item from the cart by uniqueId
$('.scnstore-remove').on('click', function(e){
e.preventDefault();
var uniqueId = $(this).data('store-unique-id');
that._failIfUndefined(uniqueId,
'data-store-unique-id not set on the element!');
that._logger('Remove line item click detected', 'info');
that._logger('Data on click: ' + uniqueId);
that.RemoveLineItem(uniqueId);
});
// External Payment Trigger
$('.external-payment-button').on('click', function(e){
e.preventDefault();
var paymentMethod = $(this).data('payment-method');
if(paymentMethod == "service_sberbank") {
var form = $('#order_form').validate();
if(!form.checkForm()) {
form.showErrors();
return;
}
}
that._failIfUndefined(paymentMethod,
'data-payment-method not set on the element!');
$(this).addClass('disabled').addClass('wait');
that._logger('External payment click detected', 'info');
that._logger('Data on click: ' + paymentMethod);
that.BeginExternalPayment(paymentMethod);
});
// I WISH THE BELOW WASN'T SO HORRIBLE LOOKING. BUT IT WORKS
/**
* ATTACH LISTENERS FOR COUNTRY AND STATE FIELDS
*
* These listen for changes on the country and state fields which
* will fire all the updates on the lists themselves.
*/
if (! $.isEmptyObject(this.orderForm)) {
that = this;
var form = this.orderForm;
// One for billing address
if (this._isset(form.BillingAddressData)) {
$('#'+form.BillingAddressData.CountryCode.Id).change(function() {
that.UpdateStateList(
form.BillingAddressData.StateProvince.Id,
form.BillingAddressData.CountryCode.Id
);
});
// When the state changes we should check if "other" was selected
// and handle appropriately
$('#'+form.BillingAddressData.StateProvince.Id).change(function() {
that._checkForOtherStateValAndHandle(
that.orderForm.BillingAddressData.StateProvince.Id
);
});
}
//
if (this._isset(form.ShippingAddressData)) {
$('#'+form.ShippingAddressData.CountryCode.Id).change(function() {
that.UpdateStateList(
form.ShippingAddressData.StateProvince.Id,
form.ShippingAddressData.CountryCode.Id
);
});
// Shipping one, too
$('#'+form.ShippingAddressData.StateProvince.Id).change(function() {
that._checkForOtherStateValAndHandle(
that.orderForm.ShippingAddressData.StateProvince.Id
);
});
}
/**
* ATTACH LISTENERS FOR ALL FORM FIELDS
*
* These listen for changes on the defined fields which will update
* the order form via ajax calls
*/
// Elements to for change/blur to call UpdateCart
var onblur = {
ShippingAddressData: [
'Email', 'FirstName', 'LastName', 'Address1', 'Address2', 'City',
'PostalCode', 'Phone'
],
BillingAddressData: [
'FirstName', 'LastName', 'Address1', 'Address2', 'City',
'PostalCode', 'Phone'
],
};
var onchange = {
ShippingAddressData: ['StateProvince', 'CountryCode'],
BillingAddressData: ['StateProvince', 'CountryCode']
};
// Use with extreme caution! Will chew up resources if you're not careful
var oninput = {
ShippingAddressData: ['PostalCode'],
BillingAddressData: ['PostalCode']
};
// Loop through onblur elements and attach listeners to them.
$.each(onblur, function(section, fields) {
for (var i = fields.length - 1; i >= 0; i--) {
if (that._isset(form[section][fields[i]])) {
var shipElemId = form[section][fields[i]].Id;
$(document).on('blur', '#'+shipElemId, function() {
that.UpdateOrderForm();
});
}
}
});
// Loop through onchange elements in the same way
$.each(onchange, function(section, fields) {
for (var i = fields.length - 1; i >= 0; i--) {
if (that._isset(form[section][fields[i]])) {
var shipElemId = form[section][fields[i]].Id;
$('#'+shipElemId).change(function() {
that.UpdateOrderForm();
});
}
}
});
// And the same with the oninput elements
$.each(oninput, function(section, fields) {
for (var i = fields.length - 1; i >= 0; i--) {
if (that._isset(form[section][fields[i]])) {
var field = fields[i];
var shipElemId = form[section][field].Id;
$('#'+shipElemId).on('input', function() {
// Special handling for postcodes. We don't want to send a call on every
// keystroke if we don't need to. This will only check ones that are on the
// list of postcode types to check
if (field == 'PostalCode') {
var countryCode = $('#'+form[section].CountryCode.Id).val();
if ($.inArray(countryCode, that._shipQuoteValidationList.PostCodeRequired) !== -1) {
that.UpdateOrderForm();
}
}
});
}
}
});
// Attach the credit card number handler here so it handles input
// standardly. This has been piloted on other payment forms with
// success.
if (form.hasOwnProperty('PaymentMethods')) {
if (form.PaymentMethods.hasOwnProperty('creditcard')) {
var cardId = form.PaymentMethods.creditcard.AccountNumber.Id;
// Attach input listener
$(document).on('input', '#'+cardId, function() {
// take the input by keystroke and format the digits
// in groups of four with no non-digit characters.
var initVal = $(this).val();
$(this).val(that._cardFormat(initVal));
// Auto-select the card type icons based on first digits
// of the card number
var cardType = that._getCardType($(this).val());
if (cardType) {
that._setCardType(cardType);
} else {
that._unsetAllCardTypes();
}
});
}
}
$(document).on('click', '.payment-card', function() {
var types = that.settings.cardTypes;
var $el = $(this);
$.each(types, function(card, regex) {
if ($el.hasClass(card)) {
that._unsetAllCardTypes();
$el.addClass('active');
}
});
});
// LANGUAGE UPDATE
$('.cart-item .language').change(function() {
var itemCode = $(this).val();
var uniqueId = $(this).closest('.cart-item')
.data('scnstore-uniqueid');
that._logger('Updating language', 'info');
that._logger('Unique ID: ' + uniqueId);
that._logger('New item code: ' + itemCode);
$(this).attr('disabled', true);
that.ChangeLanguage(uniqueId, itemCode);
});
// QUANTITY UPDATE
$('.cart-item .item-qty').change(function() {
var qty = $(this).val();
var uniqueId = $(this).closest('.cart-item')
.data('scnstore-uniqueid');
that._logger('Updating quantity', 'info');
that._logger('Unique ID: ' + uniqueId);
that._logger('New qunatity: ' + qty);
$(this).attr('disabled', true);
that.ChangeQuantity(uniqueId, qty);
});
}
};
/**
* AddToCart
* Triggers a set of subfunctions to add a specified item to the cart with
* a specified quantity
* @params string productId
* @returns object cart contents
*
*/
ScnStore.prototype.AddToCart = function(productId, qty, $el, callback){
// If no qty was passed in, then set it to 1.
if ( ! this._isset(qty) || qty == 0) {
qty = 1;
}
this._logger('Called AddToCart with: ', 'table');
this._logger({ProductID:productId,Quantity:qty});
var req = this._jsonRpcRequest("CartApi.AddToCart", [{
LineItem: {
ProductId: productId,
Quantity: qty,
},
}]);
$.ajaxq('scnstoreq', $.extend(this._jsonBp, {
context: this,
data: {r:JSON.stringify(req)},
success: function(data){
// If the call returns an error, handle it appropriately
this._displayError(data);
if (data.hasOwnProperty('result')) {
this._updateCart(data.result);
}
// Remove the loading class if it's set on the calling element
// Then add the confirmation popover if warranted
if (this._isset($el)) {
$el.removeClass('loading');
if (this._isset('callback') && callback == 'inline') {
this._atcInline($el);
return;
}
// Otheriwse fire the feedback effect
// this._atcPopover($el);
this._atcBtnFeedback($el);
}
},
failure: function(errMsg) {
this._logger('AddToCart wasn\'t successful', 'warn');
this._throwError(errMsg);
$el.removeClass('loading');
},
}));
};
/**
* CartContents
* Get an object with the full cart in it
* @params none
* @returns object cart contents
*
*/
ScnStore.prototype.CartContents = function(callback){
this._logger('Called CartContents', 'info');
var req = this._jsonRpcRequest("CartApi.CartContents");
$.ajaxq('scnstoreq', $.extend(this._jsonBp, {
context: this,
data: {r:JSON.stringify(req)},
success: function(data){
// If the call returns an error, handle it appropriately
if (data.hasOwnProperty('result')) {
this._updateCart(data.result);
}
this._displayError(data);
callback && callback();
},
failure: function(errMsg) {
this._logger('CartContents wasn\'t successful', 'warn');
this._throwError(errMsg);
},
}));
};
/**
* LineItemCount
* Return the number of items in the shopping cart
* @params none
* @returns int
*
*/
ScnStore.prototype.LineItemCount = function(){
if (this.hasOwnProperty('lastCart')) {
if ($.isEmptyObject(this.lastCart) ||
this.lastCart == false ||
this.lastCart.LineItems == null) {
return 0;
}
} else {
return 0;
}
return this.lastCart.LineItems.length;
};
/**
* GenerateShareLink
* Return the link to share on checkout page to share cart items
* @params none
* @returns int
*
*/
ScnStore.prototype.GenerateShareLink = function(){
// var urlPath = "/store/checkout.action";
var url = new URL(window.location.href);
url.search = "";
var orgId = $('[name="call-center-cos"]').val();
if(orgId && orgId != "") {
url.searchParams.set("orgid", orgId);
}
var ref = $('[name="staff-member"]').val();
if(ref && ref != "") {
url.searchParams.set("ref", ref);
}
var promoCode = $('[name="promo-code"]').val();
if(promoCode && promoCode != "") {
url.searchParams.set("promocode", promoCode);
}
if (this.hasOwnProperty('lastCart')) {
if ($.isEmptyObject(this.lastCart) ||
this.lastCart == false ||
this.lastCart.LineItems == null) {
return url.toString();
}
} else {
return url.toString();
}
for (var i = 0; i < this.lastCart.LineItems.length; i++) {
var item = this.lastCart.LineItems[i];
var id = i + 1;
url.searchParams.set("item" + id, item.ProductId);
url.searchParams.set("qty" + id, item.Quantity);
}
return url.toString();
};
/**
* CHANGE THE LANGUAGE OF AN ITEM
*
* Updating a line item langugae consists of passing in a new item code which
* contains the language and the unique product ID.
*
* Return the number of items in the shopping cart
* @params uniqueId
* @params newItemCode
* @returns new item
*
*/
ScnStore.prototype.ChangeLanguage = function(uniqueId, newItemCode) {
var that = this;
// Do some checks
if ( ! this._isset(uniqueId)) {
this._logger('The item\'s unique ID is required.', 'error');
return;
}
if ( ! this._isset(newItemCode)) {
this._logger('The new item code with proper language is required.', 'error');
return;
}
var newItem = {
UniqueId: uniqueId,
ProductId: newItemCode
};
// On success, lets try to slap the new item data on there
this.UpdateLineItem(newItem);
};
/**
* CHANGE THE QUANTITY OF AN ITEM
*
* Updating a line item quantity consists of passing in a new int(quantity)
* and calling the ajax gods from above
*
* Return the number of items in the shopping cart
* @params uniqueId
* @params newItemCode
* @returns new item
*
*/
ScnStore.prototype.ChangeQuantity = function(uniqueId, qty) {
if ( ! this._isset(uniqueId)) {
this._logger('The item\'s unique ID is required.', 'error');
return;
}
if ( ! this._isset(qty) && /^\d+$/.test(qty)) {
this._logger('The new item quantity is required and must be a number.', 'error');
return;
}
// If it was set to 0, remove the line item from the cart
if (qty == 0) {
if ($('li[data-scnstore-uniqueid='+uniqueId+']').find('.delete').length) {
// Simulate a click on this trigger as it's set up with css and such, keeping it simple
$('li[data-scnstore-uniqueid='+uniqueId+']').find('.delete').click();
return;
}
this.RemoveLineItem(uniqueId);
}
var newItem = {
UniqueId: uniqueId,
Quantity: qty
};
// On success, lets try to smack the new item data on there
this.UpdateLineItem(newItem);
};
/**
* UpdateLineItem
* Update an item in the cart
*
* Looks like this:
* {UniqueId: "b1g70ng5tr1ng", ProductId: "dmsmh.en.hc", etc.}
*
* You don't have to put all the params on there if you don't want to but
* you certainly could if you wanted to. The key is the unique id
*
* @params none
* @returns object cart contents
*
*/
ScnStore.prototype.UpdateLineItem = function(newItem){
this._logger('Called UpdateLineItem...', 'info');
var that = this;
var li = {};
$.each(this.lastCart.LineItems, function(item, attrs) {
if (attrs.UniqueId == newItem.UniqueId) {
li = that.lastCart.LineItems[item];
}
});
if (li.length == 0) {
this._logger('Item not found by unique id', 'error');
return;
}
// Roll them all together
li = $.extend({}, li, newItem);
var req = this._jsonRpcRequest("CartApi.UpdateLineItem", [{
LineItem: li,
}]);
this._logger('New item updating to:');
this._logger(li);
$.ajaxq('scnstoreq', $.extend(this._jsonBp, {
context: this,
data: {r:JSON.stringify(req)},
success: function(data){
this._logger('UpdateLineItem successful:', 'info');
if (data.hasOwnProperty('result')) {
this._updateCart(data.result);
$.each(data.result.LineItems, function(i, item) {
if (item.UniqueId == newItem.UniqueId) {
that._updateProductAttrs(item);
}
});
}
this._displayError(data);
},
failure: function(errMsg) {
this._logger('CartContents wasn\'t successful', 'warn');
this._throwError(errMsg);
},
}));
this.UpdateOrderForm();
return true;
};
/**
* RemoveLineItem
* Removes a line item with the specified unique line item id
* @params string line item id
* @returns object cart contents
*
*/
ScnStore.prototype.RemoveLineItem = function(uniqueId){
this._logger('Called RemoveLineItem with: ', 'info');
this._logger({uniqueId:uniqueId});
var req = this._jsonRpcRequest("CartApi.RemoveLineItem", [{
LineItem: {
UniqueId: uniqueId
},
}]);
$.ajaxq('scnstoreq', $.extend(this._jsonBp, {
context: this,
data: {r:JSON.stringify(req)},
success: function(data){
// If the call returns an error, handle it appropriately
this._displayError(data);
if (data.hasOwnProperty('result')) {
this._updateCart(data.result);
}
},
failure: function(errMsg) {
this._logger('RemoveLineItem call failed', 'warn');
this._throwError(errMsg);
},
}));
if ($.isEmptyObject(this.orderForm)) {
this.UpdateOrderForm();
}
};
/**
* SetReferrerName
* Sets ReferrerName
* @params referrerName
* @returns ---
*
*/
ScnStore.prototype.SetReferrerName = function(referrerName) {
var attrs = {
referrer_name: referrerName,
};
var that = this;
var req = this._jsonRpcRequest("CartApi.SetReferrerName", [attrs]);
$.ajaxq('scnstoreq', $.extend(this._jsonBp, {
context: ScnStore,
data: {r:JSON.stringify(req)},
success: function(data){
// If the call returns an error, handle it appropriately
that._displayError(data);
if (data.hasOwnProperty('result')) {
that._updateCart(data.result);
}
},
failure: function(errMsg) {
that._logger('SetReferrerName call failed', 'error');
that._throwError(errMsg);
},
}));
};
/**
* SetPromoCode
* Sets ReferrerName
* @params promoCode
* @returns ---
*
*/
ScnStore.prototype.SetPromoCode = function(promoCode) {
var attrs = {
promo_code: promoCode,
};
var that = this;
var req = this._jsonRpcRequest("CartApi.SetPromoCode", [attrs]);
$.ajaxq('scnstoreq', $.extend(this._jsonBp, {
context: ScnStore,
data: {r:JSON.stringify(req)},
success: function(data){
// If the call returns an error, handle it appropriately
that._displayError(data);
if (data.hasOwnProperty('result')) {
that._updateCart(data.result);
}
},
failure: function(errMsg) {
that._logger('SetPromoCode call failed', 'error');
that._throwError(errMsg);
},
}));
};
/**
* SetMembershipNumber
* Sets ReferrerName
* @params promoCode
* @returns ---
*
*/
ScnStore.prototype.SetMembershipNumber = function(membershipNumber) {
var attrs = {
membership_number: membershipNumber,
};
var that = this;
var req = this._jsonRpcRequest("CartApi.SetMembershipNumber", [attrs]);
$.ajaxq('scnstoreq', $.extend(this._jsonBp, {
context: ScnStore,
data: {r:JSON.stringify(req)},
success: function(data){
// If the call returns an error, handle it appropriately
that._displayError(data);
if (data.hasOwnProperty('result') && data.result) {
that._updateCart(data.result);
$('.ias-error').addClass('hidden');
$('#ias_success').removeClass('hidden');
} else {
$('#ias_success').addClass('hidden');
$('#ias_error_invalid').removeClass('hidden');
}
},
failure: function(errMsg) {
that._logger('SetMembershipNumber call failed', 'error');
that._throwError(errMsg);
},
}));
};
/**
* GetCartShareLink
*
* @params ---
* @returns promise with link to share Shipping Address, Billing Address, IAS Num & Referrer name
*
*/
ScnStore.prototype.GetCartShareLink = function() {
var that = this;
var req = this._jsonRpcRequest("CartApi.EncodeShareCartLink", []);
return new Promise(function (resolve, reject) {
$.ajaxq('scnstoreq', $.extend(that._jsonBp, {
context: ScnStore,
data: {r:JSON.stringify(req)},
success: function(data){
// If the call returns an error, handle it appropriately
that._displayError(data);
console.log(data);
resolve(data.result.ResponseInfoList[0].Data);
},
failure: function(errMsg) {
that._logger('GetCartShareLink call failed', 'error');
that._throwError(errMsg);
},
}));
});
};
/**
* DecodeShareCartLink
*
* @params data -> string to decode
* @returns updates cart and checkout information
*
*/
ScnStore.prototype.DecodeShareCartLink = function(data) {
var attrs = {
data: data
};
var that = this;
var req = this._jsonRpcRequest("CartApi.DecodeShareCartLink", [attrs]);
$.ajaxq('scnstoreq', $.extend(this._jsonBp, {
context: ScnStore,
data: {r:JSON.stringify(req)},
success: function(data){
// If the call returns an error, handle it appropriately
that._displayError(data);
console.log(data);
that.UpdateCheckoutInfo(data.result);
},
failure: function(errMsg) {
that._logger('DecodeShareCartLink call failed', 'error');
that._throwError(errMsg);
},
}));
};
/**
* UpdateCheckoutInfo
*
* @params order -> order information
* @returns updates cart and checkout information
*
*/
ScnStore.prototype.UpdateCheckoutInfo = function(order) {
var form = this.orderForm;
var shipAddr = form.ShippingAddressData;
var billAddr = form.BillingAddressData;
if (typeof shipAddr !== "undefined") {
$('[name="' + shipAddr.Email.Id + '"]').val(order.ShippingAddressData.Email);
$('[name="' + shipAddr.FirstName.Id + '"]').val(order.ShippingAddressData.FirstName);
$('[name="' + shipAddr.LastName.Id + '"]').val(order.ShippingAddressData.LastName);
$('[name="' + shipAddr.Address1.Id + '"]').val(order.ShippingAddressData.Address1);
$('[name="' + shipAddr.Address2.Id + '"]').val(order.ShippingAddressData.Address2);
$('[name="' + shipAddr.CountryCode.Id + '"]').val(order.ShippingAddressData.CountryCode);
$('[name="' + shipAddr.Phone.Id + '"]').val(order.ShippingAddressData.Phone);
$('[name="' + shipAddr.City.Id + '"]').val(order.ShippingAddressData.City);
$('[name="' + shipAddr.StateProvince.Id + '"]').val(order.ShippingAddressData.StateProvince);
$('[name="' + shipAddr.PostalCode.Id + '"]').val(order.ShippingAddressData.PostalCode);
}
if (typeof billAddr !== "undefined") {
$('[name="' + billAddr.FirstName.Id + '"]').val(order.BillingAddressData.FirstName);
$('[name="' + billAddr.LastName.Id + '"]').val(order.BillingAddressData.LastName);
$('[name="' + billAddr.Address1.Id + '"]').val(order.BillingAddressData.Address1);
$('[name="' + billAddr.Address2.Id + '"]').val(order.BillingAddressData.Address2);
$('[name="' + billAddr.Phone.Id + '"]').val(order.BillingAddressData.Phone);
$('[name="' + billAddr.CountryCode.Id + '"]').val(order.BillingAddressData.CountryCode);
$('[name="' + billAddr.City.Id + '"]').val(order.BillingAddressData.City);
$('[name="' + billAddr.StateProvince.Id + '"]').val(order.BillingAddressData.StateProvince);
$('[name="' + billAddr.PostalCode.Id + '"]').val(order.BillingAddressData.PostalCode);
}
$('[name="promo-code"]').val(order.PromoCode);
$('[name="IasNumber"]').val(order.MembershipNumber);
$('[name="staff-member"]').val(order.ReferrerName);
// apply discount
if(order.MembershipNumber != "" && order.PromoCode != "") {
$('#IasApplyBtn').click();
}
};
/**
* UPDATE UI
* General callback function that you can tie things to if you want to
* update them in the UI after the cart gets updated in any way.
* @params ---
* @returns ---
*
*/
ScnStore.prototype.UpdateUI = function() {
this._logger('Updating the UI...', 'info');
this._logger('The last cart is');
this._logger(this.lastCart);
var $counter = $('.store-cart-items-count');
// Check if there is an store-cart-items-count and update
if ($counter.length) {
if (this.LineItemCount() != 0) {
$counter.text(this.LineItemCount()).removeClass('invisible');
} else {
$counter.text('').addClass('invisible');
}
}
if (this.settings.miniCart.Enabled) {
// Update the minicart (it will check if its enabled or not)
this.UpdateMiniCart();
}
// If this is a respnse from a full recalculate, better get to updating
// all the shipping options and such
this._setShippingOptions();
this.UpdateCartTotals();
this.UpdateItemListing();
};
/**
* UPDATE CART TOTALS
*
* Updates the cart items in the checkout page via ajax and replaces the
* li in the DOM with the response. The template does all the PT
* cart accessing.
* @params ---
* @return bool
*/
ScnStore.prototype.UpdateCartTotals = function() {
this._logger('Updating Cart Totals...', 'info');
// Keep this as a pet named that
var cart = this.lastCart,
shipping = cart.Shipping,
subtotal = cart.Subtotal,
tax = cart.Tax,
discount = cart.Discount,
customs = cart.Customs,
total = cart.Total,
locale = this.settings.locale.split('_'),
currency = this.settings.currency,
lang = locale[0],
fmtc = window.FormatCurrency, // Currency formatter function
that = this;
// update totals and stuff
$totals = $('#totals_box');
$totals.find('.subtotal .totals-data bdi').html(fmtc(lang, currency, subtotal, false));
$totals.find('.shipping .totals-data bdi').html(fmtc(lang, currency, shipping, false));
$totals.find('.discount .totals-data bdi').html("-"+fmtc(lang, currency, discount, false));
$totals.find('.customs .totals-data bdi').html(fmtc(lang, currency, customs, false));
$totals.find('.tax .totals-data bdi').html(fmtc(lang, currency, tax, false));
$totals.find('.grand-total .totals-data bdi').html(fmtc(lang, currency, total, false));
// Conditional values.
var cond = ['tax', 'discount', 'customs'];
$('#totals_box .discount').attr('data-amount', discount);
$('#totals_box .customs').attr('data-amount', customs);
$('#totals_box .tax').attr('data-amount', tax);
for (var i = cond.length - 1; i >= 0; i--) {
if ($totals.find('.'+cond[i]).attr('data-amount') > 0 || cart[cond[i]] > 0) {
$totals.find('.'+cond[i]).removeClass('hidden');
} else {
$totals.find('.'+cond[i]).addClass('hidden');
}
}
};
/**
* UpdateItemListing
* Updated page html and adds classes to the items which are added to the cart.
* Then CSS makes page look different (such as "Added to Card" button)
*/
ScnStore.prototype.UpdateItemListing = function() {
$('[data-listing-product-id]').removeClass('listing__item--added-to-cart');
if (this.lastCart.LineItems) {
$.each(this.lastCart.LineItems, function(index, item) {
$('[data-listing-product-id="'+item.ProductId+'"]').addClass('listing__item--added-to-cart');
});
}
};
/**
* UpdateMiniCart
*
* Updates the minicart via ajax and replaces it in the DOM with the
* response. The template does all the PT cart accessing.
* @params ---
* @return bool
*/
ScnStore.prototype.UpdateMiniCart = function() {
this._logger('Updating Mini Cart...', 'info');
// Preserve this
var that = this;
// Update the mini_cart if it's enabled
$.get( this.settings.miniCart.AjaxUrl, function( data ) {
$( that.settings.miniCart.Container ).html( data );
// If this is not a pageload then show the cart for a few seconds
// then hide it again.
if (that.MiniCart.skipMiniCart == false) {
that.MiniCart.showMiniCart(3000);
}
// Now that it's after the first time (pageload), allow the minicart to show
that.MiniCart.skipMiniCart = false;
// Bind all the minicart DOM stuff to the JS that controls it
that._logger('Updated the Mini Cart', 'info');
return true;
});
};
ScnStore.prototype.MiniCart = {
timeoutId: 0,
skipMiniCart: true,
init: function() {
// Bind the events to the object
this.events();
},
// Minicart module manipulation
hideMiniCart: function() {
$('#mini_cart').removeClass('active');
setTimeout(function(){$('#mini_cart').addClass('hidden');}, 250);
},
showMiniCart: function(duration) {
var that = this;
// Only do this if there is something in the cart
$('#mini_cart').delay(200).removeClass('hidden').addClass('active');
if (typeof that.timeoutId !== "undefined") {
clearTimeout(that.timeoutId);
}
// If there was a duration set, then set the timeout here so the
// minicart will auto-hide.
if (typeof duration !== "undefined") {
that.timeoutId = setTimeout(function(){
that.hideMiniCart();
}, duration);
}
},
// Individual item manipulation
removeItem: function($el) {
var obj = window.ScnStore;
// Trigger the removal animation
$el.addClass('deleted');
// Timeout and remove the item from the DOM after the CSS removes it.
setTimeout(function(){
$el.remove();
obj.RemoveLineItem($el.data('scnstore-uniqueid'));
}, 300);
},
events: function() {
// Maintain this
var that = this;
// Mouse enters the cart button banner, we add active class
// to make the minicart appear
$('#banner_shopping_cart').on('mouseenter', function() {
if (window.ScnStore.LineItemCount() > 0) {
that.showMiniCart();
}
});
// Mouse leaves cart button banner, set timer to remove
// class "active" in a half of a second
$('#banner_shopping_cart').on('mouseleave', function() {
that.timeoutId = setTimeout(function(){that.hideMiniCart();}, 250);
});
// However, of the mouse then enters the minicart itself,
// clear the destruction timer and leave the class intact
$( "#mini_cart_outer" ).on('mouseenter', '#mini_cart', function() {
if (typeof that.timeoutId !== "undefined") {
clearTimeout(that.timeoutId);
}
});
// Leaves minicart, start the timer
$("#mini_cart_outer").on('mouseleave', '#mini_cart', function() {
that.timeoutId = setTimeout(function(){that.hideMiniCart();}, 500);
});
// ScnStore functionality Remove Line Item from cart
$('#mini_cart_outer').on('click','.minicart-item .delete',function() {
that.removeItem($(this).closest('.minicart-item'));
});
// Click on a close minicart button and comply
$("#mini_cart_outer").on('click', 'button.minicart-close', function(e) {
e.preventDefault();
that.hideMiniCart();
});
}
};
/**
* UpdateOrderForm
*
* The idea on the this is to take whatever data has been filled out on the
* order form and send it to the server. This takes care of all the logic
* that determines if we're doing a full recalculate or not.
*
* @params ---
* @return bool
*/
ScnStore.prototype.UpdateOrderForm = function() {
this._logger('Updating order form...', 'info');
if ( ! this._isset(this.orderForm)) {
this._logger('The order form is not set. Readme tells how to set it.', 'warn');
return;
}
// Conjur up a recepticle of the coveted input
var OrderFormData = {
FullRecalculate: false,
ShippingAddressData: {},
BillingAddressData: {},
ShippingOptionData: {}
};
// It's like that and like this and like that and uh...
var that = this;
// Capture the data in all the fields.
$.each(that.orderForm, function(section, fields) {
// Capture the shipping address data
if (section == "ShippingAddressData") {
// Stuff the address details into the object
$.each(fields, function(field, attr) {
var val = $('#' + attr.Id).val();
/**
* The "other" state field is an option for the user in case
* his region is not in the dropdown. If it's there we copy
* it into the normal state field's data object so it gets
* used by the backend when we send all the data over.
*/
if (field == 'StateProvince' && val == "other") {
val = $('#ShippingStatePorvince_other').val();
}
OrderFormData.ShippingAddressData[field] = val;
});
that._logger("Captured shipping address as:", 'info');
that._logger(OrderFormData.ShippingAddressData);
// continue to the next section...
}
// Capture the billing address fields
if (section == "BillingAddressData") {
// Stuff the address details into the object
$.each(fields, function(field, attr) {
var val = $('#' + attr.Id).val();
if (field == 'StateProvince' && val == "other") {
val = $('#BillingStatePorvince_other').val();
}
OrderFormData.BillingAddressData[field] = val;
});
that._logger("Captured billing address as:", 'info');
that._logger(OrderFormData.BillingAddressData);
}
// Capture the shipping method selected
if (section == "ShippingOptions") {
var val = $('input[name=ShipOption]').val();
OrderFormData.ShippingOptionData.ShipOption = val;
that._logger("Captured shipping option as:", 'info');
that._logger(OrderFormData.ShippingOptionData);
}
});
/**
* MERGE SHIPPING INTO BILLING IF BILLING IS BLANK
*
* Check if we have a billing address set by looking at address1
* we just copy over the shipping to the billing and hope for the best.
* Maybe should look at making this more intelligent
*/
if ( ! OrderFormData.BillingAddressData.Address1) {
this._logger('No bill addr set, copy from ship addr...', 'info');
// Copy happy shipping data to billaddr, Bob Ross style...
var sad = JSON.parse(JSON.stringify(OrderFormData.ShippingAddressData));
OrderFormData.BillingAddressData = sad;
this._logger('Merged ship addr into bill addr:', 'info');
this._logger(OrderFormData.BillingAddressData);
}
/**
* SET THE BILLING EMAIL ADDRESS ALWAYS BECAUSE ONLY THE SHIPPING ONE GETS INPUT
*
*/
OrderFormData.BillingAddressData.Email = OrderFormData.ShippingAddressData.Email;
/**
* DO A SEPERATE CALL TO SET THE SHIPPING OPTION
*
* This is how it's set up, I didn't write it that way. Anyhow...
* currently if we do another recalc and this is not set it will error.
*/
if (OrderFormData.ShippingOptionData.ShipOption) {
var shipOption = OrderFormData.ShippingOptionData.ShipOption;
this.SelectShippingOption(shipOption);
}
/**
* FULL RECALCULATE CHECK
*
* Check the required fields on shipping address to determine if
* we should do a full recalculate or not. The full reclac basically
* says to recalculate eveything including shipping options and
* all the customs, tax, etc.
*
* The fields required depend on the city and postal code. If the country
* doesn't require postal codes per shippers as defined in the postalcode
* map then we don't need it and only use the city.
*
* State/region is NOT considered for shipping rates. And theoretically,
* if we can calculate shipping we can also calculate taxes/customs. <----- IMPORTANT
*/
var ship = OrderFormData.ShippingAddressData;
var pcr = this._shipQuoteValidationList.PostCodeRequired;
var rr = this._shipQuoteValidationList.RegionRequired;
// FIRST CHECK IF THE POSTAL CODE IS REQUIRED FOR "FULL REFRESH"
// This means we flag the call to return totals, shipping data, etc.
if ($.inArray(ship.CountryCode, pcr) !== -1) {
this._logger('The post code is required', 'warn');
if (ship.PostalCode.length > 0) {
this._logger('And it\'s set to '+ship.PostalCode+' so set flag to "true"');
OrderFormData.FullRecalculate = true;
}
// NEXT CHECK IF THE REGION IS REQUIRED
} else if ($.inArray(ship.CountryCode, rr) !== -1) {
this._logger('The region is required', 'warn');
if (ship.StateProvince.length > 0) {
this._logger('And it\'s set to '+ship.StateProvince+' so set flag to "true"');
OrderFormData.FullRecalculate = true;
}
// OTHERWISE WE MINIMALLY NEED THE CITY
} else {
this._logger('The city is required', 'warn');
if (ship.City && ship.City.length > 0 && ship.CountryCode) {
this._logger('And it\'s set to '+ship.City+' so set flag to "true"');
OrderFormData.FullRecalculate = true;
}
}
// Check if taxes should be recalcualted because there are some taxes on the order already
if (this.lastCart.Tax || this.lastCart.Customs) {
OrderFormData.FullRecalculate = true;
}
// Send it to the server if the page didn't *just* load
if ( ! this.isPageLoad) {
var fr = OrderFormData.FullRecalculate;
this._logger('Calling bill and ship update. FullRecalc: '+fr);
this.SetBillingShippingAndRecalculate(OrderFormData, fr);
}
// ORG ORDER INFO ROUTING HANDLER
if (ship.City && ship.City.length > 0 && ship.CountryCode) {
this.GetLocalOrgData();
}
};
// Set the billing and shipping address and recalculate
ScnStore.prototype.SetBillingShippingAndRecalculate = function(address, fullRecalc) {
this._logger('Called SetBillingShippingAndRecalculate with: ', 'info');
this._logger(address);
// A HACK to prevent making cart empty when use lands to the confirmation page
if(window.location.pathname == "/store/review.action" && !address.ShippingAddressData.Email) {
console.log("Dont update the cart, empty data");
return;
}
var req = this._jsonRpcRequest("CartApi.SetBillingShippingAndRecalculate", [{
BillingAddressData: address.BillingAddressData,
ShippingAddressData: address.ShippingAddressData,
FullRecalculate: fullRecalc
}]);
return $.ajaxq('scnstoreq', $.extend(this._jsonBp, {
context: this,
data: {r:JSON.stringify(req)},
success: function(data){
// If the call returns an error, handle it appropriately
this._displayError(data);
if (data.hasOwnProperty('result')) {
return this._updateCart(data.result);
}
},
failure: function(errMsg) {
this._logger('SetBillingShippingAndRecalculate call failed', 'error');
this._throwError(errMsg);
},
}));
};
// Set the shipping option
ScnStore.prototype.SelectShippingOption = function(shipOption) {
this._logger('Called SelectShippingOption with: ', 'info');
this._logger(shipOption);
var req = this._jsonRpcRequest("CartApi.SelectShippingOption", [{
ShippingOptionUniqueId: shipOption,
}]);
$.ajaxq('scnstoreq', $.extend(this._jsonBp, {
context: this,
data: {r:JSON.stringify(req)},
success: function(data){
// If the call returns an error, handle it appropriately
this._displayError(data);
},
failure: function(errMsg) {
this._logger('SelectShippingOption call failed', 'warn');
this._throwError(errMsg);
},
}));
};
/**
* UPDATE STATE LIST
*
* When the state list needs to be updated, pass in the id's of the
* state list and corresponding country list ID for a good time.
*/
ScnStore.prototype.UpdateStateList = function(listId, cntyId) {
this._logger('Updating the state list for #' + listId, 'info');
$('#'+listId).addClass('disabled').attr('disabled', 'disabled');
$('#'+listId).parent().addClass('updating');
$('#'+listId+' option').remove();
this._setStateSelect(listId, $('#'+cntyId).val());
};
ScnStore.prototype.ApplyMemberDiscount = function() {
this._logger('Applying member discount.');
var fields = this.orderForm.IasMembership;
var member_info = {
number: $('#'+fields.IasNumber.Id).val(),
lifetime: $('#'+fields.IasLifetime.Id).is(':checked'),
exp_date: null
};
// If lifetime is not checked, add expiration data
if ( ! member_info.lifetime) {
member_info.exp_year = $('#'+fields.IasExpYear.Id).val();
member_info.exp_month = $('#'+fields.IasExpMonth.Id).val();
member_info.exp_date = member_info.exp_year+"-"+member_info.exp_month;
}
this._logger(member_info);
var this_year = new Date().getFullYear();
var this_month = new Date().getMonth();
// Validate the number
if (member_info.number.length > 19 || member_info.number.length < 16) {
$('#ias_error_number_length').removeClass('hidden');
return;
}
$('#ias_error_number_length').addClass('hidden');
// Validate the expiration if it's not a lifetime
if ( ! member_info.lifetime) {
var valid = false;
if (member_info.exp_year == this_year) {
if (member_info.exp_month >= this_month) {
valid = true;
}
}
else if(member_info.exp_year > this_year) {
valid = true;
}
// Validate the expiration date
if ( ! valid) {
$('#ias_error_expired').removeClass('hidden');
return;
} else {
$('#ias_error_expired').addClass('hidden');
}
}
// Hide all errors once past
$('.ias-error').addClass('hidden');
// SetPublicAttrs
var attrs = {};
attrs.public_ias_number = (member_info.number) ? member_info.number : null;
var req = this._jsonRpcRequest("CartApi.SetPublicAttrs", [{
OrderAttrs: attrs,
FullRecalculate: true
}]);
$.ajaxq('scnstoreq', $.extend(this._jsonBp, {
context: this,
data: {r:JSON.stringify(req)},
success: function(data){
// If the call returns an error, handle it appropriately
this._displayError(data);
if (data.hasOwnProperty('result')) {
$('.ias-error').addClass('hidden');
$('#ias_success').removeClass('hidden');
this._updateCart(data.result);
}
},
failure: function(errMsg) {
this._logger('SetBillingShippingAndRecalculate call failed', 'error');
this._throwError(errMsg);
},
}));
};
/**
* LOCAL ORG INFO NOTIFICATION
*
* This is one of those recursive callback data call things.
*/
ScnStore.prototype.GetLocalOrgData = function(orgData) {
var ship_address = this.lastCart.ShippingAddressData;
// Minimum criteria to fire the _loadOrgData() method
if ( ! ship_address.Address1 && ! ship_address.City && ! ship_address.CountryCode ) {
return false;
}
if ($('.hide-until-ship').hasClass('data-transfer-permission')) {
this._loadOrgData();
}
};
/**
* LOAD ORG DATA
*
* We use this to call the gelocator to get the local org to the public
* based on the shipping address information.
*
*/
ScnStore.prototype._loadOrgData = function(orgData) {
this._logger('Getting nearest org based on shipping address...', 'info');
if ( ! this._isset(orgData)) {
var url = "https://gd2.edit.firechrome.org/gcui-globaldata/get-org-for-book-order.action"; // Testing
//var url = "https://gd2.ondemandhosting.info/gcui-globaldata/get-org-for-book-order.action"; // Live
var sa = this.lastCart.ShippingAddressData;
var query =
"?street=" + sa.Address1 +
"&city=" + sa.City +
"&state=" + sa.StateProvince +
"&postcode=" + sa.PostalCode +
"&country=" + sa.CountryCode +
"&callback=ScnStore._loadOrgData";
$.ajaxq('scnstoreq', {
context: this,
url: url+query,
dataType: "script",
failure: function(errMsg) { this._throwError(errMsg);}
});
}
// Handle the callback response from the above request
else {
this._logger('Handling org data response', 'info');
this._logger(orgData, 'info');
var od = orgData.result.items[0];
// Edit the html nodes on the page
$('#nearest_church p.order-info-notice').html(
'Your order information will be sent to '+od.full_name.trim()+', your nearest Scientology Church.');
// Image and Org Data //
// ================== //
if ($('#local_org_card').attr('data-org-id') !== od.id) {
// Add the org ID to the wrapper
$('#local_org_card').attr('data-org-id', od.id);
$('.org-image').remove();
var img_url = 'https://files.ondemandhosting.info/imagecache/cropfit@w=180@cr=0,5,499,281@qa=85/data/www.scientology.org/themes/www_scientology_org2/images/generic-org02.jpg';
if (od.csiorgstatus === 'ideal') {
// Get the image filename from the org data response
ideal_img_url = 'https://files.ondemandhosting.info/imagecache/scalecrop-400x267-auto/data/shared/web/assets/videos/master_thumbs/org-'+od.id.trim()+'_tour.jpg';
// Test if image exists
var imghttp = $.ajax({
type:"HEAD",
url: ideal_img_url,
async: false
});
if (imghttp.status === 200) {
img_url = ideal_img_url;
}
}
// Make the image element
var oi = document.createElement('img');
oi.setAttribute('class', 'org-image img-fluid');
oi.setAttribute('alt', od.full_name);
oi.setAttribute('title', od.full_name);
oi.setAttribute('id', 'org_img_'+od.id);
oi.setAttribute('src', img_url);
oi.setAttribute('style', 'display: block; border-radius: 5px; max-height: 100px');
var oi_wrapper = document.getElementById('org_image_wrapper');
oi_wrapper.appendChild(oi);
// Address
$('#nearest_church p.org-address .org-street-address').html(od.address1);
$('#nearest_church p.org-address .org-city').html(od.city);
$('#nearest_church p.org-address .org-state-province').html(od.state_province);
$('#nearest_church p.org-address .org-postal-code').html(od.postal_code);
$('#nearest_church p.org-address .org-phone').html(od.phone);
}
// Reveal the thing
$('.hide-until-ship').removeClass('hidden');
// SetPublicAttrs
var attrs = {
public_NearestChurch: od.id,
public_ConsentType: 42
};
var req = this._jsonRpcRequest("CartApi.SetPublicAttrs", [{
OrderAttrs: attrs,
FullRecalculate: false
}]);
$.ajaxq('scnstoreq', $.extend(this._jsonBp, {
context: this,
data: {r:JSON.stringify(req)},
success: function(data){
// If the call returns an error, handle it appropriately
this._displayError(data);
},
failure: function(errMsg) {
this._logger('SetPublicAttrs call failed', 'error');
this._throwError(errMsg);
}
}));
}
};
/**
* BeginExternalPayment
*
* We have different payment methods which need to be initiated at some point
* they get started here.
*/
ScnStore.prototype.BeginExternalPayment = function(method) {
this._logger('Beginning External Payment using: ' + method, 'info');
// Find out what payment method is selected and handle accordingly
payMethods = window.ScnStore.orderForm.PaymentMethods;
if ( ! payMethods.hasOwnProperty(method)) {
this._logger('Payment method "'+method+'" is not defined in the settings. Aborting.', 'warn');
return;
}
var itemTitles = [];
$('.cart-item .title').each( function(index, val) {
itemTitles.push($(this).text().split("\n"));
});
var itemDescs = [];
$('.cart-item .description').each( function(index, val) {
itemDescs.push($(this).text().split("\n"));
});
var itemLangs = [];
$('.cart-item .language option').each( function(index, val) {
if ($(this).prop('selected') == true) {
itemLangs.push($(this).text());
}
});
this._logger("ExternalPayment languages", "info");
this._logger(itemLangs, "info");
var lineItems = [];
if (this.lastCart.LineItems) {
$.each(this.lastCart.LineItems, function(index, item) {
var itemName = itemTitles[index][1].trim(),
itemDesc = itemDescs[index][1].trim(),
itemLang = itemLangs[index].trim();
lineItems[index] = {
Name: itemName,
Desc: itemDesc+", "+itemLang,
Amt: item.UnitPrice,
Quantity: parseFloat(item.Quantity),
ItemCode: item.ProductId,
Index: index
};
});
}
// Double check and make sure its supported here.
var req = null;
switch (method) {
case 'service_paypal' :
var PaymentData = this._preparePayPalData();
this._logger("Beginning External Payment with: ", 'info');
this._logger(PaymentData);
// Build the request params for PayPal
req = this._jsonRpcRequest("CartApi.BeginExternalPayment", [{
Attrs: {
'Method': method,
'ReturnURL': PaymentData.ReturnURL,
'CancelURL': PaymentData.CancelURL,
'Locale': PaymentData.Locale,
'Items': lineItems,
}
}]);
break;
case 'service_sberbank':
var PaymentData = this._prepareSberbankData();
this._logger("Beginning External Payment with: ", 'info');
this._logger(PaymentData);
// Build the request params for PayPal
req = this._jsonRpcRequest("CartApi.BeginExternalPayment", [{
Attrs: {
'Method': method,
'ReturnURL': PaymentData.ReturnURL,
'CancelURL': PaymentData.CancelURL,
'Locale': PaymentData.Locale,
'Items': lineItems,
}
}]);
break;
default :
this._logger('Payment method "'+method+'" is not supported. Aborting', 'warn');
return;
}
// Do the call for External Payment
$.ajaxq('scnstoreq', $.extend(this._jsonBp, {
context: this,
data: {r:JSON.stringify(req)},
success: function(data){
// If the call returns an error, handle it appropriately
this._displayError(data);
// If the call has no error, behold the result
if (data.hasOwnProperty('result')) {
if (data.result.hasOwnProperty('RedirectURL')) {
this._redirect(data.result.RedirectURL);
} else {
var errdata = {
error: {code: 1000, message: 'There was an error beginning your PayPal payment.'}
};
this._displayError(errdata);
}
}
},
failure: function(errMsg) {
this._logger('BeginExternalPayment call failed', 'error');
this._throwError(errMsg);
},
}));
};
/**
* ExternalPaymentDetails
*
* So now we have the issue of
* they get started here.
*/
ScnStore.prototype.ExternalPaymentDetails = function(ppobject, method) {
this._logger('Fetching External Payment Details using: ' + ppobject.token, 'info');
var req = null;
// Find out what payment method is selected and handle accordingly
payMethods = window.ScnStore.orderForm.PaymentMethods;
if ( ! payMethods.hasOwnProperty(method)) {
this._logger('Payment method "'+method+'" is not defined in the settings. Aborting.', 'warn');
return;
}
// Double check and make sure its supported here.
switch (method) {
case 'service_paypal' :
this._logger("Doing ExternalPaymentDetails call with token: " + ppobject.token, 'info');
// Build the request params for PayPal
req = this._jsonRpcRequest("CartApi.ExternalPaymentDetails", [{
Attrs: {
'Method': method,
'PayPalToken': ppobject.token
}
}]);
break;
case 'service_sberbank' :
this._logger("Doing ExternalPaymentDetails call to sberbank", 'info');
// Build the request params for PayPal
req = this._jsonRpcRequest("CartApi.ExternalPaymentDetails", [{
Attrs: {
'Method': method,
}
}]);
break;
default :
this._logger('Payment method "'+method+'" is not supported. Aborting', 'warn');
return;
}
// Do the call for External Payment
$.ajaxq('scnstoreq', $.extend(this._jsonBp, {
context: this,
data: {r:JSON.stringify(req)},
success: function(data){
// If the call returns an error, handle it appropriately
this._displayError(data);
// If the call has no error, behold the result
if (data.hasOwnProperty('result')) {
if (data.result.hasOwnProperty('ShippingAddressData')) {
this._logger('ExternalPaymentDetails successful:','info');
this._logger(data.result,'info');
//
if (method == "service_paypal") {
var name = data.result.ShippingAddressData.FirstName.split(" ");
if (name.length == 4) {
data.result.ShippingAddressData.FirstName = name[0]+" "+name[1];
data.result.ShippingAddressData.LastName = name[2]+" "+name[3];
} else if (name.length == 3) {
data.result.ShippingAddressData.FirstName = name[0]+" "+name[1];
data.result.ShippingAddressData.LastName = name[2];
} else {
data.result.ShippingAddressData.FirstName = name[0];
data.result.ShippingAddressData.LastName = name[1];
}
var bfname = data.result.BillingAddressData.FirstName;
var blname = data.result.BillingAddressData.LastName;
data.result.BillingAddressData = data.result.ShippingAddressData;
data.result.BillingAddressData.FirstName = bfname;
data.result.BillingAddressData.LastName = blname;
}
if (this._populateOrderForm(data.result)) {
this.UpdateDataDisplayBoxes();
$('#review_overlay').remove();
}
} else {
var errdata = {
error: {code: 1000, message: 'There was an error processing your PayPal payment.'}};
this._displayError(errdata);
}
}
},
failure: function(errMsg) {
this._logger('ExternalPaymentDetails call failed', 'error');
this._throwError(errMsg, 'error');
},
}));
};
/**
* ResetExternalPayment
*
* So now we have the issue of
* they get started here.
*/
ScnStore.prototype.ResetExternalPayment = function(method) {
this._logger('Resetting external payment: ' + method, 'info');
// Find out what payment method is selected and handle accordingly
payMethods = window.ScnStore.orderForm.PaymentMethods;
if ( ! payMethods.hasOwnProperty(method)) {
this._logger('Payment method "'+method+'" is not defined in the settings. Aborting.', 'warn');
return;
}
var req = this._jsonRpcRequest("CartApi.ResetExternalPayment", [{
Attrs: { 'Method': method }
}]);
// Do the call for External Payment
$.ajaxq('scnstoreq', $.extend(this._jsonBp, {
context: this,
data: {r:JSON.stringify(req)},
success: function(data){
// If the call returns an error, handle it appropriately
this._displayError(data);
// If the call has no error, behold the result
if (data.hasOwnProperty('result')) {
if (data.result.hasOwnProperty('ShippingAddressData')) {
this._redirect('/store/checkout.action');
} else {
var errdata = {
error: {code: 1000, message: 'There was an error processing your PayPal payment.'}};
this._displayError(errdata);
}
}
},
failure: function(errMsg) {
this._logger('ExternalPaymentDetails call failed', 'error');
this._throwError(errMsg);
},
}));
};
/**
* CHECKOUT
*
* Run all the actions necessary to prepare the order for checkout
* and any last-minuite validation. Throw it in here.
*/
ScnStore.prototype.Checkout = function(method) {
this._logger('Doing Checkout...', 'info');
// Find out what payment method is selected and handle accordingly
payMethods = window.ScnStore.orderForm.PaymentMethods;
if ( ! payMethods.hasOwnProperty(method)) {
this._logger(
'Payment method "'+method+'" is not defined in the settings. Aborting.', 'warn');
return;
}
var PaymentData = null;
// Double check and make sure its supported here.
switch (method) {
case 'creditcard' :
PaymentData = this._captureCreditCardForm();
break;
case 'service_paypal' :
PaymentData = this._paypalVars();
break;
case 'service_sberbank' :
PaymentData = this._sberbankVars();
break;
default :
this._logger('Payment method is not supported. Aborting', 'warn');
return;
}
this._logger("Captured payment data as: ", 'info');
this._logger(PaymentData);
// Fire ze missiles.
this.ExecuteOrder(PaymentData);
};
/**
* EXECUTE ORDER
*
* This is the end of the line, honcho.
*/
ScnStore.prototype.ExecuteOrder = function(payment) {
if (payment == 'test') {
payment = {
Method: "creditcard",
AccountType: "visa",
AccountNumber: "4111111111111111",
AccountName: "Joe Test",
AccountExpiration: "2020-10",
AccountVerifCode: "123"
};
}
this._logger('Called ExecuteOrder with: ', 'info');
this._logger(payment);
// Double check and make sure its supported here.
var req = null;
switch (payment.Method) {
case 'creditcard' :
req = this._jsonRpcRequest("CartApi.ExecuteOrder", [{
PaymentAttrs: {
Method: payment.Method,
AccountType: payment.AccountType,
AccountNumber: payment.AccountNumber,
AccountName: payment.AccountName,
AccountExpiration: payment.AccountExpiration,
AccountVerifCode: payment.AccountVerifCode,
},
Currency: this.defaults.currency,
Total: this.lastCart.Total
}]);
break;
case 'service_paypal' :
req = this._jsonRpcRequest("CartApi.ExecuteOrder", [{
PaymentAttrs: {
Method: payment.Method,
PayPalToken: payment.Token,
PayPalPayerId: payment.PayerID,
AccountName: payment.AccountName,
},
Currency: this.defaults.currency,
Total: this.lastCart.Total
}]);
break;
case 'service_sberbank' :
req = this._jsonRpcRequest("CartApi.ExecuteOrder", [{
PaymentAttrs: {
Method: payment.Method,
OrderId: payment.OrderId
},
Currency: this.defaults.currency,
Total: this.lastCart.Total
}]);
break;
default :
this._logger('Payment method is not supported. Aborting', 'warn');
return;
}
this._logger(req);
$.ajaxq('scnstoreq', $.extend(this._jsonBp, {
context: this,
data: {r:JSON.stringify(req)},
success: function(data){
this._logger('ExecuteOrder call succeeded', 'info');
this._logger(data.result);
if (data.result) {
this._redirect(this.settings.successUrl);
return;
}
// If the call returns an error, handle it appropriately
this._displayError(data);
$('#checkout_btn').prop('disabled', false).removeClass('wait')
.html('Place Order »');
},
failure: function(errMsg) {
this._logger('ExecuteOrder call failed', 'warn');
this._throwError(errMsg);
},
}));
};
ScnStore.prototype._captureCreditCardForm = function() {
fields = window.ScnStore.orderForm.PaymentMethods.creditcard;
var PaymentData = {
Method: 'creditcard',
AccountType: $('input[name='+fields.AccountType.Id+']:checked').val(),
AccountNumber: $('#'+fields.AccountNumber.Id).val().replace(/\D/g,''),
AccountName: $('#'+fields.AccountName.Id).val(),
AccountExpiration: $('#'+fields.AccountExpiration.Year).val()+'-'+$('#'+fields.AccountExpiration.Month).val(),
AccountVerifCode: $('#'+fields.AccountVerifCode.Id).val()
};
return PaymentData;
};
ScnStore.prototype._paypalVars = function() {
var bd = this.orderForm.BillingAddressData,
PaymentData = {
Method: 'service_paypal',
Token: this._getQueryVariable('token'),
PayerID: this._getQueryVariable('PayerID'),
AccountName: bd.FirstName+" "+bd.LastName
};
return PaymentData;
};
ScnStore.prototype._preparePayPalData = function() {
params = window.ScnStore.orderForm.PaymentMethods.service_paypal;
var PaymentData = {
Method: 'service_paypal',
ReturnURL: params.ReturnURL,
CancelURL: params.CancelURL,
Locale: params.Locale,
};
return PaymentData;
};
ScnStore.prototype._sberbankVars = function() {
var bd = this.orderForm.BillingAddressData,
PaymentData = {
Method: 'service_sberbank',
AccountName: bd.FirstName+" "+bd.LastName,
OrderId: this._getQueryVariable('orderId')
};
return PaymentData;
};
ScnStore.prototype._prepareSberbankData = function() {
params = window.ScnStore.orderForm.PaymentMethods.service_sberbank;
var PaymentData = {
Method: 'service_sberbank',
ReturnURL: params.ReturnURL,
CancelURL: params.CancelURL,
Locale: params.Locale,
};
return PaymentData;
};
/**
* Formats a cc number to include spaces and no non-alnum chars
* Try to read at own risk.
*/
ScnStore.prototype._cardFormat = function(str) {
var delimeter = ' ';
var numbers = str.substr(0,19).replace(/[^\d]/gi,'');
var full_segment = numbers.match(/(\d){4}/g);
if (full_segment) {
var block = '';
for (i = 0; i < full_segment.length; i++) {
block += (i != full_segment.length - 1 ?
full_segment[i] + ((i < 3) ? delimeter : '') :
full_segment[i]);
}
return (numbers.length % 4 != 0 ?
block + ' ' + numbers.substr((full_segment.length * 4),numbers.length) :
block);
} else {
return numbers;
}
};
// Update the cart and all the things. Make sure you pass it a newCart
// or it's going to get very angry.
ScnStore.prototype._updateCart = function(newCart) {
if (newCart !== null) {
this._logger('Updating lastCart with newCart: ', 'info');
this._logger(newCart);
// Update the lastcart contents with the new stuff
this.lastCart = newCart;
// Tack on the currency we got from the cart update response;
if (newCart.Currency) {
this.settings.currency = newCart.Currency;
}
this.UpdateUI();
}
};
/**
* Load the order form into the orderForm object
*/
ScnStore.prototype._loadOrderForm = function() {
// Test if the order form is set on the page
if ( ! $.isEmptyObject(this.settings.orderForm)) {
// Transfer the order form from settings to its own place
this.orderForm = this.settings.orderForm;
// Since we have an order form and there is an address set on it
// we should call in the countries list to be loaded on the
// select option.
if (this.orderForm.ShippingAddressData.CountryCode.Id ||
this.orderForm.BillingAddressData.CountryCode.Id) {
this._loadCountries();
}
}
};
/*
* Check if the coutries are actually set, if not pull them down from the
* CDN and re-call this function with the countries inside. That will
* then skip the ajax step and move on to stuffing the ScnStore.countryList
* object with the data we got back.
*/
ScnStore.prototype._loadCountries = function(countries) {
var that = this;
if ( ! this._isset(countries)) {
this._logger('Loading countries...', 'info');
var url = "https://sd.ondemandhosting.info/lookups/country_list.html";
var locale = that.settings.locale.split('_');
var lang = locale[0];
$.ajaxq('scnstoreq', {
context: this,
url: url+"?locale="+lang+"&callback=ScnStore._loadCountries",
dataType: "script",
failure: function(errMsg) { this._throwError(errMsg);}
});
// If the countries argument is defined we're assuming the country
// list is being passed in via the callback in the URL, so we throw it
// on the form.
} else {
// for CPLOCIS keep only CIS countries
if(this.IsCISWebssite()) {
for (var countryCode in countries) {
if (countries.hasOwnProperty(countryCode)) {
if(!this.IsCISCountry(countryCode)) {
delete countries[countryCode];
}
}
}
}
this.countryList = countries;
this._logger('Countries loaded successfully:', 'info');
this._logger(countries);
var form = this.orderForm;
// Set for Shipping Country
var shipCntyId = form.ShippingAddressData.CountryCode.Id;
if (shipCntyId) {
this._logger('Setting ShippingCountryCode...');
var shipStateId = form.ShippingAddressData.StateProvince.Id;
this._setGeoSelectElementPair(shipCntyId, shipStateId);
$('[name="ShippingCountryCode"]').change();
}
// Set for Billing Country
var billCntyId = form.BillingAddressData.CountryCode.Id;
if (billCntyId) {
this._logger('Setting BillingCountryCode...');
$('[name="BillingCountryCode"]').change();
var billStateId = form.BillingAddressData.StateProvince.Id;
this._setGeoSelectElementPair(billCntyId, billStateId);
}
}
};
/**
* SET GEO SELECT ELEMENT PAIR
*
* I know this has a crazy name but at least it hints as what it's for.
* Basically we are going to call this on multiple elements on the same
* page in succession so I wanted to keep it DRY by putting this
* into a function. It's for country and state dropdowns (pairs)
*/
ScnStore.prototype._setGeoSelectElementPair = function(cntyId, stateId) {
country = this._setCountrySelect(cntyId);
// Now trigger the state list update
if (this._isset(stateId) && stateId != false) {
this._setStateSelect(stateId, country);
}
};
/**
* Since we're doing this on both billing and shipping, I put this into
* a function. Basically build the country option set and append it to the
* select element by ID as pased in.
*/
ScnStore.prototype._setCountrySelect = function(cntyId) {
var cntySet = this._createOptionSet(this.countryList, true);
var $list = $('#' + cntyId);
// Add the option set of countries to the select element
$list.append(cntySet);
// Now select the country that was last set on the list
var country = $list.data('last-country-code');
if (country) {
this._logger('Last country was '+country+', setting it.');
$list.val(country.toUpperCase());
} else {
this._logger('No last country set, falling back to locale...');
var ll = "en".split("_");
if (ll.length > 1) {
country = ll[1].toUpperCase();
this._logger('Locale found: ' + ll[1]);
$list.val(ll[1].toUpperCase());
} else {
country = "US";
this._logger('Locale not found, defaulting to "US".');
$list.val("US");
}
}
// Finally, we should update the last-country-set data attr so if we
// update this again on the same pageload, we won't reset user input
$list.attr('data-last-country-code', country);
$list.removeAttr('disabled');
return country;
};
// This function should be called any time the country changes
ScnStore.prototype._setStateSelect = function(listId, country) {
var that = this;
var rand = parseInt(Math.random()*(99999999-10000000)+10000000);
var callback = '_stateList_' + rand;
this._logger('Name of the callback is: '+callback);
// This is the callback that will then update the correct state list
// I'm setting this FIRST to make sure it's there when the call comes back
window[callback] = function(stateList) {
that._logger('Got the states for #'+listId);
// Since the state list comes back with country prefixes on the state
// abbreviations, we need to strip them off, with this doohickey
var o = {};
$.each(stateList, function(code, name) {
var parts = code.split('-');
var abbr = parts[1];
o[abbr] = name;
});
stateList = o;
var stateSet = that._createOptionSet(stateList, true);
var $list = $('#' + listId);
// Add the option set of countries to the select element
$list.append(stateSet);
// Now select the state that was last set on the list if present
// and also make sure it's an option on the new list.
var last_state = $list.data('last-state');
var last_state_in_list = false;
if (last_state) {
that._logger('Last state was '+last_state+', checking if it\'s here.');
$.each(stateList, function(code, name) {
if (code == last_state.toUpperCase()) {
$list.val(last_state.toUpperCase());
last_state_in_list = true;
return;
}
});
}
// @TODO Make this somehow more elegant...
// Now let's add a little localization. We should set the first
// item in the list as a label for the state/region and use the
// local name for it (State, Territory, Region, Province, etc.)
var label = null;
var c = country;
if (c == "US" || c == "MX" || c == "BR" || c == "DE") {
label = 'State...';
} else if (c == "AU") {
label = 'Territory/State...';
} else if (c == "ZA" || c == "IT" || c == "TW" || c == "NZ") {
label = 'Province...';
} else if (c == "CA") {
label = 'Province/Territory...';
} else if (c == "GB") {
label = 'County...';
} else {
label = 'Region...';
}
if (last_state || last_state_in_list) {
$list.prepend('');
} else {
$list.prepend('');
}
$list.append('');
// Finally, we should update the last-state data attr so if we
// update this again on the same pageload, we won't reset user input
$list.attr('data-last-state', last_state);
$list.removeClass('disabled').removeAttr("disabled");
$list.parent().removeClass('updating');
};
// Make the call to get the states list by country id and let the callback
// do all the heavy lifting
this._loadStateProvinces(country, callback);
};
/**
* LOAD STATE/PROVINCES
*
* This is similar to _loadCountires() but for states on those countries.
* Right now it's called by _setStateSelect as that function attaches a
* unique callback to window that will take the state list it gets and
* makes the select dropdown for that form element specifically.
*
*/
ScnStore.prototype._loadStateProvinces = function(data, callback) {
this._logger('Getting states for '+ data +'...', 'info');
var url = "https://sd.ondemandhosting.info/lookups/state_list.html";
this._logger("URL: "+url+"?countryCode=" + data + "&callback=" + callback);
$.ajaxq('scnstoreq', {
context: this,
url: url+"?countryCode=" + data + "&callback=" + callback,
dataType: "script",
failure: function(errMsg) { this._throwError(errMsg);}
});
};
/**
* CREATE OPTION SET
* Since we're doing this on multiple dropdowns in the form we shoud just put
* the iterator here in one place.
*
* Takes a key-value pair of the data you want to make options with.
* Example: {IN:"Indiana"}
*/
ScnStore.prototype._createOptionSet = function(map, sort) {
this._logger(map);
// Sort the text values because they come as sorted by keys. Lame.
if (sort) {
map = this._sortProperties(map);
}
// We make a documentFragment instead of using tons of jQuery.append's
// as this was proven to be 3-4x faster for large lists per John Resig:
// http://ejohn.org/blog/dom-documentfragments/
var optSet = document.createDocumentFragment();
$.each(map, function(code, name) {
var option = document.createElement("option");
option.textContent = name;
option.value = code;
optSet.appendChild(option);
});
return optSet;
};
// This is just quick and dirty. This is called by a listener on the state
// field ang checks if the value is "other". If so, do some magical stuff
// to create an input field to specify what is meant by "other".
ScnStore.prototype._checkForOtherStateValAndHandle = function(listId) {
this._logger('Calling _checkForOtherStateValAndHandle', 'info');
var $list = $('#'+listId);
var listVal = $list.val();
if (listVal == "other") {
// Check if there is already an "other" input field there
if ( ! $('#'+listId+'_other').length) {
this._logger('There\'s no "other" input and it is selected. Making one...');
var $listParent = $list.parent();
// Create a new input for alt
var input = document.createElement("input");
input.type = "text";
input.setAttribute("id", listId + "_other");
input.setAttribute("style",
"display: inline-block; width: 68%; float: left; margin-left: 2.5%"
);
input.setAttribute("class", "form-control");
// Make room for the new input by shrinking the existing list
$list.css({
width: '29.5%',
float: 'left'
}).addClass('otherState');
// Slam it into the little container for the stae field
$listParent.append(input);
$('#'+listId + "_other").focus();
this._logger('done.');
}
} else {
this._logger('The selected state is not "other"');
// So now we know that the user selected something other than
// "other"
if ($list.hasClass('otherState')) {
this._logger('Removing "other" text input');
$list.removeClass('otherState pull-left').css({width:'',float:''});
$('#'+listId+'_other').remove();
}
}
};
/**
* SET SHIPPING OPTIONS
*
* Pretty self explanatory
*/
ScnStore.prototype._setShippingOptions = function() {
if ( ! this.hasOwnProperty('orderForm')) {
return;
}
if ( ! this.orderForm.hasOwnProperty('ShippingOptions')) {
return;
}
this._logger('Setting shipping options...', 'info');
var cart = this.lastCart;
var shipOptWrapperId = this.orderForm.ShippingOptions.Id;
// So there are no shipping options in the *last* cart
if ( ! cart.ShippingOptions) {
this._logger('No ShippingOptions available yet.', 'warn');
// Lop through any stuff in the options list and
$('#' + shipOptWrapperId + ' li').each(function(index, val) {
if ($(this).attr('id') == 'ship_option_notice') {
$(this).removeClass('hidden');
} else {
$(this).remove();
}
});
return;
}
var that = this;
var options = cart.ShippingOptions;
var $optionContainer = $('#'+that.orderForm.ShippingOptions.Id);
this._logger('Found some shipping options, here they are:');
this._logger(options);
// Remove the old options and de-register them from this.orderForm
$('#' + shipOptWrapperId + ' li').each(function(index, val) {
if ($(this).attr('id') == 'ship_option_notice') {
$(this).addClass('hidden');
} else {
$(this).remove();
}
});
delete this.orderForm.ShippingOptions.Options;
this.orderForm.ShippingOptions.Options = {};
for (var i = options.length - 1; i >= 0; i--) {
$optionContainer.append(that._buildShippingOption(options[i]));
}
$('#' + shipOptWrapperId + ' input[type=radio]:first-child').attr('checked', 'checked');
};
ScnStore.prototype._buildShippingOption = function(option) {
this._logger('Building option for ' + option.Name);
// Some randomness for unique IDs
var rand = parseInt(Math.random()*(99999-10000)+10000);
var elId = 'ship_option_' + rand;
var currency = this.settings.currency;
var locale = this.settings.locale.split('_');
var lang = locale[0];
var form = this.orderForm;
// Build a list item for this option
var wrapper = document.createElement('li');
wrapper.setAttribute('class', 'shipping-option col');
// Make the input
var input = document.createElement('input');
input.setAttribute('type', 'radio');
input.setAttribute('name', 'ShipOption');
input.setAttribute('value', option.UniqueId);
input.setAttribute('id', elId);
wrapper.appendChild(input);
// Make the label
var label = document.createElement('label');
label.setAttribute('for', elId);
label.setAttribute('class', 'd-flex flex-row align-content-center align-items-center');
// Make the contents of the label
var fauxRdoBtn = document.createElement('div');
fauxRdoBtn.setAttribute('class', 'faux-radio-btn');
label.appendChild(fauxRdoBtn);
// Make a container for title and description
var contentWrapper = document.createElement('div');
contentWrapper.setAttribute('class', 'ship-opt-content d-flex flex-column');
// The title
var title = document.createElement('div');
title.setAttribute('class', 'title');
title.appendChild(document.createTextNode(option.Name));
contentWrapper.appendChild(title);
// The body
var body = document.createElement('div');
body.setAttribute('class', 'body');
body.appendChild(document.createTextNode(
'Will ship within one business day'));
contentWrapper.appendChild(body);
// Stick the contentWrapper inside the label
label.appendChild(contentWrapper);
// The rate
var rate = document.createElement('div');
rate.setAttribute('class', 'rate ml-auto');
rate.appendChild(document.createTextNode(
window.FormatCurrency(lang, currency, option.Amount, true)
));
label.appendChild(rate);
// Now stick the label on the wrapper
wrapper.appendChild(label);
// Finally, register this shipping option with the orderForm
form.ShippingOptions.Options[option.UniqueId] = {id: elId};
this._logger('Done. Element ID is ' + elId);
return wrapper;
};
ScnStore.prototype._populateOrderForm = function(data) {
var form = this.orderForm;
$.each(data.BillingAddressData, function(i,v) {
if (form.BillingAddressData.hasOwnProperty(i)) {
form.BillingAddressData[i] = v;
}
});
$.each(data.ShippingAddressData, function(i,v) {
if (form.ShippingAddressData.hasOwnProperty(i)) {
form.ShippingAddressData[i] = v;
}
});
this.orderForm = form;
if (this.SetBillingShippingAndRecalculate(this.orderForm, true)) {
return true;
}
return false;
};
ScnStore.prototype.UpdateDataDisplayBoxes = function() {
var form = this.orderForm;
var shipAddr = form.ShippingAddressData;
var billAddr = form.BillingAddressData;
if (typeof shipAddr !== "undefined") {
if ($(".shipping-address-box")) {
$(".shipping-address-box .first-name").text(shipAddr.FirstName);
$(".shipping-address-box .last-name").text(shipAddr.LastName);
$(".shipping-address-box .address1").text(shipAddr.Address1);
if (shipAddr.Address2) { $(".shipping-address-box .address2").html("
"+shipAddr.Address2); }
$(".shipping-address-box .city").text(shipAddr.City);
$(".shipping-address-box .state-province").text(shipAddr.StateProvince);
$(".shipping-address-box .zip-postal").text(shipAddr.PostalCode);
$(".shipping-address-box .country").text(shipAddr.CountryCode);
}
}
if (typeof billAddr !== "undefined") {
if ($(".billing-address-box")) {
$(".billing-address-box .email").text(billAddr.Email);
$(".billing-address-box .first-name").text(billAddr.FirstName);
$(".billing-address-box .last-name").text(billAddr.LastName);
$(".billing-address-box .address1").text(billAddr.Address1);
if (billAddr.Address2) { $(".billing-address-box .address2").html("
"+billAddr.Address2); }
$(".billing-address-box .city").text(billAddr.City);
$(".billing-address-box .state-province").text(billAddr.StateProvince);
$(".billing-address-box .zip-postal").text(billAddr.PostalCode);
$(".billing-address-box .country").text(billAddr.CountryCode);
}
}
return true;
};
// PAYMENT FORM STUFF
ScnStore.prototype._initPaymentForm = function() {
if ( ! this.orderForm) {
return false;
}
var form = this.orderForm;
// For all things on this level
if (form.hasOwnProperty('PaymentMethods')) {
// For all credit card related initilizations
if (form.PaymentMethods.hasOwnProperty('creditcard')) {
// set expiration month to PT month
var mid = form.PaymentMethods.creditcard.AccountExpiration.Month;
var yid = form.PaymentMethods.creditcard.AccountExpiration.Year;
var date = new Date();
$('#'+mid).val(date.getMonth() + 1);
$('#'+yid).val(date.getFullYear());
}
}
return true;
};
ScnStore.prototype._getCardType = function(val) {
// Auto-select the card type icons based on first digits
// of the card number. Regex from regular-expressions.info
var types = this.settings.cardTypes;
var ret = false;
$.each(types, function(card, expr) {
var re = new RegExp(expr, 'g');
if (val.match(re)) {
ret = card;
}
});
return ret;
};
ScnStore.prototype._setCardType = function(type) {
$('.payment-card').each(function(i,el) {
if ($(el).hasClass(type)) {
$(el).addClass('active');
$(el).siblings().prop('checked', true);
}
});
};
ScnStore.prototype._unsetAllCardTypes = function() {
$('.payment-card').removeClass('active');
$('input[name=CardType]').prop('checked', false);
};
// Add to Cart Popover. So this is the little popover element that will appear over the normal add-to-cart
// buttons when they get clicked. We want to provide some feedback for clicking the link and some 8-C on
// getting them to the cart or to keep looking.
ScnStore.prototype._atcPopover = function($el) {
if ($el.hasClass('no-popover')) {
return;
}
var rand = Math.floor(Math.random()*90000) + 10000;
var rmid = 'rm_' + rand;
var txt = document.createElement('p');
txt.setAttribute('class', 'text-success');
txt.setAttribute('style', 'margin: 0; font-weight: bold;');
txt.appendChild(document.createTextNode('Added to your cart'));
var btn1 = document.createElement('a');
btn1.setAttribute('class', 'btn');
btn1.setAttribute('class', 'btn-primary');
btn1.setAttribute('class', 'btn-sm');
btn1.setAttribute('href', '/store/checkout.action');
btn1.setAttribute('style', '');
btn1.appendChild(document.createTextNode('Proceed to Checkout'));
var btn2 = document.createElement('button');
btn2.setAttribute('class', 'btn btn-sm btn-light');
btn2.setAttribute('id', rmid);
btn2.setAttribute('style', '');
btn2.appendChild(document.createTextNode('×'));
var buttons = document.createElement('div');
buttons.setAttribute('style', 'white-space: nowrap');
buttons.appendChild(btn1);
buttons.appendChild(btn2);
var content = document.createElement('div');
content.appendChild(txt);
content.appendChild(buttons);
var options = {
placement: 'top',
trigger: 'focus',
html: true,
template: '