Steven Benner’s Blog

Random articles about programming, computing, and the internet.

Custom link click tracking using Omniture

Warning: This article is very old

This article was written in 2010. Things change over time. So the information presented here may no longer be current and up-to-date, or it may have even become factually inaccurate. There may be useful information here, but you should check recent sources before relying on this article.

Omniture logo

Omniture is the de facto standard tracking and analytics system that most online retailers use. It has a suite of reporting metrics and allows for custom reporting variables. It is primarily designed for online stores to track usage, conversions and sales.

Admittedly, I am not that big a fan of Omniture. Their SiteCatalyst reporting application is slower than Google Analytics and not as flexible as it could be. My biggest gripe is their tracking JavaScript code, it’s just plain terrible. It’s slow, obfuscated (really? why do this?), bloated, impossible to debug and not built using modern practices. Oh, and they’re also insanely expensive if you want to get at all fancy.

This article is simply a straight how-to for building a custom link tracking JavaScript in the hopes that I can save some other JavaScript developer out there the headaches and tears usually associated with learning advanced Omniture implementations.

Omniture basics

The Omniture JavaScript does all of its work through the s object. This object has many different methods and properties that are all ingeniously designed to confuse and torment would-be implementers.

For custom tracking metrics you will be using prop codes, which are called “Custom Insights” in the SiteCatalyst reporting software. You associate a specific prop code to a specific piece of data. So you may associate s.prop1 to be the category of a product and s.prop2 to be the name of the product.

The way we track is by loading up prop variables with strings then telling the Omniture code to run. This is done by calling s.t() for a pageview track or s.tl() for a link click track.

How to track a click action with custom prop data

The code to track an action is actually quite simple, loading prop values and calling the tracking method. Here is what a tracking script looks like. This example is the basic custom click tracking code that you would expect to see in an onclick event attached to an anchor tag.

s.linkTrackVars = 'prop1,prop2';
s.linkTrackEvents = 'None';
s.prop1 = 'Televisions';
s.prop2 = 'Samsung - LN40B530 - 40" LCD TV - 1080p (FullHD)';
s.tl(this, 'o');

Now, this is fairly self-explanatory, but I’ll break this down line for line so you know exactly what’s happening and what these statement mean.

There you have it, not incredibly complex, but not obviously simple either. There are a bunch of other properties that you can attach to a trackLinks event, but this is what 99% of them look like.

There are two standard ways to attach these custom link tracking events to the anchors that you want to track: the stupid method, and the smart method.

For this rest of this article I will assume that you want to use the smart method. Basically, we will build a script that will look at every single anchor tag in the DOM and attach the click tracking events to any links matching whatever criteria you choose. This script will automatically run when the page loads.

Defining the criteria and search pattern

First off, you must be able to identify the links that you want to track. I have used two different systems for this: Searching href tags and adding rel tags.

Searching href tags is generally the best option. This means matching the link href and attaching the event based on that data. However I have found situations where I didn’t want to track every link that had the string “products.aspx” in it, so I chose to add rel tags to those links that I did want to track. This way I could just search for links that had rel="product" in them.

The technique is still basically the same, we will gather all of the anchors in the document into an array, then iterate through the array, attaching the appropriate events and data to those anchor which meet our criteria for each type of page.

The basic script structure

No matter what you choose as your search criteria and trackable data you will need a script that has at least the following features:

The basic JavaScript object that I use for this

// Omniture click tracking
// https://stevenbenner.com/2010/03/custom-link-click-tracking-using-omniture/

if(!sb_trackLinks) { var sb_trackLinks = new Object(); };

sb_trackLinks = {
	// initialize the click-tracking system and attach events
	init: function() {
		// verify that we are running in a modern browser, if not give up silently
		if (!document.getElementsByTagName) return false;

		// iterate through all of the links in the document
		var anchors = document.getElementsByTagName('a');
		for (var i=anchors.length-1; i>=0; i--) {

			// SEARCH STATEMENTS AND TRACKING VARIABLE BUILDING GOES HERE

		};
	},

	// tracking function
	track: function(obj, propCodes, linkType = 'o') {
		s = s_gi(s_account);
		// reset tracking properties
		s.linkTrackVars = '';
		s.linkTrackEvents = 'None';
		// iterate through each property in propCodes
		for (code in propCodes) {
			s.linkTrackVars += code + ','; // build the CSV list
			s = propCodes;     // attach the prop code
		}
		// track it
		s.tl(obj, linkType);
	},

	// non-breaking/non-overriding/cross-browser event attachment
	addEvent: function(obj, evType, fn, useCapture) {
		if (obj.addEventListener) {
			obj.addEventListener(evType, fn, useCapture);
			return true;
		} else if (obj.attachEvent) {
			var r = obj.attachEvent('on' + evType, fn);
			return r;
		} else {
			obj['on' + evType] = fn;
		};
	}
};

// auto-init on load
sb_trackLinks.addEvent(window, 'load', sb_trackLinks.init);

This is the basic outline of my JavaScript click tracking object. It has three methods and automatically runs itself in the window.onload event.

I’ve put a lot of thought into optimizing this script. For instance, notice that I reverse iterate through the anchors array. This technique has been proven to be faster because the array length is only calculated once when the for statement begins. If you do the while i<array.length style then the length is calculated once for every iteration in the loop.

When you build any kind of tracking or analytics JavaScript your highest priority should be to shave every nanosecond from it’s operations. It is acceptable to sacrifice readability for performance here. After all, as a web developer your primary job should be to improve user experience, tracking scripts are a necessity but do not benefit users in any way, so you do your best to reduce their impact on user experience.

There are three methods in the sb_trackLinks object:

Crafting the search and tracking variables

Now that we have a basic structure that we can use, all that we need to build is the anchor search statements, tracking variable and event attachments.

This needs to be custom built for your specific needs. The application, URL structure and tracking needs are all critical parts of this equation. If you are using friendly URLs and need to parse data from them then you will may have to deconstruct the URI to grab the specific data that you want to track in prop codes. If you need data from somewhere else on the page you will have to figure out out to grab it. Et cetera.

I’ll provide you with an example based on our previous examples. In this case we are tracking clicks in an online store application. We are using standard query string parameters in our URLs and do not need any other special data.

This is what our for statement will look like for this site:

for (var i=anchors.length-1; i>=0; i--) {
	// parse the query string into an object
	var queryString = {};
	anchors[i].href.replace(
		new RegExp("([^?=&]+)(=([^&]*))?", "g"),
		function($0, $1, $2, $3) { queryString[$1] = $3; }
	);

	// conditionals to match link href targets
	if (anchors[i].href == 'http://your.domain/' || anchors[i].href.indexOf('home.aspx') > -1) {
		// home page
		sb_trackLinks.addEvent(anchors[i], 'click', (function() {
				return function() {
					try {
						sb_trackLinks.track(
							this,
							{
								'prop1': 'home'
							}
						);
					} catch(e) {}
					return true;
				};
			})()
		);
	} else if (anchors[i].href.indexOf('category.aspx') > -1) {
		// category pages
		sb_trackLinks.addEvent(anchors[i], 'click', (function(catId) {
				return function() {
					try {
						sb_trackLinks.track(
							this,
							{
								'prop1': 'category',
								'prop2': catId
							}
						);
					} catch(e) {}
					return true;
				};
			})(queryString['category_id'])
		);
	} else if (anchors[i].href.indexOf('product.aspx') > -1) {
		// product pages
		sb_trackLinks.addEvent(anchors[i], 'click', (function(catId, productId, productName) {
				return function() {
					try {
						sb_trackLinks.track(
							this,
							{
								'prop1': 'product',
								'prop2': catId,
								'prop3': productId,
								'prop4': productName
							}
						);
					} catch(e) {}
					return true;
				};
			})(queryString['category_id'], queryString['product_id'], queryString['product_name'])
		);
	};
};

This is a bit technical, and uses some advanced JavaScript techniques. However, it should be fairly understandable if you know your way around JavaScript.

Since we need to grab query string values for most of our tracked pages I start off by parsing the query string into an object.

I then proceed down a list of if statements that try to match a specific page. Inside each if statement I attach an onclick event to the current anchor. That event passes our current data down through the JavaScript scope via a (function(v){})(data) statement. Inside that event I call the track() method with the data and return true so that the browser follows the link once the code as been executed.

This can get very long indeed if you have a lot of different types of pages in your web application that need click tracking. As always, order your if statements by commonality, putting the most commonly matched statements first. The script engine will have to fail every if check to get to the last statement. This minor optimization may save you a millisecond or two.

It is very important that you wrap all of the code inside the event in a try{}catch(e){} just in case something somewhere breaks. If you do not do this then the links on your site will stop working if anything goes wrong. There is nothing quite as asinine as links not working because the tracking script is broken.

Putting it all together

Now that we’ve built the search and attach code all we have to do is insert it into our base script. The result looks like this:

// Omniture click tracking
// https://stevenbenner.com/2010/03/custom-link-click-tracking-using-omniture/

if(!sb_trackLinks) { var sb_trackLinks = new Object(); };

sb_trackLinks = {
	// initialize the click-tracking system and attach events
	init: function() {
		// verify that we are running in a modern browser, if not give up silently
		if (!document.getElementsByTagName) return false;

		// iterate through all of the links in the document
		var anchors = document.getElementsByTagName('a');
		for (var i=anchors.length-1; i>=0; i--) {
			// parse the query string into an object
			var queryString = {};
			anchors[i].href.replace(
				new RegExp("([^?=&]+)(=([^&]*))?", "g"),
				function($0, $1, $2, $3) { queryString[$1] = $3; }
			);

			// conditionals to match link href targets
			if (anchors[i].href == 'http://your.domain/' || anchors[i].href.indexOf('home.aspx') > -1) {
				// home page
				sb_trackLinks.addEvent(anchors[i], 'click', (function() {
						return function() {
							try {
								sb_trackLinks.track(
									this,
									{
										'prop1': 'home'
									}
								);
							} catch(e) {}
							return true;
						};
					})()
				);
			} else if (anchors[i].href.indexOf('category.aspx') > -1) {
				// category pages
				sb_trackLinks.addEvent(anchors[i], 'click', (function(catId) {
						return function() {
							try {
								sb_trackLinks.track(
									this,
									{
										'prop1': 'category',
										'prop2': catId
									}
								);
							} catch(e) {}
							return true;
						};
					})(queryString['category_id'])
				);
			} else if (anchors[i].href.indexOf('product.aspx') > -1) {
				// product pages
				sb_trackLinks.addEvent(anchors[i], 'click', (function(catId, productId, productName) {
						return function() {
							try {
								sb_trackLinks.track(
									this,
									{
										'prop1': 'product',
										'prop2': catId,
										'prop3': productId,
										'prop4': productName
									}
								);
							} catch(e) {}
							return true;
						};
					})(queryString['category_id'], queryString['product_id'], queryString['product_name'])
				);
			};
		};
	},

	// tracking function
	track: function(obj, propCodes, linkType = 'o') {
		s = s_gi(s_account);
		// reset tracking properties
		s.linkTrackVars = '';
		s.linkTrackEvents = 'None';
		// iterate through each property in propCodes
		for (code in propCodes) {
			s.linkTrackVars += code + ','; // build the CSV list
			s = propCodes;     // attach the prop code
		}
		// track it
		s.tl(obj, linkType);
	},

	// non-breaking/non-overriding/cross-browser event attachment
	addEvent: function(obj, evType, fn, useCapture) {
		if (obj.addEventListener) {
			obj.addEventListener(evType, fn, useCapture);
			return true;
		} else if (obj.attachEvent) {
			var r = obj.attachEvent('on' + evType, fn);
			return r;
		} else {
			obj['on' + evType] = fn;
		};
	}
};

// auto-init on load
sb_trackLinks.addEvent(window, 'load', sb_trackLinks.init);

Save this code in the s_code.js (or whatever you named it) script for your Omniture-enabled web site and it will start sending click data back to your Omniture account.

Gotchas

There are some possible problems that may crop up using this system.

Omniture link tracking can be used for more than just links. You can use the s.tl() method to send any data at any time for any reason. Do you want to track when someone puts their mouse over an image, or how long they waited before scrolling, or even what text they selected? You can do all of that, and more if you really want to.

If you ask me those fine-grain viewer eyeball focus type of reports are not only invasive but genuinely useless. However, I’ve been asked to do even worse invasive and pointless tracking before. If you need to you can send any data you want on any DOM event, or even with timers.

The only thing that you must overcome is that the object you pass to s.tl() must be an anchor, or more specifically, something with an href attribute. There is nothing about this in the documentation, but it simply doesn’t seem to work unless it has an anchor with an href passed to it. You can get around this by creating an element or simply passing true.

This is, however, the subject of a different article.

Conclusion

As you can see it’s not that difficult to implement custom link tracking with Omniture and you can get some very powerful reports without that much work. I still cannot give my glowing stamp of approval for Omniture as a reporting solution, for various reasons, but if you are using it then hopefully this tutorial will save you some time and headaches.

If you have any questions or comments please leave them in the form below and I will do my best to answer them. You can also take a look at the Custom Link Tracking post in the Omniture blog. It’s a year old, but the author is still responding to questions.

Comments

Ben Gaines’s avatar Ben Gaines

Steven,

I hope you don’t mind me jumping in here; if you do, please feel free to delete this comment. :)

Great technical post — thanks for sharing. Hopefully it will help some of your readers implement link tracking more effectively. Thanks for sharing your insights (and for linking back to my higher-level post on the topic from last year); this is an extremely useful technique for capturing just about anything that isn’t considered a Page View.

I’m not sure whether you’re a frequent SiteCatalyst user or not, but I would like to respond to a few of the charges you’ve levied against the product.

> Their SiteCatalyst reporting application is
> woefully slow and inept.

While I haven’t found performance issues to be extremely widespread during my four years working with and supporting the tool, we’ve actually made a number of platform improvements over the past year that improve performance significantly. Massive reports (large date range, many metrics, tons of unique values, etc.) may return more slowly than others, but examples of this should be few and far between. Additionally, if users are experiencing reporting slowness, there absolutely ARE things that we can look into.

> If you want any kind of custom collation or
> correlation report you have to call them and
> have them custom craft the report for you.

I don’t believe this has been true in at least a few years. SiteCatalyst admins can set up their own correlations (as well as managing the majority of other settings) directly within the tool. At the same time, if you’ve run into features that you cannot enable/manage within the Admin Console, please let me know and I’ll at least find out why the given feature is not managed there.

> But my biggest gripe is their tracking JavaScript
> code, it’s just plain terrible. It’s slow,
> obfuscated (really? why do this?), bloated,
> impossible to debug and not built using modern
> practices.

A few things. First, note that we’ve released a few “alternative” data collection methods (PHP, Java, XML Data Insertion) over the past couple of years if you truly hate our JavaScript. Personally, the Data Insertion (XML) API is my favorite. Second, would you be open to further discussion on specific points of complaint (at the code level) regarding our JavaScript? We do have a team of qualified engineers who work on updating the functionality contained there. Who knows — your feedback may prove valuable in resolving some of the issues with the code that bother you. We’re always open to feedback and have an excellent track record of applying these requests into our code.

Feel free to reach out to me directly at omniturecare at adobe dot com if you’d like to discuss further!

Thanks,
Ben Gaines
Community Manager
Omniture, an Adobe company

Andreas’s avatar Andreas

Hi Steven,

although I really like your Javascript coding style I don’t agree with alot of the points above.

Let’s start at the beginning:
>> It’s slow, obfuscated (really? why do this?), bloated, impossible to debug and not built using modern practices.
I recently did some speed testing for my implemenations and my s_code (H20.3 + tons of custom code) runs withing ~83ms in FF3.5 at the page load. I think that’s not too bad.
Starting with H20.3 the pagecode is not obfuscated anymore and so can the transfer can be gzipped pretty well while loading.
I’m sure the script could be reduced by some bytes, but it does a great job for cross-browser compatibility. Since we have pretty fast internet connections these days, 1-2k extra are not too bad.
Impossible to debug – hm…by default I agree. But if there are really some problems with it, there are some ways to debug. Firebug is your friend.

>> This object has many different methods and properties that are all ingeniously designed to confuse and torment would-be implementers.
I agree. BUT.. the functions coders should use are documented.

>> 500ms link delay
If you’re passing the ‘this’ pointer to a href, OMTR actually only adds the 500ms timeout if the link will be opened in the same window AND the timeout will be shortened if the image returns faster than that. (look into RDC to get fastest response times)

>> There is nothing about this in the documentation
There is a whitepaper available talking about link tracking

>> Use asynchronous script loading.
This has pros and cons. Some users want to send tracking as soon as possible even if the page has not fully loaded.

>> Save this code in a js file and link that file on every page in our Omniture-enabled web application
Ouch! Don’t do it… I like your idea of saving execution time by tweaking the code but including this script as an additional file would add so much additional load time (TCP connection, load time, etc.) that any time-savings by tweaking are lost.
If you really want to load this code on any page, add it to your existing Omniture script.

Ok, let’s get back to the initial purpose of your code.
I’m not sure why you do not use existing Omniture code and so benefit from existing event handlers. I think in regards of saving execution time you should go that route.
Look into the s.linkHandler plugin and put your code into the s_doPlugins function. Omniture already observes all clicks onto the document and so there is no need to add additional handlers. Additionally you would get around your “Links added after page load” gotcha. If the link is added properly to the document event bubbling should do its job.

Best,
Andreas

Richard’s avatar Richard

Hey Steven —

Have you tried using Mixpanel? http://mixpanel.com

They are like a more modern analytics provider that does basically what you want!

Richard

Andreas’s avatar Andreas

Steven,
since a single additional image/css/js/…-load is far above the ~83ms I don’t care about this insignificant time ;)
If you see a lag for your tabs, just pass a “true” so the first param and you should be good.
Why is RDC a pain to set up? Unless you’re using FPC this is a fairly easy change. Just get the approval from the AM and then change 1-2 lines of code. Can’t see any pain points here.
Make sure you’re using the s_doPlugins function. It get’s called on all clicks (+ s.t(l) calls + media calls). You can use var u=s.p_gh(); to get the URL of the element that was clicked. (s.p_gh is a utility function you need to add, just google it). Use var o=s.eo?s.eo:s.lnk; to get the object that was clicked. The s.linkHandler can be used to do some automatic filtering, e.g. var u=s.linkHandler(‘/section/’) to only get links to a specific section. (there are also s.exitLinkHandler and s.downloadLinkHandler).

Andreas

Ben Gaines’s avatar Ben Gaines

Thanks for the update, Steven. I wasn’t commenting in an attempt to get you to remove any negative tone, per se, I just wanted to be clear about some changes, and about our attitude toward good developer feedback. (Incidentally, Andreas beat me to a few points, which is great. He probably knows the Omniture JavaScript code as well as just about anyone outside of Omniture.)

I completely understand your point about form names. It looks like the site was using the “Form Analysis” plug-in, which is a nifty little code snippet that can, in cases such as these, be a complete nightmare. We actually don’t even give it out anymore unless the customer is working with our Omniture Consulting group so that we can help avoid situations like the one you described. In many, many cases, there are more efficient, more powerful, and just plain better ways to analyze form usage/abandon.

As Andreas pointed out, we’ve made a number of code improvements over the past several years. H.5 code puts you, I believe, in late 2005; even H.17 is almost two years old now. Many of the incremental improvements we’ve made since then help to address some of the issues you mentioned; in any case, I will definitely be sharing your feedback (which was extremely detailed — you obviously know what kind of info developers need!) with our Engineering and Product Management teams.

> Why wasn’t 2o7.net simply converted to run this
> system by default?

That is a great question, and I don’t have the answer presently. I’ll do my best to find out and report back to you. Generally, though, we’ve tried to make the RDC transition as painless as possible.

> I’ve had some bad experiences with Omniture plugins
> (for instance the form issue I cited earlier). Can
> you point me to some documentation for this plugin?

The linkHandler plug-in is fairly innocuous, but I am guessing it would actually be even easier for you to write your own “plug-in” to add an event handler to every link on the page. I did something similar for automatic exit link tracking a few months ago; you can read about it at http://is.gs/6n. (NOTE: WordPress keeps deciding to replace my straight quotes with smart quotes, even inside of a <pre> and a <code>, so if you decide to use the code make sure to locate and change those to straight quotes.)

Anyway, this is a fantastic conversation and, as I said, I will be circulating it around the office. Feel free to let me know if you have any other concerns that I might have missed.

Thanks,
Ben

Ben Gaines’s avatar Ben Gaines

Oh, I forgot to mention your point about multiple installs: I could not agree more. I would love to see this handled by allowing admin users to quickly and easily control the object name that their code is using. Using ‘s’ could still be default, but other options could be made available. While an adept developer can make this change manually, we still recommend letting our ClientCare team generate code when using an object name other than ‘s’. That process could certainly be improved. Action item for me.

Thanks again,
Ben

John’s avatar John

Hey Steve,

Thanks for the insightful post – but I’ve also good disagree with your implementation :)

For many webpages, this script will be attaching hundreds (thousands?) of event handlers. That’s going to be slow.

Plus if you dynamically add any links to the page after the fact, then you’re tracking code isn’t going to run on them.

Much better to use an event delegation approach and attach a single event handler to the document element and then listen for clicks whose target is a link. This is how jQuery’s “live” method works.

http://api.jquery.com/live/

Karl Swedberg also wrote an excellent introduction to the technique on Learning jQuery:

http://www.learningjquery.com/2008/03/working-with-events-part-1

Cheers,

John

Richard Morgan’s avatar Richard Morgan

“Use asynchronous script loading. The Omniture code is pretty large and requires a relatively long initialization time (which can be improved by not obfuscating). Why not use a short setup code with only basic information like account name and config options. Then load and execute the core code after the page has downloaded. This will speed up page loads and make client script easier to maintain. It has the added benefit of making clients think about the config items that they customize.”

— I need to do exactly this. We are now loading all of our js files asynchronously except for s_code.js, and Firebug is showing that even though it’s right before our tag, it is blocking most of our images. Just wondering if there are any pitfalls. Having trouble finding anyone who’s done this before.

Phill’s avatar Phill

Steve, this post was fantasic. I only wish Omniture had included this in there own documentation. I couldn’t find it anywhere…. maybe they need some SEO… ;)

K.Adam White’s avatar K.Adam White

Resurrecting a somewhat old thread to thank you for posting this—I came across your article after implementing a similar system myself, and it was good to have that approach validated by such a thorough article!
The one addendum I will add is to link to my own version of that sb_tracklinks.track() function: I took a slightly different approach that takes a few more options, primarily to use an object-based approach to defining events, eVars and props but also to let you point events at an arbitrary tracking suite for debugging purposes. It’s a little heavier than your code, but for us it improved readability within our jQuery event handlers.
I posted the code at https://gist.github.com/2470993

Jesse Pakin’s avatar Jesse Pakin

I’ve thought about what you’ve got here. I did something very similar but i have a jquery dependency – the reason being i invoke the track and inits through pubsub (jquery events). My thinking was this would be the most unobtrusive implementation. If jquery isn’t available I have bigger fish to fry. :-)

But what do you think about pubsub in general for this task? I know there are jQuery dependency free mechanisms, so if you really want to go dependency free you could try that route.

Hedi’s avatar Hedi

Try This asynchronous Library it can help too!

Audrey’s avatar Audrey

I know this is old, but I’m going to ask anyway:

Any word on an updated version of the concept? You’re one of two people with useful posts on automatic link tracking. We currently use Omniture’s setupLinkTrack plugin, but it also has the “links added after page load” issue. Throw dynamic elements and AJAX into the world of Omniture, and you’ve got a mess.

> …I can rewrite this whole thing to use one event to capture
> every link. It would even fix the “Links added after page load” gotcha”

Tom Rogers’s avatar Tom Rogers

Steven,

Line 4 of your script is pointless and may be deleted.

The idea, of course, is that you don’t want to re-initialize your sb_trackLinks object if it already exists.

However, line 6 explicitly re-declares the entire object, rather than overriding the init, track, and addEvent properties the extant object may already possess.

I think the overall pattern is just fine, it’s just that line 6 guarantees that line 4 is irrelevant in all cases.

That’s it! Thanks for the post. I agree that it can be unnecessarily difficult working with Omniture’s bizarre script API, and I appreciate the time you spent explaining some of the details of your implementation.

Skara Brae’s avatar Skara Brae

Hi Steven,

Thanks for the article, I came across it while implementing a similar thing. I was wondering if you had any thoughts or suggestions on the following.

If you develop a Javascript link tracking library, and that captured data needs to be sent to a remote HTTP endpoint, how would you go about securing the remote endpoint against rubbish data being submitted?

Given it’ll operate from the client’s browser, I was thinking some kind of one-time password whereby the server-side generates a key for each page request that the Javascript uses, and its verified on the remote server… but I haven’t put much thought into it.

If anyone is aware of a servlet filter or some other open source mechanism which does this in a fairly simple fashion I’d be interested in the info.

Frans’s avatar Frans

Hmm… I was reading the discussion between you and the Omniture guy. He said the code would not be obfuscted starting H20.3. So now I have version 1.4.5 (the new AppMeasurement library) and it’s obfuscated again. I hope someone from Omniture/Adobe can comment on this because it’s really disappointing.