diff --git a/sitecatalyst.info b/sitecatalyst.info
old mode 100644
new mode 100755
index ee6844a..1fa6db7
--- a/sitecatalyst.info
+++ b/sitecatalyst.info
@@ -3,6 +3,7 @@ description = Adds SiteCatalyst javascript tracking code to your site's pages.
 core = 7.x
 
 configure = admin/config/system/sitecatalyst
+dependencies[] = entity_modified
 
 files[] = sitecatalyst.module
 files[] = sitecatalyst.install
diff --git a/sitecatalyst.module b/sitecatalyst.module
old mode 100644
new mode 100755
index e852ae1..296d2a7
--- a/sitecatalyst.module
+++ b/sitecatalyst.module
@@ -10,6 +10,8 @@
  * @author: Alexander Ross (bleen18)
  */
 
+define('SITECATALYST_TOKEN_CACHE', 'sitecatalyst:tag_token_results');
+
 /**
  * Implements hook_help().
  */
@@ -140,7 +142,7 @@ function sitecatalyst_page_alter(&$page) {
 
   // Add any custom code snippets if specified and replace any tokens.
   $context = _sitecatalyst_get_token_context();
-  $codesnippet = token_replace(variable_get('sitecatalyst_codesnippet', ''), $context, array(
+  $codesnippet = sitecatalyst_token_replace(variable_get('sitecatalyst_codesnippet', ''), $context, array(
     'clear' => TRUE,
     'sanitize' => TRUE,
   )) . "\n";
@@ -317,6 +319,14 @@ function sitecatalyst_variables_form_validate($form, &$form_state) {
 }
 
 /**
+ * Submit function for the variables form.
+ */
+function sitecatalyst_variables_form_submit($form, &$form_state) {
+  // clear our cached token generation, since it may have just changed anyway.
+  cache_clear_all(SITECATALYST_TOKEN_CACHE, 'cache');
+}
+
+/**
  * Given the values entered into the sitecatalyst variables form, remove any empty
  * variables (i.e. both "name" & "value" are blank).
  */
@@ -444,7 +454,7 @@ function _sitecatalyst_format_variables(array $variables = array()) {
 
     // Cannot use check_plain() here because $key may contain quotes (e.g. 's.contextData["tve_domain"]').
     $key = htmlspecialchars($key, ENT_NOQUOTES, 'UTF-8');
-    $value = token_replace($value, $context, array(
+    $value = sitecatalyst_token_replace($value, $context, array(
       'clear' => TRUE,
       'sanitize' => FALSE,
     ));
@@ -454,6 +464,86 @@ function _sitecatalyst_format_variables(array $variables = array()) {
   return $variables_formatted;
 }
 
+
+/**
+ * Replaces all tokens in a given string with appropriate values, with caching.
+ *
+ * This function is a memoizing wrapper for token_replace(), which is quite slow
+ * and inefficient.  It takes advantage of specific knowledge about how this
+ * module works to cache the result of token replacement. It is not a fully
+ * general solution but works for this module.
+ *
+ * @param string $text
+ *   A string potentially containing replaceable tokens.
+ * @param $data
+ *   (optional) An array of keyed objects. See token_replace(). Known objects
+ *   here will also be used to form the cache key.
+ * @param array $options
+ *   An array of options to pass to token_replace().  See that function for
+ *   further documentation.
+ * @return string
+ *   Text with tokens replaced.
+ *
+ * @see token_replace()
+ */
+function sitecatalyst_token_replace($text, $data = array(), array $options = array()) {
+  $processed_strings =& drupal_static(__FUNCTION__, NULL);
+
+  // Short-circuit the degenerate case, just like token_replace() does.
+  $text_tokens = token_scan($text);
+  if (empty($text_tokens)) {
+    return $text;
+  }
+
+  // Determine the cache key for this text string. That way we can cache reliably.
+  $key = _sitecatalyst_token_replace_make_key($text, $data);
+
+  // Lookup any already-cached token replacements.
+  if (is_null($processed_strings)) {
+    $cache = cache_get(SITECATALYST_TOKEN_CACHE, 'cache');
+    $processed_strings = $cache
+      ? $cache->data
+      : array();
+  }
+
+  // If the processed string we're looking for isn't already in the cache,
+  // then, and only then, do we call the expensive token_replace() (and cache
+  // the result).
+  if (!isset($processed_strings[$key]) || is_null($processed_strings[$key])) {
+    // Regenerate this particular replacement.
+    $processed_strings[$key] = token_replace($text, $data, $options);
+    cache_set(SITECATALYST_TOKEN_CACHE, $processed_strings, 'cache', CACHE_TEMPORARY);
+  }
+
+  return $processed_strings[$key];
+}
+
+/**
+ * Generates an identifying key for the lookup to be processed.
+ *
+ * @param string $text
+ *   The text to be processed.
+ * @param array $data
+ *   The array of data parameters that will be passed to token_generate().
+ *   We'll use knowledge of what is expected in that array to build a
+ *   meaningful lookup key.
+ * @return string
+ *   The key in the lookup array that corresponds to this tokenization request.
+ */
+function _sitecatalyst_token_replace_make_key($text, array $data) {
+
+  // $text may be arbitrarily long, which can slow-down lookups. Hashing it
+  // keeps uniqueness but guarantees a manageable size. Since this value won't
+  // be used as the cache key itself we're not limited to 255 characters but
+  // it will be nicer on array lookups in PHP.
+  $keys[] = sha1($text);
+  $keys[] = isset($data['node']->nid) ? $data['node']->nid . '-' . entity_modified_last('node', $data['node'])  : NULL;
+  $keys[] = isset($data['menu']->menu_name) ? $data['menu']->menu_name . '-' . entity_modified_last('menu', $data['menu']) : NULL;
+  $keys[] = isset($data['tag']->machinename) ? $data['tag']->machinename . '-' . entity_modified_last('tag', $data['tag']) : NULL;
+
+  return implode('|', array_filter($keys));
+}
+
 /**
  * Helper form builder for a variables form.
  */
