├── LICENSE ├── README.md ├── app ├── code │ └── community │ │ └── SomethingDigital │ │ └── AjaxAddToCart │ │ ├── Model │ │ └── Observer.php │ │ └── etc │ │ └── config.xml ├── design │ └── frontend │ │ └── base │ │ └── default │ │ ├── layout │ │ └── somethingdigital_ajaxaddtocart.xml │ │ └── template │ │ └── somethingdigital │ │ └── ajaxaddtocart │ │ ├── loading-modal.phtml │ │ └── page │ │ └── html │ │ ├── footer-js.phtml │ │ ├── popup.phtml │ │ └── showcase.phtml ├── etc │ └── modules │ │ └── SomethingDigital_AjaxAddToCart.xml └── locale │ └── en_US │ └── SomethingDigital_AjaxAddToCart.csv ├── modman └── skin └── frontend └── base └── default ├── css └── somethingdigital │ └── ajaxaddtocart │ └── ajaxaddtocart.css └── js └── somethingdigital └── ajaxaddtocart └── ajaxaddtocart.js /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2015 Something Digital http://www.somethingdigital.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SomethingDigital_AjaxAddToCart 2 | == 3 | 4 | Objective 5 | -- 6 | 7 | The aim is to provide a drop-in replacement for the tradition card add form post, which requires a redirect in vanilla Magento. 8 | 9 | Requirements 10 | -- 11 | 12 | - Magento 1.9+ (RWD theme compatibility) 13 | - jQuery 14 | 15 | Configurable JS Options 16 | -- 17 | | Option | Default | Description | 18 | |------------------|--------------------------------------|------------------------------------------------------------| 19 | | scroll | false | This will scroll the page up and open mini cart on success instead of displaying a success popup. | 20 | | scrollDuration | 250 | Duration of scroll animation in ms. | 21 | | popupDuration | 5 | Duration in seconds that the pop up notification displays. | 22 | | triggerMinicart | true | This will show the minicart when product added successfully. | 23 | 24 | Events Fired through JS 25 | -- 26 | **sd_ajaxaddtocart:success** 27 | 28 | After a product is successfully added to the cart, a success event called `sd_ajaxaddtocart:success` is fired. It is easy to hook into this with jQuery. An example of listening for the event and firing a notification using [jGrowl](https://github.com/stanlemon/jGrowl): 29 | 30 | ``` 31 | $j(document).on( "sd_ajaxaddtocart:success", function(e, data) { 32 | $j.jGrowl(data.message, { sticky: true, header: 'Added to Basket', footer: 'Go to basket' }); 33 | }); 34 | ``` 35 | 36 | **sd_ajaxaddtocart:failure** 37 | 38 | If a product can not be successfully added to the cart, a failure event called `sd_ajaxaddtocart:failure` is fired. 39 | 40 | 41 | License 42 | -- 43 | 44 | The MIT License 45 | 46 | Copyright (c) 2015 Something Digital http://www.somethingdigital.com 47 | 48 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 49 | 50 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 51 | 52 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 53 | -------------------------------------------------------------------------------- /app/code/community/SomethingDigital/AjaxAddToCart/Model/Observer.php: -------------------------------------------------------------------------------- 1 | _lastUpdatedItem. 22 | * @param Varien_Event_Observer $observer 23 | * @return void 24 | */ 25 | public function checkoutCartUpdateItemComplete(Varien_Event_Observer $observer) 26 | { 27 | $this->_lastUpdatedItem = $observer->getItem(); 28 | } 29 | 30 | /** 31 | * Postdispatch for Cart Delete Action to sniff for Ajax Delete 32 | * Clear headers before sending response, to fix duplicate Content-Type issue 33 | * @param Varien_Event_Observer $observer 34 | * @return void 35 | */ 36 | public function controllerActionPostdispatchCheckoutCartAjaxDelete(Varien_Event_Observer $observer) 37 | { 38 | $response = Mage::app()->getResponse(); 39 | 40 | $response->clearHeaders() 41 | ->setHeader('Content-Type', 'application/json'); 42 | } 43 | 44 | /** 45 | * Postdispatch for Cart Update Action to sniff for Ajax Update 46 | * Clear headers before sending response, to fix duplicate Content-Type issue 47 | * @param Varien_Event_Observer $observer 48 | * @return void 49 | */ 50 | public function controllerActionPostdispatchCheckoutCartAjaxUpdate(Varien_Event_Observer $observer) 51 | { 52 | $response = Mage::app()->getResponse(); 53 | 54 | $response->clearHeaders() 55 | ->setHeader('Content-Type', 'application/json'); 56 | } 57 | 58 | /** 59 | * Postdispatch for Cart Add Action to sniff for Ajax Add 60 | * @param Varien_Event_Observer $observer 61 | * @return void 62 | */ 63 | public function controllerActionPostdispatchCheckoutCartAdd(Varien_Event_Observer $observer) 64 | { 65 | $controllerAction = $observer->getControllerAction(); 66 | $response = Mage::app()->getResponse(); 67 | 68 | if(!$controllerAction->getRequest()->isAjax()) { 69 | return; 70 | } 71 | 72 | $result = $this->_buildAddResponse($controllerAction); 73 | $this->_sendAjaxResponse($response, $result); 74 | } 75 | 76 | /** 77 | * Postdispatch for Cart reconfigure action to update the mini cart 78 | * @param Varien_Event_Observer $observer 79 | * @return void 80 | */ 81 | public function controllerActionPostdispatchCheckoutCartUpdateItemOptions(Varien_Event_Observer $observer) 82 | { 83 | $controllerAction = $observer->getControllerAction(); 84 | $response = Mage::app()->getResponse(); 85 | 86 | if(!$controllerAction->getRequest()->isAjax()) { 87 | return; 88 | } 89 | 90 | $result = $this->_buildUpdateResponse($controllerAction); 91 | $this->_sendAjaxResponse($response, $result); 92 | } 93 | 94 | protected function _sendAjaxResponse($response, $result) 95 | { 96 | $response->clearAllHeaders(); 97 | $responseCode = $result['status'] === self::STATUS_SUCCESS ? 200 : 500; 98 | $response->setHttpResponseCode($responseCode); 99 | 100 | $response->clearHeaders() 101 | ->setHeader('Content-Type', 'application/json') 102 | ->setBody($this->_getCoreHelper()->jsonEncode($result)); 103 | } 104 | 105 | protected function _buildAddResponse($controllerAction) 106 | { 107 | return $this->_buildCommonResponse($controllerAction, '%s was added to your shopping cart.', 'Cannot add the item to shopping cart.'); 108 | } 109 | 110 | protected function _buildUpdateResponse($controllerAction) 111 | { 112 | $result = $this->_buildCommonResponse($controllerAction, '%s was updated in your shopping cart.', 'Cannot update the item.'); 113 | if ($this->_lastUpdatedItem !== null) { 114 | $result['product_addtocart_form_action'] = Mage::getUrl('checkout/cart/updateItemOptions', array('id' => $this->_lastUpdatedItem->getId())); 115 | } 116 | return $result; 117 | } 118 | 119 | protected function _buildCommonResponse($controllerAction, $successMessage, $errorMessage) 120 | { 121 | /* @var $catalogModel Mage_Core_Model_Catalog_Product */ 122 | $catalogModel = Mage::getModel('catalog/product'); 123 | 124 | $result = []; 125 | $storeId = Mage::app()->getStore()->getId(); 126 | $productId = Mage::app()->getRequest()->getParam('product'); 127 | $product = $catalogModel->setStoreId($storeId)->load($productId); 128 | 129 | try { 130 | if (!$product) { 131 | $result['status'] = self::STATUS_ERROR; 132 | $result['message'] = $this->_getCoreHelper()->__('Unable to find Product ID'); 133 | return $result; 134 | } 135 | 136 | //assemble the message 137 | $message = $this->_getCoreHelper()->__($successMessage, $product->getName()); 138 | $result['status'] = self::STATUS_SUCCESS; 139 | $result['message'] = $message; 140 | $result['minicart_head'] = $this->_getMinicartHtml($controllerAction); 141 | 142 | } catch(Mage_Core_Exception $e) { 143 | $result['status'] = self::STATUS_ERROR; 144 | $result['message'] = $this->_formatErrorMessages(array($this->_getCoreHelper()->__($errorMessage))); 145 | 146 | Mage::logException($e); 147 | } 148 | 149 | // Clear messages out as well, but let's check if any were errors. 150 | /** @var Mage_Checkout_Model_Session $checkoutSession */ 151 | $checkoutSession = Mage::getSingleton('checkout/session'); 152 | $checkoutMessages = $checkoutSession->getMessages(true); 153 | /** @var Mage_Core_Model_Message_Abstract[] $checkoutErrors */ 154 | $checkoutErrors = $checkoutMessages->getErrors(); 155 | /** @var Mage_Core_Model_Message_Abstract[] $checkoutErrors */ 156 | $checkoutNotices = $checkoutMessages->getItemsByType(Mage_Core_Model_Message::NOTICE); 157 | 158 | if (!empty($checkoutNotices) && $result['status'] == self::STATUS_SUCCESS) { 159 | $specifyOptionsMessageArray = array ( 160 | Mage::getModel('catalog/product_type_configurable')->getSpecifyOptionMessage(), 161 | Mage::helper('catalog')->__('Please specify the quantity of product(s).') 162 | ); 163 | foreach ($checkoutNotices as $notice) { 164 | if (in_array($notice->getText(), $specifyOptionsMessageArray)) { 165 | // This is really an error, let's make sure we surface it. 166 | $checkoutErrors[] = $notice; 167 | } 168 | } 169 | } 170 | 171 | // If there were errors, let's change the response. 172 | if (!empty($checkoutErrors) && $result['status'] == self::STATUS_SUCCESS) { 173 | $result['status'] = self::STATUS_ERROR; 174 | // Remove the minicart html, to avoid confusion. 175 | unset($result['minicart_head']); 176 | 177 | $messages = array(); 178 | foreach ($checkoutErrors as $error) { 179 | $messages[] = $this->_getCoreHelper()->escapeHtml($error->getText()); 180 | } 181 | $result['message'] = $this->_formatErrorMessages($messages); 182 | } 183 | 184 | return $result; 185 | } 186 | 187 | protected function _formatErrorMessages($htmlMessages) 188 | { 189 | return ''; 190 | } 191 | 192 | /** 193 | * Retrieve the minicart_head block's html 194 | * @param Mage_Core_Controller_Varien_Action $controllerAction the request's controller 195 | * @return string 196 | */ 197 | protected function _getMinicartHtml($controllerAction) 198 | { 199 | $controllerAction->loadLayout(); 200 | $sidebar = $controllerAction->getLayout()->getBlock('minicart_head'); 201 | if ($sidebar) { 202 | $sidebarHtml = Mage::getSingleton('core/url')->sessionUrlVar($sidebar->toHtml()); 203 | return '
' . $sidebarHtml . '
'; 204 | } 205 | return ''; 206 | } 207 | 208 | protected function _getCoreHelper() 209 | { 210 | if ($this->_coreHelper === null) { 211 | /** @var Mage_Core_Helper_Data $helper */ 212 | $helper = Mage::helper('core'); 213 | $this->_coreHelper = $helper; 214 | } 215 | return $this->_coreHelper; 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /app/code/community/SomethingDigital/AjaxAddToCart/etc/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 1.1.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | somethingdigital_ajaxaddtocart.xml 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | SomethingDigital_AjaxAddToCart_Model_Observer 21 | controllerActionPostdispatchCheckoutCartAdd 22 | 23 | 24 | 25 | 26 | 27 | 28 | SomethingDigital_AjaxAddToCart_Model_Observer 29 | controllerActionPostdispatchCheckoutCartAjaxDelete 30 | 31 | 32 | 33 | 34 | 35 | 36 | SomethingDigital_AjaxAddToCart_Model_Observer 37 | controllerActionPostdispatchCheckoutCartAjaxUpdate 38 | 39 | 40 | 41 | 42 | 43 | 44 | SomethingDigital_AjaxAddToCart_Model_Observer 45 | controllerActionPostdispatchCheckoutCartUpdateItemOptions 46 | 47 | 48 | 49 | 50 | 51 | 52 | SomethingDigital_AjaxAddToCart_Model_Observer 53 | checkoutCartUpdateItemComplete 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | SomethingDigital_AjaxAddToCart.csv 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /app/design/frontend/base/default/layout/somethingdigital_ajaxaddtocart.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | skin_csscss/somethingdigital/ajaxaddtocart/ajaxaddtocart.css 6 | 7 | 8 | 9 | skin_jsjs/somethingdigital/ajaxaddtocart/ajaxaddtocart.js 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | skin_csscss/somethingdigital/ajaxaddtocart/ajaxaddtocart.css 19 | 20 | 21 | 22 | skin_jsjs/somethingdigital/ajaxaddtocart/ajaxaddtocart.js 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/design/frontend/base/default/template/somethingdigital/ajaxaddtocart/loading-modal.phtml: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 | 8 | 9 |

__('Loading…') ?>

10 |
11 |
-------------------------------------------------------------------------------- /app/design/frontend/base/default/template/somethingdigital/ajaxaddtocart/page/html/footer-js.phtml: -------------------------------------------------------------------------------- 1 | getCssJsHtml() ?> 2 | getChildHtml() ?> 3 | -------------------------------------------------------------------------------- /app/design/frontend/base/default/template/somethingdigital/ajaxaddtocart/page/html/popup.phtml: -------------------------------------------------------------------------------- 1 | 3 |
4 |
5 |
6 |

__('Added to Cart'); ?>

7 | 8 |
9 |
10 |

11 | __('The product was added to your cart'); ?> 12 |

13 | 16 |
17 |
18 |
19 | -------------------------------------------------------------------------------- /app/design/frontend/base/default/template/somethingdigital/ajaxaddtocart/page/html/showcase.phtml: -------------------------------------------------------------------------------- 1 | 3 |
4 | -------------------------------------------------------------------------------- /app/etc/modules/SomethingDigital_AjaxAddToCart.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | community 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/locale/en_US/SomethingDigital_AjaxAddToCart.csv: -------------------------------------------------------------------------------- 1 | "Added to Cart","Added to Cart" 2 | "The product was added to your cart","The product was added to your cart" 3 | "Go to Cart","Go to Cart" 4 | "Close","Close" 5 | "%s product was added to your shopping cart.","%s product was added to your shopping cart." 6 | "%s products were added to your shopping cart.","%s products were added to your shopping cart." 7 | "%s was added to your shopping cart.","%s was added to your shopping cart." 8 | -------------------------------------------------------------------------------- /modman: -------------------------------------------------------------------------------- 1 | #SomethingDigital_AjaxAddToCart 2 | # app 3 | app/code/community/SomethingDigital/AjaxAddToCart 4 | app/design/frontend/base/default/layout/somethingdigital_ajaxaddtocart.xml 5 | app/design/frontend/base/default/template/somethingdigital/ajaxaddtocart 6 | 7 | # skin 8 | skin/frontend/base/default/css/somethingdigital/ajaxaddtocart 9 | skin/frontend/base/default/js/somethingdigital/ajaxaddtocart 10 | 11 | # etc 12 | app/etc/modules/SomethingDigital_AjaxAddToCart.xml 13 | 14 | # translation 15 | app/locale/en_US/SomethingDigital_AjaxAddToCart.csv 16 | -------------------------------------------------------------------------------- /skin/frontend/base/default/css/somethingdigital/ajaxaddtocart/ajaxaddtocart.css: -------------------------------------------------------------------------------- 1 | .sd-ajax-add-to-cart___pop-up--template { 2 | display: none; 3 | } 4 | 5 | .sd-ajax-add-to-cart___pop-up--showcase:empty { 6 | display: none; 7 | } 8 | 9 | .sd-ajax-add-to-cart___pop-up--showcase { 10 | position: fixed; 11 | top: 0; 12 | right: 0; 13 | width: 100%; 14 | max-width: 425px; 15 | z-index: 9999; 16 | } 17 | 18 | .sd-ajax-add-to-cart-popup { 19 | margin: 20px; 20 | background: white; 21 | -webkit-box-shadow: 0px 0px 11px 0px rgba(0,0,0,0.5); 22 | -moz-box-shadow: 0px 0px 11px 0px rgba(0,0,0,0.5); 23 | box-shadow: 0px 0px 11px 0px rgba(0,0,0,0.5); 24 | } 25 | 26 | .sd-ajax-add-to-cart-popup__header { 27 | position: relative; 28 | padding: 10px 20px; 29 | background-color: #333; 30 | } 31 | 32 | .sd-ajax-add-to-cart-popup__header h3 { 33 | margin: 0; 34 | font-size: 16px; 35 | color: white; 36 | text-transform: uppercase; 37 | } 38 | 39 | .sd-ajax-add-to-cart-popup__close { 40 | position: absolute; 41 | top: 0; 42 | right: 0; 43 | margin: 10px 20px; 44 | padding: 0; 45 | font-size: 12px; 46 | color: white; 47 | text-decoration: none; 48 | background-color: transparent; 49 | border: 0; 50 | } 51 | 52 | .sd-ajax-add-to-cart-popup__message { 53 | margin-bottom: 10px; 54 | } 55 | 56 | .sd-ajax-add-to-cart-popup__content { 57 | padding: 20px; 58 | padding-top: 10px; 59 | } 60 | 61 | .sd-ajax-add-to-cart-popup__psuedo-button { 62 | padding: 11px 20px; 63 | font-size: 14px; 64 | line-height: 18px; 65 | text-align: center; 66 | text-decoration: none; 67 | text-transform: uppercase; 68 | border: 0; 69 | padding: 10px 20px; 70 | color: #000; 71 | background-color: #fff; 72 | border: 1px solid #1e1c1d; 73 | } 74 | -------------------------------------------------------------------------------- /skin/frontend/base/default/js/somethingdigital/ajaxaddtocart/ajaxaddtocart.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function($){ 4 | var getDefaultErrorHtml = function() { 5 | return ''; 8 | }; 9 | 10 | window.sdAjaxaddtocart = { 11 | init:function(productAddToCartForm, options) { 12 | 13 | var settings = $.extend({ 14 | scroll: false, 15 | scrollDuration: 250, 16 | triggerPopup: true, 17 | popupDuration: 0, // 0 means infinite -- for accessibility concerns. USe 1+ for specific duration 18 | popupTrackFocus: true, 19 | triggerMinicart: true, 20 | triggerLoadingModal: true 21 | }, options); 22 | 23 | var $body = $('body'); 24 | 25 | productAddToCartForm.submit = productAddToCartForm.submit.wrap(function($super, button, url) { 26 | var form = this.form; 27 | var oldUrl = form.action; 28 | var e = null; 29 | var args = {button: button, url: url, form: form}; 30 | var weHaveALoadingModal = typeof loadingModal !== 'undefined'; // check if site has a loadingModal 31 | 32 | if (this.validator.validate()) { 33 | $body.addClass('locked'); 34 | 35 | // Fire submit event on submit -- useful for custom loaders 36 | $(document).trigger("sd_ajaxaddtocart:submit", [args]); 37 | 38 | if (weHaveALoadingModal && settings.triggerLoadingModal) { 39 | loadingModal.show(); 40 | } 41 | 42 | try { 43 | $.ajax({ 44 | url: form.action, 45 | dataType: 'json', 46 | type: 'post', 47 | data: form.serialize() 48 | }) 49 | .done(function(data){ 50 | var $updatedCart = $(data.minicart_head); 51 | var headerCartHtml = $updatedCart.find('#header-cart').html(); 52 | var skipCartHtml = $updatedCart.find('.skip-cart').html(); 53 | 54 | var $notificationTemplate = $('#sd-ajax-add-to-cart___pop-up--template').children(); 55 | var $notificationShowcase = $('#sd-ajax-add-to-cart___pop-up--showcase'); 56 | 57 | // Do we need to update the product form's action? 58 | // This allows one to continue configuring, for example. 59 | if (data.product_addtocart_form_action) { 60 | $(form).prop('action', data.product_addtocart_form_action); 61 | } 62 | 63 | $body.removeClass('locked'); 64 | 65 | // If add to cart from quickview, close quickview 66 | if (typeof(window.sdQuickview) != "undefined" 67 | && $('#sd-quickview').is(':visible')) { 68 | window.sdQuickview.close(); 69 | } 70 | 71 | if(settings.scroll) { 72 | $('html,body').animate({scrollTop: 0}, settings.scrollDuration); 73 | } 74 | 75 | //apply the minicart update and unfurl it 76 | $('#header-cart').html(headerCartHtml); 77 | 78 | var $cartLink = $('.skip-cart'); 79 | $cartLink.html(skipCartHtml); 80 | if ($updatedCart.find('.skip-cart').hasClass('no-count')) { 81 | $cartLink.addClass('no-count'); 82 | } else { 83 | $cartLink.removeClass('no-count'); 84 | } 85 | 86 | if (settings.triggerMinicart) { 87 | $cartLink.trigger('click'); 88 | } 89 | 90 | // Fire success event on success and pass through data returned from response 91 | $(document).trigger("sd_ajaxaddtocart:success", [data, args]); 92 | 93 | // Show our popup 94 | if (!settings.scroll && settings.triggerPopup) { 95 | // Close minicart 96 | $('#header-cart__link').removeClass('skip-active'); 97 | $('#header-cart').removeClass('skip-active'); 98 | 99 | // Clone our template 100 | var $notification = $notificationTemplate.clone(); 101 | var fadeTimeout = null; 102 | 103 | if (data.message) { 104 | $notification.find('.sd-ajax-add-to-cart-popup__message').text(data.message); 105 | } 106 | 107 | $notification.find('.sd-ajax-add-to-cart-popup__close').on('click', function(e) { 108 | e.preventDefault(); 109 | $notification.hide(); 110 | $(document).trigger('sd_ajaxaddtocart:popup-close', ['sd_ajaxaddtocart:popup', $notification]); 111 | clearTimeout(fadeTimeout); 112 | fadeTimeout = null; 113 | }); 114 | 115 | $notification.appendTo($notificationShowcase); 116 | $(document).trigger('sd_ajaxaddtocart:popup-open', ['sd_ajaxaddtocart:popup', $notification]); 117 | if (settings.popupTrackFocus) { 118 | // This marks the notification element as focusable, although not in the tab order. 119 | $notification.attr('tabindex', '-1').focus(); 120 | } 121 | 122 | var doFade = function() { 123 | var $active = $(document.activeElement); 124 | if ($active.closest($notificationShowcase).length != 0 && settings.popupTrackFocus) { 125 | // We're focused in the notification. Wait for it to blur. 126 | $active.one('blur', tryFadeOnBlur); 127 | } else { 128 | $(document).trigger('sd_ajaxaddtocart:popup-close', ['sd_ajaxaddtocart:popup', $notification]); 129 | $notification.fadeOut(); 130 | } 131 | }; 132 | var tryFadeOnBlur = function() { 133 | // If they closed, we're done. Just ignore. 134 | if (fadeTimeout === null) { 135 | return; 136 | } 137 | 138 | var $active = $(document.activeElement); 139 | if ($active.closest($notificationShowcase).length != 0 && settings.popupTrackFocus) { 140 | // We're STILL focused in the notification. Wait yet more. 141 | $active.one('blur', tryFadeOnBlur); 142 | } else { 143 | // Okay, now let's do the timeout again. We'll check again if they refocus. 144 | fadeTimeout = setTimeout(doFade, settings.popupDuration * 1000); 145 | } 146 | }; 147 | 148 | if (settings.popupDuration !== 0) { 149 | fadeTimeout = setTimeout(doFade, settings.popupDuration * 1000); 150 | } 151 | } 152 | }) 153 | .fail(function(jqXHR){ 154 | var data = jqXHR.responseJSON; 155 | var errorMessages = data ? data.message : getDefaultErrorHtml(); 156 | 157 | // display failure message 158 | // if add to cart from quickview 159 | if (typeof(window.sdQuickView) == "object" && $('#sd-quickview').is(':visible')) { 160 | window.sdQuickview.content.prepend(errorMessages); 161 | 162 | // autoscroll up to the error message 163 | $('html, body').animate({ 164 | scrollTop: $('.messages').offset().top - 100 165 | }, 'fast'); 166 | 167 | //remove the failure message after 5s 168 | window.sdQuickview.content.find('.messages') 169 | .delay(5000) 170 | .fadeOut(function(){ 171 | $(this).remove(); 172 | }); 173 | } else { 174 | $('.col-main').prepend(errorMessages); 175 | 176 | // autoscroll up to the error message 177 | $('html, body').animate({ 178 | scrollTop: $('.messages').offset().top - 100 179 | }, 'fast'); 180 | 181 | //remove the failure message after 5s 182 | $('.col-main .messages') 183 | .delay(5000) 184 | .fadeOut(function(){ 185 | $(this).remove(); 186 | }); 187 | } 188 | 189 | // Fire success event on failure and pass through data returned from response 190 | $(document).trigger("sd_ajaxaddtocart:failure", [data, args]); 191 | 192 | //unset the modal block 193 | if (weHaveALoadingModal && settings.triggerLoadingModal) { 194 | loadingModal.remove(); 195 | } 196 | }) 197 | .always(loadingModal.remove); 198 | } catch (e) { 199 | console.error(e); 200 | } 201 | this.form.action = oldUrl; 202 | if (e) { 203 | throw e; 204 | } 205 | } 206 | }.bind(productAddToCartForm)); 207 | } 208 | }; 209 | 210 | if (typeof(productAddToCartForm) != "undefined") { 211 | // override default options with global sdAjaxaddtocartOptions variable 212 | var cartOptions = (typeof(sdAjaxaddtocartOptions) != "undefined")? sdAjaxaddtocartOptions : {}; 213 | 214 | sdAjaxaddtocart.init(productAddToCartForm, cartOptions); 215 | } 216 | 217 | })(jQuery); 218 | --------------------------------------------------------------------------------