├── 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 '
- ' . implode('
- ', $htmlMessages) . '
';
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 '';
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 |
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 |
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 '- ' +
6 | Translator.translate('Please check your network connection and try again.') +
7 | '
';
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 |
--------------------------------------------------------------------------------