/*!
 * jQuery Analytics Library v1.0.3
 * http://www.anthologymarketing.com/
 *
 * Copyright 2011, Evan Nagle, Anthology Marketing Group
 * Licensed under GPL Version 2 license
 * http://www.gnu.org/licenses/gpl-2.0.html
 */
(function($) {
	$.ga = {
		//specify google account info
		//you MUST call $.ga.configure() BEFORE making any other calls
		configure: function(args) {			
			//sanity check
			var eventsHolder = args.events || [];
			var filesHolder = args.files || [];
			var accountsHolder = args.accounts || [];
			var accountHolder = args.account;
			
			args.events = $.ga.events || [];
			args.files = $.ga.files || [];
			args.accounts = $.ga.accounts || [];
			
			if(args.account) {
				delete args.account;
			}
			
			//store args in $.ga
			$.extend($.ga, args);
			
			//merge account and accounts
			if (accountHolder) {
				accountsHolder.push(accountHolder);
			}

			//loop through accounts, and validate
			$.ga.waitForGaq(function() {
				for(var i in accountsHolder) {
					$.ga.addAccount(accountsHolder[i]);
					$.ga.accountsLoaded = true;
				}
			});
				
			//loop through events, and validate 
			for(var i in eventsHolder) {
				$.ga.addEvent(eventsHolder[i]);
			}
			
			//loop through files, and validate
			for(var i in filesHolder) {
				$.ga.addFile(filesHolder[i]);
			}
			
			//fluent response
			return $.ga;
		},
		
		//automatically inserts script tags into document head
		//http://code.google.com/apis/analytics/docs/tracking/asyncTracking.html
		insertScript : function(ops) {
			if ($.ga.accounts) {
				//set gaq variable
				window._gaq = window._gaq || [];
				
				//load script
				var gaSrc = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
				
				if (!$.ga.ajaxLoad) {
					//write scripts to page
					var ga = document.createElement('script'); 
					ga.type = 'text/javascript'; 
					ga.async = $.ga.async;
					ga.src = gaSrc
					
					var s = document.getElementsByTagName('script')[0]; 
					s.parentNode.insertBefore(ga, s);
					
					if (ops.success) {
						$.ga.waitForGaqTracker(ops.success);
					}					
				}
				else {
					$.ajax({
						type: 'GET',
						dataType: 'jsonp',
						url: gaSrc,
						cache: true
					});
					
					if(ops.success) {
						$.ga.waitForGaqTracker(ops.success);
					}
				}
			}
		},
		
		//add multiple google accounts, 
		//creates a new pageTracker per tracker
		addAccounts : function() {
			$.ga.accounts = $.ga.accounts || [];
			
			for(var i = 0; i < arguments.length; i++) {
				$.ga.addAccount(arguments[i]);
			}
			
			return $.ga;
		},
		
		//add a google account
		//creates a single pageTracker
		addAccount : function(account) {
			if(account instanceof Array) {
				return $.ga.addAccounts.apply(null, account);
			}

			var startTracking = function(a) {
				return function() {
					$.ga.accounts[a] = _gat._getTracker(a);
					$.ga.log('account added: ' + a);
					
					if ($.ga.trackPageViews) {
						$.ga.log(a + ' page view tracked');
						$.ga.accounts[a]._trackPageview();
					}						
				}
			};
			
			$.ga.waitForGaqTracker(startTracking(account));
			return $.ga;
		},
		
		//gets default tracker
		//this is the tracker that was first specified
		getDefaultTracker : function()
		{
			for (first in $.ga.accounts) {
				return $.ga.accounts[first];
			}
		},
		
		//get a specifice tracker
		//you can also use the format $.ga.accounts['UA-NNNNNNN-N']...
		getTracker : function(account) 
		{
			if (account) {
				return $.ga.accounts[account];
			}
			else {
				return $.ga.getDefaultTracker();
			}
		},
		
		//perform an action on each tracker
		//in the $.ga.accounts array
		eachTracker: function(fn) {
			for(var i in $.ga.accounts) {
				fn($.ga.accounts[i]);
			}
		},
		
		//add a list of events to the $.ga.events array
		//events with id's can be accessed via $.ga.events[<event id>]
		addEvents : function() {
			$.ga.events = $.ga.events || [];

			for(var i = 0; i < arguments.length; i++) {
				$.ga.addEvent(arguments[i]);
			}
			
			//fluent response
			return $.ga;
		},
		
		//add a single event to the $.ga.events array
		//events with id's can be accessed via $.ga.events[<event id>]		
		addEvent : function(event) {
			if(event instanceof Array) {
				return $.ga.addEvents.apply(null, event);
			}
			
			//if the event object is valid, add it to the events array
			if ($.ga.validateEvent(event)) {
				$.ga.events[event.id] = event;
			}
			
			//add listener
			$.ga.setEventListener(event);
			
			//fluent response
			return $.ga;			
		},
		
		//for an event to be added to the $.ga.events array,
		//it must contain all of the necessary properties.
		//if any properties are missing, an error will be logged to the console,
		//and the event will NOT be added to $.ga.events
		validateEvent : function(event) {
			var isValid = true;
			
			//category is required
			if (!event.category) {
				if(event.id) {
					$.ga.log('error adding ' + event.id);
				}
				
				$.ga.log('	category required for event');
				isValid  = false;
			}
			if (!event.action) {
				if(event.id) {
					$.ga.log('error adding ' + event.id);
				}
						
				$.ga.log('	action required for event');
				isValid = false;
			}
			if (!event.selector) {
				event.selector = event.$;
			}
			if (isValid) {
				//generate id
				id = event.id || $.ga.guid();
				event.timesFired = event.timesFired || 0;
				event.isActive = typeof(event.isActive) == 'undefined' ? true : event.isActive;
			}
			
			return isValid;
		},
		
		//loop through list of events in the $.ga.events array;
		//bind those events to user-generated page events.
		setEventListener: function(handler) {
			$(function() {
				$.ga.waitForGaq(function() {
					if (handler.selector) {
						$(handler.selector).bind(handler.event || 'click',  { event : handler }, function(e) {
							$.ga.fireEvent($(this), e.data.event);
						});
					}
				});
			});
		},
		
		//add a list of files to the $.ga.files array
		//files with id's can be accessed via $.ga.files[<file id>]
		addFiles : function() {
			$.ga.files = $.ga.files || [];

			for(var i = 0; i < arguments.length; i++) {
				$.ga.addFile(arguments[i]);
			}
			
			//fluent response
			return $.ga;
		},
		
		//add a single file to the $.ga.files array
		//files with id's can be accessed via $.ga.files[<file id>]		
		addFile : function(file) {
			if(file instanceof Array) {
				return $.ga.addFiles.apply(null, file);
			}
			
			//if the file object is valid, add it to the files array
			if ($.ga.validateFile(file)) {
				$.ga.files[file.id] = file;
			}
			
			//add listener
			$.ga.setFileListener(file);
			
			//fluent response
			return $.ga;			
		},
		
		//for an file to be added to the $.ga.files array,
		//it must contain all of the necessary properties.
		//if any properties are missing, an error will be logged to the console,
		//and the file will NOT be added to $.ga.files
		validateFile : function(file) {
			var isValid = true;
			
			//category is required
			if (!(file.extension || file.ext || file.pattern)) {
				if(file.id) {
					$.ga.log('error adding file ' + file.id);
				}
				
				$.ga.log('	extension or pattern is required.');
				isValid  = false;
			}
			if (isValid) {
				//generate id
				file.isFile = true;
				file.id = file.id || $.ga.guid();
				file.timesFired = file.timesFired || 0;
				file.isActive = typeof(file.isActive) == 'undefined' ? true : file.isActive;
				//file.maxFires = file.maxFires || 1;
			}
			
			return isValid;
		},
		
		//loop through list of files in the $.ga.events array;
		//bind those events to user download clicks.
		setFileListener: function(handler) {
			$(function() {
				$.ga.waitForGaq(function() {
					var selector = handler.selector || 'a[href*="' + (handler.extension || handler.ext) + '"]';
					$(selector).bind('mousedown keydown', { file: handler }, function(e) {		
						var file = e.data.file;
						file.category = file.category || 'downloads';
						file.action = file.action || $(this).attr('href');
						file.$el = $(this);
						$.ga.fire(file);
					});
				});
			});
		},
		
		internalLinkPattern : new RegExp("^(https?):\/\/" + window.location.host, "i"),
		
		setOutgoingLinkListeners: function() {
			//modeled after Drupal google analytics module
			//http://drupal.org/project/google_analytics
			//and: http://css-tricks.com/track-outgoing-clicks-in-google-analytics-with-jquery/
			$('a:not(.nofollow)').bind('click keypress', function(event) {
				var $this = $(this);
				var href = $this.attr('href');			
				
				if (href && (!window.location.host || !$.ga.internalLinkPattern.test(href))) {
					try {
						if ($this.is("a[href^=mailto:],area[href^=mailto:]")) {
							if ($.ga.trackMailToLinks) {
							  // Mailto link clicked.
							  $.ga.fire({
								category: 'Mails',
								action: 'Click',
								label: href
							  });
							}
						}
						else if ($.ga.trackOutgoingLinks && href.substring(0,4) == 'http') {
						  // External link clicked.
						  $.ga.fire({
							category: 'Outgoing links',
							action: 'Click',
							label: href
						  });
						}	
					}
					catch (error) {
						$.ga.log('eror tracking outgoing link: ' + href);
					}
				}
			});
		},
		
		//manually fire an event (or events)
		//should only be called AFTER the document is ready.
		//this function can be used to fire a single event: $.ga.fire('<event id>');
		//it can be used to fire an anonymous event: $.ga.fire({ action: 'actionValue', label:'labelValue', <etc.>});
		//it can be used to fire all of the events associated with a particular element: $.ga.fire($('<selector>'));
		//it can be used to fire multiple events in one call: $.ga.fire(<item 1>, <item 2>, <etc.>);
		fire : function(el) {
			if (arguments.length > 1) {
				//convert the arguments into an array, and recurse
				$.ga.fire([].splice.call(arguments,0));
			}
			else if (el.constructor == Array) {
				//loop through items in array, recurse, call fire per item
				for(var i in el) {
					var item = el[i];
					$.ga.fire(item);
				}
			}
			else {
				switch (typeof(el)) {
					case 'function' : 
						//fire the function, recurse, call fire on the returned value
						$.ga.fire(el());
						break;
					case 'string' :
					case 'number' :
						//fire the event with the given id
						if ($.ga.events[el]) {
							$.ga.fireEventById(el);
						}
						break;
					case 'object' : 
						if (el instanceof jQuery) {
							//fire all of the events associated with this particular element
							$.ga.fireTrackingForElement(el);
							break;
						}
						else if ($.ga.validateEvent(el)) {
							//fire an anonymous event
							$.ga.fireEvent(el.$el, el);
							break;
						}
					default:
						//log error
						$.ga.log('no suitable firing event found for passed argument.');
				}
			}
			
			//fluent response
			return $.ga;
		},
		
		//fire an event, push event tracking data to google.
		//only tracks event if all event-based conditionals are met.
		fireEvent : function(el, event) {		
			//this data will be passed back to functions in the events object
			//allows for easy configuration and hooking
			var data = {
				el: el,
				$el: $(el),
				event: event,
				location: window.location,
				absolutePath: window.location.href, 
				relativePath: window.location.pathname,
				query: window.location.search
			};
			
			if ((event.isActive) && 
				(!event.maxFires || (event.timesFired - (event.minEvents || 0) + 1) < event.maxFires || event.ignoreFire) &&
				(!event.bind || event.bind(data)) &&
				(!event.urlPattern || window.location.pathname.match(event.urlPattern))) {

				//add tick to counter
				event.timesFired++;
				
				//min number of fires not yet met
				if (event.minEvents && event.timesFired < event.minEvents) {
					return;
				}
			
				//convert functions into data
				var curEvent = $.ga.handleEventFns(event, data);
				
				//update data event prior to hooking
				data.event = curEvent;
				
				//fire hook function
				//allows user-specified overriding
				if (event.hook) {
					if (!event.hook(data)) {
						return;
					}
				}
				
				//add metadata to jquery element
				if (el && el instanceof jQuery) {
					var metadata = el.data('eventTracking');
					if (!metadata) {
						metadata = [];
					}
					
					//add event to metadata
					metadata.push({
						event : event,
						stamp : new Date().getTime()
					});

					//update metadata
					el.data('eventTracking', metadata);
				}
							
				var firer = (function(e) {
					return function() {
						$.ga.waitForAccount(e.account, function(t) {
							if (!e.label && !e.value)
							{
								t._trackEvent(e.category, e.action);
							}
							else if (!e.value)
							{
								t._trackEvent(e.category, e.action, e.label);
							}
							else
							{
								t._trackEvent(e.category, e.action, e.label, e.value);
							}
							
							//TODO: remove log calls prior to production
							//log('$.ga: [' + e.category + ', ' +  e.action + ', ' + e.label + ', ' + e.value + ']');	
							$.ga.log('event tracked: ' +  t._getAccount() + ' [' + e.category + ', ' + e.action + ', ' + (e.label || 'none') + ', ' + (e.value || 'none') + ']');						
						});
					}
				})(curEvent);
				
				//wait for gaq, push out the event
				$.ga.waitForGaqTracker(firer);
				
				//fire success event
				if(event.success) {
					event.success();
				}
			}		
		},
		
		//force a pageview to fire
		//loads a hidden iframe on the current page
		//iframe must contain necessary script tags
		firePageView : function(url) {
			$(function() {
				var iframe = $('<iframe />');
				iframe.attr('src', url);
				iframe.css('width', '1px');
				iframe.css('height', '1px');
				
				$('body').append(iframe);
			});
		},
		
		//fire ALL of the events associated with a single jQuery object
		fireTrackingForElement : function(element) {
			for(var i in $.ga.events) {			
				var event = $.ga.events[i];
				var jQueryObjs = $(event.selector);
					
				for(var j in element.toArray()) {
					var htmlObj = element[j];
					
					if ($.inArray(htmlObj, jQueryObjs.toArray()) >= 0) {
						$.ga.fireEvent(element, event);
						continue;
					}
				}
			}
		},
		
		//fire the event with a given id
		fireEventById: function(id, countAsFire) {
			var event = $.extend($.ga.events[id], {
				ignoreFire: countAsFire
			});
			
			//get jQuery element
			var el = event.selector? $(event.selector) : null;
			
			//fire event
			$.ga.fireEvent(el, event);
		},
		
		//fire fn when _gaq is registered
		waitForGaq : function(fn, attemptsLeft) {
			//100 = default number of attempts
			//i.e. 10 seconds of async waiting, max
			var tick = attemptsLeft || 30; 
		
			if (typeof(_gaq) != 'object' || !_gaq._getAsyncTracker) {
				//_gaq isn't registered yet
				if (tick > 1) {
					//recurse
					setTimeout(function() {
						$.ga.waitForGaq(fn, tick - 1);
					}, 100)
				}
				else {
					//no ticks left, log error
					$.ga.log('failed to load window.gaq');
				}
			}
			else {
				//gaq is loaded, fire fn
				fn();
			}
		},
		
		//wait for trackers to be added to $.ga.accounts
		waitForGaqTracker : function(fn, attemptsLeft) {
			var tick = attemptsLeft || 30;
			
			if (!$.ga.accountsLoaded) {
				if (tick > 1) {
					//recurse
					setTimeout(function() {
						$.ga.waitForGaqTracker(fn, tick - 1);
					}, 100);		
				}
				else {
					//no ticks left, log error
					$.ga.log('failed to load window.gat');
				}
			}
			else {
				//gaq trackers are laoded, fire fn
				fn();
			}
		},

		waitForAccount : function(account, fn, attemptsLeft) {
			var tick = attemptsLeft || 30;
			
			if(!account) {
				var t = $.ga.getDefaultTracker();
				if (t) {
					account = t._getAccount();
				}
			}

			if (!$.ga.accounts[account]) {
				if (tick > 1) {
					//recurse
					setTimeout(function() {
						$.ga.waitForAccount(account, fn, tick - 1);
					}, 100);
				}
				else {
					$.ga.log('failed to find account: ' + account);
				}
			}
			else {
				fn($.ga.accounts[account]);
			}
		},
		
		//converts fns in an event object into usable values
		handleEventFns : function(event, data) {
			var curEvent = new Object();
		
			for(var i in event) {
				var property = event[i];
			
				if (typeof(property) == 'function' && i != 'hook' && i != 'success' && i != 'bind') {
					curEvent[i] = property(data);
				}
				else {
					curEvent[i] = property;
				}
			}
			
			return curEvent;
		},
		
		//deactivate an event
		deactivate : function(id) {
			($.ga.events[id] || {}).isActive = false;
		},
		
		//activate an event
		activate : function(id) {
			($.ga.events[id] || {}).isActive = true;
		},
		
		//create a unique identifier
		guid : function() {
			var S4 = function() {
				return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
			}

			return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
		},
		
		pluginFns : [],
		
		plugin : function(p) {
			if(p.init) {
				$.ga.pluginFns.push(p.init);
				delete p.init;
			}
		
			$.extend($.ga, p);
		},
		
		//log events to the console
		log : function(a) {
			if ($.ga.writeToLog || $.ga.debug) {
				log('$.ga ' + a);
			}
		}
	}
	
	$(function() {
		//insert script tag, track pageviews
		$.ga.insertScript({
			success: function(){
				$.ga.setOutgoingLinkListeners();
				for(var i = 0; i < $.ga.pluginFns.length; i++) {
					$.ga.pluginFns[i]();
				}
			}
		});
	});
	
	$.extend($.fn, {
		track : function(ops) {
			if (!ops) {
				return $.ga.fireTrackingForElement($(this));
			}
			
			return this.each(function() {
				ops.selector = ops.selector || $(this);
				$.ga.addEvent(ops);
			});
		},
		eventsTracked : function() {
			return $(this).data('eventTracking');
		}
	});
	
	window.log = function(){
	  log.history = log.history || []; 
	  log.history.push(arguments);
	  if(this.console){
		console.log( Array.prototype.slice.call(arguments) );
	  }
	};
})(jQuery);

