====== FoxyCart's Javascript: foxycart.js ====== FoxyCart's ''foxycart.js'' file is full of goodies that you probably won't ever need to think twice about. If default configurations are an anathema to you, if you need to get advanced for a custom integration, or if you're just curious about what's under the hood please explore this page and the "raw" version of the ''foxycart.js'', and [[http://forum.foxycart.com|let us know]] if you have any questions. ===== What It Does ===== The ''foxycart.js'' file: * Handles all cross-domain sessions, ensuring sessions don't get lost regardless of browser privacy settings (so long as //some// cookies are enabled). * Automatically (mostly) handles determining at what level to set your store's FoxyCart cookie. (ie. Determines to set it at ''.example.com'' or ''.example.co.uk'' or ''.custom.example.com''.) * Makes it easy for custom events or validation to be run before products are added to the cart. * Makes it easy for custom events on an attempted add-to-cart (ie. the default [[http://colorpowered.com/colorbox/|Colorbox]] modal window, custom [[.:json|JSONP]] functionality, etc.). * Makes it easy for custom events or validation to be run //after// products are added to the cart. * Automatically attaches ''onclick'' and ''onsubmit'' handling to all links and forms (even dynamically created elements) pointing to your store's FoxyCart cart URL. * Keeps the FoxyCart [[.:json|JSON]] object (''FC.json'') current before and after cart requests. * Alerts if jQuery isn't loaded. ===== Available Files ===== * ''http://cdn.foxycart.com/YOURDOMAIN/foxycart.colorbox.js'': Compressed. Includes the code to automatically set your store's domain, your store's FoxyCart domain, to initialize the ''FC'' object as ''fcc'', and to attach [[http://colorpowered.com/colorbox/|Colorbox]] to all add-to-cart links and forms. * ''http://cdn.foxycart.com/YOURDOMAIN/foxycart.js'': Compressed. Includes the code to automatically set your store's domain, your store's FoxyCart domain, and to initialize the ''FC'' object as ''fcc''. Note that the ''preprocess'' and ''process'' events don't actually have any behavior in this version of the file, and that ''postprocess'' is not referenced at all, since ''postprocess'' should be called by whatever ''process'' events you've defined. * ''http://cdn.foxycart.com/YOURDOMAIN/foxycart.raw.js'': Uncompressed. Does not set your store's domain, your store's FoxyCart domain, nor does it initialize the ''fcc'' object. This will generally only be used for special circumstances or advanced users needing access to the uncompressed code. ===== What's Different from Previous Versions ===== Almost everything but some of the ways that sessions are set has been rewritten, but here are the main changes from the ''foxycart_includes.js'' in v0.6.0: * No more ''.foxycart'' class necessary for add-to-cart links and forms. Instead it relies on the ''href'' or ''action'' of the link or form pointing to your FoxyCart's domain. * The ''fc_json'' object is now accessible as ''FC.json''. * ''fc_PreProcess()'' has been changed to a ''preprocess()'' method that's more flexible, called differently, expecting different arguments. * ''fc_BuildFoxyCart()'' has similarly been changed to a ''postprocess()'' method described below. * Setting the FoxyCart Session ID (''fcsid'') at a subdomain, third-level domain, or specific path is now possible without rewriting core functions. * ''fc_AddSession'' doesn't exist anymore, the current session can be retrieved using ''fcc.session_get()''. Note that the new javascript uses jQuery's ''live()'' event to find any links or forms that are directed at FoxyCart and adds the session to them automatically, so you don't generally have to use this. If you are altering the href of a link dynamically though, you will need to re-add the session to the updated href, otherwise FoxyCart will assume its a new link and you'll get double ups on the process functions (ie: 2 carts in colorbox). * ''fc_tb_show'' doesn't exist anymore as 070 now uses colorbox as the default modal window. If you check out the ''foxycart.js'' you add to your page, you can see the colorbox being initialized there. Its just a vanilla colorbox implementation so you can use colorbox for whatever else you need modals for too. ===== How It Works & Configuration ===== While most of this should happen automatically, especially if you're using the ''foxycart.js'' or ''foxycart.complete.js'' versions (as opposed to the ''foxycart.raw.js''), there are a few things that need to be configured correctly. ==== ''storedomain'': How Your Links and Forms Are Handled ==== The first configuration that the ''FC'' object in ''foxycart.js'' takes is the path to your store's FoxyCart cart, like ''yourstorename.foxycart.com''. This setting is critical (and usually set automatically unless you're using the "raw" version of ''foxycart.raw.js''), as this is how the script determines which links to attach the click or submit events to, as well as when and how to add the session ID to any and all requests to your store's FoxyCart requests. Without this setting configured correctly it is highly likely that your customers will lose their cart contents when they attempt a checkout. ==== ''sitedomain'': The Domain on Which Your Visitor's FoxyCart Cookies Are Set ==== The ''sitedomain'' is the second argument required, but unlike the ''storedomain'', the ''sitedomain'' is only used to determine where to set the cookie for the visitor's FoxyCart session. If your ''sitedomain'' is passed in as ''example.com'' or ''www.example.com'', the ''fcsid'' (FoxyCart Session ID) cookie is set at the second-level domain, which is ''.example.com''. If your ''sitedomain'' is a third-level domain like ''example.co.uk'' or ''subdomain.example.com'', the cookie would be set at ''.example.co.uk'' or ''.subdomain.example.com''. This setting is only important if your site isn't at the second-level domain, or if you want to restrict your FoxyCart sessions by subdomain. For example, if your site is ''example.co.uk'', you don't want the cookie set at ''.co.uk''. Or if you want to have different FoxyCart sites at ''donations.example.com'' and ''products.example.com'', you'd need the cookies //not// to be set at ''.example.com'' or the sessions would overlap with unexpected results. (Another example would be if your site is at a 3rd party provider like ''example.squarespace.com'', you don't want all ''*.squarespace.com'' sites sharing your FoxyCart sessions.) The only two things to keep in mind are: - The ''www'' subdomain is effectively ignored, so if you //do// want to lock your sessions down to ''.www.example.com'' you'll need to set the ''sitedomain'' value to something like ''www1.example.com''. The actual value doesn't matter; it's only counting the dots in the value after it strips the ''www''. - You //cannot// have the FoxyCart javascript at any other subdomains if you are isolating by subdomain. For example, if you have two separate stores at ''products.example.com'' and ''donations.example.com'' you //cannot// also have your ''foxycart.js'' at ''www.example.com'' or ''example.com'', as that will set the session cookie at ''.example.com'', which will override the ''products'' and ''donations'' session cookies. ==== ''cookiepath'': The Path on Which Your Visitor's FoxyCart Cookies Are Set ==== The ''cookiepath'' determines the ''fcsid'' cookie's path. This is almost always going to be empty, so the cookies would be set at ''.example.com'', but if you do need to have multiple FoxyCart sessions on the same domain you could use this setting to restrict cookies to something like ''.example.com/en/'' and ''.example.com/es/'' (to split English and Spanish FoxyCart stores, for example). The ''cookiepath'' value must end in a ''/'' or you may run into issues, especially with Internet Explorer. The leading slash is automatically applied by the ''foxycart.js'', so if you want a path of ''/en/'' you would enter ''cookiepath = 'en/';''. *Example:* Suppose you have a donations section and a bookstore, located at ''http://example.com/donations/'' and ''http://example.com/bookstore/''. (Note that the trailing slash is critical. If you have something like ''http://example.com/bookstore'' and ''http://example.com/donations'' this method will not work and the sessions will collide.) Add the following code before your calls to ''foxycart.complete.js'' or ''foxycart.js''. Obviously, change out the "donations" with "bookstore" depending on where you're placing this code. ===== Helper Functions ==== ==== Loading the Cart on Page Load ==== If you find yourself needing to have the default [[http://jacklmoore.com/colorbox/|Colorbox]]-powered FoxyCart cart load itself when your site is loaded, the ''foxycart.colorbox.js'' file has a helper function that loads the cart with a ''cart=view'' command. To accomplish this, simply link to a page on your site (one that has the ''foxycart.colorbox.js'' linked on it) with ''fc_open=true'' in the anchor. So the ''href'' in a link to your site might look like this: http://example.com/landing_page.html#fc_open=true That will call ''fcc.events.cart.process.execute'' with your FoxyCart cart's "view" parameter. ==== "Mini-Cart" Display and HTML Helper Functionality ==== When the ''FC'' class is initialized (via the ''.init()'' method) and on the ''.cart_update()'' method, elements with the following classes or IDs are modified: * ''#fc_minicart'', ''.fc_minicart'': If the ''FC.json.product_count'' is greater than 0, these elements will be shown. Otherwise they'll be hidden * ''#fc_quantity'', ''.fc_quantity'': The inner HTML will be replaced by the value in ''FC.json.product_count''. * ''#fc_total_price'', ''.fc_total_price'': The inner HTML will be replaced by the value in ''FC.json.total_price'', formatted using the ''_currency_format'' function (which adds decimals but doesn't add currency symbols). My Cart: 0 - $0.00 ==== ''session_get'' Appending the FCSID to Requests ==== === What It Is === The ''fcsid'' (FoxyCart Session ID) cookie stores the unique session ID of the customer, and is arguably the most important piece of a FoxyCart implementation. If the ''fcsid'' isn't sent to FoxyCart when the cart is requested, the customer will lose the entire contents of their cart, and may see other errors as well. === How It Works: Overview === When a webpage with ''foxycart.js'' loads, ''foxycart.js'' attempts to set a ''fcsid'' cookie according to the ''sitedomain'' and ''cookiepath'' values discussed above. Most of this is handled automatically === How It Works: Details === The ''session_get'' method will look for and set an ''fcsid'' cookie with the value retrieved from (in this order): - A hash in the loaded URL (ie. ''#fcsid=abc123''). - An ''fcsid'' cookie. - The ''FC.session_id'' in the [[.:json|JSON]]. === How To Use It: JSONP and Dynamically Modified Elements === If you're generating actions (cart-adds, ''JSONP'' requests, dynamically changing an add-to-cart link's ''href'', etc.), you'll need to manually add the ''fcsid'' to your request. Luckily, this is fairly easy with the ''session_get()'' method. Assuming a normal ''foxycart.complete.js'' or ''foxycart.js'' inclusion in your template, you would create the link or ''JSONP'' URL like this (in javascript): // Just the link to the cart itself var cart_request = 'https://'+storedomain+'/cart?'+fcc.session_get(); // Adding a product var cart_request = 'https://'+storedomain+'/cart?name=Test_Product&price=9.99'+fcc.session_get(); // Retrieving the cart via JSONP jQuery.getJSON('https://'+storedomain+'/cart?'+fcc.session_get()+'&output=json&callback=?', function(data) { // callback function goes here }); The ''fcc.session_get()'' call will return something like ''&fcsid=abc123su2eoba8r9oknf7qa4b3''. If you just need the ''fcsid'' value itself you can easily retrieve it from the ''JSON'' with ''FC.session_id''. Developer Note: if you are developing with Chrome and on an IP address, Chrome won't let you save cookies to that IP or to localhost so it's best to do any session_get() calls with another browser during development. [[http://code.google.com/p/chromium/issues/detail?id=56211]] Are you **//creating//** or **//modifying//** elements? If you're dynamically //creating// add-to-cart links or forms, you don't need to bother with this at all. It will be handled automatically. If however you're //modifying// the ''href'' of an add-to-cart link that was present on pageload you //do// need to append the ''fcsid''. This has to do with how FoxyCart binds ''onclick'' handlers to elements while also making it easy to add new add-to-cart link or form elements. ===== Adding Your Own Events ===== The following "events" below have the following methods: * ''add'': Adds the function to the event's function array. * ''add_pre'': Adds the function to the event's function array //before// any other functions in the array. * ''resume'': Resumes a previously "paused" event. If ''pause'' is returned, execution of the event function array stops, but it keeps its place. This is very useful if you need to ensure that a request * ''execute'': Execute's all the functions in the array, stopping if ''false'' is returned. (This is mostly an internal method, and won't likely be used by even very advanced integrations.) ==== ''ready'': When the Cart is Ready on Pageload ==== === What It Is === If you need to execute javascript on pageload, but you require the cart contents, the ''ready'' event will allow you to execute code once the FoxyCart cart object is ready. === How It Works: Overview === When the ''foxycart.js'' is loaded on a page, it will retrieve the cart data when the page is ready (on jQuery's ''document.ready'' event). === How It Works: Details === Unlike the other events, the ''ready'' event's ''execute()'' method does not get called with any arguments. ==== ''preprocess'': Before an Add-To-Cart ==== === What It Is === Attaching a custom event to be run before an item is added to the cart can be useful if you need to do client-side validation of a form or value before allowing the cart-add to happen. For example, you may have a "Custom Note" field on an add-to-cart form that's required. Or a "Gift Recipient Email Address" field that must be completed for the item to be added to the cart. Using the ''preprocess'' event allows all of this and more. === How It Works: Overview === When a link or form pointing to your FoxyCart's cart URL (ie. ''https://example.foxycart.com/cart'') is clicked or submitted, ''foxycart.js'' first checks to see if any functions have been added to your ''preprocess'' array. If any functions exist, they are run in order of entry (first-in-first out) passing in two arguments: - The submitted element itself. - An array of name/value pairs to be submitted to the cart-add request. This array is built using an "unserialize" function inside the ''FC'' object. If //any// function in the ''preprocess'' array returns ''false'', the cart-add is aborted. (The actual ''onclick'' or ''onsubmit'' event of the add-to-cart link or form returns ''false'', so the default behavior of the element is bypassed as well.) No other indication is given, so if you have a ''preprocess'' function that returns false it should also alert the user. If no ''preprocess'' functions are added, this step is effectively skipped. === How It Works: Details === Assuming the default ''fcc'' object, the first step is to create a function and add it to the ''preprocess'' array: foxycart_ga = function(e, arr) { var href = ''; if (e.tagName == 'A') { href = e.href; } else if (e.tagName == 'FORM') { href = 'https://'+storedomain+'/cart?'+jQuery(e).serialize(); } if (!href.match("cart=(checkout|updateinfo)") && !href.match("redirect=")) { pageTracker._trackPageview('/cart'); console.info('foo1'); return true; } } fcc.events.cart.preprocess.add(foxycart_ga); Or just create an anonymous function straight in the array, like this: fcc.events.cart.preprocess.add(function(e, arr) { var href = ''; if (e.tagName == 'A') { href = e.href; } else if (e.tagName == 'FORM') { href = 'https://'+storedomain+'/cart?'+jQuery(e).serialize(); } if (!href.match("cart=(checkout|updateinfo)") && !href.match("redirect=")) { pageTracker._trackPageview('/cart'); console.info('foo1'); return true; } }); That function fires off a request to Google Analytics's ''_trackPageview()'' function before the cart is added, and returns ''true''. Because it returns ''true'' the ''.execute()'' method of the ''preprocess'' object will continue on to the next function in its array. If the ''execute()'' method runs all the functions in the ''preprocess'' array without hitting a return of ''false'', the cart-add request moves on to the ''process'' event. ==== ''process'': The Actual Add-To-Cart Event ==== === What It Is === The ''process'' event is where you can define what happens when a visitor to your site clicks an "add to cart" link or form. You may want to display an unobtrusive indication that the item has been added to their cart; you may wish to display a modal window (like the default [[http://colorpowered.com/colorbox/|Colorbox]] script); you may want to send the visitor directly to checkout if certain products are in the cart; or you may want to do a combination of all that and more. All of this functionality can be defined in the ''process'' event. === Skipping It Entirely === If you don't want anything fancy and just want your links and forms to load the cart up straight, just don't add any functions to ''process''. It's that easy. If no functions are present then your add-to-cart links and forms will behave normally. === How It Works: Overview === The ''process'' functionality is nearly identical to the ''preprocess'' functionality described above, but whether ''true'' or ''false'' is returned may be a bit more nuanced. Generally you'll only want _one_ event in the ''process'' array, though you can have more if necessary. One thing to keep in mind is that a ''return false;'' is probably desired if you actually do anything here, such as a [[.:json|JSONP request]] or a modal window call, since //that// is where the item is added to the cart. If you don't return ''false'' then the add-to-cart link or form will fire and the request will likely be duplicated. === How It Works: Details === After any ''preprocess'' events have been run, the ''process'' event is first checked to determine if any functions have been added. If the ''process'' array is empty, ''true'' is returned and the default behavior of the add-to-cart element that was submitted continues. Adding a function to the ''process'' array is the same as with the ''preprocess'' examples above, but obviously replacing ''preprocess'' with ''process''. Here's an example of the way [[http://colorpowered.com/colorbox/|Colorbox]] is attached to cart-add requests in the default ''foxycart.complete.js'' file: fcc.events.cart.process.add(function(e){ var href = ''; if (e.tagName == 'A') { href = e.href; } else if (e.tagName == 'FORM') { href = 'https://'+storedomain+'/cart?'+jQuery(e).serialize(); } if (href.match("cart=(checkout|updateinfo)") || href.match("redirect=")) { return true; } else { jQuery.colorbox({ href: href, iframe: true, width: "700px", height: "70%", onClosed: function(){fcc.events.cart.postprocess.execute(e);} }); return false; } }); This example first checks to see if the element that was submitted is a link (''A'') or form (''FORM''). It then sets the ''URL'' to be submitted to the cart. If that cart request should bypass the cart, however, it returns ''true'', thus allowing the link or form to complete its behavior unimpeded (and bypassing the modal window entirely). If the cart-add request does belong in a modal window the code instantiates a Colorbox and loads the cart in an ''iframe''. It then returns ''false'', preventing the link or form from submitting again (since loading it in the Colorbox's iframe has already made our cart request. Notice the ''onClosed'' function. That comes next. Onward, ho! ==== ''postprocess'': After the Add-To-Cart ==== === What It Is === The ''postprocess'' event is //not called directly// by ''foxycart.js'', but is available to use if you need it. This can be useful, for example, if you want to refresh the ''FC.json'' when your modal window is closed, as is the case with the default ''foxycart.complete.js''. === How It Works: Overview === Just like the ''preprocess'' and ''process'', you can add multiple functions to the ''postprocess'' even. Notice the call to ''postprocess'' in the ''process'' example above, in the ''onClosed'' parameter. That's telling Colorbox to run the ''postprocess'' events when the modal window is closed. If we add a function like below, then the ''cart_update()'' method will be called (which refreshes the ''FC.json'' and updates the "minicart" helper functions). fcc.events.cart.postprocess.add(function(){ fcc.cart_update.call(fcc); }); (The ''.call(fcc)'' is necessary to prevent the ''this'' keyword in JavaScript from going wonky.) ==== Pausing and Resuming Event Execution ==== The need for pausing and resuming an event is most often seen in adding products to the cart. Sometimes you may need to add or remove a product silently before a cart request is sent, or you may need to add custom fields to the session for affiliate or analytics tracking. Because JavaScript is a non-blocking language, it's likely that a request to a 3rd party affiliate tracking system might return slower than the add-to-cart request, which could cause problems. In order to work around this problem, FoxyCart supports pausing and resuming an event. To pause an event, for example, the ''preprocess'' event, you'd return ''"pause"'' (as a string) instead of ''true'' or ''false''. Here's a heavily commented example of adding a product to the cart silently: fcc.events.cart.preprocess.add_pre(function(e, arr) { if (arr['cart'] != "view") { console.log("Preprocess - adding a product silently"); var jsonString = ""; jsonString = 'https://' + fcc.storedomain + '/cart?output=json&1:quantity=2&1:name=Add%20in%20preprocess&1:price=3.23&1:weight=5&1:code=pre1'; $.getJSON(jsonString+'&callback=?' + fcc.session_get(), function(data) { console.log("And added.") console.info(this); FC.json = data; // fcc.cart_update(); console.log('= = = = = = = = = = = = = = = = = = = = ='); console.info(this); fcc.events.cart.preprocess.resume(); }); return "pause"; } else { return true; } }); Notice the ''return "pause";''. That halts the further execution of the ''preprocess'' event. So how to resume? Notice the ''fcc.events.cart.preprocess.resume();''. That's similar to calling ''fcc.events.cart.preprocess.execute();'', but instead of starting at the beginning, it resumes from where it was last paused. If you have any questions on any of this, please don't hesitate to [[http://forum.foxycart.com/|post on our forum]]. We're happy to help.