Resources & How To

Library Overview

This document is a write up on the Strands Recommender Javascript Library configuration and usage. The library is intended to facilitate the integration of the recommender by automatically handling important concepts like the user management and by offering a broad set of functions to interact with the recommendations API.

The library offers several advantages compared to an API based integration:

  • Lightning fast plug & play recommendation widget integration: fine tune the looks from our dashboard, get the integration snippet, paste it into your page and you are done.
  • Since the code runs in your visitor’s browser it adds no delay to the normal rendering of your page.
  • Complex and error-prone areas like the user id management are automatically handled.
  • Many extension options are available to customize the library experience to your particular needs like customizable recommendation rendering or asynchronous behavioral event submission.

In order to get started with the library, two conditions have to be met :

  • you already have an account in the system with its corresponding API ID.
  • you have already completed the catalog configuration steps in the Dashboard.

Basic Configuration

Library Installation

The installation of the library has two parts:

  • first include the library file holding the code and functions
  • second call the SBS.Worker.go() function with its mandatory parameter: your customer API ID (available in your registration e-mail and in the account section of our Dashboard

The aforementioned code must be placed at the end of the HTML document, right before the </body> closing tag. This guarantees that the document is ready when the code loads while preventing it from slowing down the normal rendering of the page.

    <!– Library placed at the end of the HTML Document, before the /body closing tag -–>
    <script type="text/javascript" 
       src="http://bizsolutions.strands.com/sbsstatic/js/sbsLib-1.0.min.js"></script>
    <script type="text/javascript">
    try{
      SBS.Worker.go("REPLACE_WITH_YOUR_APID");
    } catch (e){};
    </script>
    

Asynchronous Behavioral Tracking

The next step is to provide the recommendation engine with information about the habits of your site visitors to help it improve its recommendations over time.

To do so a set of event types is available to track the most significant activities of the users in your site. Most of them will be associated to an item in your catalog.

The submission method works asynchronously and gives a higher priority to the normal page rendering before it executes where the events are initially defined and stored but not sent. The library keeps them aside until it is done with the retrieval and rendering of the recommendations and only then will it submit the events. The impact of the events on the global loading time is therefore minimized.

To achieve this, the javascript definition objects for each event taking place have to be appended to the global StrandsTrack array any time in the page code. If this happens before the library loads, they will be stored until ready to submit. If the addition happens when the normal submission has already happened, the library will know it and submit accordingly.

The latter type is interesting to address events triggering with a user action on a page that would be handled via Ajax without a page refresh. Imagine an item preview as a popup inside the page or an addition to the shopping cart that does not lead to the shopping cart page.

One way to get going would be to include a javascript tag like the following before the library tag:

<script type="text/javascript">
//Make sure first that the array is defined
if (typeof StrandsTrack=="undefined"){StrandsTrack=[];}
//Item was visisted
StrandsTrack.push({
  event:"visited",
  item: "REPLACE_WITH_VISITED_ITEM_ID"
});
</script>
<script type="text/javascript" 
   src="http://bizsolutions.strands.com/sbsstatic/js/sbsLib-1.0.min.js"></script>

...

Another would be to include the event definitions inside the same try/catch block as the SBS.Worker.go() call:

<script type="text/javascript" 
   src="http://bizsolutions.strands.com/sbsstatic/js/sbsLib-1.0.min.js"></script>
<script type="text/javascript"> try{ //Event Definitions //Make sure first that the array is defined if (typeof StrandsTrack=="undefined"){StrandsTrack=[];} //Item was visisted StrandsTrack.push({ event:"visited", item: "REPLACE_WITH_VISITED_ITEM_ID" }); //Launch processing SBS.Worker.go("REPLACE_WITH_YOUR_APID"); } catch (e){}; </script>

The full list of events is available in the tracking section of the Dashboard where a wizard will guide you through the integration process. Most common events are explained next as reference.

Item visited

The most common event; should be included in each item (description) page of your site

Parameters:

  • event (String) “visited”
  • item (String) the unique identifier for the item
<script type="text/javascript">
if (typeof StrandsTrack=="undefined"){StrandsTrack=[];}
//Item was visisted
StrandsTrack.push({
event:"visited",
item: "REPLACE_WITH_VISITED_ITEM_ID"
});
</script>
    

Shopping Cart Updated

Placed in the shopping cart page. Notifies our backend of the current user shopping cart contents

Parameters:

  • event (String) “updateshoppingcart”
  • items (String[]) array holding the unique identifier for each item in the cart
<script type="text/javascript">
if (typeof StrandsTrack=="undefined"){StrandsTrack=[];}
//Order was placed (serveral items were purchased)
StrandsTrack.push({
  event:"updateshoppingcart",
  items: [
      "REPLACE_WITH_CART_ITEM1_ID",
    "REPLACE_WITH_CART_ITEM2_ID"
  ]
})
</script>
    

Items Purchased:

Placed in the order confirmation page. Submits the contents of a valid order with the price and quantities for each of its items. A unique order id is required.

Parameters:

  • event (String) “purchased”
  • orderid (String) unique order identifier
  • items (PurchasedItem[]) array holding an entry for each item in the cart

PurchasedItem format:

  • id (String) unique identifier for the item
  • quantity (Integer|String) the amount of units purchased of this item; can be included as a String representation of the number
  • price (Float|String) the price per unit of this item; can be included as a String representation of the number with ‘.’ as the decimal separator
<script type="text/javascript">
if (typeof StrandsTrack=="undefined"){StrandsTrack=[];}
//Order was placed (serveral items were purchased)
StrandsTrack.push({
  event:"purchased",
  orderid:"REPLACE_WITH_ORDER_ID",
  items: [
    {id:"REPLACE_WITH_PURCHASED_ITEM1_ID",
     price:"REPLACE_WITH_ITEM1_PRICE",quantity:"REPLACE_WITH_ITEM1_QUANTITY"},
    {id:"REPLACE_WITH_PURCHASED_ITEM2_ID",
     price:"REPLACE_WITH_ITEM2_PRICE",quantity:"REPLACE_WITH_ITEM2_QUANTITY"}
  ]
})
</script>
    

NOTE: The especial event userlogged has its own section later on.

Recommendation Widgets

Once done with the behavioral tracking the next step is to configure the recommendation widgets that will hold the recommendations to be included in your site. Both the looks and the logic behind each widget can be configured in the recommendations section of our Dashboard.

Once the setup is satisfactory, the integration is as simple as copying the integration snippet generated from the widget listing and pasting it into the desired areas of your website.

Remember that the more visible the widgets are the stronger their impact will be in helping your visitors find what they are looking for (and therefore convert) and the more likely new visitors will keep browsing your site when arriving from external sources like search engines. Most common placements would be the site’s home, item, category, shopping cart and order confirmation pages.


Widget Installation

The way to place a widget in one of your pages is to insert a <div> tag wit all required parameters in the desired body position. Note that for widgets to work the library must have also been included in the page as explained above.

Widget div parameters

  • class (required) “strandsRecs”
  • tpl (required) recommendation template identifier that represents both the looks and the logic that have been associated to the widget from the Dashboard’s recommendations section.
  • item (required*) id of the item(s) on which to base the recommendations.

    It is only required for pages tightly associated to an item (set). On an item page it would be the item being shown, on a checkout or cart page it could be the list of items in the cart. Pages like a home or category section don’t need an item.

    If more than one item has to be included, a list of ids can be provided using “_._” as separator.

    {item1_id}_._{item2_id} … _._{itemN_id}
  • dfilter (optional) dynamic filter to restrict the recommendations with a parameter not defined as part of the template. Only items matching the dfilter criteria will be returned.

    The most common use-case would be an installation where a single tpl is used for all category pages of your site (this is good for tracking the performance of the widget as a single unit); in that case it is desired to limit the recommendations to the current category being viewed.

    Format:

    Name and value (list) are to be separated by a double colon “::” and if more than one possible value is desired for that catalog property, other values can be added separated by double semi-colons “;;” (meaning a logical OR between the property values)

    {catalog_property_name}::{property_value_1};;{property_value_2};;;;{property_value_n}

    If filtering by more than one catalog property is desired (category and gender for instance), other dfilter parameters can be appended using “_._” as separator (meaning a logical AND in this case between the different property types).

    {catalog_property1_name}::{property1_value_1};;{property1_value_2};;;;{property1_value_n} _._ {catalog_property2_name} :: {property2_value1} …


Widget Examples

  • Generic item page widget
    <div class="strandsRecs" tpl="prod_1" item="REPLACE_WITH_VIEWED_ITEM_ID"></div>
    
  • Multi item shopping cart page widget
    <div class="strandsRecs" tpl="cart-1" item="{CART_ITEM1_ID}_._{ITEM2_ID}"></div>
    
  • Itemless home page widget
    <div class="strandsRecs" tpl="home-1"></div>
    
  • Category page widget
    <div class="strandsRecs" tpl="ctgy-1" dfilter="category::Shoes"></div>
    
  • Category page widget with gender selection
    <div class="strandsRecs" tpl="ctgy-1" dfilter="ctgy::Shoes_._gender::W"></div>
    

User Login

The library manages the tracking of each site visitor automatically, assigning a persistent, unique and anonymous user cookie to all of them. This is enough for the system to function properly.

The only problem with that is that the Strands cookie will not let you know who that user is in your system. To notify our backend of the link between our anonymous users and your internal user identifiers the login concept exists.

The way to execute the login is to add the special userlogged event with the logged user id as its user parameter on each page where the user is known to be logged into your system.

 

userlogged

Placed on each page view where the user is logged into your system. Guarantees that the Strands Recommender backend will be able to associate all past anonymous activity to an id known to you.

Parameters:

  • event (String) “userlogged”
  • user (String) logged identifier of a registered user
<script type="text/javascript" 
   src="http://bizsolutions.strands.com/sbsstatic/js/sbsLib-1.0.min.js"></script>
<script type="text/javascript">
try{
  if (typeof StrandsTrack=="undefined"){StrandsTrack=[];}
  StrandsTrack.push({
    event:"userlogged",
    user: "REPLACE_WITH_THE_LOGGED_USER_ID"
  });
  SBS.Worker.go("REPLACE_WITH_YOUR_APID");
} catch (e){};
</script>

 

Legacy login

An older integration method now discontinued and not recommended but still supported exists. It is achieved by including a user parameter in both the event and widget definitions only when the user is logged into your system.

Such a definition of the user parameter has been more often than not the source of faulty integrations. Its misuse easily leads to the creation of macro user-profiles aggregating behavioral data from different users.

<script type="text/javascript">
if (typeof StrandsTrack=="undefined"){StrandsTrack=[];}
//Item was visisted
StrandsTrack.push({
  event:"visited",
  user: "REPLACE_WITH_THE_LOGGED_USER_ID",
  item: "REPLACE_WITH_VISITED_ITEM_ID"
});
</script>
<div class="strandsRecs" tpl="home-1"  
      user="REPLACE_WITH_THE_LOGGED_USER_ID"></div>

Advanced Features

The library provides a set of advanced features to let power users customize the integration experience one step further. Examples of this would be the ability to define customized recommendation rendering functions or to add listeners to certain library execution milestones.


Customized Recommendation Rendering

The main stopper for the adoption of the javascript library in the past was the limitation in styles offered by the standard recommendation widgets and their looks editor in the Dashboard. And the fact is that meeting the needs of all customers in the design field with a single solution like that is indeed not possible.

To overcome this limitation a custom rendering method definition is provided so that customers can redefine the way they want the widgets to render in their pages. This lets the library handle all of the tedious integration matters while leaving you with full control over the looks of your recommendations.

  • In order to get started, configure a recommendation template from the recommendations section in the dashboard as you would do with a default widget installation.

    Note: the widget looks for the template need not be configured (since you will be handling them yourself) except for one detail: the amount of items you wish to render has to be defined there.

  • Once satisfied with the logic definition and with the amount of recommendations, integrate the widget normally in the desired section of your page as explained in the integration pop-up, for instance:

    <div class="strandsRecs" tpl="SOME_TPL" 
            item="REPLACE_WITH_VIEWED_ITEM_ID" dfilter="category::Food"></div>
    
  • Define next your custom rendering functions either as inline javascript or as an external script, for instance:

    <script type="text/javascript" 
       src="path_to_custom_library/custom_widget_renderer.js"></script>
       
    OR
    
    <script type="text/javascript">
    
    function SOME_TPL_rendering_function(rec_info){
       //rendering code for tpl="SOME_TPL"
    }
    
    function all_other_templates_rendering_function(rec_info){
       //rendering code for all templates but "SOME_TPL"
    }
    
    </script>
    

    Note: the function definition/inclusion must happen before its registration with SBS.Recs.setRenderer

    Note2: the rendering logic must include onclick events for the rendered items as explained later

  • Include our library normally and before calling SBS.Worker.go() do the linking of the rendering functions with the SBS.Recs.setRenderer() method.

    <script type="text/javascript" 
       src="http://bizsolutions.strands.com/sbsstatic/js/sbsLib-1.0.min.js"></script>
    <script type="text/javascript">
    try{    
      SBS.Recs.setRenderer(SOME_TPL_rendering_function,"SOME_TPL");
      SBS.Recs.setRenderer(all_other_templates_rendering_function);
      SBS.Worker.go("REPLACE_WITH_YOUR_APID");
    } catch (e){};
    </script>
    

Method specs

SBS.Recs.setRenderer(custom_renderer, tpl)

Defines a rendering function that will be called when the recommendations for a given template (if tpl is provided) or for all templates (if no tpl is provided). The latter is overriden by any per-template invocation.

Parameters:

  • custom_renderer {function} (required) function that will conduct the custom rendering of the recommendations (its format will be explained next)

  • tpl {String} (optional) template name for which the function will be used; omit to define a global renderer that will handle all widget templates for which a specific renderer has not been defined

custom_renderer(rec_info)

All custom rendering functions supplied to SBS.Recs.setRenderer have to follow this spec. They will be called when the corresponding recommendations arrive and their main goal will be to render the recommendations in the page.

Important Note: The recommender requires this functions to accurately track the clicks on the recommendation set in order to work properly. An onclick call to the SBS.Tracking.onRecClick function (explained next) is required on all custom rendered items.


Parameters:

  • rec_info {Object} JSON object holding the recommendations and the parameters extracted from the widget definition like the id of the destination DOM node or the tpl identifier

    • rrq {String} recommendation request unique identifier; required parameter for the SBS.Tracking.onRecClick method

    • tpl {String} template identifier used

    • user {String} user used by the library to request the recommendations; will match the anonymous cookie or the logged id of the user if a login ever happened

    • id {String} Destination DOM node id in which to render the recommendations; if no id existed for it the library automatically creates one

    • item {String} item attribute in the widget definition used to base the recommendations on it

    • dfilter {String} dfilter attribute in the widget definition if any

    • recommendations {Array} recommended items array; explained next

    Example

    {
      rrq: "123",          //recommendation request id
      tpl: "SOME_TPL",           //tpl used in the recs request
      user: "SBS_1234_1235"      //user used in the recs request
      id: "strandsRecNode01",    //recs destination DOM node id
      item: "brownie001",        //item the recs were based on
      dfilter: "category::Food", //dfilter if any was used
      recommendations: []        //recommendations array
    }
  • rec_info.recommendations {Array} JSON array with the received recommendations for the given template; this would match the contents of the generic API JSON response object’s “recommendations” field

    [
      //first recommended item
      {      
    
         "itemId": "brownie007",     //item's ID
         "metadata": {                     //item metadata
     
            //mandatory item properties
            "name": "Big Brownie",
            "description": "Sweet Chocolate",
            "link": "http://www.mystore.com/big_brownie/",
            "price": 10.0,
            "picture": "http://a0.akamai.net/bigbrown.png",
            "tags": ["sweet", "big"],   
            
            //rest of the catalog properties
            "properties": {             
                
                //NOTE: since they could all be multivalued here, 
                //      values are contained inside of an array        
                
                "category": ["Food"],
                "subcategory": ["Dessert"],
                "brand": ["Nestle"],
                "instock": ["Y"] 
            }
                
         }
      },
    
      // other recommended items ... 
    
    ]

SBS.Tracking.onRecClick(item,tpl,rrq)

Function that must be called whenever a recomendation is clicked.

Parameters:

  • item {String} (required) itemId of the item from the JSON recommendation object that is being rendered

  • tpl {String} (required) template identifier as in the rec_info object

  • rrq {String} (required) recommendation request identifier as in the rec_info object

Rendering Example:

<div class="strandsRecs" id="strandsRecNode01" tpl="SOME_TPL">
 <a href="http://www.mystore.com/big_brownie/" 
   onclick="SBS.Tracking.onRecClick('brownie002','SOME_TPL','123');return true;">
   Big Brownie <img src="http://a0.akamai.net/bigbrown.png" />
 </a>
   
 ...
   
</div>

Note: The tracking of clicks is a bit tricky since it happens right before the browser switches to a new page. The above inclusion mechanism where the onclick node attribute is used has been widely tested and is strongly recommended as the way to fulfill this requirement.


Library Execution-Milestone Listeners

The library triggers events at different points of interest during its execution cycle. Listener functions can be added to react to them or even modify the result of following actions

To add a listener to an event the method SBS.Event.subscribe is available together with its counterpart SBS.Event.unsubscribe.

Example:

<script type="text/javascript">
   //Define a listener 
   onRecsReceived = function(rec_info){
       //DO SOMETHING
   }
</script>
<script type="text/javascript" 
   src="http://bizsolutions.strands.com/sbsstatic/js/sbsLib-1.0.min.js"></script>
<script type="text/javascript">
try{    
  //Subscribe a listener before the library launches
  SBS.Event.subscribe('recs.received',onRecsReceived);
  //Launch library
  SBS.Worker.go("REPLACE_WITH_YOUR_APID");
} catch (e){};
</script>

The list of public event types follows:


cookie.setup

One of the first steps run by the library is initializing the cookie layer after checking whether cookies are active in the visitor’s browser. When the initialization is complete this event is triggered with the cookie activation status as parameter.

<script type="text/javascript">
   SBS.Event.subscribe("cookie.setup", function(con){
      alert("Are cookies on? :: "+con)
   });
</script>


recs.received

The library automatically parses the widget definitions in the page and starts requesting the corresponding recommendations.

Upon each arrival and before the rendering process starts this event is triggered with the same rec_info parameter that will be passed to the rendering methods.

<script type="text/javascript">
   SBS.Event.subscribe("recs.received",function(rec_info){
      if (rec_info){
         var recs = rec_info.recommendations;
         alert(" tpl="+rec_info.tpl+
           " id="+rec_info.id+
           " rrq="+rec_info.rrq+
           " rec_count="+(recs?recs.length:"0")
         );
      }
   });
</script>


widget.rendered

After rendering each widget the library triggers this event with the same rec_info parameter that was used in the rendering. Note that custom renderers will not trigger the event.

<script type="text/javascript">
   SBS.Event.subscribe("widget.rendered",function(rec_info){
      if (rec_info){
         var recs = rec_info.recommendations;
         alert(" tpl="+rec_info.tpl+
           " id="+rec_info.id+
           " rrq="+rec_info.rrq+
           " rec_count="+(recs?recs.length:"0")
         );
      }
   });
</script>

Method specs

SBS.Event.subscribe(event, callback, options)

Subscribes a callback to the given event type. The callback will be called whenever the event triggers.

Parameters:

  • event {String} (required) valid event name

  • callback {function} (required) function to call when the event triggers; its parameters will depend on the event type

  • options {Object} (optional) extra configuration options

    • options.delete {Boolean} (optional) set to true to remove all previous listeners

    • options.before {Boolean} (optional) set to true to have the callback be run the first among all previous listeners

SBS.Event.unsubscribe(event, callback)

Removes a given callback from the list of listeners subscribed to the given event.

Parameters:

  • event {String} (required) valid event name

  • callback {function} (required) function to remove

Cross-Domain Site Tracking

If your site spans multiple domains or different subdomains living under the same parent, several steps have to be followed in order to consistently track site visitors across them all.

The library stores the user information it needs to track your visitors in a set of cookies that by default are not accessible from within a different domain or even sibling subdomains.

To overcome this limitation and unify all of the activity for each site visitor under a single profile, there are three main scenarios that the library can currently cover:

  • Subdomains: Your site is split across different subdomains of a domain you own, for example you could have your store under http://store.mydomain.com and your checkout pages under https://secure.mydomain.com. Note that even http://store.com and http://www.store.com are to be considered different subdomains.
  • Different top-level domains: A common example of this scenario would be having a 3rd party shopping cart vendor hosting your checkout pages, for instance: http://store.mydomain.com for your product pages and https://checkout.mycartprovider.com for the carts and payments.
  • Different domains and subdomains: A hybrid of the above two where your site transverses different domains with multiple subdomains for some or all of them.


Multiple subdomains

If your situation is that of owning a top-level domain where all of your store sections hang as different subdomains from it the solution is quite simple.

Let’s say that the involved subdomains are three: http://mydomain.com for the home, http://www.mydomain.com for the product pages and http://checkout.mydomain.com for the checkout and cart pages. You would just need to tell the library to use the common top level domain shared by all of them: .mydomain.com (note the leading dot here).

The way to do so is the following:

  • Define the corresponding top-level domain before calling SBS.Worker.go() in all of the subdomains where the library is embedded
    SBS.addSetting("domain",".mydomain.com");
    SBS.Worker.go("YOUR_API_ID");
    


Multiple top-level domains

In this case, your site spans different domains, for instance: http://store.mydomain.com and https://checkout.mycartprovider.com.

The key procedure to achieve the desired synchronization will be to “hack” all links and forms present in each of your domains that point to the other ones so that the library can append a set of parameters to the URLs and read them back once the remote pages are loaded.

To enable cross domain tracking across different top-level domains follow the next steps:

  • Enable the module right before calling SBS.Worker.go()
    SBS.addSetting("xDomain",true);
    SBS.Worker.go("YOUR_API_ID");
    

  • Add the following code to all of your links and forms pointing to your other domains
    ...
    <a href="http://checkout.mycartdomain.com/add.html?id=123" 
       onclick="SBS.Xdom.link(this);"></a>
    ...
    <form action="http://checkout.mycartdomain.com/checkout.html" 
       onclick="SBS.Xdom.form(this);"></form>
    ...
    

    (the form and link functions take both the DOM node as an argument and adjust its URL before the real redirection action takes place)

  • Adjust any other links to your other domains with SBS.Xdom.URL before redirecting
    document.location=SBS.Xdom.URL(
      "http://checkout.mycartdomain.com/addtocart.html?item=1234");
    

  • Finally, make sure that the Strands library is loaded and initialized together with the SBS.Xdom module in all of the remote domain’s pages (that correspond to the “hacked” URLS mentioned above) even if no recommendations or tracking events are to be sent from them. This way the library will be available to read and process the cross-domain parameters:
    <!– Library placed JUST ONCE at the end of the HTML Document, 
        before the /body closing tag -–>
    <script type="text/javascript" 
     src="http://bizsolutions.strands.com/sbsstatic/js/sbsLib-1.0.min.js"></script>
    <script type="text/javascript">
    try{
      //Activate cross domain tracking
      SBS.addSetting("xDomain",true);
      //Launch the library
      SBS.Worker.go("REPLACE_WITH_YOUR_APID");
    } catch (e){};
    </script>
    
    

If you are using Google Analytics or any other javascript based tracking system you will find this cross-domain mechanism analogous to theirs.

The Strands library should be able to work in parallel with them but care has to be taken in order to ensure that the final URL will carry the parameters for all of the tracking systems.

In the case of Google analytics, the above example would result in:

...
<a href="http://checkout.mycartdomain.com/add.html?id=123" 
   onclick="SBS.Xdom.link(this);gaq.push(['_link',this.href]);return false;"></a>
...
<form action="http://checkout.mycartdomain.com/checkout.html" 
   onclick="SBS.Xdom.form(this);gaq.push(['_linkByPost',this]);"></form>
...


Multiple domains and subdomains

In this case, multiple top-level domains exist and for some (or even all) of them, different subdomains are used.

In this case the procedure is similar to the top-level domains scenario with the addition of the parent subdomain definition where necessary:

  • Proceed the same way you would have for the multiple top-domain scenario.
  • Add the parent domain definition before all SBS.Worker.go() calls in each of the affected child subdomains, together with the Xdom activation setting:
      //Activate top-level cross domain tracking
      SBS.addSetting("xDomain",true);
      //Add parent domain for all library inclussions in pages where the 
      //multiple-subdomains apply
      SBS.addSetting("domain",".mystore.com");
      //Initialize the library
      SBS.Worker.go("YOUR_API_ID");
    

    If your store lived under http://store.mydomain.com and http://mydomain.com together with a checkout domain of http://checkout.mycartprovider.com, the “domain” setting would have to be “.mydomain.com” for all library inclusions in the first two and should be omitted for the last one (since no other subdomains are needed for checkout).