
/**
 * GAIN - Google Analytics In oNe - 
 * ========================================
 * Version 1
 * 
 * Sets up tracking for pageviews and outbound(offsite) links
 * 
 * Note: you need to put you Analytics ID at the bottom of this script
 */


/**
 * onDOMLoad Javascript Function
 * Author: Ross Illingworth
 * URL: www.techmale.com
 * License: GPL.
 * This takes the work from Dean Edwards,Matthias Miller & John Resig,
 * at http://dean.edwards.name/weblog/2006/06/again/#comment5338 and
 * makes it into a useable function for all browsers.
 */

// SafariTimer: holds the id of the timer for safari browser
window.sFT = false;
//DOMLoadFunctions: Array of functions to run when DOM is loaded
window.onDLFs = [];
// Function to call when onDOMload activates
window.callDLFs = function(){
    // check if we have already been here.
    if (arguments.callee.done) {
        //alert("onDOMLoad Functions already run");
        return;
    }
    arguments.callee.done = true;
    // Stop safariTimer if Active
    if (sFT) {
        clearInterval(sFT);
    }
    //Now loop through functions and call them
    var func;
    for (i in onDLFs) {
        if (onDLFs.hasOwnProperty(i)) {
            if (typeof(func = onDLFs[i]) == "function") {
                //alert(func +" = "+ typeof func);
                func.apply(document);
            }
        }
    }
};

// MAIN Function
function onDOMLoad(func){
    // add your function to the function array
    onDLFs.push(func);
    // check if we have already done the setup
    if (arguments.callee.done) {
        //alert("Already setup doOnDOMLoaded");
        return;
    }
    arguments.callee.done = true;
    
    //this will work on Firefox or IE
    if (document.addEventListener) {
        //FOR FIREFOX
        //alert("Using addEventListener");
        document.addEventListener("DOMContentLoaded", callDLFs, false);
    }else if (document.all && !window.opera) {
        //FOR IE
        document.write('<script type="text/javascript" id="contentloadtag" defer="defer" src="javascript:void(0);"><\/script>');
        var contentloadtag = document.getElementById("contentloadtag");
        contentloadtag.onreadystatechange = function(){
            if (this.readyState == "complete") {
                //alert("Using readystate");
                callDLFs.apply(document);
            }
        };
    }
    
    // THIS WORKS on SAFARI
    if (/Safari/i.test(navigator.userAgent)) {
        sFT = setInterval(function(){
            if (/loaded|complete/.test(document.readyState)) {
                clearInterval(sFT);
                //alert("Using Safari");
                // call target function
                callDLFs.apply(document);
            }
        }, 10);
    }
    
    //and finally if all else fails, use onload
    // first check if anything has been set up previously
    var old = (window.onload) ? window.onload : function(){
    };

    window.onload = function(e){
        //alert("Now calling onLoad"); 
        old(e);
        callDLFs();
    };
}


/**
 * This is required to add the forEach method to Arrays,
 * if it is missing from the javascript implementation
 * This version comes directly from the Mozilla foundation
 */
//##############################
if (!Array.prototype.forEach) {
    //  console.log("Adding forEach");
    Array.prototype.forEach = function(fun /*, thisp*/){
        var len = this.length;
        if (typeof fun != "function") 
            throw new TypeError();
        
        var thisp = arguments[1];
        for (var i = 0; i < len; i++) {
            if (i in this) 
                fun.call(thisp, this[i], i, this);
        }
    };
}
//##############################
/**
 * Add Filter function if missing
 */
if (!Array.prototype.filter) {
    Array.prototype.filter = function(fun /*, thisp*/){
        var len = this.length;
        if (typeof fun != "function") {
            throw new TypeError();
        }
        var res = new Array();
        var thisp = arguments[1];
        for (var i = 0; i < len; i++) {
            if (i in this) {
                var val = this[i]; // in case fun mutates this
                if (fun.call(thisp, val, i, this)) 
                    res.push(val);
            }
        }
        return res;
    };
}
//##############################


/**
 * Repeat a function after a set length of time
 * This allows a function to check if required conditions are met
 * and call itself again after a set period of time
 * @param {Object} time_ms
 * @param {Object} scope [Optional]
 */
Function.prototype.repeat = function(time_ms, scope){
    var func = this;
    var args = this.arguments || [null];
    var f = function(){
        TM.log("now calling: " + func);
        func.apply(scope, args);
    };
    TM.log("setting  " + func + " repeat to " + time_ms);
    setTimeout(f, time_ms, window);
}


TM = {
    /**
     * An Predefined Empty function
     */
    empty: function(){
    },
    /**
     * A basic logging function
     * @param {Object} msg
     */
    log: function(msg){
        if (TM.debug) {
            if (window["console"]){
				console.log(msg);
			}else {
	            window.status = msg;
	        }   
        }
    },
    debug: false,
    /**
     * Function To load a Script asyncronusly
     * This massively reduces page load times
     * @param {Object} path
     * @param {Object} type
     */
    require: function(path, type){
        type = type || "SCRIPT";
        var d = document.createElement(type);
        d.src = path;
        d.onload = function(){
            TM.log('script loaded ' + path);
        };
        // ADD SCRIPT to document
        var header = document.getElementsByTagName("HEAD")[0];
        header.appendChild(d);
        TM.log("added SCRIPT ELEMENT to DOC: " + path);
    },
    /** 
     * Binds a function to an object and wraps it in a new function
     * suggested by www.alistapart.com/articles/getoutbindingsituations
     * this can easily be extended to allow currying
     * @param {Object} object
     * @param {Object} method
     */
    createBoundWrapper: function(object, method){
        return function(){
            return method.apply(object, arguments);
        };
    },
    /**
     * Container for Regular Expression Functions
     */
    REGEX: {
        protocol: new RegExp("^[^:]+://"),
        currentHost: new RegExp(document.location.hostname)        
		//currentHost: new RegExp("epikone.com")
        //currentHost: new RegExp(/epikone\.com/)
        ,
		// Chris Egner 2008.10.21
		download: new RegExp("\.(7z|aac|avi|csv|doc|exe|flv|gif|gz|jpe?g|js|mp(3|4|e?g)|mov|pdf|phps|png|ppt|rar|sit|tar|torrent|txt|wma|wmv|xls|xml|zip)$"),
        /**
         * Generate a FILTER Function
         * Tests that regex against objectProperty equals requiredState
         * @param {Object} regex
         * @param {Object} objectProperty
         * @param {Object} requiredState
         */
        def_RegexTestFilterForObjectProperty: function(regex, objectProperty, requiredState){
            return function(el, ind, arr){
                if (!el[objectProperty]) {
                    TM.log("Object " + el + " is missing property " + objectProperty);
                    return false;
                }
                return (regex.test(el[objectProperty]) == requiredState);
            }
        }
    },
    /**
     * Container for DOM functions
     */
    DOM: {
        /**
         * Utility function to convert a collection object to an Array
         * Warning: asyncronus removal of elements will cause array to be incorrect
         * @param {Object} collection to convert
         */
        collectionToArray: function(c){
            var a = [];
            for (var i = 0, len = c.length; i < len; i++) 
                a[a.length] = c[i];
            return a;
        },
        /**
         * Utility function: get an element collection array
         * @param {Object} tagName
         */
        getElementsArrayByTagName: function(tagName){
            var collection = document.getElementsByTagName(tagName);
            var arr = TM.DOM.collectionToArray(collection);
            return arr;
        }
    }
};



/*****************************************************************************
 * This is the code that controls the tracking insertion
 * As you can see it is very short and very tight
 * ==============================================
 * This is an example of Functional Programming:
 *   where you use one function to create another,
 *   and then pass that function to a third
 * Functional programming is like writing a recipe compared to writing detailed instructions
 *
 * This style of programming also allows you to dynamically create most of your required functions
 * It means shorter code that is faster to load and easier to understand.
 *
 * Get the complete Functional Programming Libary from TechnicalMagic.com
 ********************************************************************************/
TM.GOOGLE = {
    OUTBOUND:{
        /**
         * The main function to call to insert outbound link tracking
         */
        trackLinks: function(){
            // get all links from the page as an array
			// filter the links so we only have outbound links
            // firstly: only if "http://" is present, as this is required by external links
            // secondly: remove all links with the current hostname in the link, as these are local
			// then loop through the remaining links and make each link trackable
			var allLinks = TM.DOM.getElementsArrayByTagName("a");
            var fullLinks = allLinks.filter(TM.REGEX.def_RegexTestFilterForObjectProperty(TM.REGEX.protocol, "href", true));
            var outboundLinks = fullLinks.filter(TM.REGEX.def_RegexTestFilterForObjectProperty(TM.REGEX.currentHost, "href", false));
            outboundLinks.forEach(TM.GOOGLE.OUTBOUND.makeTrackable);
            TM.log(outboundLinks.length + " links modified");
        },
        /**
         * Take a link and add tracking code
         * Uses onMouseDown, as it is faster, because it works before the page is changed
         * If we used onclick instead, the page could change before the data is sent
         * @param {Object} el
         * @param {Object} ind
         * @param {Object} arr
         */
        makeTrackable: function(el, ind, arr){
            var oldFunc = (typeof el.onmousedown === 'function') ? TM.createBoundWrapper(el, el.onclick) : TM.empty;
            el.onmousedown = TM.GOOGLE.OUTBOUND.def_TrackableLinkFunc(oldFunc, el.href);
            //el.style.color = TM.GOOGLE.OUTBOUND.color;
        },
		color:"",
        /**
         * This is an example of dynamically creating a function, ie: Functional Programming
         * We create a function that will send the right info to google when called
         * ## NOTE ## pageTracker must already exist for this to work
         * @param {Object} oldFunc
         * @param {Object} href
         */
        def_TrackableLinkFunc: function(oldFunc, href){
            return function(){
                // check if pageTracker exists
                if (window["pageTracker"]) {
                    // remove "http://" from link url
					// log outbound link to google
                    // call pre-existing onmousedown function
                    href = href.replace(TM.REGEX.protocol, "");
                    pageTracker._trackPageview('/outbound/' + href);
                    oldFunc();
                }
                else {
                    TM.log("pageTracker is UnAvailable");
                }
            };
        }
    },
	
	// Chris Egner 2008.10.21
	FILE:{
		trackFiles: function() {
			var allLinks = TM.DOM.getElementsArrayByTagName("a");
            var fileDownloadLinks = allLinks.filter(TM.REGEX.def_RegexTestFilterForObjectProperty(TM.REGEX.download, "href", true));
            fileDownloadLinks.forEach(TM.GOOGLE.FILE.makeTrackable);
		},
		makeTrackable: function(el, ind, arr){
            var oldFunc = (typeof el.onmousedown === 'function') ? TM.createBoundWrapper(el, el.onclick) : TM.empty;
            el.onmousedown = TM.GOOGLE.FILE.def_TrackableLinkFunc(oldFunc, el.href);
        },
		def_TrackableLinkFunc: function(oldFunc, href){
            return function(){
                if (window["pageTracker"]) {
                    pageTracker._trackPageview(href);
                    oldFunc();
                }
                else {
                    TM.log("pageTracker is UnAvailable");
                }
            };
        }
	},
    
    /**
     * Track this page in Google
     * Also sets up pageTracker, 
     * so needed for outBound link tracking
     * @param {Object} UID
     */
    trackPageView: function(){
        if(TM.GOOGLE.googleID === "empty"){
			return;
		}
		if (!TM.GOOGLE.isAnalyticsLoaded()) {
            TM.log("trackPageView waiting for _gat");
            TM.GOOGLE.trackPageView.repeat(500);
            return;
        }
        TM.log("Tracking Page, UID:" + TM.GOOGLE.googleID);
		pageTracker = _gat._getTracker(TM.GOOGLE.googleID);
        pageTracker._setLocalRemoteServerMode();
        pageTracker._initData();
        pageTracker._trackPageview();
    },
	googleID:"empty",
    /**
     * Load the analytics SCRIPT
     */
    loadAnalytics: function(){
        var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
        var path = gaJsHost + "google-analytics.com/ga.js";
        TM.require(path);
    },
	/**
	 * Check if Analytics is loaded
	 */
	isAnalyticsLoaded:function(){
		return (window["_gat"])?true:false;
	}	
};

/**
 * ########################################################################
 * Edit the code below to modify 
 * ########################################################################
 */

/**
 * Turn on debugging output
 */
TM.debug = false;
/**
 * Sets the color of the outbound links
 */
TM.GOOGLE.OUTBOUND.color = "";
/**
 * Put your google analytics ID here
 */
TM.GOOGLE.googleID = "UA-825956-2";

onDOMLoad(TM.GOOGLE.loadAnalytics);
onDOMLoad(TM.GOOGLE.trackPageView);
onDOMLoad(TM.GOOGLE.OUTBOUND.trackLinks);
onDOMLoad(TM.GOOGLE.FILE.trackFiles);