123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677 |
- /*!
- * The PayPal Mini Cart
- * Visit http://www.minicartjs.com/ for details
- * Use subject to license agreement as set forth at the link below
- *
- * @author Jeff Harrell
- * @license https://github.com/jeffharrell/MiniCart/blob/master/LICENSE eBay Open Source License Agreement
- */
- if (typeof PAYPAL == 'undefined' || !PAYPAL) {
- var PAYPAL = {};
- }
- PAYPAL.apps = PAYPAL.apps || {};
- (function () {
- /**
- * Default configuration
- */
- var config = {
- /**
- * The parent element the cart should "pin" to
- */
- parent: document.body,
-
- /**
- * Edge of the window to pin the cart to
- */
- displayEdge: 'right',
-
- /**
- * Distance from the edge of the window
- */
- edgeDistance: '50px',
-
- /**
- * HTML target property for the checkout form
- */
- formTarget: null,
-
- /**
- * The base path of your website to set the cookie to
- */
- cookiePath: '/',
-
- /**
- * Strings used for display text
- */
- strings: {
- button: '',
- subtotal: '',
- discount: '',
- shipping: '',
- processing: ''
- },
-
- /**
- * Unique ID used on the wrapper element
- */
- name: 'PPMiniCart',
-
- /**
- * Boolean to determine if the cart should "peek" when it's hidden with items
- */
- peekEnabled: true,
- /**
- * The URL of the PayPal website
- */
- paypalURL: 'https://www.paypal.com/cgi-bin/webscr',
-
- /**
- * The base URL to the visual assets
- */
- assetURL: 'http://www.minicartjs.com/build/',
-
- events: {
- /**
- * Custom event fired before the cart is rendered
- */
- onRender: null,
-
- /**
- * Custom event fired after the cart is rendered
- */
- afterRender: null,
-
- /**
- * Custom event fired before the cart is hidden
- *
- * @param e {event} The triggering event
- */
- onHide: null,
-
- /**
- * Custom event fired after the cart is hidden
- *
- * @param e {event} The triggering event
- */
- afterHide: null,
-
- /**
- * Custom event fired before the cart is shown
- *
- * @param e {event} The triggering event
- */
- onShow: null,
-
- /**
- * Custom event fired after the cart is shown
- *
- * @param e {event} The triggering event
- */
- afterShow: null,
-
- /**
- * Custom event fired before a product is added to the cart
- *
- * @param data {object} Product object
- */
- onAddToCart: null,
-
- /**
- * Custom event fired after a product is added to the cart
- *
- * @param data {object} Product object
- */
- afterAddToCart: null,
-
- /**
- * Custom event fired before a product is removed from the cart
- *
- * @param data {object} Product object
- */
- onRemoveFromCart: null,
-
- /**
- * Custom event fired after a product is removed from the cart
- *
- * @param data {object} Product object
- */
- afterRemoveFromCart: null,
-
- /**
- * Custom event fired before the checkout action takes place
- *
- * @param e {event} The triggering event
- */
- onCheckout: null,
-
- /**
- * Custom event fired before the cart is reset
- */
- onReset: null,
-
- /**
- * Custom event fired after the cart is reset
- */
- afterReset: null
- }
- };
- /**
- * Mini Cart application
- */
- PAYPAL.apps.MiniCart = (function () {
-
- var minicart = {},
- isShowing = false,
- isRendered = false;
-
-
- /** PRIVATE **/
-
- /**
- * PayPal form cmd values which are supported
- */
- var SUPPORTED_CMDS = { _cart: true, _xclick: true };
-
-
- /**
- * The form origin that is passed to PayPal
- */
- var BN_VALUE = 'MiniCart_AddToCart_WPS_US';
-
-
- /**
- * Regex filter for cart settings, which appear only once in a cart
- */
- 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)/;
-
-
- /**
- * Adds the cart's CSS to the page in a <style> element.
- * The CSS lives in this file so that it can leverage properties from the config
- * and doesn't require an additional down. To override the CSS see the FAQ.
- */
- var _addCSS = function () {
- var name = config.name,
- css = [],
- style, head;
- 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); } ');
- 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; } ');
- css.push('#' + name + ' li { position:relative; margin:-1px 0 0; padding:6px 5px 6px 0; border-top:1px solid #f2f2f2; } ');
- css.push('#' + name + ' li a { display: block; width: 155px; color:#333; text-decoration:none; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } ');
- css.push('#' + name + ' li a span { color:#999; font-size:10px; } ');
- css.push('#' + name + ' li .quantity { position:absolute; top:.5em; right:78px; width:22px; padding:1px; border:1px solid #83a8cc; text-align:right; } ');
- css.push('#' + name + ' li .price { position:absolute; top:.5em; right:4px; } ');
- 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; } ');
- 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; } ');
- css.push('#' + name + ' p:hover { cursor:pointer; } ');
- 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; } ');
- css.push('#' + name + ' p .shipping { display:block; font-size:10px; font-weight:normal; color:#999; } ');
- style = document.createElement('style');
- style.type = 'text/css';
-
- if (style.styleSheet) {
- style.styleSheet.cssText = css.join('');
- } else {
- style.appendChild(document.createTextNode(css.join('')));
- }
- head = document.getElementsByTagName('head')[0];
- head.appendChild(style);
- };
-
-
- /**
- * Builds the DOM elements required by the cart
- */
- var _buildDOM = function () {
- var UI = minicart.UI,
- cmd, type, bn, parent, version;
-
- UI.wrapper = document.createElement('div');
- UI.wrapper.id = config.name;
-
- cmd = document.createElement('input');
- cmd.type = 'hidden';
- cmd.name = 'cmd';
- cmd.value = '_cart';
-
- type = cmd.cloneNode(false);
- type.name = 'upload';
- type.value = '1';
-
- bn = cmd.cloneNode(false);
- bn.name = 'bn';
- bn.value = BN_VALUE;
-
- UI.cart = document.createElement('form');
- UI.cart.method = 'post';
- UI.cart.action = config.paypalURL;
-
- if (config.formTarget) {
- UI.cart.target = config.formTarget;
- }
-
- UI.cart.appendChild(cmd);
- UI.cart.appendChild(type);
- UI.cart.appendChild(bn);
- UI.wrapper.appendChild(UI.cart);
-
- UI.itemList = document.createElement('ul');
- UI.cart.appendChild(UI.itemList);
-
- UI.summary = document.createElement('p');
- UI.cart.appendChild(UI.summary);
-
- UI.button = document.createElement('input');
- UI.button.type = 'submit';
- UI.button.value = config.strings.button || 'Checkout';
- UI.summary.appendChild(UI.button);
-
- UI.subtotal = document.createElement('span');
- UI.subtotal.innerHTML = config.strings.subtotal || 'Subtotal: ';
-
- UI.subtotalAmount = document.createElement('span');
- UI.subtotalAmount.innerHTML = '0.00';
-
- UI.subtotal.appendChild(UI.subtotalAmount);
- UI.summary.appendChild(UI.subtotal);
-
- UI.shipping = document.createElement('span');
- UI.shipping.className = 'shipping';
- UI.shipping.innerHTML = config.strings.shipping || 'does not include shipping & tax';
- UI.summary.appendChild(UI.shipping);
-
- // Workaround: IE 6 and IE 7/8 in quirks mode do not support position:fixed in CSS
- if (window.attachEvent && !window.opera) {
- version = navigator.userAgent.match(/MSIE\s([^;]*)/);
-
- if (version) {
- version = parseFloat(version[1]);
-
- if (version < 7 || (version >= 7 && document.compatMode === 'BackCompat')) {
- UI.cart.style.position = 'absolute';
- UI.wrapper.style[config.displayEdge] = '0';
- UI.wrapper.style.setExpression('top', 'x = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop');
- }
- }
- }
-
- parent = (typeof config.parent === 'string') ? document.getElementById(config.parent) : config.parent;
- parent.appendChild(UI.wrapper);
- };
-
-
- /**
- * Attaches the cart events to it's DOM elements
- */
- var _bindEvents =function () {
- var ui = minicart.UI,
- forms, form, i;
-
- // Look for all "Cart" and "Buy Now" forms on the page and attach events
- forms = document.getElementsByTagName('form');
- for (i = 0; i < forms.length; i++) {
- form = forms[i];
-
- if (form.cmd && SUPPORTED_CMDS[form.cmd.value]) {
- minicart.bindForm(form);
- }
- }
-
- // Hide the Mini Cart for all non-cart related clicks
- $.event.add(document, 'click', function (e) {
- if (isShowing) {
- var target = e.target,
- cartEl = ui.cart;
- if (!(/input|button|select|option/i.test(target.tagName))) {
- while (target.nodeType === 1) {
- if (target === cartEl) {
- return;
- }
- target = target.parentNode;
- }
-
- minicart.hide(null);
- }
- }
- });
-
- // Run the checkout code when submitting the form
- $.event.add(ui.cart, 'submit', function (e) {
- _checkout(e);
- });
-
- // Show the cart when clicking on the summary
- $.event.add(ui.summary, 'click', function (e) {
- var target = e.target;
-
- if (target !== ui.button) {
- minicart.toggle(e);
- }
- });
- // Update other windows when HTML5 localStorage is updated
- if (window.attachEvent && !window.opera) {
- $.event.add(document, 'storage', function (e) {
- // IE needs a delay in order to properly see the change
- setTimeout(_redrawCartItems, 100);
- });
- } else {
- $.event.add(window, 'storage', function (e) {
- // Safari, Chrome, and Opera can filter on updated storage key
- // Firefox can't so it uses a brute force approach
- if ((e.key && e.key == config.name) || !e.key) {
- _redrawCartItems();
- }
- });
- }
- };
- /**
- * Parses the userConfig (if applicable) and overwrites the default values
- */
- var _parseUserConfig = function (userConfig) {
- var key;
-
- // TODO: This should recursively merge the config values
- for (key in userConfig) {
- if (typeof config[key] !== undefined) {
- config[key] = userConfig[key];
- }
- }
- };
- /**
- * Loads the stored data and builds the cart
- */
- var _parseStorage = function () {
- var data, length, i;
-
- if ((data = $.storage.load())) {
- length = data.length;
-
- for (i = 0; i < length; i++) {
- if (_renderProduct(data[i])) {
- isShowing = true;
- }
- }
- }
- };
-
- /**
- * Data parser used for forms
- *
- * @param form {HTMLElement} An HTML form
- * @return {object}
- */
- var _parseForm = function (form) {
- var raw = form.elements,
- data = {},
- pair, value, length, i, len;
-
- for (i = 0, len = raw.length; i < len; i++) {
- pair = raw[i];
-
- if ((value = $.util.getInputValue(pair))) {
- data[pair.name] = value;
- }
- }
-
- return data;
- };
-
-
- /**
- * Massage's a object's data in preparation for adding it to the user's cart
- *
- * @param data {object} An object of WPS xclick style data to add to the cart. The format is { product: '', settings: '' }.
- * @return {object}
- */
- var _parseData = function (data) {
- var product = {},
- settings = {},
- existing, option_index, key, len, match, i, j;
-
- // Parse the data into a two categories: product and settings
- for (key in data) {
- if (SETTING_FILTER.test(key)) {
- settings[key] = data[key];
- } else {
- product[key] = data[key];
- }
- }
-
- // Check the products to see if this variation already exists
- // If it does then reuse the same object
- for (i = 0, len = minicart.products.length; i < len; i++) {
- existing = minicart.products[i].product;
-
- // Do the product name and number match
- if (product.item_name === existing.item_name && product.item_number === existing.item_number) {
- // Products are a match so far; Now do all of the products options match?
- match = true;
- j = 0;
-
- while (existing['os' + j]) {
- if (product['os' + j] !== existing['os' + j]) {
- match = false;
- break;
- }
-
- j++;
- }
-
- if (match) {
- product.offset = existing.offset;
- break;
- }
- }
- }
- // Normalize the values
- product.href = product.href || window.location.href;
- product.quantity = product.quantity || 1;
- product.amount = product.amount || 0;
-
- // Add Mini Cart specific settings
- if (settings['return'] && settings['return'].indexOf('#') == -1) {
- settings['return'] += '#' + config.name + '=reset';
- }
-
- // Add option amounts to the total amount
- option_index = (product.option_index) ? product.option_index : 0;
- while (product['os' + option_index]) {
- i = 0;
-
- while (typeof product['option_select' + i] != 'undefined') {
- if (product['option_select' + i] == product['os' + option_index]) {
- product.amount = product.amount + parseFloat(product['option_amount' + i]);
- break;
- }
-
- i++;
- }
-
- option_index++;
- }
-
- return {
- product: product,
- settings: settings
- };
- };
-
- /**
- * Resets the card and renders the products
- */
- var _redrawCartItems = function () {
- minicart.products = [];
- minicart.UI.itemList.innerHTML = '';
- minicart.UI.subtotalAmount.innerHTML = '';
-
- _parseStorage();
- minicart.updateSubtotal();
- };
-
-
- /**
- * Renders the product in the cart
- *
- * @param data {object} The data for the product
- */
- var _renderProduct = function (data) {
- var ui = minicart.UI,
- cartEl = ui.cart,
- product = new ProductNode(data, minicart.UI.itemList.children.length + 1),
- offset = data.product.offset,
- keyupTimer, hiddenInput, key;
-
- minicart.products[offset] = product;
-
- // Add hidden settings data to parent form
- for (key in data.settings) {
- if (cartEl.elements[key]) {
- if (cartEl.elements[key].value) {
- cartEl.elements[key].value = data.settings[key];
- } else {
- cartEl.elements[key] = data.settings[key];
- }
- } else {
- hiddenInput = document.createElement('input');
- hiddenInput.type = 'hidden';
- hiddenInput.name = key;
- hiddenInput.value = data.settings[key];
-
- cartEl.appendChild(hiddenInput);
- }
- }
-
- // if the product has no name or number then don't add it
- if (product.isPlaceholder) {
- return false;
- // otherwise, setup the new element
- } else {
- // Click event for "x"
- $.event.add(product.removeInput, 'click', function () {
- _removeProduct(product, offset);
- });
-
- // Event for changing quantities
- var currentValue = product.quantityInput.value;
-
- $.event.add(product.quantityInput, 'keyup', function () {
- var that = this;
-
- keyupTimer = setTimeout(function () {
- var value = parseInt(that.value, 10);
-
- if (!isNaN(value) && value != currentValue) {
- currentValue = value;
-
- product.setQuantity(value);
- // Delete the product
- if (!product.getQuantity()) {
- _removeProduct(product, offset);
- }
- minicart.updateSubtotal();
- $.storage.save(minicart.products);
- }
- }, 250);
- });
-
- // Add the item and fade it in
- ui.itemList.insertBefore(product.liNode, ui.itemList.firstChild);
- $.util.animate(product.liNode, 'opacity', { from: 0, to: 1 });
-
- return true;
- }
- };
-
- /**
- * Removes a product from the cart
- *
- * @param product {ProductNode} The product object
- * @param offset {Number} The offset for the product in the cart
- */
- var _removeProduct = function (product, offset) {
- var events = config.events,
- onRemoveFromCart = events.onRemoveFromCart,
- afterRemoveFromCart = events.afterRemoveFromCart;
-
- if (typeof onRemoveFromCart == 'function') {
- if (onRemoveFromCart.call(minicart, product) === false) {
- return;
- }
- }
-
- product.setQuantity(0);
- product.quantityInput.style.display = 'none';
-
- $.util.animate(product.liNode, 'opacity', { from: 1, to: 0 }, function () {
- $.util.animate(product.liNode, 'height', { from: 18, to: 0 }, function () {
- try {
- product.liNode.parentNode.removeChild(product.liNode);
- } catch (e) {
- // fail
- }
-
- // regenerate the form element indexes
- var products = minicart.UI.cart.getElementsByTagName('li'),
- products_len = products.length,
- inputs,
- inputs_len,
- input,
- matches,
- i, j, k = 1;
-
- for (i = 0 ; i < products_len; i++) {
- inputs = products[i].getElementsByTagName('input');
- inputs_len = inputs.length;
-
- for (j = 0; j < inputs_len; j++) {
- input = inputs[j];
- matches = /(.+)_[0-9]+$/.exec(input.name);
-
- if (matches && matches[1]) {
- input.name = matches[1] + '_' + k;
- }
- }
-
- k++;
- }
-
- if (typeof afterRemoveFromCart == 'function') {
- afterRemoveFromCart.call(minicart, product);
- }
- });
- });
-
- minicart.products[offset].product.item_name = '';
- minicart.products[offset].product.item_number = '';
-
- minicart.updateSubtotal();
- $.storage.save(minicart.products);
- };
-
-
- /**
- * Event when the cart form is submitted
- *
- * @param e {event} The form submission event
- */
- var _checkout = function (e) {
- var onCheckout = config.events.onCheckout;
-
- if (typeof onCheckout == 'function') {
- if (onCheckout.call(minicart, e) === false) {
- e.preventDefault();
- return;
- }
- }
-
- minicart.UI.button.value = config.strings.processing || 'Processing…';
- };
-
-
- /** PUBLIC **/
-
- /**
- * Array of ProductNode
- */
- minicart.products = [];
-
-
- /**
- * Container for UI elements
- */
- minicart.UI = {};
-
- /**
- * Renders the cart, creates the configuration and loads the data
- *
- * @param userConfig {object} User settings which override the default configuration
- */
- minicart.render = function (userConfig) {
- var events = config.events,
- onRender = events.onRender,
- afterRender = events.afterRender,
- hash, cmd;
-
- if (typeof onRender == 'function') {
- if (onRender.call(minicart) === false) {
- return;
- }
- }
-
- if (!isRendered) {
- // Overwrite default configuration with user settings
- _parseUserConfig(userConfig);
-
- // Render the cart UI
- _addCSS();
- _buildDOM();
- _bindEvents();
-
- // Check if a transaction was completed
- // The "return" form param is modified to contain a hash value
- // with "PPMiniCart=reset". If this is seen then it's assumed
- // that a transaction was completed and we should reset the cart.
- hash = location.hash.substring(1);
-
- if (hash.indexOf(config.name + '=') === 0) {
- cmd = hash.split('=')[1];
- if (cmd == 'reset') {
- minicart.reset();
- location.hash = '';
- }
- }
- }
-
- // Process any stored data and render it
- // TODO: _parseStorage shouldn't be so tightly coupled here and one
- // should be able to redraw without re-parsing the storage
- _redrawCartItems();
-
- // Trigger the cart to peek on first load if any products were loaded
- if (!isRendered) {
- if (isShowing) {
- setTimeout(function () {
- minicart.hide(null);
- }, 500);
- } else {
- $.storage.remove();
- }
- }
-
- isRendered = true;
-
- if (typeof afterRender == 'function') {
- afterRender.call(minicart);
- }
- };
- /**
- * Binds a form to the Mini Cart
- *
- * @param form {HTMLElement} The form element to bind
- */
- minicart.bindForm = function (form) {
- if (form.add) {
- $.event.add(form, 'submit', function (e) {
- e.preventDefault(e);
- var data = _parseForm(e.target);
- minicart.addToCart(data);
- });
- } else if (form.display) {
- $.event.add(form, 'submit', function (e) {
- e.preventDefault();
- minicart.show(e);
- });
- } else {
- return false;
- }
- return true;
- };
- /**
- * Adds a product to the cart
- *
- * @param data {object} Product object. See _parseData for format
- * @return {boolean} True if the product was added, false otherwise
- */
- minicart.addToCart = function (data) {
- var events = config.events,
- onAddToCart = events.onAddToCart,
- afterAddToCart = events.afterAddToCart,
- success = false,
- productNode, offset;
-
- if (typeof onAddToCart === 'function') {
- if (onAddToCart.call(minicart, data) === false) {
- return;
- }
- }
-
- data = _parseData(data);
- offset = data.product.offset;
-
- // Check if the product has already been added; update if so
- if ((productNode = (typeof offset !== 'undefined' && minicart.products[offset]))) {
- productNode.product.quantity += parseInt(data.product.quantity || 1, 10);
- productNode.setPrice(data.product.amount * productNode.product.quantity);
- productNode.setQuantity(productNode.product.quantity);
-
- success = true;
- // Add a new DOM element for the product
- } else {
- data.product.offset = minicart.products.length;
- success = _renderProduct(data);
- }
-
- minicart.updateSubtotal();
- minicart.show(null);
-
- $.storage.save(minicart.products);
-
- if (typeof afterAddToCart === 'function') {
- afterAddToCart.call(minicart, data);
- }
-
- return success;
- };
- /**
- * Iterates over each product and calculates the subtotal
- *
- * @return {number} The subtotal
- */
- minicart.calculateSubtotal = function () {
- var amount = 0,
- products = minicart.products,
- product, item, price, discount, len, i;
-
- for (i = 0, len = products.length; i < len; i++) {
- item = products[i];
-
- if ((product = item.product)) {
- if (product.quantity && product.amount) {
- price = product.amount;
- discount = item.getDiscount();
-
- amount += parseFloat((price * product.quantity) - discount);
- }
- }
- }
-
- return amount.toFixed(2);
- };
-
-
- /**
- * Updates the UI with the current subtotal and currency code
- */
- minicart.updateSubtotal = function () {
- var ui = minicart.UI,
- cartEl = ui.cart.elements,
- subtotalEl = ui.subtotalAmount,
- subtotal = minicart.calculateSubtotal(),
- level = 1,
- currency_code, currency_symbol, hex, len, i;
- // Get the currency
- currency_code = '';
- currency_symbol = '';
- if (cartEl.currency_code) {
- currency_code = cartEl.currency_code.value || cartEl.currency_code;
- } else {
- for (i = 0, len = cartEl.length; i < len; i++) {
- if (cartEl[i].name == 'currency_code') {
- currency_code = cartEl[i].value || cartEl[i];
- break;
- }
- }
- }
- // Update the UI
- subtotalEl.innerHTML = $.util.formatCurrency(subtotal, currency_code);
-
- // Yellow fade on update
- (function () {
- hex = level.toString(16);
- level++;
-
- subtotalEl.style.backgroundColor = '#ff' + hex;
- if (level >= 15) {
- subtotalEl.style.backgroundColor = 'transparent';
-
- // hide the cart if there's no total
- if (subtotal == '0.00') {
- minicart.hide(null, true);
- }
-
- return;
- }
- setTimeout(arguments.callee, 30);
- })();
- };
-
- /**
- * Shows the cart
- *
- * @param e {event} The triggering event
- */
- minicart.show = function (e) {
- var from = parseInt(minicart.UI.cart.offsetTop, 10),
- to = 0,
- events = config.events,
- onShow = events.onShow,
- afterShow = events.afterShow;
-
- if (e && e.preventDefault) { e.preventDefault(); }
-
- if (typeof onShow == 'function') {
- if (onShow.call(minicart, e) === false) {
- return;
- }
- }
-
- $.util.animate(minicart.UI.cart, 'top', { from: from, to: to }, function () {
- if (typeof afterShow == 'function') {
- afterShow.call(minicart, e);
- }
- });
-
- minicart.UI.summary.style.backgroundPosition = '-195px 2px';
- isShowing = true;
- };
-
-
- /**
- * Hides the cart off the screen
- *
- * @param e {event} The triggering event
- * @param fully {boolean} Should the cart be fully hidden? Optional. Defaults to false.
- */
- minicart.hide = function (e, fully) {
- var ui = minicart.UI,
- cartEl = ui.cart,
- summaryEl = ui.summary,
- cartHeight = (cartEl.offsetHeight) ? cartEl.offsetHeight : document.defaultView.getComputedStyle(cartEl, '').getPropertyValue('height'),
- summaryHeight = (summaryEl.offsetHeight) ? summaryEl.offsetHeight : document.defaultView.getComputedStyle(summaryEl, '').getPropertyValue('height'),
- from = parseInt(cartEl.offsetTop, 10),
- events = config.events,
- onHide = events.onHide,
- afterHide = events.afterHide,
- to;
- // make the cart fully hidden
- if (fully || minicart.products.length === 0 || !config.peekEnabled) {
- to = cartHeight * -1;
- // otherwise only show a little teaser portion of it
- } else {
- to = (cartHeight - summaryHeight - 8) * -1;
- }
- if (e && e.preventDefault) { e.preventDefault(); }
-
- if (typeof onHide == 'function') {
- if (onHide.call(minicart, e) === false) {
- return;
- }
- }
-
- $.util.animate(cartEl, 'top', { from: from, to: to }, function () {
- if (typeof afterHide == 'function') {
- afterHide.call(minicart, e);
- }
- });
-
- summaryEl.style.backgroundPosition = '-195px -32px';
- isShowing = false;
- };
-
- /**
- * Toggles the display of the cart
- *
- * @param e {event} The triggering event
- */
- minicart.toggle = function (e) {
- if (isShowing) {
- minicart.hide(e);
- } else {
- minicart.show(e);
- }
- };
-
-
- /**
- * Resets the cart to it's initial state
- */
- minicart.reset = function () {
- var ui = minicart.UI,
- events = config.events,
- onReset = events.onReset,
- afterReset = events.afterReset;
-
- if (typeof onReset === 'function') {
- if (onReset.call(minicart) === false) {
- return;
- }
- }
-
- minicart.products = [];
- if (isShowing) {
- ui.itemList.innerHTML = '';
- ui.subtotalAmount.innerHTML = '';
- minicart.hide(null, true);
- }
-
- $.storage.remove();
-
- if (typeof afterReset === 'function') {
- afterReset.call(minicart);
- }
- };
-
- // Expose the object as public methods
- return minicart;
- })();
-
-
- /**
- * An HTMLElement which displays each product
- *
- * @param data {object} The data for the product
- * @param position {number} The product number
- */
- var ProductNode = function (data, position) {
- this._view(data, position);
- };
-
-
- ProductNode.prototype = {
- /**
- * Creates the DOM nodes and adds the product content
- *
- * @param data {object} The data for the product
- * @param position {number} The product number
- */
- _view: function (data, position) {
- var name, price, quantity, discount, discountNum, options, hiddenInput, key;
- this.product = data.product;
- this.settings = data.settings;
-
- this.liNode = document.createElement('li');
- this.nameNode = document.createElement('a');
- this.metaNode = document.createElement('span');
- this.discountNode = document.createElement('span');
- this.discountInput = document.createElement('input');
- this.priceNode = document.createElement('span');
- this.quantityInput = document.createElement('input');
- this.removeInput = document.createElement('input');
-
- // Don't add blank products
- if (!this.product || (!this.product.item_name && !this.product.item_number)) {
- this.isPlaceholder = true;
- return;
- }
- // Name
- if (this.product.item_name) {
- name = this.product.item_name;
- }
-
- this.nameNode.innerHTML = name;
- this.nameNode.title = name;
- this.nameNode.href = this.product.href;
- this.nameNode.appendChild(this.metaNode);
-
- // Meta info
- if (this.product.item_number) {
- this.metaNode.innerHTML = '<br />#' + this.product.item_number;
- }
-
- // Options
- options = this.getOptions();
-
- for (key in options) {
- this.metaNode.innerHTML += '<br />' + key + ': ' + options[key];
- }
- // Discount
- discount = this.getDiscount();
-
- this.discountInput.type = 'hidden';
- this.discountInput.name = 'discount_amount_' + position;
- this.discountInput.value = discount;
-
- this.metaNode.appendChild(this.discountNode);
-
- // Price
- price = this.getPrice();
- this.priceNode.className = 'price';
-
- // Quantity
- quantity = this.getQuantity();
-
- this.quantityInput.name = 'quantity_' + position;
- this.quantityInput.className = 'quantity';
- this.quantityInput.setAttribute('autocomplete', 'off');
-
- this.setQuantity(quantity);
-
- // Remove button
- this.removeInput.type = 'button';
- this.removeInput.className = 'remove';
-
- // Build out the DOM
- this.liNode.appendChild(this.nameNode);
- this.liNode.appendChild(this.quantityInput);
- this.liNode.appendChild(this.discountInput);
- this.liNode.appendChild(this.removeInput);
- this.liNode.appendChild(this.priceNode);
-
- // Add in hidden product data
- for (key in this.product) {
- if (key !== 'quantity' && key.indexOf('discount_') === -1) {
- hiddenInput = document.createElement('input');
- hiddenInput.type = 'hidden';
- hiddenInput.name = key + '_' + position;
- hiddenInput.value = this.product[key];
-
- this.liNode.appendChild(hiddenInput);
- }
- }
- },
-
-
- /**
- * Calculates the discount for a product
- *
- * @return {Object} An object with the discount amount or percentage
- */
- getDiscount: function () {
- var data = {},
- discount = 0,
- discountNum = this.product.discount_num || -1;
-
- // Discounts: Amount-based
- if (this.product.discount_amount) {
- // Discount amount for the first item
- discount = parseFloat(this.product.discount_amount);
- // Discount amount for each additional item
- if (this.product.discount_amount2) {
- quantity = this.getQuantity();
-
- if (quantity > 1) {
- discount += Math.max(quantity - 1, discountNum) * parseFloat(this.product.discount_amount2);
- }
- }
- // Discounts: Percentage-based
- } else if (this.product.discount_rate) {
- // Discount amount on the first item
- discount = this.product.amount * parseFloat(this.product.discount_rate) / 100;
-
- // Discount amount for each additional item
- if (this.product.discount_rate2) {
- quantity = this.getQuantity();
-
- if (quantity > 1) {
- discount += Math.max(quantity - 1, discountNum) * this.product.amount * parseFloat(this.product.discount_amount2) / 100;
- }
- }
- }
-
- return discount && discount.toFixed(2);
- },
-
-
- /**
- * Returns an object of options for the product
- *
- * @return {Object}
- */
- getOptions: function () {
- var options = {},
- i = 0;
-
- while (typeof this.product['on' + i] !== 'undefined') {
- options[this.product['on' + i]] = this.product['os' + i];
- i++;
- }
-
- return options;
- },
-
-
- /**
- * Utility function to set the quantity of this product
- *
- * @param value {number} The new value
- */
- setQuantity: function (value) {
- var discount;
-
- value = parseInt(value, 10);
- this.product.quantity = value;
-
- if (this.quantityInput.value != value) {
- this.quantityInput.value = value;
-
- if ((discount = this.getDiscount())) {
- this.discountInput.value = discount;
-
- this.discountNode.innerHTML = '<br />';
- this.discountNode.innerHTML += config.strings.discount || 'Discount: ';
- this.discountNode.innerHTML += $.util.formatCurrency(discount, this.settings.currency_code);
- }
- }
-
- this.setPrice(this.product.amount * value);
- },
-
-
- /**
- * Utility function to get the quantity of this product
- *
- * @return {number}
- */
- getQuantity: function () {
- return (typeof this.product.quantity !== undefined) ? this.product.quantity : 1;
- },
-
-
- /**
- * Utility function to set the price of this product
- *
- * @param value {number} The new value
- */
- setPrice: function (value) {
- value = parseFloat(value, 10);
-
- this.priceNode.innerHTML = $.util.formatCurrency(value.toFixed(2), this.settings.currency_code);
- },
-
-
- /**
- * Utility function to get the price of this product
- *
- * @return {number}
- */
- getPrice: function () {
- return (this.product.amount * this.getQuantity()).toFixed(2);
- }
- };
-
-
-
- /** UTILITY **/
-
- var $ = {};
-
- $.storage = (function () {
- var name = config.name;
-
- // Use HTML5 client side storage
- if (window.localStorage) {
- return {
- /**
- * Loads the saved data
- *
- * @return {object}
- */
- load: function () {
- var data = localStorage.getItem(name);
-
- if (data) {
- data = JSON.parse(decodeURIComponent(data));
- }
-
- return data;
- },
-
-
- /**
- * Saves the data
- *
- * @param items {object} The list of items to save
- */
- save: function (items) {
- var data = [],
- item, len, i;
-
- if (items) {
- for (i = 0, len = items.length; i < len; i++) {
- item = items[i];
- data.push({
- product: item.product,
- settings: item.settings
- });
- }
-
- data = encodeURIComponent(JSON.stringify(data));
- localStorage.setItem(name, data);
- }
- },
-
-
- /**
- * Removes the saved data
- */
- remove: function () {
- localStorage.removeItem(name);
- }
- };
-
- // Otherwise use cookie based storage
- } else {
- return {
-
- /**
- * Loads the saved data
- *
- * @return {object}
- */
- load: function () {
- var key = name + '=',
- data, cookies, cookie, value, i;
- try {
- cookies = document.cookie.split(';');
- for (i = 0; i < cookies.length; i++) {
- cookie = cookies[i];
- while (cookie.charAt(0) === ' ') {
- cookie = cookie.substring(1, cookie.length);
- }
- if (cookie.indexOf(key) === 0) {
- value = cookie.substring(key.length, cookie.length);
- data = JSON.parse(decodeURIComponent(value));
- }
- }
- } catch(e) {}
- return data;
- },
-
-
- /**
- * Saves the data
- *
- * @param items {object} The list of items to save
- */
- save: function (items, duration) {
- var date = new Date(),
- data = [],
- item, len, i;
- if (items) {
- for (i = 0, len = items.length; i < len; i++) {
- item = items[i];
- data.push({
- product: item.product,
- settings: item.settings
- });
- }
-
- duration = duration || 30;
- date.setTime(date.getTime() + duration * 24 * 60 * 60 * 1000);
- document.cookie = config.name + '=' + encodeURIComponent(JSON.stringify(data)) + '; expires=' + date.toGMTString() + '; path=' + config.cookiePath;
- }
- },
- /**
- * Removes the saved data
- */
- remove: function () {
- this.save(null, -1);
- }
- };
- }
- })();
-
-
- $.event = (function () {
- /**
- * Events are added here for easy reference
- */
- var cache = [];
-
- // Non-IE events
- if (document.addEventListener) {
- return {
- /**
- * Add an event to an object and optionally adjust it's scope
- *
- * @param obj {HTMLElement} The object to attach the event to
- * @param type {string} The type of event excluding "on"
- * @param fn {function} The function
- * @param scope {object} Object to adjust the scope to (optional)
- */
- add: function (obj, type, fn, scope) {
- scope = scope || obj;
-
- var wrappedFn = function (e) { fn.call(scope, e); };
-
- obj.addEventListener(type, wrappedFn, false);
- cache.push([obj, type, fn, wrappedFn]);
- },
- /**
- * Remove an event from an object
- *
- * @param obj {HTMLElement} The object to remove the event from
- * @param type {string} The type of event excluding "on"
- * @param fn {function} The function
- */
- remove: function (obj, type, fn) {
- var wrappedFn, item, len, i;
- for (i = 0; i < cache.length; i++) {
- item = cache[i];
- if (item[0] == obj && item[1] == type && item[2] == fn) {
- wrappedFn = item[3];
- if (wrappedFn) {
- obj.removeEventListener(type, wrappedFn, false);
- delete cache[i];
- }
- }
- }
- }
- };
-
- // IE events
- } else if (document.attachEvent) {
- return {
- /**
- * Add an event to an object and optionally adjust it's scope (IE)
- *
- * @param obj {HTMLElement} The object to attach the event to
- * @param type {string} The type of event excluding "on"
- * @param fn {function} The function
- * @param scope {object} Object to adjust the scope to (optional)
- */
- add: function (obj, type, fn, scope) {
- scope = scope || obj;
-
- var wrappedFn = function () {
- var e = window.event;
- e.target = e.target || e.srcElement;
- e.preventDefault = function () {
- e.returnValue = false;
- };
- fn.call(scope, e);
- };
- obj.attachEvent('on' + type, wrappedFn);
- cache.push([obj, type, fn, wrappedFn]);
- },
- /**
- * Remove an event from an object (IE)
- *
- * @param obj {HTMLElement} The object to remove the event from
- * @param type {string} The type of event excluding "on"
- * @param fn {function} The function
- */
- remove: function (obj, type, fn) {
- var wrappedFn, item, len, i;
- for (i = 0; i < cache.length; i++) {
- item = cache[i];
- if (item[0] == obj && item[1] == type && item[2] == fn) {
- wrappedFn = item[3];
- if (wrappedFn) {
- obj.detachEvent('on' + type, wrappedFn);
- delete cache[i];
- }
- }
- }
- }
- };
- }
- })();
-
-
- $.util = {
- /**
- * Animation method for elements
- *
- * @param el {HTMLElement} The element to animate
- * @param prop {string} Name of the property to change
- * @param config {object} Properties of the animation
- * @param callback {function} Callback function after the animation is complete
- */
- animate: function (el, prop, config, callback) {
- config = config || {};
- config.from = config.from || 0;
- config.to = config.to || 0;
- config.duration = config.duration || 10;
- config.unit = (/top|bottom|left|right|width|height/.test(prop)) ? 'px' : '';
- var step = (config.to - config.from) / 20,
- current = config.from;
- (function () {
- el.style[prop] = current + config.unit;
- current += step;
- if ((step > 0 && current > config.to) || (step < 0 && current < config.to) || step === 0) {
- el.style[prop] = config.to + config.unit;
- if (typeof callback === 'function') {
- callback();
- }
- return;
- }
- setTimeout(arguments.callee, config.duration);
- })();
- },
-
-
- /**
- * Convenience method to return the value of any type of form input
- *
- * @param input {HTMLElement} The element who's value is returned
- */
- getInputValue: function (input) {
- var tag = input.tagName.toLowerCase();
- if (tag == 'select') {
- return input.options[input.selectedIndex].value;
- } else if (tag == 'textarea') {
- return input.innerHTML;
- } else {
- if (input.type == 'radio') {
- return (input.checked) ? input.value : null;
- } else if (input.type == 'checkbox') {
- return (input.checked) ? input.value : null;
- } else {
- return input.value;
- }
- }
- },
-
-
- /**
- * Formats a float into a currency
- *
- * @param amount {float} The currency amount
- * @param code {string} The three letter currency code
- */
- formatCurrency: function (amount, code) {
- // TODO: The supported currency patterns need to be refined and
- // should support values for before, after, decimal, and separator.
- var currencies = {
- AED: { before: '\u062c' },
- ANG: { before: '\u0192' },
- ARS: { before: '$' },
- AUD: { before: '$' },
- AWG: { before: '\u0192' },
- BBD: { before: '$' },
- BGN: { before: '\u043b\u0432' },
- BMD: { before: '$' },
- BND: { before: '$' },
- BRL: { before: 'R$' },
- BSD: { before: '$' },
- CAD: { before: '$' },
- CHF: { before: '' },
- CLP: { before: '$' },
- CNY: { before: '\u00A5' },
- COP: { before: '$' },
- CRC: { before: '\u20A1' },
- CZK: { before: 'Kc' },
- DKK: { before: 'kr' },
- DOP: { before: '$' },
- EEK: { before: 'kr' },
- EUR: { before: '\u20AC' },
- GBP: { before: '\u00A3' },
- GTQ: { before: 'Q' },
- HKD: { before: '$' },
- HRK: { before: 'kn' },
- HUF: { before: 'Ft' },
- IDR: { before: 'Rp' },
- ILS: { before: '\u20AA' },
- INR: { before: 'Rs.' },
- ISK: { before: 'kr' },
- JMD: { before: 'J$' },
- JPY: { before: '\u00A5' },
- KRW: { before: '\u20A9' },
- KYD: { before: '$' },
- LTL: { before: 'Lt' },
- LVL: { before: 'Ls' },
- MXN: { before: '$' },
- MYR: { before: 'RM' },
- NOK: { before: 'kr' },
- NZD: { before: '$' },
- PEN: { before: 'S/' },
- PHP: { before: 'Php' },
- PLN: { before: 'z' },
- QAR: { before: '\ufdfc' },
- RON: { before: 'lei' },
- RUB: { before: '\u0440\u0443\u0431' },
- SAR: { before: '\ufdfc' },
- SEK: { before: 'kr' },
- SGD: { before: '$' },
- THB: { before: '\u0E3F' },
- TRY: { before: 'TL' },
- TTD: { before: 'TT$' },
- TWD: { before: 'NT$' },
- UAH: { before: '\u20b4' },
- USD: { before: '$' },
- UYU: { before: '$U' },
- VEF: { before: 'Bs' },
- VND: { before: '\u20ab' },
- XCD: { before: '$' },
- ZAR: { before: 'R' }
- },
- currency = currencies[code] || {},
- before = currency.before || '',
- after = currency.after || '';
-
- return before + amount + after;
- }
- };
-
-
- /**
- * JSON Parser - See http://www.json.org/js.html
- */
- if(!this.JSON){JSON={};}(function(){function f(n){return n<10?"0"+n:n;}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(key){return this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z";};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key);}if(typeof rep==="function"){value=rep.call(holder,key,value);}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null";}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null";}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v;}if(rep&&typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==="string"){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v);}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v);}}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v;}}if(typeof JSON.stringify!=="function"){JSON.stringify=function(value,replacer,space){var i;gap="";indent="";if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" ";}}else{if(typeof space==="string"){indent=space;}}rep=replacer;if(replacer&&typeof replacer!=="function"&&(typeof replacer!=="object"||typeof replacer.length!=="number")){throw new Error("JSON.stringify");}return str("",{"":value});};}if(typeof JSON.parse!=="function"){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}return reviver.call(holder,key,value);}cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4);});}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j;}throw new SyntaxError("JSON.parse");};}}());
- })();
|