diff --git a/google_analytics_et.module b/google_analytics_et.module
index 973fd71..9e08b57 100644
--- a/google_analytics_et.module
+++ b/google_analytics_et.module
@@ -161,10 +161,18 @@ function _google_analytics_et_sanitize_event_trackers($selectors) {
   $sanitized_selectors = array();
   if (is_array($selectors)) {
     foreach ($selectors as $key => $selector) {
-      if($key != 'debug') {
-        foreach ($selector as $s) {
+      if ($key != 'debug') {
+        foreach ($selector as $selector_key => $s) {
+          // Normal parameters.
           if (!is_array($s)) {
-            $s = filter_xss($s);
+            $selector[$selector_key] = filter_xss($s);
+          }
+          else {
+            // Tokens subarray.
+            foreach ($s as $token_key => $token) {
+              $selector[$selector_key][$token_key]['selector'] = filter_xss($selector[$selector_key][$token_key]['selector']);
+              $selector[$selector_key][$token_key]['method'] = filter_xss($selector[$selector_key][$token_key]['method']);
+            }
           }
         }
       }
diff --git a/js/google_analytics_et.js b/js/google_analytics_et.js
index b308bcc..4582c11 100644
--- a/js/google_analytics_et.js
+++ b/js/google_analytics_et.js
@@ -1,7 +1,8 @@
 Drupal.behaviors.googleAnalyticsET = {
   attach : function (context) {
-    // make sure that the google analytics event tracking object or the universal analytics tracking function exists
-    // if not then exit and don't track
+    // Make sure that the google analytics event tracking object or the
+    // universal analytics tracking function exists if not then exit and
+    // don't track.
     if(typeof _gaq == "undefined" && typeof ga == "undefined"){
       return;
     }
@@ -13,9 +14,10 @@ Drupal.behaviors.googleAnalyticsET = {
       label: '',
       value: 0,
       noninteraction: false,
-      options: []
+      options: [],
+      tokens: []
     };
-    var s = new Array();
+    var s = [];
     for(var i = 0; i < settings.selectors.length; i++) {
       s[i] = settings.selectors[i].selector;
     }
@@ -25,7 +27,7 @@ Drupal.behaviors.googleAnalyticsET = {
         jQuery(settings.selectors[i].selector).once('GoogleAnalyticsET').bind(settings.selectors[i].event,
           function(event) {
             settings.selectors[i] = jQuery.extend(defaultOptions, settings.selectors[i]);
-            trackEvent(jQuery(this), i, settings.selectors[i].options, settings.selectors[i].category, settings.selectors[i].action, settings.selectors[i].label, settings.selectors[i].value, settings.selectors[i].noninteraction)
+            trackEvent(jQuery(this), i, settings.selectors[i].options, settings.selectors[i].category, settings.selectors[i].action, settings.selectors[i].label, settings.selectors[i].value, settings.selectors[i].noninteraction, settings.selectors[i].tokens);
           }
         );
       }
@@ -33,7 +35,7 @@ Drupal.behaviors.googleAnalyticsET = {
     );
   }
 
-}
+};
 
 /**
  * trackEvent does the actual call to _gaq.push with the _trackEvent type.
@@ -53,24 +55,79 @@ Drupal.behaviors.googleAnalyticsET = {
  * @param opt_value
  *   An integer that you can use to provide numerical data about the user
  *   event.
- * @param opt_oninteraction
+ * @param opt_noninteraction
  *   A boolean that when set to true, indicates that the event hit will not
  *   be used in bounce-rate calculation.
+ * @param tokens
+ *   A array of tokens and where to find their values in the DOM.
  */
-function trackEvent($obj, id, options, category, action, opt_label, opt_value, opt_noninteraction) {
-  var href = $obj.attr('href') == undefined ? false : String($obj.attr('href'));
+function trackEvent($obj, id, options, category, action, opt_label, opt_value, opt_noninteraction, tokens) {
+  var index;
+  // Load token data if present
+  if (undefined != tokens) {
+    for (index in tokens) {
+      tokens[index].value = jQuery(tokens[index].selector)[tokens[index].method]();
+    }
+  }
+  // Replace tokens with values, if present.
+  var properties = {};
+  properties.category = {'string': category};
+  properties.action = {'string': action};
+  properties.label = {'string': opt_label};
+  for (index in properties) {
+    var string = properties[index].string;
+    var stringFirstLetter = string[0];
+
+    // Standard token (from event object).
+    if (stringFirstLetter == "!") {
+      switch (string) {
+        case "!text":
+          properties[index].value = $obj.text();
+          break;
+        case "!href":
+          var href = $obj.attr('href') == undefined ? false : String($obj.attr('href'));
+          properties[index].value = href;
+          break;
+        case "!currentPage":
+          properties[index].value = window.location.href;
+          break;
+        default:
+          properties[index].value = "Error: Token not found";
+      }
+    }
+
+    // Custom token.
+    else if (stringFirstLetter == "%") {
+      if (undefined != tokens) {
+        // Token is present.
+        if (undefined != tokens[string]) {
+          properties[index].value = tokens[string].value;
+        }
+        // No token found, or not resolved by jQuery method.
+        else {
+          properties[index].value = "Error: Token not found";
+        }
+      }
+    }
+
+    // Standard text
+    else {
+      properties[index].value = string;
+    }
+  }
+
   if (typeof this.options == 'undefined') {
     this.options = {};
   }
   this.options[id] = options;
 
-  category = category == '!text' ? String($obj.text()) : (category == '!href' ? href : (category == '!currentPage' ? String(window.location.href) : String(category)));
-  action = action == '!text' ? String($obj.text()) : (action == '!href' ? href : (action == '!currentPage' ? String(window.location.href) : String(action)));
-  opt_label = opt_label == '!text' ? String($obj.text()) : (opt_label == '!href' ? href : (opt_label == '!currentPage' ? String(window.location.href) : String(opt_label)));
+  category = properties.category.value;
+  action = properties.action.value;
+  opt_label = properties.label.value;
 
   // Google complains if category or action are not set so we fail gracefully.
   if (!category || !action) {
-    console.log("Google Analytics Event Tracking: category and action must be set.")
+    console.log("Google Analytics Event Tracking: category and action must be set.");
     return;
   }
 
@@ -85,7 +142,7 @@ function trackEvent($obj, id, options, category, action, opt_label, opt_value, o
   }
 
   if (opt_label == '!test' || Drupal.settings.googleAnalyticsETSettings.settings.debug) {
-    debugEvent($obj, category, action, opt_label, opt_value, opt_noninteraction);
+    debugEvent($obj, category, action, opt_label, opt_value, opt_noninteraction, tokens);
   }
   else if ( typeof ga == 'function' ) {
     ga('send', {
@@ -105,7 +162,7 @@ function trackEvent($obj, id, options, category, action, opt_label, opt_value, o
 /**
  * A simple debug function that matches the trackEvent function.
  */
-function debugEvent($obj, category, action, opt_label, opt_value, opt_noninteraction) {
+function debugEvent($obj, category, action, opt_label, opt_value, opt_noninteraction, tokens) {
   // Saftey First, safe use of console in IE.
   // http://blog.patspam.com/2009/the-curse-of-consolelog
   if (!("console" in window)) {
@@ -113,13 +170,14 @@ function debugEvent($obj, category, action, opt_label, opt_value, opt_noninterac
   }
   else {
     var trackerObject = {
-        category : category,
-        action : action,
-        opt_label : opt_label,
-        opt_value : opt_value,
-        opt_noninteraction : opt_noninteraction,
-        $object : $obj
-    }
+      category : category,
+      action : action,
+      opt_label : opt_label,
+      opt_value : opt_value,
+      opt_noninteraction : opt_noninteraction,
+      $object : $obj,
+      tokens: tokens
+    };
     console.log(trackerObject);
   }
 }
