Development guidelines

This section describes the guidelines you should follow when developing your own plugins.

In this section

General guidelines
Interaction type structure and design
Interaction type naming convention
Implementation best practices


General guidelines

 

§ BCGG001 - A website should never be dependent on BlueConic for correct processing

Within your interaction type code, do not do any of the following:

  • Remove any existing event handlers from DOM elements.
  • Perform call requests in a synchronized manner without a timeout — this could block further processing.
  • Block the default event (return FALSE or e.preventDefault) or stop event propagation (e.stopPropagation).
  • Introduce infinite loops.
  • Perform HTTP requests on an HTTPS website.

It is permitted to block default events on content clicks generated by BlueConic (in a banner, for example).



§ BCGG002 - Use the BlueConic JavaScript API for common utility functions (managing cookies, JSON, CSS, and so forth)

The BlueConic Javascript API contains utility functions that simplify plugin development and reduce the amount of custom code you need to write. Make full use of the available methods for performing the following tasks:

  • JSON parsing and serializing (blueConicClient.json.*)
  • Reading and writing cookies (blueConicClient.util.cookie.*)
  • Loading custom CSS and scripts (blueConicClient.util.load*)
  • Using arrays (blueConicClient.util.array.*)

See the online help for the BlueConic JavaScript Front-end API for complete information on the available methods.



§ BCGG003 - Design for optimal performance

In order for BlueConic to run as unobtrusively as possible, it is essential that custom plugins be designed for optimal performance. This basically means minimizing the amount of data (traffic) between BlueConic and the browser. To achieve this, follow these basic rules:

  • Minify all JavaScript libraries in your plugin for both the interaction type and its dependent libraries.
  • When declaring dependencies, try to re-use dependencies from plugins that have already been deployed in BlueConic. For example, if you need jQuery for your plugin and another plugin already depends on, for example, jQuery 1.7.2, make sure your plugin also depends on that same version. BlueConic will only transfer and load the (exact) same library once.
  • Minimize the number of remote procedure calls (RPCs) to BlueConic:
    • Preload profile properties
    • Avoid setting empty profile properties
    • Update BlueConic profiles in batches
  • Use blueConicClient.getBaseURL() to create references to static content which is part of a custom plugin. This ensures that the content is cached properly by the browser.
  • On websites that are built to handle high peaks of traffic, try to offload all requests for static content requests from BlueConic to the webserver(s), for example CSS stylesheets and/or images that the plugin requires in order to trigger a dialogue.

You can check which plugin code and dependencies BlueConic sends to the browser by requesting the URLs below. Use these URLs to verify whether you have properly implemented the performance guidelines described above:

http://<example.blueconic.net>/plugin/plugin/plugin.js (plugin code)
http://<example.blueconic.net>/plugin/library/library.js (dependencies)

Tools like Google PageSpeed (https://developers.google.com/speed/pagespeed/insights_extensions) also help you analyze the client-side performance and provides you with other useful tips.



§ BCGG004 - Design for all popular browsers

BlueConic has been designed to work for all client-side (browser, browser version, JavaScript framework) and server-side (CMS, web server) technologies. Your custom plugins should also be designed to work in these environments. Therefore, make sure the plugin works regardless of which browser (version) is used by the visitor. You should at least test in your plugin in the following browsers:

  • Internet Explorer 10 and higher
  • FireFox (newest version)
  • Chrome (newest version)
  • Opera (newest version)
  • Safari (newest version)

Test the plugin on a live website by using a proxy.

Back to top


Interaction type structure and design

 

§ BCGD001 - Use declared dependencies to load the required JavaScript libraries (JQuery, for example)

If you want to use external libraries like jQuery in your interaction type, you should create a dependency on those libraries. To add an dependency to an external library, you must use the "dependencies" section of the interaction type descriptor. External libraries are referenced by their URL. For example:

<dependencies>
  <platform range="2.2" />
  <libraries>
    <library url="http://code.jquery.com/jquery-1.7.2.min.js" type="javascript"/>
  </libraries>
</dependencies>

You must only use external libraries in the onLoad() method. All global objects and functions which are created by an external library are added to the context of the interaction. This means that all global objects and functions are only accessible through the scope of the interaction. You should use the following keyword to access the scope of the interaction in your interaction type:

// jQuery and '$' are added to the scope of the interaction
this.$.fancybox(settings);

The framework removes global objects and functions introduced by external libraries from the global context if they can cause a conflict with the web page. You should not rely on global objects created by external libraries.



§ BCGD002 - Do not define global functions

When introducing functions, add them within the definition of your interaction type. BlueConic interaction types must be as non-intrusive as possible because run on any channel. Placing functions within the definition prevents you from overriding any functions a customer defines. Furthermore, this allows you to use expressive function names (without any prefixes or suffixes). Be aware that calling these functions using the following pointer does not always work:

BAD

  function arraySum(a) {
  }
  var InteractionTypeImpl = InteractionType.extend({
    int sum = arraySum(array);
  });

GOOD

  var InteractionTypeImpl = InteractionType.extend({
      int sum = this.arraySum(array);
      function arraySum(a) {
      }
  });



§ BCGD003 - Interaction type documentation
  • Write a Javadoc block for every interaction type. This block should explain what the interaction type does, how the parameters affect its behavior and the profile properties it uses.
  • Write a Javadoc block for every function defined within the interaction type.

For example:

/**
* Gets the value of a profile property with the name provided and evaluates the JSON String as an Object. When empty, returns
* the default value provided, or null when none provided.
*/
getParameterObjectValue : function(parameters, parameterId, parameterDefaultValue) {
   .....
}



§ BCGD004 - Use constants for property names and parameters

List parameters first, then the property names. For example:

var InteractionTypeImpl = InteractionType.extend({
   parameterIdContentPatterns : 'contentPatterns',
   parameterIdAddConversion : 'addConversion',
   parameterIdPropertyId : 'propertyId',
   parameterIdPropertySetValue : 'propertySetValue',
   parameterIdPropertyAddValue : 'propertyAddValue',
   propertyIdLastInteractionClicked : 'lastInteractionClicked',
   propertyIdLastInteractionClickedTimeout : 'lastInteractionClickedTimeout',

   ....

});



§ BCGD005 - Wrap asynchronous logic in a Try/Catch statement

The BlueConic script only handles exceptions thrown in the synchronous parts of the interactions, therefore when an exception is thrown outside the synchronous logic, all further execution of interactions stops. To avoid this, always wrap your asynchronous interaction logic in a Try/Catch statement. For example:

onLoad : function() {
   var ia = this;
   ia.blueConicClient.profile.updateProfile(this, function() {
      try {
         ia.act();
      } catch (e) {
         ia.blueConicClient.util.log('Error occured when executing interaction ' + interactionId + ': ' + e);
      }
   }
}

act: function() {
   // Interaction logic
}



§ BCGD006 - Centralize validation logic for required inputs

Centralizing your validation logic increases readability and maintainability of your plugin code. For example:

/**
 * Validates whether any parameter within the list of required parameterNames provided has a non empty value. If so, does
 * nothing. If not, throws an exception.
 */
validateRequiredParameters : function(parameters, parameterNames) {
   for ( var i = 0; i < parameterNames.length; i++) {
      var parameterName = parameterNames[i];
      if (!parameters[parameterName]) {
         var parameterNotProvidedMsg = 'The following required parameter has not been provided: ' + parameterName;
         bcLog(parameterNotProvidedMsg);
         throw parameterNotProvidedMsg;
      }
   }
}



§ BCGD007 - Minimize the use of custom JavaScript libraries

Minimizing JavaScript libraries increases performance and minimizes loading times.

Back to top


Interaction type naming convention

 

§ BCGN001 - Use CamelCase for profile properties, functions and parameters

Use the CamelCase naming convention. CamelCase is the practice of writing compound words or phrases in which the elements are joined without spaces, with each element's initial letter capitalized within the compound and the first letter being either upper or lower case as in "laBelle", "BackColor", and "iPod".



§ BCGN002 - Name interactions types based on channel, function and type

Make sure your custom plugins are easily distinguishable from the standard and reusable BlueConic plugins. It should always be clear to both developers as well as (functional) users which interactions they can use and modify. An interaction name should reflect the following plugin traits:

  • What functionality does it provide? Is it a listener or an action?
  • On which channel is the interaction type deployed?

Back to top


Implementation best practices

 

§ BCGL001 - Separate generic and customer-specific properties

In order to determine which profile properties should be combined into a single listener, you need to make a distinction between generic properties and customer-specific properties.

  • Generic profile properties are not specific to any domain. Examples are the referring URL and the browser name and version. Listeners for generic properties can run anywhere and be placed within a separate listener. Listeners for generic profile properties are candidates to be considered reusables or even to be added to BlueConic as a default listener.
  • Customer-specific profile properties, for example a customer identification number, are usually retrieved from a customer-specific location. Listeners for customer-specific properties are never reusable and should be thoroughly tested before they are applied to new channels.



§ BCGL002 - Separate derived properties dependent on your situation

Some profile properties are derived on the basis of the values of other properties. For example, the age of a visitor can be determined by their date of birth or the average value of an order can be determined from the known prices of a list of orders. You should separate derived profile properties from the source properties they are derived from. The benefits of doing so is that the derived properties are more easily readable and the structure is simplified. One drawback of this is that because the order in which listeners are executed is not set, there might be a delay before profile properties can be updated. Therefore, when possible, always put source profile properties in the same listener. The benefit is that you know exactly when they have changed and then you can immediately update the derived profile properties when necessary. The best strategy to use depends on the situation. In general, follow these rules:

  • Save source profile properties in a format that you can easily process.
  • Save coherent information as a JSON string (serialized JSON object). By parsing this string, you can directly work with an object instead of having to implement complex parsing logic. To use JSON, you need to load a library that defines the global JSON object. This library can be retrieved from www.json.org/js.html and can be loaded into your interaction type using the blueConicClient.util.load JavaScript API method.

To illustrate this guideline, consider the following example. Let's say you want to keep track of the favorite soccer team of a visitor and show team-related banners based on his or her preferences. The favorite team is determined by the engagement score the visitor has gathered per team. In this case, you will have to keep track of the number of points per team (the source property) and determine the favorite team accordingly (derived property) for which you can create segments:

profile._teamscore:    {"psv":100, "ajax":50, "feyenoord":15}
profile.favorite_team:  "psv"



§ BCGL003 - Bind listen logic to the form submit event

It is best to bind listen logic to the form submit event. When you want to add listen logic to button click events, you would have to bind it to more than one button because multiple buttons could be used to submit the same form. Instead, you should bind it to the form submit event which every button will trigger. Be careful in situations in which buttons trigger AJAX requests without submitting the form. In this case, you should bind to the click event instead.



§ BCGL004 - Try to implement listeners in a generic way

When an input field has the same class on several forms (for example, an e-mail address has the class "bcEmail"), you can generically get the form within which the input element exists and obtain the field value based on its class. Any potential value conversions can also be implemented in a generic manner by passing conversion functions as function pointers. For example:

/**
    * Determines the form based of the form fields (provided as a list of selectors). The form fields might not exist, so for every
    * field provided we check its existance and whether it has a surrounding form. If so, the first form encountered is returned. We
    * assume the fields provided do not reside on multiple forms on one page.
    */
   getFormByFieldSelectors : function(fieldSelectors) {
      var formElement = null;
      for ( var i = 0; i < fieldSelectors.length; i++) {
         var fieldSelector = fieldSelectors[i];
         var formFieldElement = this.jQuery(fieldSelector);
         var parentForms = formFieldElement.parents('form');
         if (parentForms.size() > 0) {
            formElement = this.jQuery(parentForms.get(0));
         }
      }
      return formElement;
   }



§ BCGL005 - Use stable selectors within your listener

In a listener, you typically select form elements using JQuery with a CSS selector and then obtain their values. Try to use the most stable selectors possible so that it will still work if any of the HTML is modified. The best way to do so is to tell your customer which IDs and/or classes they should use for BlueConic ("bcEmail", for example). If this is not possible, consider the following constructs (ordered from good to undesirable):

  • Selector based on existing IDs or classes within the website.
  • Selector based on other unique element attributes.
  • Selector based on certain HTML hierarchy (for example "body p .paragraph h2").
  • Selector based on the order of HTML elements (for example ".product:eq(2)").



§ BCGL006 - Save dates as a time in milliseconds

This allows you to easily parse the time to a date and subsequently add or subtract days. Don't use JSON.stringify for dates because the resulting format varies per browser.



§ BCGL007 - Group properties in categories based on segments

Common segments include:

  • Functionality-based, for example "Technical Information", "Navigation Information", "Personal Information"
  • Channel-based or domain-based categories

Always use English for category names.



§ BCGL008 - Save profile property values in the natural language of the visitor

Because profile property values are used in the BlueConic dashboard to create segments of profiles, they should always be readable by a human, for example, a Boolean type property value should be "yes" or "ja" instead of "TRUE".