Cart Ajax API is always showing Sold out when adding a new variant

I am developing an app to create product bundles. Firstly, I help customers to create a new variant with the quantity equals to 1 after selection. After that, I am using Cart ajax API to add this new variant to Cart. However, when I add the new variant with Fetch API, it's always showing sold out

{status: 422, message: "Cart Error", description: "The product xxxx is already sold out."}

Here is the code to add the new variant to Cart (I copied from doc):

addNewVariantToCart(variant_id) {
        let formData = {
           'items': [{
            'id': parseInt(variant_id),
            'quantity': 1
            }]
          };
		
        fetch('/cart/add.js', {
          method: 'POST',
          headers: {
            //               'Accept': 'application/json',
            'Content-Type': 'application/json'
          },
          body: JSON.stringify(formData)
        })
        .then(response => response.json())
        .then(data => {
          console.log('Success adding variant to cart:', data);
          //             console.log('Your bundle has been added to cart! Check your cart now.')
        })
        .catch((error) => {
          console.error('Error:', error);
        });
      }

When I run these code in Chrome console by substituting the new variant id, it works without any problem. What's wrong here?

SPO - SEO App to research keywords & edit social link preview
0 Likes
MikeBee
New Member
5 0 0

Hi,

I have the same error message when trying to add to cart a product out of stock; I tried everything to get a pop message that this product is out of stock instead of this alert but it didn't work!!

 

errorFashe.jpg

 

 

This is below my Ajaxify.js.liquid:

/*============================================================================
(c) Copyright 2015 Shopify Inc. Author: Carson Shold (@cshold). All Rights Reserved.

Plugin Documentation - https://shopify.github.io/Timber/#ajax-cart

Ajaxify the add to cart experience and flip the button for inline confirmation,
show the cart in a modal, or a 3D drawer.

This file includes:
- Basic Shopify Ajax API calls
- Ajaxify plugin

This requires:
- jQuery 1.8+
- handlebars.min.js (for cart template)
- modernizer.min.js
- snippet/ajax-cart-template.liquid

JQUERY API (c) Copyright 2009-2015 Shopify Inc. Author: Caroline Schnapp. All Rights Reserved.
Includes slight modifications to addItemFromForm.
==============================================================================*/
if ((typeof Shopify) === 'undefined') { Shopify = {}; }

/*============================================================================
API Helper Functions
==============================================================================*/
function attributeToString(attribute) {
if ((typeof attribute) !== 'string') {
attribute += '';
if (attribute === 'undefined') {
attribute = '';
}
}
return jQuery.trim(attribute);
}

/*============================================================================
API Functions
- Shopify.format money is defined in option_selection.js.
If that file is not included, it is redefined here.
==============================================================================*/
if ( !Shopify.formatMoney ) {
Shopify.formatMoney = function(cents, format) {
var value = '',
placeholderRegex = /\{\{\s*(\w+)\s*\}\}/,
formatString = (format || this.money_format);

if (typeof cents == 'string') {
cents = cents.replace('.','');
}

function defaultOption(opt, def) {
return (typeof opt == 'undefined' ? def : opt);
}

function formatWithDelimiters(number, precision, thousands, decimal) {
precision = defaultOption(precision, 2);
thousands = defaultOption(thousands, ',');
decimal = defaultOption(decimal, '.');

if (isNaN(number) || number == null) {
return 0;
}

number = (number/100.0).toFixed(precision);

var parts = number.split('.'),
dollars = parts[0].replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1' + thousands),
cents = parts[1] ? (decimal + parts[1]) : '';

return dollars + cents;
}

switch(formatString.match(placeholderRegex)[1]) {
case 'amount':
value = formatWithDelimiters(cents, 2);
break;
case 'amount_no_decimals':
value = formatWithDelimiters(cents, 0);
break;
case 'amount_with_comma_separator':
value = formatWithDelimiters(cents, 2, '.', ',');
break;
case 'amount_no_decimals_with_comma_separator':
value = formatWithDelimiters(cents, 0, '.', ',');
break;
}

return formatString.replace(placeholderRegex, value);
};
}

Shopify.onProduct = function(product) {
// alert('Received everything we ever wanted to know about ' + product.title);
};

Shopify.onCartUpdate = function(cart) {
// alert('There are now ' + cart.item_count + ' items in the cart.');
};

Shopify.updateCartNote = function(note, callback) {
var params = {
type: 'POST',
url: '/cart/update.js',
data: 'note=' + attributeToString(note),
dataType: 'json',
success: function(cart) {
if ((typeof callback) === 'function') {
callback(cart);
}
else {
Shopify.onCartUpdate(cart);
}
},
error: function(XMLHttpRequest, textStatus) {
Shopify.onError(XMLHttpRequest, textStatus);
}
};
jQuery.ajax(params);
};

Shopify.onError = function(XMLHttpRequest, textStatus) {
var data = eval('(' + XMLHttpRequest.responseText + ')');
if (!!data.message) {
alert(data.message + '(' + data.status + '): ' + data.description);
} else {
alert('Error : ' + Shopify.fullMessagesFromErrors(data).join('; ') + '.');
}
};

/*============================================================================
POST to cart/add.js returns the JSON of the line item associated with the added item
==============================================================================*/
Shopify.addItem = function(variant_id, quantity, callback) {
var quantity = quantity || 1;
var params = {
type: 'POST',
url: '/cart/add.js',
data: 'quantity=' + quantity + '&id=' + variant_id,
dataType: 'json',
success: function(line_item) {
if ((typeof callback) === 'function') {
callback(line_item);
}
else {
Shopify.onItemAdded(line_item);
}
},
error: function(XMLHttpRequest, textStatus) {
Shopify.onError(XMLHttpRequest, textStatus);
}
};
jQuery.ajax(params);
};

/*============================================================================
POST to cart/add.js returns the JSON of the line item
- Allow use of form element instead of id
- Allow custom error callback
==============================================================================*/
Shopify.addItemFromForm = function(form, callback, errorCallback) {
var params = {
type: 'POST',
url: '/cart/add.js',
data: jQuery(form).serialize(),
dataType: 'json',
success: function(line_item) {
if ((typeof callback) === 'function') {
callback(line_item, form);
}
else {
Shopify.onItemAdded(line_item, form);
}
},
error: function(XMLHttpRequest, textStatus) {
if ((typeof errorCallback) === 'function') {
errorCallback(XMLHttpRequest, textStatus);
}
else {
Shopify.onError(XMLHttpRequest, textStatus);
}
}
};
jQuery.ajax(params);
};

// Get from cart.js returns the cart in JSON
Shopify.getCart = function(callback) {
jQuery.getJSON('/cart.js', function (cart, textStatus) {
if ((typeof callback) === 'function') {
callback(cart);
}
else {
Shopify.onCartUpdate(cart);
}
});
};

// GET products/<product-handle>.js returns the product in JSON
Shopify.getProduct = function(handle, callback) {
jQuery.getJSON('/products/' + handle + '.js', function (product, textStatus) {
if ((typeof callback) === 'function') {
callback(product);
}
else {
Shopify.onProduct(product);
}
});
};

// POST to cart/change.js returns the cart in JSON
Shopify.changeItem = function(line, quantity, callback) {
var params = {
type: 'POST',
url: '/cart/change.js',
data: 'quantity=' + quantity + '&line=' + line,
dataType: 'json',
success: function(cart) {
if ((typeof callback) === 'function') {
callback(cart);
}
else {
Shopify.onCartUpdate(cart);
}
},
error: function(XMLHttpRequest, textStatus) {
Shopify.onError(XMLHttpRequest, textStatus);
}
};
jQuery.ajax(params);
};

/*============================================================================
Ajaxify Shopify Add To Cart
==============================================================================*/
var ajaxifyShopify = (function(module, $) {

'use strict';

// Public functions
var init;

// Private general variables
var settings, isUpdating, cartInit, $drawerHeight, $cssTransforms, $cssTransforms3d, $nojQueryLoad, $w, $body, $html;

// Private plugin variables
var $formContainer, $btnClass, $wrapperClass, $addToCart, $flipClose, $flipCart, $flipContainer, $cartCountSelector, $cartCostSelector, $toggleCartButton, $modal, $cartContainer, $drawerCaret, $modalContainer, $modalOverlay, $closeCart, $drawerContainer, $prependDrawerTo, $callbackData={};

// Private functions
var updateCountPrice, flipSetup, revertFlipButton, modalSetup, showModal, sizeModal, hideModal, drawerSetup, showDrawer, hideDrawer, sizeDrawer, loadCartImages, formOverride, itemAddedCallback, itemErrorCallback, cartUpdateCallback, setToggleButtons, flipCartUpdateCallback, buildCart, cartTemplate, adjustCart, adjustCartCallback, createQtySelectors, qtySelectors, scrollTop, toggleCallback, validateQty;

/*============================================================================
Initialise the plugin and define global options
==============================================================================*/
init = function (options) {

// Default settings
settings = {
method: 'drawer', // Method options are drawer, modal, and flip. Default is drawer.
formSelector: 'form[action^="/cart/add"]',
addToCartSelector: 'input[type="submit"]',
cartCountSelector: null,
cartCostSelector: null,
toggleCartButton: null,
btnClass: null,
wrapperClass: null,
useCartTemplate: false,
moneyFormat: '{% raw %}${{amount}}{% endraw %}',
disableAjaxCart: false,
enableQtySelectors: true,
prependDrawerTo: 'body',
onToggleCallback: null
};

// Override defaults with arguments
$.extend(settings, options);

// Make sure method is lower case
settings.method = settings.method.toLowerCase();

// Select DOM elements
$formContainer = $(settings.formSelector);
$btnClass = settings.btnClass;
$wrapperClass = settings.wrapperClass;
$addToCart = $formContainer.find(settings.addToCartSelector);
$flipContainer = null;
$flipClose = null;
$cartCountSelector = $(settings.cartCountSelector);
$cartCostSelector = $(settings.cartCostSelector);
$toggleCartButton = $(settings.toggleCartButton);
$modal = null;
$prependDrawerTo = $(settings.prependDrawerTo);

// CSS Checks
$cssTransforms = Modernizr.csstransforms;
$cssTransforms3d = Modernizr.csstransforms3d;

// General Selectors
$w = $(window);
$body = $('body');
$html = $('html');

// Track cart activity status
isUpdating = false;

// Check if we can use .load
$nojQueryLoad = $html.hasClass('lt-ie9');
if ($nojQueryLoad) {
settings.useCartTemplate = false;
}

// Setup ajax quantity selectors on the any template if enableQtySelectors is true
if (settings.enableQtySelectors) {
qtySelectors();
}

// Enable the ajax cart
if (!settings.disableAjaxCart) {
// Handle each case add to cart method
switch (settings.method) {
case 'flip':
flipSetup();
break;

case 'modal':
modalSetup();
break;

case 'drawer':
drawerSetup();
break;
}

// Escape key closes cart
$(document).keyup( function (evt) {
if (evt.keyCode == 27) {
switch (settings.method) {
case 'flip':
case 'drawer':
hideDrawer();
break;
case 'modal':
hideModal();
break;
}
}
});

if ( $addToCart.length ) {
// Take over the add to cart form submit
formOverride();
}
}

// Run this function in case we're using the quantity selector outside of the cart
adjustCart();
};

updateCountPrice = function (cart) {
if ($cartCountSelector) {
$cartCountSelector.html(cart.item_count).removeClass('hidden-count');

if (cart.item_count === 0) {
$cartCountSelector.addClass('hidden-count');
}
}
if ($cartCostSelector) {
$cartCostSelector.html(Shopify.formatMoney(cart.total_price, settings.moneyFormat));
}
};

flipSetup = function () {
// Build and append the drawer in the DOM
drawerSetup();

// Stop if there is no add to cart button
if ( !$addToCart.length ) {
return
}

// Wrap the add to cart button in a div
$addToCart.addClass('flip-front').wrap('<div class="flip"></div>');

// Write a (hidden) Checkout button, a loader, and the extra view cart button
var checkoutBtn = $('<a href="/cart" class="flip-back" style="background-color: #C00; color: #fff;" id="flip-checkout">' + {{ 'cart.general.checkout' | t | json }} + '</a>').addClass($btnClass),
flipLoader = $('<span class="ajaxifyCart-loader"></span>'),
flipExtra = $('<div class="flip-extra">' + {{ 'cart.general.or' | t | json }} + ' <a href="#" class="flip-cart">' + {{ 'cart.general.view_cart' | t | json }} + ' (<span></span>)</a></div>');

// Append checkout button and loader
checkoutBtn.insertAfter($addToCart);
flipLoader.insertAfter(checkoutBtn);

// Setup new selectors
$flipContainer = $('.flip');

if (!$cssTransforms3d) {
$flipContainer.addClass('no-transforms')
}

// Setup extra selectors once appended
flipExtra.insertAfter($flipContainer);
$flipCart = $('.flip-cart');

$flipCart.on('click', function(e) {
e.preventDefault();
showDrawer(true);
});

// Reset the button if a user changes a variation
$('input[type="checkbox"], input[type="radio"], select', $formContainer).on('click', function() {
revertFlipButton();
})
};

revertFlipButton = function () {
$flipContainer.removeClass('is-flipped');
};

modalSetup = function () {
// Create modal DOM elements with handlebars.js template
var source = $("#modalTemplate").html(),
template = Handlebars.compile(source);

// Append modal and overlay to body
$body.append(template).append('<div id="ajaxifyCart-overlay"></div>');

// Modal selectors
$modalContainer = $('#ajaxifyModal');
$modalOverlay = $('#ajaxifyCart-overlay');
$cartContainer = $('#ajaxifyCart');

// Close modal when clicking the overlay
$modalOverlay.on('click', hideModal);

// Create a close modal button
$modalContainer.prepend('<button class="ajaxifyCart--close" title="' + {{ 'cart.general.close_cart' | t | json }} + '">' + {{ 'cart.general.close_cart' | t | json }} + '</button>');
$closeCart = $('.ajaxifyCart--close');
$closeCart.on('click', hideModal);

// Add a class if CSS translate isn't available
if (!$cssTransforms) {
$modalContainer.addClass('no-transforms')
}

// Update modal position on screen changes
$(window).on({
orientationchange: function(e) {
if ($modalContainer.hasClass('is-visible')) {
sizeModal('resize');
}
}, resize: function(e) {
// IE8 fires this when overflow on body is changed. Ignore IE8.
if (!$nojQueryLoad && $modalContainer.hasClass('is-visible')) {
sizeModal('resize');
}
}
});

// Toggle modal with cart button
setToggleButtons();
};

showModal = function (toggle) {
$body.addClass('ajaxify-modal--visible');
// Build the cart if it isn't already there
if ( !cartInit && toggle ) {
Shopify.getCart(cartUpdateCallback);
} else {
sizeModal();
}
};

sizeModal = function(isResizing) {
if (!isResizing) {
$modalContainer.css('opacity', 0);
}

// Position modal by negative margin
$modalContainer.css({
'margin-left': - ($modalContainer.outerWidth() / 2),
'opacity': 1
});

$modalContainer.addClass('is-visible');

scrollTop();

toggleCallback({
'is_visible': true
});
};

hideModal = function (e) {
$body.removeClass('ajaxify-modal--visible');
if (e) {
e.preventDefault();
}

if ($modalContainer) {
$modalContainer.removeClass('is-visible');
$body.removeClass('ajaxify-lock');
}

toggleCallback({
'is_visible': false
});
};

drawerSetup = function () {
// Create drawer DOM elements with handlebars.js template
var source = $("#drawerTemplate").html(),
template = Handlebars.compile(source),
data = {
wrapperClass: $wrapperClass
};

// Append drawer (defaults to body)
$prependDrawerTo.prepend(template(data));

// Drawer selectors
$drawerContainer = $('#ajaxifyDrawer');
$cartContainer = $('#ajaxifyCart');
$drawerCaret = $('.ajaxifyDrawer-caret > span');

// Toggle drawer with cart button
setToggleButtons();

// Position caret and size drawer on resize if drawer is visible
var timeout;
$(window).resize(function() {
clearTimeout(timeout);
timeout = setTimeout(function(){
if ($drawerContainer.hasClass('is-visible')) {
positionCaret();
sizeDrawer();
}
}, 500);
});

// Position the caret the first time
positionCaret();

// Position the caret
function positionCaret() {
if ($toggleCartButton.offset()) {
// Get the position of the toggle button to align the carat with
var togglePos = $toggleCartButton.offset(),
toggleWidth = $toggleCartButton.outerWidth(),
toggleMiddle = togglePos.left + toggleWidth/2;

$drawerCaret.css('left', toggleMiddle + 'px');
}
}
};

showDrawer = function (toggle) {
// If we're toggling with the flip method, use a special callback
if (settings.method == 'flip') {
Shopify.getCart(flipCartUpdateCallback);
}
// opening the drawer for the first time
else if ( !cartInit && toggle) {
Shopify.getCart(cartUpdateCallback);
}
// simple toggle? just size it
else if ( cartInit && toggle ) {
sizeDrawer();
}

// Show the drawer
$drawerContainer.addClass('is-visible');

scrollTop();

toggleCallback({
'is_visible': true
});
};

hideDrawer = function () {
$drawerContainer.removeAttr('style').removeClass('is-visible');
scrollTop();
toggleCallback({
'is_visible': false
});
};

sizeDrawer = function ($empty) {
if ($empty) {
$drawerContainer.css('height', '0px');
} else {
$drawerHeight = $cartContainer.outerHeight();
$('.cart-row img').css('width', 'auto'); // fix Chrome image size bug
$drawerContainer.css('height', $drawerHeight + 'px');
}
};

loadCartImages = function () {
// Size cart once all images are loaded
var cartImages = $('img', $cartContainer),
count = cartImages.length,
index = 0;

cartImages.on('load', function() {
index++;

if (index==count) {
switch (settings.method) {
case 'modal':
sizeModal();
break;
case 'flip':
case 'drawer':
sizeDrawer();
break;
}
}
});
};

formOverride = function () {
$formContainer.submit(function(e) {
e.preventDefault();

// Add class to be styled if desired
$addToCart.removeClass('is-added').addClass('is-adding');

// Remove any previous quantity errors
$('.qty-error').remove();

Shopify.addItemFromForm(e.target, itemAddedCallback, itemErrorCallback);

// Set the flip button to a loading state
switch (settings.method) {
case 'flip':
$flipContainer.addClass('flip--is-loading');
break;
}
});
};

itemAddedCallback = function (product) {
$addToCart.removeClass('is-adding').addClass('is-added');

// Slight delay of flip to mimic a longer load
switch (settings.method) {
case 'flip':
setTimeout(function () {
$flipContainer.removeClass('flip--is-loading').addClass('is-flipped');
}, 600);
break;
}
Shopify.getCart(cartUpdateCallback);
};

itemErrorCallback = function (XMLHttpRequest, textStatus) {
switch (settings.method) {
case 'flip':
$flipContainer.removeClass('flip--is-loading');
break;
}

var data = eval('(' + XMLHttpRequest.responseText + ')');
if (!!data.message) {
if (data.status == 422) {
$formContainer.after('<div class="errors qty-error">'+ data.description +'</div>')
}
}
};

cartUpdateCallback = function (cart) {
// Update quantity and price
updateCountPrice(cart);

switch (settings.method) {
case 'flip':
$('.flip-cart span').html(cart.item_count);
break;
case 'modal':
buildCart(cart);
break;
case 'drawer':
buildCart(cart);
if ( !$drawerContainer.hasClass('is-visible') ) {
showDrawer();
} else {
scrollTop();
}
break;
}
};

setToggleButtons = function () {
// Reselect the element in case it just loaded
$toggleCartButton = $(settings.toggleCartButton);

if ($toggleCartButton) {
// Turn it off by default, in case it's initialized twice
$toggleCartButton.off('click');

// Toggle the cart, based on the method
$toggleCartButton.on('click', function(e) {
e.preventDefault();

switch (settings.method) {
case 'modal':
if ( $modalContainer.hasClass('is-visible') ) {
hideModal();
} else {
showModal(true);
}
break;
case 'drawer':
case 'flip':
if ( $drawerContainer.hasClass('is-visible') ) {
hideDrawer();
} else {
showDrawer(true);
}
break;
}

});

}
};

flipCartUpdateCallback = function (cart) {
buildCart(cart);
};

buildCart = function (cart) {
// Empty cart if using default layout or not using the .load method to get /cart
if (!settings.useCartTemplate || cart.item_count === 0) {
$cartContainer.empty();
}

// Show empty cart
if (cart.item_count === 0) {
$cartContainer.append('<h2>' + {{ 'cart.general.empty' | t | json }} + '</h2><span>' + {{ 'cart.general.continue_browsing_html' | t | json }} + '</span>');

switch (settings.method) {
case 'modal':
sizeModal('resize');
break;
case 'flip':
case 'drawer':
sizeDrawer();

if (!$drawerContainer.hasClass('is-visible') && cartInit) {
sizeDrawer(true);
}
break;
}
return;
}

// Use the /cart template, or Handlebars.js layout based on theme settings
if (settings.useCartTemplate) {
cartTemplate(cart);
return;
}

// Handlebars.js cart layout
var items = [],
item = {},
data = {};

var source = $("#cartTemplate").html(),
template = Handlebars.compile(source);

// Add each item to our handlebars.js data
$.each(cart.items, function(index, cartItem) {

var itemAdd = cartItem.quantity + 1,
itemMinus = cartItem.quantity - 1,
itemQty = cartItem.quantity + ' x';

/* Hack to get product image thumbnail
* - Remove file extension, add _small, and re-add extension
* - Create server relative link
*/
var prodImg = cartItem.image.replace(/(\.[^.]*)$/, "_small$1").replace('http:', '');
var prodName = cartItem.title.replace(/(\-[^-]*)$/, "");
var prodVariation = cartItem.title.replace(/^[^\-]*/, "").replace(/-/, "");

// Create item's data object and add to 'items' array
item = {
id: cartItem.variant_id,
line: index + 1, // Shopify uses a 1+ index in the API
url: cartItem.url,
img: prodImg,
name: prodName,
variation: prodVariation,
itemAdd: itemAdd,
itemMinus: itemMinus,
itemQty: itemQty,
price: Shopify.formatMoney(cartItem.price, settings.moneyFormat)
};

items.push(item);
});

// Gather all cart data and add to DOM
data = {
items: items,
totalPrice: Shopify.formatMoney(cart.total_price, settings.moneyFormat),
btnClass: $btnClass
}
$cartContainer.append(template(data));

// With new elements we need to relink the adjust cart functions
adjustCart();

// Setup close modal button and size drawer
switch (settings.method) {
case 'modal':
loadCartImages();
break;
case 'flip':
case 'drawer':
if (cart.item_count > 0) {
loadCartImages();
} else {
sizeDrawer(true);
}
break;
default:
break;
}

// Mark the cart as built
cartInit = true;
};

cartTemplate = function (cart) {
$cartContainer.load('/cart form[action="/cart"]', function() {

// With new elements we need to relink the adjust cart functions
adjustCart();

// Size drawer at this point
switch (settings.method) {
case 'modal':
loadCartImages();
break;
case 'flip':
case 'drawer':
if (cart.item_count > 0) {
loadCartImages();
} else {
sizeDrawer(true);
}
break;
default:
break;
}

// Mark the cart as built
cartInit = true;
});
}

adjustCart = function () {
// This function runs on load, and when the cart is reprinted

// Create ajax friendly quantity fields and remove links in the ajax cart
if (settings.useCartTemplate) {
createQtySelectors();
}

// Prevent cart from being submitted while quantities are changing
$body.on('submit', 'form.cart-form', function(evt) {
if (isUpdating) {
evt.preventDefault();
}
});

// Update quantify selectors
var qtyAdjust = $('.ajaxifyCart--qty span');

// Add or remove from the quantity
qtyAdjust.off('click');
qtyAdjust.on('click', function() {
var el = $(this),
line = el.data('line'),
qtySelector = el.siblings('.ajaxifyCart--num'),
qty = parseInt( qtySelector.val() );

qty = validateQty(qty);

// Add or subtract from the current quantity
if (el.hasClass('ajaxifyCart--add')) {
qty = qty + 1;
} else {
qty = qty <= 0 ? 0 : qty - 1;
}

// If it has a data-line, update the cart.
// Otherwise, just update the input's number
if (line) {
updateQuantity(line, qty);
} else {
qtySelector.val(qty);
}

});

// Update quantity based on input on change
var qtyInput = $('.ajaxifyCart--num');
qtyInput.off('change');
qtyInput.on('change', function() {
var el = $(this),
line = el.data('line'),
qty = el.val();

// Make sure we have a valid integer
if( (parseFloat(qty) == parseInt(qty)) && !isNaN(qty) ) {
// We have a number!
} else {
// Not a number. Default to 1.
el.val(1);
return;
}

// If it has a data-line, update the cart
if (line) {
updateQuantity(line, qty);
}
});

// Highlight the text when focused
qtyInput.off('focus');
qtyInput.on('focus', function() {
var el = $(this);
setTimeout(function() {
el.select();
}, 50);
});

// Completely remove product
$('.ajaxifyCart--remove').on('click', function(e) {
var el = $(this),
line = el.data('line') || null,
qty = 0;

// Without a data-line, let the default link action take over
if (!line) {
return;
}

e.preventDefault();
updateQuantity(line, qty);
});

function updateQuantity(line, qty) {
isUpdating = true;

// Add activity classes when changing cart quantities
if (!settings.useCartTemplate) {
var row = $('.ajaxifyCart--row[data-line="' + line + '"]').addClass('ajaxifyCart--is-loading');
} else {
var row = $('.cart-row[data-line="' + line + '"]').addClass('ajaxifyCart--is-loading');
}

if ( qty === 0 ) {
row.addClass('is-removed');
}

// Slight delay to make sure removed animation is done
setTimeout(function() {
Shopify.changeItem(line, qty, adjustCartCallback);
}, 250);
}

// Save note anytime it's changed
var noteArea = $('textarea[name="note"]');
noteArea.off('change');
noteArea.on('change', function() {
var newNote = $(this).val();

// Simply updating the cart note in case they don't click update/checkout
Shopify.updateCartNote(newNote, function(cart) {});
});
if (typeof GoogleWalletButton === "function") GoogleWalletButton();
if (typeof AmazonPaymentsPayButton === "function") AmazonPaymentsPayButton();
};

adjustCartCallback = function (cart) {
isUpdating = false;

// Update quantity and price
updateCountPrice(cart);

// Hide the modal or drawer if we're at 0 items
if ( cart.item_count === 0 ) {
// Handle each add to cart method
switch (settings.method) {
case 'modal':
break;
case 'flip':
case 'drawer':
hideDrawer();
break;
}
}

// Reprint cart on short timeout so you don't see the content being removed
setTimeout(function() {
Shopify.getCart(buildCart);
}, 150)
};

createQtySelectors = function() {
// If there is a normal quantity number field in the ajax cart, replace it with our version
if ($('input[type="number"]', $cartContainer).length) {
$('input[type="number"]', $cartContainer).each(function() {
var el = $(this),
currentQty = parseInt(el.val());

var itemAdd = currentQty + 1,
itemMinus = currentQty - 1,
itemQty = currentQty + ' x';

var source = $("#ajaxifyQty").html(),
template = Handlebars.compile(source),
data = {
line: el.attr('data-line'),
itemQty: itemQty,
itemAdd: itemAdd,
itemMinus: itemMinus
};

// Append new quantity selector then remove original
el.after(template(data)).remove();
});
}

// If there is a regular link to remove an item, add attributes needed to ajaxify it
if ($('a[href^="/cart/change"]', $cartContainer).length) {
$('a[href^="/cart/change"]', $cartContainer).each(function() {
var el = $(this).addClass('ajaxifyCart--remove');
});
}
};

qtySelectors = function() {
// Change number inputs to JS ones, similar to ajax cart but without API integration.
// Make sure to add the existing name and id to the new input element
var numInputs = $('input[type="number"]');

// Qty selector has a minimum of 1 on the product page
// and 0 in the cart (determined on qty click)
var qtyMin = 0;

if (numInputs.length) {
numInputs.each(function() {
var el = $(this),
currentQty = parseInt(el.val()),
inputName = el.attr('name'),
inputId = el.attr('id');

var itemAdd = currentQty + 1,
itemMinus = currentQty - 1,
itemQty = currentQty;

var source = $("#jsQty").html(),
template = Handlebars.compile(source),
data = {
id: el.data('id'),
itemQty: itemQty,
itemAdd: itemAdd,
itemMinus: itemMinus,
inputName: inputName,
inputId: inputId
};

// Append new quantity selector then remove original
el.after(template(data)).remove();
});

// Setup listeners to add/subtract from the input
$('.js--qty-adjuster').on('click', function() {
var el = $(this),
id = el.data('id'),
qtySelector = el.siblings('.js--num'),
qty = parseInt( qtySelector.val() );

var qty = validateQty(qty);
qtyMin = $body.hasClass('template-product') ? 1 : qtyMin;

// Add or subtract from the current quantity
if (el.hasClass('js--add')) {
qty = qty + 1;
} else {
qty = qty <= qtyMin ? qtyMin : qty - 1;
}

// Update the input's number
qtySelector.val(qty);
});
}
};

scrollTop = function () {
if ($body.scrollTop() > 0 || $html.scrollTop() > 0) {
$('html, body').animate({
scrollTop: 0
}, 250, 'swing');
}
};

toggleCallback = function (data) {
// Run the callback if it's a function
if (typeof settings.onToggleCallback == 'function') {
settings.onToggleCallback.call(this, data);
}
};

validateQty = function (qty) {
if((parseFloat(qty) == parseInt(qty)) && !isNaN(qty)) {
// We have a valid number!
return qty;
} else {
// Not a number. Default to 1.
return 1;
}
};

module = {
init: init
};

return module;

}(ajaxifyShopify || {}, jQuery));

0 Likes