// ==UserScript==
// @name           Google Analytics Downloader
// @namespace      Analytics
// @include        https://www.google.com/analytics/reporting/referring_sources?*
// @include        https://www.google.com/analytics/reporting/keywords?*
// ==/UserScript==

//Developer: Sal Uryasev at Juice Analytics (juiceanalytics.com)


//change the configuration for each of the two sites respectively
if(window.location.href.match('referring_sources?'))
{
    //max table display limit
    display_limit = 200
    // how much growth is necessary before the element is considered to have had enough of a traffic increase
    growth_tolerance = .5;
    //minimum number of elements in order to be considered as a new referrer/keyword
    minimum_number_elements = 1
    //limit is the number of elements that can be pulled; larger numbers will slow down script, but will have results for less common keywords
    limit = 10000;
    // how many days you want to compare to the rest of the range
    look_back = 3;
}
else if(window.location.href.match('keywords?'))
{
    //max table display limit
    display_limit = 200
    // how much growth is necessary before the element is considered to have had enough of a traffic increase
    growth_tolerance = .2;
    //minimum number of elements in order to be considered as a new referrer/keyword
    minimum_number_elements = 10
    //limit is the number of elements that can be pulled; larger numbers will slow down script, but will have results for less common keywords
    limit = 1000;
    // how many days you want to compare to the rest of the range
    look_back = 7;
}

//minimum number of referals per day in order for a site to be considered in the growth category
growth_per_day_treshold = 1;

//END OF SYSTEM CONFIGURATION
//edit code below at your own risk

//Determine what site you're on
//Add specific limitations to individual sites to override defaults if necessary
if(window.location.href.match('referring_sources?'))
{
    site_ref = 'referring_sources'
    download_report_ref = 'ReferringSourcesReport'
    label_ref = 'Referring Sites'
    site_link_ref = '<a target="GA_LINKER" href="http://nnnamenn"><img border="0" title="View this link" alt="View this link" src="/analytics/reporting/images/icons/url_icon.gif"></a>nnnamenn</a>'
}
else if(window.location.href.match('keywords?'))
{
    site_ref = 'keywords'
    download_report_ref = 'KeywordsReport'
    label_ref = 'Keywords'
    site_link_ref = 'nnnamenn'
}

//Cleanse out google date reference in case there is a refresh
document.getElementById('f_primaryBegin').value = "";
document.getElementById('f_primaryEnd').value = "";

//Display the button
var tab_element;
tab_element = window.document.createElement ("SPAN");
tab_html = '<br><br><div><div><div class="insert"><div class="insert_1"><button> Who sent me unusual traffic? </button><dd></dd></div></div></div></div>';  //html for button
loading_html = '<br><br><div><div><div class="insert"><div class="insert_1"><button> Loading... </button><dd></dd></div></div></div></div>';   //substitute with loading button
final_html = '<br><br><div><div><div></div></div></div><br>'
tab_element.innerHTML = tab_html;
tab_element.addEventListener("click", dostuff, true);
html_insert_it(document.evaluate('/HTML[1]/BODY[1]/DIV[1]/DIV[2]/DIV[1]/DIV[9]/DIV[1]', 
                document, null, XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue,tab_element);

//HTML for the head of the data table
var head_string = (<r><![CDATA[
    <div id="Table">
		<table class="records table_view" id="f_table_data">
			<thead>
				<tr>
					<th colspan="2" class="text">
						Source
					</th>
					<th class="" id="f_column_0">
						<u>Visits</u>
					</th>
					<th class="" id="f_column_1">
						<u>Pages/Visit</u>
					</th>
					<th class="" id="f_column_2">
						<u>Avg. Time on Site</u>
					</th>
					<th class="" id="f_column_3">
						<u>% New Visits</u>
					</th>
					<th class="" id="f_column_4">
						<u>Bounce Rate</u>
					</th>
				</tr>
			</thead> ]]></r>).toString();

//HTML for the body of the data table
//nnnamenn, nnnum1nn, etc. are used as block indicators to insert data			
var body_string = (<r><![CDATA[ <tbody class="">
				<tr class="highlight">
					<td class="count">
						nncountnn.
					</td>
					<td class="text">
						<div title="nnnamenn" class="text_wrapper">
							<div class="text_wrapper">
								kksite_link_refkk
							</div>
						</div>
					</td>
					<td id="f_cell_0_0" class="sort">
						nnnum1nn
					</td>
					<td id="f_cell_0_1" class="">
						nnnum2nn
					</td>
					<td id="f_cell_0_2" class="">
						nnnum3nn
					</td>
					<td id="f_cell_0_3" class="">
						nnnum4nn
					</td>
					<td id="f_cell_0_4" class="">
						nnnum5nn
					</td>
				</tr> ]]></r>).toString();
//add variations for different types of sites
body_string = body_string.replace('kksite_link_refkk', site_link_ref)

//HTML for the end of the data table				
var tail_string = (<r><![CDATA[	</div>
]]></r>).toString();

function increment_date(datum, days_to_increment)
{
    //change a google analytics date string by a certain number of days
    one_day = 1000*60*60*24;
    dyear = datum.toString().substring(0,4);
    dmonth = datum.toString().substring(4,6);
    dday = datum.toString().substring(6,8);
    obj_datum = new Date(dyear, dmonth - 1, dday);
    final_datum = new Date();
    final_datum.setTime( obj_datum.getTime() + ( days_to_increment * one_day ) );
    return (10000 * (final_datum.getYear() + 1900) + 100 * (final_datum.getMonth() + 1) + final_datum.getDate());
}

function count_days(datum1, datum2)
{
    //calculate difference in days between two google analytics dates
    one_day = 1000*60*60*24;
    dyear = datum1.toString().substring(0,4);
    dmonth = datum1.toString().substring(4,6);
    dday = datum1.toString().substring(6,8);
    obj_datum1 = new Date(dyear, dmonth - 1, dday);
    dyear = datum2.toString().substring(0,4);
    dmonth = datum2.toString().substring(4,6);
    dday = datum2.toString().substring(6,8);
    obj_datum2 = new Date(dyear, dmonth - 1, dday);
    return (Math.round( ( obj_datum1.getTime() - obj_datum2.getTime() ) / one_day ) );
}

function date_to_string(datum)
{
    //convert mm/dd/yyyy to yyyymmdd
    var re = new RegExp(/(\d{2})\/(\d{2})\/(\d{4})/);
    vals = re.exec(datum);
    return (vals[3] + vals[1] + vals[2]);
}

function convert_array(arr)
{
    //convert the raw data from the CSV file into the format found on webpage
    out_arr = new Array();
    out_arr[0] = arr[0];
    out_arr[1] = arr[1];
    out_arr[2] = Number(arr[2]).toFixed(2).toString();
    min = Math.round((arr[3] / 60) - ((arr[3] % 60)/60));
    if (min >= 10)
    {str_min = min.toString();}
    else
    {str_min = "0" + min.toString();}
    sec = Math.round(arr[3] % 60);
    if (sec >= 10)
    {str_sec = sec.toString();}
    else
    {str_sec = "0" + sec.toString();}
    out_arr[3] = "00:" + str_min + ":" + str_sec;
    out_arr[4] = Number(arr[4] * 100).toFixed(2).toString() + '%';
    out_arr[5] = Number(arr[5] * 100).toFixed(2).toString() + '%';
    return out_arr;
}

function html_insert_it(element, new_element) {
    //shortcut function to insert html before an element
  element.parentNode.insertBefore(new_element, element);
};
function html_insert_after(element, new_element) {
    //shortcut function to insert html after an element
  element.parentNode.insertBefore(new_element, element.nextSibling);
};

function dostuff()
{
    //main function that is called if button is clicked
    
    //Google Analytics uses a different structure to record dates if they have been changed via the date app upon the page
    //urlstartdate and urlenddate refer to the dates referenced in the URL
    //startdate and enddate refer to the dates whether referenced in the URL or defined with javascript
    var startdate, enddate, urlstartdate, urlenddate, olddate, newdata;
    tab_element.innerHTML = loading_html;    
    //pull out urlstartdate/urlenddate    
    var date1 = new RegExp(/(\d{8})/);
    var date2 = new RegExp(/(\d{8})&/);
    urlstartdate = date1.exec(window.location.href)[1];
    urlenddate = date2.exec(window.location.href)[1];
    var changeddate1 = document.getElementById('f_primaryBegin').value;
    var changeddate2 = document.getElementById('f_primaryEnd').value;
    if (changeddate1)
    {
        startdate = date_to_string(changeddate1);
        enddate = date_to_string(changeddate2);
    }
    else
    {
        startdate = urlstartdate;
        enddate = urlenddate;
    }
    middate = increment_date(enddate, -1 * look_back);
    lentime = count_days(middate, startdate);
        //request the CSV file download and pull it in as a string
        //two files are downloaded: one for the full dataset and one for the full dataset minus the last some days
        GM_xmlhttpRequest({
            method: 'GET',
            url: (window.location.href.replace(site_ref + '?', 'export?fmt=2&')).replace(urlenddate, middate).replace(urlstartdate, startdate) + '&limit=' + limit.toString() + '&&rpt=' + download_report_ref,
            headers: {
                'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',
                'Accept': 'application/atom+xml,application/xml,text/xml',
            },
            onload: function(responseDetails) {
                //wait till the file is downloaded, and execute this when ready
                olddata = responseDetails.responseText;
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: (window.location.href.replace(site_ref + '?', 'export?fmt=2&')).replace(urlenddate,enddate).replace(urlstartdate,startdate) + '&limit=' + limit.toString() + '&&rpt=' + download_report_ref,
                    headers: {
                        'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',
                        'Accept': 'application/atom+xml,application/xml,text/xml',
                    },
                    onload: function(responseDetails) {
                        var trunkoldlist = new Array();
                        var trunkfulllist = new Array();
                        var newlinkers = new Array(); 
                        var biggrowers = new Array();
                        var bigshrinkers = new Array();
                        fulldata = responseDetails.responseText;
                        oldlist = olddata.split(/\n/g);
                        fulllist = fulldata.split(/\n/g);
                        //trim the downloaded files to only leave the data
                        //trunkfulllist and trunkoldlist will contain arrays of strings
                        //fulldata
                        for (i=0;1;i++)
                        {
                            if (fulllist[i].substr(0,7)=='# Table')
                            {
                                stopper = i+3;
                                break;
                            }
                        }
                        for (i=stopper;1;i++)
                        {
                            if (fulllist[i].substr(0,7)=='# -----')
                            {break;}
                            trunkfulllist[i-stopper] = fulllist[i];
                        }
                        //olddata
                        for (i=0;1;i++)
                        {
                            if (oldlist[i].substr(0,7)=='# Table')
                            {
                                stopper = i+3;
                                break;
                            }
                        }
                        //get width of data to do comparisson later
                        data_width = oldlist[stopper-1].split(',').length
                        for (i=stopper;1;i++)
                        {
                            if (oldlist[i].substr(0,7)=='# -----')
                            {break;}
                            trunkoldlist[i-stopper] = oldlist[i];
                        }
                        //cut out the names of all the networks; doing this ahead of time increases efficiency
                        //also check for any unusual rows (odd numbers of elements) and drop them
                        //also drop everything that is smaller than the minimum number of elements to increase efficiency
                        trunkoldnames = new Array();
                        for (a in trunkoldlist)
                        {
                            arr = trunkoldlist[a].split(',')
                            if (arr.length == data_width && arr[1]>=minimum_number_elements)
                            {
                                trunkoldnames.push(arr[0]);
                            }
                            else
                            {
                                trunkoldnames.push("")
                            }
                        }
                        //loop through the full list and see what elements are missing in the old list, and what elements have grown by more than the required percent
                        for (a in trunkfulllist)
                        {   
                            k=0;
                            tfularr = trunkfulllist[a].split(',');
                            if (tfularr.length == data_width && tfularr[1]>=minimum_number_elements)   //test to see if csv row is the right length, and if this qualifies due to minimum_number_elements
                            {
                                for (b=0;b<trunkoldlist.length;b++)
                                {
                                    if ((trunkoldnames[b]) && (tfularr[0]==trunkoldnames[b]))
                                    {
                                        toldarr = trunkoldlist[b].split(',');
                                        k=1;
                                        if ((((1 + growth_tolerance) * toldarr[1] / lentime) <= (tfularr[1] - toldarr[1]) / look_back)
                                                && (tfularr[1] - toldarr[1]) >= (look_back * growth_per_day_treshold))
                                        {
                                            //if more than required percent growth, add to biggrowers array to display later
                                            biggrowers.push(trunkfulllist[a]);
                                        }
                                        if ((((1 - growth_tolerance) * toldarr[1] / lentime) >= (tfularr[1] - toldarr[1]) / look_back)
                                                && (tfularr[1] - toldarr[1]) >= (look_back * growth_per_day_treshold))
                                        {
                                            bigshrinkers.push(trunkfulllist[a]);
                                        }
                                        b = trunkoldlist.length;
                                    }      
                                }
                                if (k==0)
                                {
                                    //if cannot find the element in the old list, add it into the newlinkers array to display later
                                    if(window.location.href.match('keywords?'))
                                    {biggrowers.push(trunkfulllist[a]);}
                                    else
                                    {newlinkers.push(trunkfulllist[a]);}
                                }
                            } //if (trularr.length == 11)
                        }
                        body = ""
                        //only display new linkers if the page is not the keywords page
                        if(!window.location.href.match('keywords?'))
                        {
                            for (a in newlinkers)
                            {
                                if (a>=display_limit) {break;}
                                arr = convert_array(newlinkers[a].split(','));
                                body = body + body_string.replace(/nncountnn/, (Number(a) + 1).toString()).replace(/nnnamenn/g,arr[0]).replace(/nnnum1nn/,arr[1]).replace(/nnnum2nn/,arr[2]).replace(/nnnum3nn/,arr[3]).replace(/nnnum4nn/,arr[4]).replace(/nnnum5nn/,arr[5]);
                            }
                            new_tab_element = window.document.createElement ("SPAN");
                            new_tab_html = '<h3>New ' + label_ref + ' Appearing in Last ' + look_back.toString() + ' Days:</h3><br>' + head_string + body + tail_string + '<br>';
                            new_tab_element.innerHTML = new_tab_html;
                            html_insert_after(tab_element,new_tab_element);
                        }
                        //build bigshrinkers table
                        new_tab_element = window.document.createElement ("SPAN");
                        body = ""
                        for (a in bigshrinkers)
                        {
                            if (a>=display_limit) {break;}
                            barr = bigshrinkers[a].split(',')
                            //Find elements in full list and replace numbers in biggrowers with those numbers
                            //look for the element in the full array, and get the corresponding numbers there
                            for (b in trunkfulllist)
                            {
                                farr = trunkfulllist[b].split(',');
                                if (farr[0] == barr[0])
                                {arr = convert_array(farr)}
                            }
                            body = body + body_string.replace(/nncountnn/, (Number(a) + 1).toString()).replace(/nnnamenn/g,arr[0]).replace(/nnnum1nn/,arr[1]).replace(/nnnum2nn/,arr[2]).replace(/nnnum3nn/,arr[3]).replace(/nnnum4nn/,arr[4]).replace(/nnnum5nn/,arr[5]);
                        }
                        new_tab_html = '<h3>' + label_ref + ' With ' + (growth_tolerance * 100).toString() + '% Lower Traffic Over the Last ' + look_back.toString() + ' Days:</b></h3><br>' + head_string + body + tail_string + '<br>';
                        new_tab_element.innerHTML = new_tab_html;
                        html_insert_after(tab_element,new_tab_element);
                        new_tab_element = window.document.createElement ("SPAN");
                        body = ""
                        //build biggrowers table
                        for (a in biggrowers)
                        {
                            if (a>=display_limit) {break;}
                            barr = biggrowers[a].split(',')
                            //Find elements in full list and replace numbers in biggrowers with those numbers
                            //look for the element in the full array, and get the corresponding numbers there
                            for (b in trunkfulllist)
                            {
                                farr = trunkfulllist[b].split(',');
                                if (farr[0] == barr[0])
                                {arr = convert_array(farr)}
                            }
                            body = body + body_string.replace(/nncountnn/, (Number(a) + 1).toString()).replace(/nnnamenn/g,arr[0]).replace(/nnnum1nn/,arr[1]).replace(/nnnum2nn/,arr[2]).replace(/nnnum3nn/,arr[3]).replace(/nnnum4nn/,arr[4]).replace(/nnnum5nn/,arr[5]);
                        }
                        new_tab_html = '<h3>' + label_ref + ' With ' + (growth_tolerance * 100).toString() + '% Higher Traffic Over the Last ' + look_back.toString() + ' Days:</b></h3><br>' + head_string + body + tail_string + '<br>';
                        new_tab_element.innerHTML = new_tab_html;
                        html_insert_after(tab_element,new_tab_element);
                        tab_element.innerHTML = final_html;
                    }
                });
            }
        });
}
