Skip to content
This repository
Browse code

Adds proper build of changes made by Tim

Adds a proper build of the JS files
and ups the version number to 1.0.1
  • Loading branch information...
commit c2fb8ea3f9caa11a5548d9f5a96f9cf541485c5c 1 parent 0c91750
Cristiano Betta cbetta authored
3  README.md
Source Rendered
@@ -74,11 +74,14 @@ All of PayPal's [HTML button variables](https://cms.paypal.com/us/cgi-bin/?cmd=_
74 74 * `data-tax` Transaction-based tax override variable.
75 75 * `data-size` For button images: `small` and `large` work. For QR codes enter the pixel length of the longest side.
76 76 * `data-locale` The desired locale of the PayPal site.
  77 +* `data-callback` The IPN notify URL to be called on completion of the transaction.
77 78
78 79
79 80 ## Editable fields
80 81 Creating editable fields is easy! Just add `-editable` to the name of your variable, e.g. `data-quantity-editable`, and an input field will magically appear for your users.
81 82
  83 +## Callback notification
  84 +On completion of a transaction you can get a payment notification ([IPN](https://www.x.com/developers/paypal/documentation-tools/ipn/integration-guide/IPNIntro)) on a callback URL you specify using the `data-callback` attribute. An [IPN simulator](https://developer.paypal.com/webapps/developer/applications/ipn_simulator) is available on the sandbox.
82 85
83 86 ## Localization
84 87 * Changing the default language of a button can be done by setting the variable `data-lc` with the correct locale code, e.g. es_ES.
1,738 dist/paypal-button-minicart.js
<
... ... @@ -1,1742 +1,9 @@
1 1 /*!
2 2 * PayPalJSButtons
3 3 * JavaScript integration for PayPal's payment buttons
4   - * @version 1.0.0 - 2013-03-17
  4 + * @version 1.0.1 - 2013-03-26
5 5 * @author Jeff Harrell <https://github.com/jeffharrell/>
6 6 */
7   -/*!
8   - * MiniCart
9   - *
10   - * Improve your PayPal integration by creating an overlay which appears as a user adds products to their cart.
11   - *
12   - * @version 2.5.0 - 2012-12-02, 1:04:43 PM
13   - * @author Jeff Harrell <https://github.com/jeffharrell/>
14   - * @url http://www.minicartjs.com/
15   - * @license <eBay Open Source License Agreement <https://github.com/jeffharrell/MiniCart/blob/master/LICENSE>>
16   - */
17   -if (typeof PAYPAL === 'undefined' || !PAYPAL) {
18   - var PAYPAL = {};
19   -}
20   -
21   -PAYPAL.apps = PAYPAL.apps || {};
22   -
23   -
24   -(function () {
25   - 'use strict';
26   -
27   - /**
28   - * Default configuration
29   - */
30   - var config = {
31   - /**
32   - * The parent element the cart should "pin" to
33   - */
34   - parent: document.body,
35   -
36   - /**
37   - * Edge of the window to pin the cart to
38   - */
39   - displayEdge: 'right',
40   -
41   - /**
42   - * Distance from the edge of the window
43   - */
44   - edgeDistance: '50px',
45   -
46   - /**
47   - * HTML target property for the checkout form
48   - */
49   - formTarget: null,
50   -
51   - /**
52   - * The base path of your website to set the cookie to
53   - */
54   - cookiePath: '/',
55   -
56   - /**
57   - * The number of days to keep the cart data
58   - */
59   - cartDuration: 30,
60   -
61   - /**
62   - * Strings used for display text
63   - */
64   - strings: {
65   - button: '',
66   - subtotal: '',
67   - discount: '',
68   - shipping: '',
69   - processing: ''
70   - },
71   -
72   - /**
73   - * Unique ID used on the wrapper element
74   - */
75   - name: 'PPMiniCart',
76   -
77   - /**
78   - * Boolean to determine if the cart should "peek" when it's hidden with items
79   - */
80   - peekEnabled: true,
81   -
82   - /**
83   - * The URL of the PayPal website
84   - */
85   - paypalURL: 'https://www.paypal.com/cgi-bin/webscr',
86   -
87   - /**
88   - * The base URL to the visual assets
89   - */
90   - assetURL: 'http://www.minicartjs.com/build/',
91   -
92   - events: {
93   - /**
94   - * Custom event fired before the cart is rendered
95   - */
96   - onRender: null,
97   -
98   - /**
99   - * Custom event fired after the cart is rendered
100   - */
101   - afterRender: null,
102   -
103   - /**
104   - * Custom event fired before the cart is hidden
105   - *
106   - * @param e {event} The triggering event
107   - */
108   - onHide: null,
109   -
110   - /**
111   - * Custom event fired after the cart is hidden
112   - *
113   - * @param e {event} The triggering event
114   - */
115   - afterHide: null,
116   -
117   - /**
118   - * Custom event fired before the cart is shown
119   - *
120   - * @param e {event} The triggering event
121   - */
122   - onShow: null,
123   -
124   - /**
125   - * Custom event fired after the cart is shown
126   - *
127   - * @param e {event} The triggering event
128   - */
129   - afterShow: null,
130   -
131   - /**
132   - * Custom event fired before a product is added to the cart
133   - *
134   - * @param data {object} Product object
135   - */
136   - onAddToCart: null,
137   -
138   - /**
139   - * Custom event fired after a product is added to the cart
140   - *
141   - * @param data {object} Product object
142   - */
143   - afterAddToCart: null,
144   -
145   - /**
146   - * Custom event fired before a product is removed from the cart
147   - *
148   - * @param data {object} Product object
149   - */
150   - onRemoveFromCart: null,
151   -
152   - /**
153   - * Custom event fired after a product is removed from the cart
154   - *
155   - * @param data {object} Product object
156   - */
157   - afterRemoveFromCart: null,
158   -
159   - /**
160   - * Custom event fired before the checkout action takes place
161   - *
162   - * @param e {event} The triggering event
163   - */
164   - onCheckout: null,
165   -
166   - /**
167   - * Custom event fired before the cart is reset
168   - */
169   - onReset: null,
170   -
171   - /**
172   - * Custom event fired after the cart is reset
173   - */
174   - afterReset: null
175   - }
176   - };
177   -
178   -
179   - if (!PAYPAL.apps.MiniCart) {
180   -
181   - /**
182   - * Mini Cart application
183   - */
184   - PAYPAL.apps.MiniCart = (function () {
185   -
186   - var minicart = {},
187   - isShowing = false,
188   - isRendered = false;
189   -
190   -
191   - /** PRIVATE **/
192   -
193   - /**
194   - * PayPal form cmd values which are supported
195   - */
196   - var SUPPORTED_CMDS = { _cart: true, _xclick: true };
197   -
198   -
199   - /**
200   - * The form origin that is passed to PayPal
201   - */
202   - var BN_VALUE = 'MiniCart_AddToCart_WPS_US';
203   -
204   -
205   - /**
206   - * Regex filter for cart settings, which appear only once in a cart
207   - */
208   - var SETTING_FILTER = /^(?:business|currency_code|lc|paymentaction|no_shipping|cn|no_note|invoice|handling_cart|weight_cart|weight_unit|tax_cart|page_style|image_url|cpp_|cs|cbt|return|cancel_return|notify_url|rm|custom|charset)/;
209   -
210   -
211   - /**
212   - * Adds the cart's CSS to the page in a <style> element.
213   - * The CSS lives in this file so that it can leverage properties from the config
214   - * and doesn't require an additional download. To override the CSS see the FAQ.
215   - */
216   - var _addCSS = function () {
217   - var name = config.name,
218   - css = [],
219   - style, head;
220   -
221   - css.push('#' + name + ' form { position:fixed; float:none; top:-250px; ' + config.displayEdge + ':' + config.edgeDistance + '; width:265px; margin:0; padding:50px 10px 0; min-height:170px; background:#fff url(' + config.assetURL + 'images/minicart_sprite.png) no-repeat -125px -60px; border:1px solid #999; border-top:0; font:13px/normal arial, helvetica; color:#333; text-align:left; -moz-border-radius:0 0 8px 8px; -webkit-border-radius:0 0 8px 8px; border-radius:0 0 8px 8px; -moz-box-shadow:1px 1px 1px rgba(0, 0, 0, 0.1); -webkit-box-shadow:1px 1px 1px rgba(0, 0, 0, 0.1); box-shadow:1px 1px 1px rgba(0, 0, 0, 0.1); } ');
222   - css.push('#' + name + ' ul { position:relative; overflow-x:hidden; overflow-y:auto; height:130px; margin:0 0 7px; padding:0; list-style-type:none; border-top:1px solid #ccc; border-bottom:1px solid #ccc; } ');
223   - css.push('#' + name + ' li { position:relative; margin:-1px 0 0; padding:6px 5px 6px 0; border-top:1px solid #f2f2f2; } ');
224   - css.push('#' + name + ' li a { display: block; width: 155px; color:#333; text-decoration:none; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } ');
225   - css.push('#' + name + ' li a span { color:#999; font-size:10px; } ');
226   - css.push('#' + name + ' li .quantity { position:absolute; top:.5em; right:78px; width:22px; padding:1px; border:1px solid #83a8cc; text-align:right; } ');
227   - css.push('#' + name + ' li .price { position:absolute; top:.5em; right:4px; } ');
228   - css.push('#' + name + ' li .remove { position:absolute; top:9px; right:60px; width:14px; height:14px; background:url(' + config.assetURL + 'images/minicart_sprite.png) no-repeat -134px -4px; border:0; cursor:pointer; } ');
229   - css.push('#' + name + ' p { margin:0; padding:0 0 0 20px; background:url(' + config.assetURL + 'images/minicart_sprite.png) no-repeat; font-size:13px; font-weight:bold; } ');
230   - css.push('#' + name + ' p:hover { cursor:pointer; } ');
231   - css.push('#' + name + ' p input { float:right; margin:4px 0 0; padding:1px 4px; text-decoration:none; font-weight:normal; color:#333; background:#ffa822 url(' + config.assetURL + 'images/minicart_sprite.png) repeat-x left center; border:1px solid #d5bd98; border-right-color:#935e0d; border-bottom-color:#935e0d; -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; } ');
232   - css.push('#' + name + ' p .shipping { display:block; font-size:10px; font-weight:normal; color:#999; } ');
233   -
234   - style = document.createElement('style');
235   - style.type = 'text/css';
236   -
237   - if (style.styleSheet) {
238   - style.styleSheet.cssText = css.join('');
239   - } else {
240   - style.appendChild(document.createTextNode(css.join('')));
241   - }
242   -
243   - head = document.getElementsByTagName('head')[0];
244   - head.appendChild(style);
245   - };
246   -
247   -
248   - /**
249   - * Builds the DOM elements required by the cart
250   - */
251   - var _buildDOM = function () {
252   - var UI = minicart.UI,
253   - cmd, type, bn, parent, version;
254   -
255   - UI.wrapper = document.createElement('div');
256   - UI.wrapper.id = config.name;
257   -
258   - cmd = document.createElement('input');
259   - cmd.type = 'hidden';
260   - cmd.name = 'cmd';
261   - cmd.value = '_cart';
262   -
263   - type = cmd.cloneNode(false);
264   - type.name = 'upload';
265   - type.value = '1';
266   -
267   - bn = cmd.cloneNode(false);
268   - bn.name = 'bn';
269   - bn.value = BN_VALUE;
270   -
271   - UI.cart = document.createElement('form');
272   - UI.cart.method = 'post';
273   - UI.cart.action = config.paypalURL;
274   -
275   - if (config.formTarget) {
276   - UI.cart.target = config.formTarget;
277   - }
278   -
279   - UI.cart.appendChild(cmd);
280   - UI.cart.appendChild(type);
281   - UI.cart.appendChild(bn);
282   - UI.wrapper.appendChild(UI.cart);
283   -
284   - UI.itemList = document.createElement('ul');
285   - UI.cart.appendChild(UI.itemList);
286   -
287   - UI.summary = document.createElement('p');
288   - UI.cart.appendChild(UI.summary);
289   -
290   - UI.button = document.createElement('input');
291   - UI.button.type = 'submit';
292   - UI.button.value = config.strings.button || 'Checkout';
293   - UI.summary.appendChild(UI.button);
294   -
295   - UI.subtotal = document.createElement('span');
296   - UI.subtotal.innerHTML = config.strings.subtotal || 'Subtotal: ';
297   -
298   - UI.subtotalAmount = document.createElement('span');
299   - UI.subtotalAmount.innerHTML = '0.00';
300   -
301   - UI.subtotal.appendChild(UI.subtotalAmount);
302   - UI.summary.appendChild(UI.subtotal);
303   -
304   - UI.shipping = document.createElement('span');
305   - UI.shipping.className = 'shipping';
306   - UI.shipping.innerHTML = config.strings.shipping || 'does not include shipping &amp; tax';
307   - UI.summary.appendChild(UI.shipping);
308   -
309   - // Workaround: IE 6 and IE 7/8 in quirks mode do not support position:fixed in CSS
310   - if (window.attachEvent && !window.opera) {
311   - version = navigator.userAgent.match(/MSIE\s([^;]*)/);
312   -
313   - if (version) {
314   - version = parseFloat(version[1]);
315   -
316   - if (version < 7 || (version >= 7 && document.compatMode === 'BackCompat')) {
317   - UI.cart.style.position = 'absolute';
318   - UI.wrapper.style[config.displayEdge] = '0';
319   - UI.wrapper.style.setExpression('top', 'x = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop');
320   - }
321   - }
322   - }
323   -
324   - parent = (typeof config.parent === 'string') ? document.getElementById(config.parent) : config.parent;
325   - parent.appendChild(UI.wrapper);
326   - };
327   -
328   -
329   - /**
330   - * Attaches the cart events to it's DOM elements
331   - */
332   - var _bindEvents = function () {
333   - var ui = minicart.UI,
334   - forms, form, i;
335   -
336   - // Look for all "Cart" and "Buy Now" forms on the page and attach events
337   - forms = document.getElementsByTagName('form');
338   -
339   - for (i = 0; i < forms.length; i++) {
340   - form = forms[i];
341   -
342   - if (form.cmd && SUPPORTED_CMDS[form.cmd.value]) {
343   - minicart.bindForm(form);
344   - }
345   - }
346   -
347   - // Hide the Mini Cart for all non-cart related clicks
348   - $.event.add(document, 'click', function (e) {
349   - if (isShowing) {
350   - var target = e.target,
351   - cartEl = ui.cart;
352   -
353   - if (!(/input|button|select|option/i.test(target.tagName))) {
354   - while (target.nodeType === 1) {
355   - if (target === cartEl) {
356   - return;
357   - }
358   -
359   - target = target.parentNode;
360   - }
361   -
362   - minicart.hide(null);
363   - }
364   - }
365   - });
366   -
367   - // Run the checkout code when submitting the form
368   - $.event.add(ui.cart, 'submit', function (e) {
369   - _checkout(e);
370   - });
371   -
372   - // Show the cart when clicking on the summary
373   - $.event.add(ui.summary, 'click', function (e) {
374   - var target = e.target;
375   -
376   - if (target !== ui.button) {
377   - minicart.toggle(e);
378   - }
379   - });
380   -
381   - // Update other windows when HTML5 localStorage is updated
382   - if (window.attachEvent && !window.opera) {
383   - $.event.add(document, 'storage', function (e) {
384   - // IE needs a delay in order to properly see the change
385   - setTimeout(_redrawCartItems, 100);
386   - });
387   - } else {
388   - $.event.add(window, 'storage', function (e) {
389   - // Safari, Chrome, and Opera can filter on updated storage key
390   - // Firefox can't so it uses a brute force approach
391   - if ((e.key && e.key === config.name) || !e.key) {
392   - _redrawCartItems();
393   - }
394   - });
395   - }
396   - };
397   -
398   -
399   - /**
400   - * Parses the userConfig (if applicable) and overwrites the default values
401   - */
402   - var _parseUserConfig = function (userConfig) {
403   - var key;
404   -
405   - // TODO: This should recursively merge the config values
406   - for (key in userConfig) {
407   - if (typeof config[key] !== undefined) {
408   - config[key] = userConfig[key];
409   - }
410   - }
411   - };
412   -
413   -
414   - /**
415   - * Loads the stored data and builds the cart
416   - */
417   - var _parseStorage = function () {
418   - var data, length, i;
419   -
420   - if ((data = $.storage.load())) {
421   - length = data.length;
422   -
423   - for (i = 0; i < length; i++) {
424   - if (_renderProduct(data[i])) {
425   - isShowing = true;
426   - }
427   - }
428   - }
429   - };
430   -
431   -
432   - /**
433   - * Data parser used for forms
434   - *
435   - * @param form {HTMLElement} An HTML form
436   - * @return {object}
437   - */
438   - var _parseForm = function (form) {
439   - var raw = form.elements,
440   - data = {},
441   - pair, value, i, len;
442   -
443   - for (i = 0, len = raw.length; i < len; i++) {
444   - pair = raw[i];
445   -
446   - if ((value = $.util.getInputValue(pair))) {
447   - data[pair.name] = value;
448   - }
449   - }
450   -
451   - return data;
452   - };
453   -
454   -
455   - /**
456   - * Massage's a object's data in preparation for adding it to the user's cart
457   - *
458   - * @param data {object} An object of WPS xclick style data to add to the cart. The format is { product: '', settings: '' }.
459   - * @return {object}
460   - */
461   - var _parseData = function (data) {
462   - var product = {},
463   - settings = {},
464   - existing, option_index, key, len, match, i, j;
465   -
466   - // Parse the data into a two categories: product and settings
467   - for (key in data) {
468   - if (SETTING_FILTER.test(key)) {
469   - settings[key] = data[key];
470   - } else {
471   - product[key] = data[key];
472   - }
473   - }
474   -
475   - // Check the products to see if this variation already exists
476   - // If it does then reuse the same object
477   - for (i = 0, len = minicart.products.length; i < len; i++) {
478   - existing = minicart.products[i].product;
479   -
480   - // Do the product name and number match
481   - if (product.item_name === existing.item_name && product.item_number === existing.item_number) {
482   - // Products are a match so far; Now do all of the products options match?
483   - match = true;
484   - j = 0;
485   -
486   - while (existing['os' + j]) {
487   - if (product['os' + j] !== existing['os' + j]) {
488   - match = false;
489   - break;
490   - }
491   -
492   - j++;
493   - }
494   -
495   - if (match) {
496   - product.offset = existing.offset;
497   - break;
498   - }
499   - }
500   - }
501   -
502   - // Normalize the values
503   - product.href = product.href || window.location.href;
504   - product.quantity = product.quantity || 1;
505   - product.amount = product.amount || 0;
506   -
507   - // Add Mini Cart specific settings
508   - if (settings['return'] && settings['return'].indexOf('#') === -1) {
509   - settings['return'] += '#' + config.name + '=reset';
510   - }
511   -
512   - // Add option amounts to the total amount
513   - option_index = (product.option_index) ? product.option_index : 0;
514   -
515   - while (product['os' + option_index]) {
516   - i = 0;
517   -
518   - while (typeof product['option_select' + i] !== 'undefined') {
519   - if (product['option_select' + i] === product['os' + option_index]) {
520   - product.amount = product.amount + parseFloat(product['option_amount' + i]);
521   - break;
522   - }
523   -
524   - i++;
525   - }
526   -
527   - option_index++;
528   - }
529   -
530   - return {
531   - product: product,
532   - settings: settings
533   - };
534   - };
535   -
536   -
537   - /**
538   - * Resets the card and renders the products
539   - */
540   - var _redrawCartItems = function (silent) {
541   - minicart.products = [];
542   - minicart.UI.itemList.innerHTML = '';
543   - minicart.UI.subtotalAmount.innerHTML = '';
544   -
545   - _parseStorage();
546   - minicart.updateSubtotal(silent);
547   - };
548   -
549   -
550   - /**
551   - * Renders the product in the cart
552   - *
553   - * @param data {object} The data for the product
554   - */
555   - var _renderProduct = function (data) {
556   - var ui = minicart.UI,
557   - cartEl = ui.cart,
558   - product = new ProductNode(data, minicart.UI.itemList.children.length + 1),
559   - offset = data.product.offset,
560   - keyupTimer, hiddenInput, key;
561   -
562   - minicart.products[offset] = product;
563   -
564   - // Add hidden settings data to parent form
565   - for (key in data.settings) {
566   - if (cartEl.elements[key]) {
567   - if (cartEl.elements[key].value) {
568   - cartEl.elements[key].value = data.settings[key];
569   - } else {
570   - cartEl.elements[key] = data.settings[key];
571   - }
572   - } else {
573   - hiddenInput = document.createElement('input');
574   - hiddenInput.type = 'hidden';
575   - hiddenInput.name = key;
576   - hiddenInput.value = data.settings[key];
577   -
578   - cartEl.appendChild(hiddenInput);
579   - }
580   - }
581   -
582   - // if the product has no name or number then don't add it
583   - if (product.isPlaceholder) {
584   - return false;
585   - // otherwise, setup the new element
586   - } else {
587   - // Click event for "x"
588   - $.event.add(product.removeInput, 'click', function () {
589   - _removeProduct(product, offset);
590   - });
591   -
592   - // Event for changing quantities
593   - var currentValue = product.quantityInput.value;
594   -
595   - $.event.add(product.quantityInput, 'keyup', function () {
596   - var that = this;
597   -
598   - keyupTimer = setTimeout(function () {
599   - var value = parseInt(that.value, 10);
600   -
601   - if (!isNaN(value) && value !== currentValue) {
602   - currentValue = value;
603   -
604   - product.setQuantity(value);
605   -
606   - // Delete the product
607   - if (!product.getQuantity()) {
608   - _removeProduct(product, offset);
609   - }
610   -
611   - minicart.updateSubtotal();
612   - $.storage.save(minicart.products);
613   - }
614   - }, 250);
615   - });
616   -
617   - // Add the item and fade it in
618   - ui.itemList.insertBefore(product.liNode, ui.itemList.firstChild);
619   - $.util.animate(product.liNode, 'opacity', { from: 0, to: 1 });
620   -
621   - return true;
622   - }
623   - };
624   -
625   -
626   - /**
627   - * Removes a product from the cart
628   - *
629   - * @param product {ProductNode} The product object
630   - * @param offset {Number} The offset for the product in the cart
631   - */
632   - var _removeProduct = function (product, offset) {
633   - var events = config.events,
634   - onRemoveFromCart = events.onRemoveFromCart,
635   - afterRemoveFromCart = events.afterRemoveFromCart;
636   -
637   - if (typeof onRemoveFromCart === 'function') {
638   - if (onRemoveFromCart.call(minicart, product) === false) {
639   - return;
640   - }
641   - }
642   -
643   - product.setQuantity(0);
644   - product.quantityInput.style.display = 'none';
645   -
646   - $.util.animate(product.liNode, 'opacity', { from: 1, to: 0 }, function () {
647   - $.util.animate(product.liNode, 'height', { from: 18, to: 0 }, function () {
648   - try {
649   - product.liNode.parentNode.removeChild(product.liNode);
650   - } catch (e) {
651   - // fail
652   - }
653   -
654   - // regenerate the form element indexes
655   - var products = minicart.UI.cart.getElementsByTagName('li'),
656   - products_len = products.length,
657   - inputs,
658   - inputs_len,
659   - input,
660   - matches,
661   - i, j, k = 1;
662   -
663   - for (i = 0 ; i < products_len; i++) {
664   - inputs = products[i].getElementsByTagName('input');
665   - inputs_len = inputs.length;
666   -
667   - for (j = 0; j < inputs_len; j++) {
668   - input = inputs[j];
669   - matches = /(.+)_[0-9]+$/.exec(input.name);
670   -
671   - if (matches && matches[1]) {
672   - input.name = matches[1] + '_' + k;
673   - }
674   - }
675   -
676   - k++;
677   - }
678   -
679   - if (typeof afterRemoveFromCart === 'function') {
680   - afterRemoveFromCart.call(minicart, product);
681   - }
682   - });
683   - });
684   -
685   - minicart.products[offset].product.item_name = '';
686   - minicart.products[offset].product.item_number = '';
687   -
688   - minicart.updateSubtotal();
689   - $.storage.save(minicart.products, config.cartDuration);
690   - };
691   -
692   -
693   - /**
694   - * Event when the cart form is submitted
695   - *
696   - * @param e {event} The form submission event
697   - */
698   - var _checkout = function (e) {
699   - var onCheckout = config.events.onCheckout;
700   -
701   - if (typeof onCheckout === 'function') {
702   - if (onCheckout.call(minicart, e) === false) {
703   - e.preventDefault();
704   - return;
705   - }
706   - }
707   -
708   - minicart.UI.button.value = config.strings.processing || 'Processing...';
709   - };
710   -
711   -
712   - /** PUBLIC **/
713   -
714   -
715   - /**
716   - * Array of ProductNode
717   - */
718   - minicart.products = [];
719   -
720   -
721   - /**
722   - * Container for UI elements
723   - */
724   - minicart.UI = {};
725   -
726   -
727   - /**
728   - * Renders the cart, creates the configuration and loads the data
729   - *
730   - * @param userConfig {object} User settings which override the default configuration
731   - */
732   - minicart.render = function (userConfig) {
733   - var events, onRender, afterRender, hash, cmd;
734   -
735   - // Overwrite default configuration with user settings
736   - _parseUserConfig(userConfig);
737   -
738   - events = config.events;
739   - onRender = events.onRender;
740   - afterRender = events.afterRender;
741   -
742   - if (typeof onRender === 'function') {
743   - if (onRender.call(minicart) === false) {
744   - return;
745   - }
746   - }
747   -
748   - if (!isRendered) {
749   - // Render the cart UI
750   - _addCSS();
751   - _buildDOM();
752   - _bindEvents();
753   -
754   - // Check if a transaction was completed
755   - // The "return" form param is modified to contain a hash value
756   - // with "PPMiniCart=reset". If this is seen then it's assumed
757   - // that a transaction was completed and we should reset the cart.
758   - hash = location.hash.substring(1);
759   -
760   - if (hash.indexOf(config.name + '=') === 0) {
761   - cmd = hash.split('=')[1];
762   -
763   - if (cmd === 'reset') {
764   - minicart.reset();
765   - location.hash = '';
766   - }
767   - }
768   - }
769   -
770   - // Process any stored data and render it
771   - // TODO: _parseStorage shouldn't be so tightly coupled here and one
772   - // should be able to redraw without re-parsing the storage
773   - _redrawCartItems(true);
774   -
775   - // Trigger the cart to peek on first load if any products were loaded
776   - if (!isRendered) {
777   - if (isShowing) {
778   - setTimeout(function () {
779   - minicart.hide(null);
780   - }, 500);
781   - } else {
782   - $.storage.remove();
783   - }
784   - }
785   -
786   - isRendered = true;
787   -
788   - if (typeof afterRender === 'function') {
789   - afterRender.call(minicart);
790   - }
791   - };
792   -
793   -
794   - /**
795   - * Binds a form to the Mini Cart
796   - *
797   - * @param form {HTMLElement} The form element to bind
798   - */
799   - minicart.bindForm = function (form) {
800   - if (form.add) {
801   - $.event.add(form, 'submit', function (e) {
802   - e.preventDefault(e);
803   -
804   - var data = _parseForm(e.target);
805   - minicart.addToCart(data);
806   - });
807   - } else if (form.display) {
808   - $.event.add(form, 'submit', function (e) {
809   - e.preventDefault();
810   - minicart.show(e);
811   - });
812   - } else {
813   - return false;
814   - }
815   -
816   - return true;
817   - };
818   -
819   - /**
820   - * Adds a product to the cart
821   - *
822   - * @param data {object} Product object. See _parseData for format
823   - * @return {boolean} True if the product was added, false otherwise
824   - */
825   - minicart.addToCart = function (data) {
826   - var events = config.events,
827   - onAddToCart = events.onAddToCart,
828   - afterAddToCart = events.afterAddToCart,
829   - success = false,
830   - productNode, offset;
831   -
832   - data = _parseData(data);
833   - offset = data.product.offset;
834   -
835   - if (typeof onAddToCart === 'function') {
836   - if (onAddToCart.call(minicart, data.product) === false) {
837   - return;
838   - }
839   - }
840   -
841   - // Check if the product has already been added; update if so
842   - if ((productNode = this.getProductAtOffset(offset))) {
843   - productNode.product.quantity += parseInt(data.product.quantity || 1, 10);
844   - productNode.setPrice(data.product.amount * productNode.product.quantity);
845   - productNode.setQuantity(productNode.product.quantity);
846   -
847   - success = true;
848   - // Add a new DOM element for the product
849   - } else {
850   - data.product.offset = minicart.products.length;
851   - success = _renderProduct(data);
852   - }
853   -
854   - minicart.updateSubtotal();
855   - minicart.show(null);
856   -
857   - $.storage.save(minicart.products, config.cartDuration);
858   -
859   - if (typeof afterAddToCart === 'function') {
860   - afterAddToCart.call(minicart, data);
861   - }
862   -
863   - return success;
864   - };
865   -
866   -
867   - /**
868   - * Returns a product from the Mini Cart's interal storage
869   - *
870   - * @param offset {number} The offset of the product
871   - * @return {ProductNode}
872   - */
873   - minicart.getProductAtOffset = function (offset) {
874   - return (typeof offset !== 'undefined' && this.products[offset]);
875   - };
876   -
877   -
878   - /**
879   - * Iterates over each product and calculates the subtotal
880   - *
881   - * @return {number} The subtotal
882   - */
883   - minicart.calculateSubtotal = function () {
884   - var amount = 0,
885   - products = minicart.products,
886   - product, item, price, discount, len, i;
887   -
888   - for (i = 0, len = products.length; i < len; i++) {
889   - item = products[i];
890   -
891   - if ((product = item.product)) {
892   - if (product.quantity && product.amount) {
893   - price = product.amount;
894   - discount = item.getDiscount();
895   -
896   - amount += parseFloat((price * product.quantity) - discount);
897   - }
898   - }
899   - }
900   -
901   - return amount.toFixed(2);
902   - };
903   -
904   -
905   - /**
906   - * Updates the UI with the current subtotal and currency code
907   - */
908   - minicart.updateSubtotal = function (silent) {
909   - var ui = minicart.UI,
910   - cartEl = ui.cart.elements,
911   - subtotalEl = ui.subtotalAmount,
912   - subtotal = minicart.calculateSubtotal(),
913   - level = 1,
914   - currency_code, currency_symbol, hex, len, i;
915   -
916   - // Get the currency
917   - currency_code = '';
918   - currency_symbol = '';
919   -
920   - if (cartEl.currency_code) {
921   - currency_code = cartEl.currency_code.value || cartEl.currency_code;
922   - } else {
923   - for (i = 0, len = cartEl.length; i < len; i++) {
924   - if (cartEl[i].name === 'currency_code') {
925   - currency_code = cartEl[i].value || cartEl[i];
926   - break;
927   - }
928   - }
929   - }
930   -
931   - // Update the UI
932   - subtotalEl.innerHTML = $.util.formatCurrency(subtotal, currency_code);
933   -
934   - // Yellow fade on update
935   - if (!silent) {
936   - (function doFade() {
937   - hex = level.toString(16);
938   - level++;
939   -
940   - subtotalEl.style.backgroundColor = '#ff' + hex;
941   -
942   - if (level >= 15) {
943   - subtotalEl.style.backgroundColor = 'transparent';
944   -
945   - // hide the cart if there's no total
946   - if (subtotal === '0.00') {
947   - minicart.reset();
948   - }
949   -
950   - return;
951   - }
952   -
953   - setTimeout(doFade, 30);
954   - })();
955   - }
956   - };
957   -
958   -
959   - /**
960   - * Shows the cart
961   - *
962   - * @param e {event} The triggering event
963   - */
964   - minicart.show = function (e) {
965   - var from = parseInt(minicart.UI.cart.offsetTop, 10),
966   - to = 0,
967   - events = config.events,
968   - onShow = events.onShow,
969   - afterShow = events.afterShow;
970   -
971   - if (e && e.preventDefault) { e.preventDefault(); }
972   -
973   - if (typeof onShow === 'function') {
974   - if (onShow.call(minicart, e) === false) {
975   - return;
976   - }
977   - }
978   -
979   - $.util.animate(minicart.UI.cart, 'top', { from: from, to: to }, function () {
980   - if (typeof afterShow === 'function') {
981   - afterShow.call(minicart, e);
982   - }
983   - });
984   -
985   - minicart.UI.summary.style.backgroundPosition = '-195px 2px';
986   - isShowing = true;
987   - };
988   -
989   -
990   - /**
991   - * Hides the cart off the screen
992   - *
993   - * @param e {event} The triggering event
994   - * @param fully {boolean} Should the cart be fully hidden? Optional. Defaults to false.
995   - */
996   - minicart.hide = function (e, fully) {
997   - var ui = minicart.UI,
998   - cartEl = ui.cart,
999   - summaryEl = ui.summary,
1000   - cartHeight = (cartEl.offsetHeight) ? cartEl.offsetHeight : document.defaultView.getComputedStyle(cartEl, '').getPropertyValue('height'),
1001   - summaryHeight = (summaryEl.offsetHeight) ? summaryEl.offsetHeight : document.defaultView.getComputedStyle(summaryEl, '').getPropertyValue('height'),
1002   - from = parseInt(cartEl.offsetTop, 10),
1003   - events = config.events,
1004   - onHide = events.onHide,
1005   - afterHide = events.afterHide,
1006   - to;
1007   -
1008   - // make the cart fully hidden
1009   - if (fully || minicart.products.length === 0 || !config.peekEnabled) {
1010   - to = cartHeight * -1;
1011   - // otherwise only show a little teaser portion of it
1012   - } else {
1013   - to = (cartHeight - summaryHeight - 8) * -1;
1014   - }
1015   -
1016   - if (e && e.preventDefault) { e.preventDefault(); }
1017   -
1018   - if (typeof onHide === 'function') {
1019   - if (onHide.call(minicart, e) === false) {
1020   - return;
1021   - }
1022   - }
1023   -
1024   - $.util.animate(cartEl, 'top', { from: from, to: to }, function () {
1025   - if (typeof afterHide === 'function') {
1026   - afterHide.call(minicart, e);
1027   - }
1028   - });
1029   -
1030   - summaryEl.style.backgroundPosition = '-195px -32px';
1031   - isShowing = false;
1032   - };
1033   -
1034   -
1035   - /**
1036   - * Toggles the display of the cart
1037   - *
1038   - * @param e {event} The triggering event
1039   - */
1040   - minicart.toggle = function (e) {
1041   - if (isShowing) {
1042   - minicart.hide(e);
1043   - } else {
1044   - minicart.show(e);
1045   - }
1046   - };
1047   -
1048   -
1049   - /**
1050   - * Resets the cart to it's initial state
1051   - */
1052   - minicart.reset = function () {
1053   - var ui = minicart.UI,
1054   - events = config.events,
1055   - onReset = events.onReset,
1056   - afterReset = events.afterReset;
1057   -
1058   - if (typeof onReset === 'function') {
1059   - if (onReset.call(minicart) === false) {
1060   - return;
1061   - }
1062   - }
1063   -
1064   - minicart.products = [];
1065   -
1066   - if (isShowing) {
1067   - ui.itemList.innerHTML = '';
1068   - ui.subtotalAmount.innerHTML = '';
1069   - minicart.hide(null, true);
1070   - }
1071   -
1072   - $.storage.remove();
1073   -
1074   - if (typeof afterReset === 'function') {
1075   - afterReset.call(minicart);
1076   - }
1077   - };
1078   -
1079   -
1080   - // Expose the object as public methods
1081   - return minicart;
1082   - })();
1083   -
1084   -
1085   -
1086   - /**
1087   - * An HTMLElement which displays each product
1088   - *
1089   - * @param data {object} The data for the product
1090   - * @param position {number} The product number
1091   - */
1092   - var ProductNode = function (data, position) {
1093   - this._view(data, position);
1094   - };
1095   -
1096   -
1097   - ProductNode.prototype = {
1098   - /**
1099   - * Creates the DOM nodes and adds the product content
1100   - *
1101   - * @param data {object} The data for the product
1102   - * @param position {number} The product number
1103   - */
1104   - _view: function (data, position) {
1105   - var name, price, quantity, discount, options, hiddenInput, key;
1106   -
1107   - this.product = data.product;
1108   - this.settings = data.settings;
1109   -
1110   - this.liNode = document.createElement('li');
1111   - this.nameNode = document.createElement('a');
1112   - this.metaNode = document.createElement('span');
1113   - this.discountNode = document.createElement('span');
1114   - this.discountInput = document.createElement('input');
1115   - this.priceNode = document.createElement('span');
1116   - this.quantityInput = document.createElement('input');
1117   - this.removeInput = document.createElement('input');
1118   -
1119   - // Don't add blank products
1120   - if (!this.product || (!this.product.item_name && !this.product.item_number)) {
1121   - this.isPlaceholder = true;
1122   - return;
1123   - }
1124   -
1125   - // Name
1126   - if (this.product.item_name) {
1127   - name = this.product.item_name;
1128   - }
1129   -
1130   - this.nameNode.innerHTML = name;
1131   - this.nameNode.title = name;
1132   - this.nameNode.href = this.product.href;
1133   - this.nameNode.appendChild(this.metaNode);
1134   -
1135   - // Meta info
1136   - if (this.product.item_number) {
1137   - this.metaNode.innerHTML = '<br />#' + this.product.item_number;
1138   - }
1139   -
1140   - // Options
1141   - options = this.getOptions();
1142   -
1143   - for (key in options) {
1144   - this.metaNode.innerHTML += '<br />' + key + ': ' + options[key];
1145   - }
1146   -
1147   - // Discount
1148   - discount = this.getDiscount();
1149   -
1150   - if (discount) {
1151   - this.discountInput.type = 'hidden';
1152   - this.discountInput.name = 'discount_amount_' + position;
1153   - this.discountInput.value = discount;
1154   -
1155   - this.metaNode.appendChild(this.discountNode);
1156   - }
1157   -