/* Usage Analysis Javascript Library
 *
 * Name:     usage_analysis.js
 * Author:   Bradley J. Hughes
 * Version:  1.0
 */

    //  Constant URL value for the Cross Domain URL to included /path/to/null.js or
    //  /path/to/pixel.gif.
    var CROSSDOMAINURL =
"crossdomain.capitalone.com/common/scripts/null.js";

    //  Set to true for testing, false otherwise,
    //  enables Analytics to be rendered in cleartext
    var TESTING = false;

/**
 * Writes the application silo Analytic object request.
 *
 * Arguments: attributes, an array of attributes where each element of the
 *            array is of the form attributeName=attributeValue.
 *            scriptsURI, a string representing the /path/to/null.js or
 *            /path/to/pixel.gif.  ScriptsURI is assumed to be correct, no
 *            special validation is done here.
 *
 * Used by:   calling function.
 */
function writeAnalytic(attributes, scriptsURI)
{
    if (typeof attributes == "undefined" || attributes == null ) {
        return false;
    }
    if (typeof scriptsURI == "undefined" || scriptsURI == null || scriptsURI == "") {
        return false;
    }

    var appDomainURL = protocol() + "//" + host() + scriptsURI;

    document.write(analyticObject(attributes, appDomainURL));

    return true;
}

/**
 * Writes the Cross Domain Analytic object request.
 *
 * Arguments: attributes, an array of attributes where each element of the
 *            array is of the form attributeName=attributeValue.
 *
 * Used by:   calling function.
 */
function writeCrossDomainAnalytic(attributes)
{
    if (typeof CROSSDOMAINURL == "undefined" || CROSSDOMAINURL == null || CROSSDOMAINURL == "") {
        return false;
    }

    // Remap file protocol for cross-domain Analytic when testing locally
    if (protocol() == "file:") {
        protocol = "http:";
    }
    else {
        protocol = protocol();
    }

    var crossDomainUrl = protocol + "//" + CROSSDOMAINURL;
    document.write(analyticObject(attributes, crossDomainUrl));

    return true;
}

/**
 * Builds and returns the analytic object tag.
 * Used by the writeAnalytic and writeCrossDomainAnalytic functions.
 */
function analyticObject(attributes, targetURL)
{
    var openingTag  = "", closingTag = "";
    var objType = objectType(targetURL).toUpperCase();

    if (TESTING == true) {
        openingTag = "";
        closingTag = "<br>";
    }
    else if (objType == "JS") {
        openingTag = "<script language=\"JavaScript\" src=\"";
        closingTag = "\"></script>";
    }
    else if (objType == "IMG") {
        openingTag = "<image src=\"";
        closingTag = "\">";
    }
    else {
        return "";
    }

    var analyticObject = openingTag + targetURL + query(cleanAttributes(attributes)) +
                         closingTag;

    return analyticObject;
}

/**
 * Returns the value of the object being requested.
 *
 *     "JS"   for a JavaScript object,
 *     "IMG"  for a Image object,
 *     "NULL" neither a JavaScript or Image specified by the targetURL,
 *     "TEST" used for testing purposes.
 *
 * The targetURL argument is assumed to be the URL of either a 
 * JavaScript (.js) or an image (.gif).
 *
 * Used by the analyticObject function.
 */
function objectType(targetURL)
{
    if (typeof targetURL == "undefined" || targetURL == null) return "NULL";

    if (targetURL.indexOf(".js") > 0) {
        return "JS";
    }
    else if (targetURL.indexOf(".gif") > 0 ||
             targetURL.indexOf(".jpg") > 0 ||
             targetURL.indexOf(".jpeg") > 0) 
    {
        return "IMG";
    }
    else return "SOMETHINGELSE";
}

/**
 * Returns the protocol of the page request.
 *
 *     "http:"  for an http page request,
 *     "https:" for an https page request,
 *     "file:"  if testing on local machine.
 *
 * Used by the writeAnalytic and writeCrossDomainAnalytic functions.
 */
function protocol()
{
    return location.protocol;
}

/**
 * Returns the host of the page request.
 * Used by the writeAnalytic function.
 */
function host()
{
    return location.host;
}

/**
 * Builds and returns the query string, which will be "?Log=1" followed by
 * name-value pairs for each attribute to be captured in the analytic object
 * request.
 * Used by the analyticObject function.
 */
function query(attributes)
{
    var queryString = "";
    
    if (attributes) {
        queryString = "?Log=1";
        for (var i=0; i < attributes.length; i++) {
            queryString += "&" + attributes[i];
        }
    }
    return queryString;
}

/**
 * Returns a clean attributes array, where each element of the array is of
 * the format "attributeName=numericvalue" or "attributeName='textvalue'".
 * Used by the analyticObject function.
 */
function cleanAttributes(attributes)
{
    var cleanAttribs = new Array();
    var cleanAttribsIndex = 0;

    var splitAttributes = new Array();

    if (attributes) {
        // Iterate through each attribute
        for (var i=0; i < attributes.length; i++) {

            // Process only attributes containing an element            
            if(attributes[i]) {
                // Process only where elements contains "=value" or "value" (no attribute= specified)
                if(attributes[i].search(/=[ '\=A-Za-z0-9]/) != -1 ||
                   attributes[i].search(/=/) == -1) {

                    // Split attributes on equal symbol, separator of name=value
                    splitAttributes = attributes[i].split("=");
                    // Iterate through each element of the split array
                    for(var j=0; j < splitAttributes.length; j++) {
                        // remove single and double quotes, trim before and after
                        splitAttributes[j] = ltrim(rtrim(removeQuotes(splitAttributes[j])));
                    }

                    switch(splitAttributes.length) {
                        case 1:
                            // Only attribute value specified
                            attributes[i] = "Attribute" + i + "=" + encode(splitAttributes[0]);
                            break;
                        default:
                            // Attribute name=value specified, any extra values ignored
                            var attributeName = clean(splitAttributes[0]);
                            if (attributeName == "") {
                                attributes[i] = "Attribute" + i + "=" + encode(splitAttributes[1]);
                            }
                            else {
                                attributes[i] = attributeName + "=" + encode(splitAttributes[1]);
                            }
                            break;
                    }
                    // Add cleaned attribute to new array
                    cleanAttribs[cleanAttribsIndex] = attributes[i];
                    cleanAttribsIndex++;
                }
            }
        }
    }
    else {
        return false;
    }

    return cleanAttribs;
}

/**
 * Returns encoded string for use within a URL.
 * Used by cleanAttributes function to encode the attribute value.
 */
function encode(str)
{
    if (typeof str == "undefined"  || str == null || str == "") {
        return "";
    }

    var OKCHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +    // Alphabetic
                  "abcdefghijklmnopqrstuvwxyz" +
                  "0123456789" +                    // Numeric
                  "-_.!~*'()";                      // RFC2396
    var HEX =     "0123456789ABCDEF";

    var unEncodedString = str;
    var encodedString = "";
    for (var i = 0; i < unEncodedString.length; i++ ) {
        var ch = unEncodedString.charAt(i);
        if (ch == " ") {
            // Encode space with a "+", rather than %20
            encodedString += "+";
        }
        else if (OKCHARS.indexOf(ch) != -1) {
            encodedString += ch;
        }
        else {
            var charCode = ch.charCodeAt(0);
            if (charCode > 255) {
                // Unicode character cannot be encoded as URL encoding only supports 
                // 8-bit characters.  Substituting a space (+) for the character.
                encodedString += "+";
            }
            else {
                encodedString += "%";
                encodedString += HEX.charAt((charCode >> 4) & 0xF);
                encodedString += HEX.charAt(charCode & 0xF);
            }
        }
    }

    return encodedString;
}

/**
 * Returns a cleaned string of only alphabetic or number symbols, minus the URL
 * special characters or other characters that would require URL encoding.
 * Used by the cleanAttributes function to clean the attribute name in the
 * attribute name-value pair.
 */
function clean(str)
{
    if (typeof str == "undefined"  || str == null || str == "") {
        return "";
    }

    var CLEANCHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + // Alphabetic
                     "abcdefghijklmnopqrstuvwxyz" +
                     "0123456789";                  // Numeric;

    var patientString = str;
    var cleanedString = "";
    for (var i = 0; i < patientString.length; i++ ) {
        var ch = patientString.charAt(i);
        if (CLEANCHARS.indexOf(ch) != -1) {
            cleanedString += ch;
        }
        // else omit character from cleaned string
    }
    
    return cleanedString;
}

/**
 * Returns string quoted with single quotes "'" unless the string is a
 * numerical value.
 * Used by cleanAttributes function to quote the attribute value of the 
 * attribute name-value pair.
 */
function singleQuote(str)
{
    if (typeof str == "undefined" || str == null) return str;

    str = "" + str;
    
    if (isNumeric(str)) {
        return str;
    }
    return "'" + str + "'";
}

/**
 * Returns string with all single and double quotes removed.
 * Used by the cleanAttributes function to remove quotes from the 
 * attribute value.
 */
function removeQuotes(str) 
{
    if (typeof str == "undefined" || str == null) return str;

    str = "" + str;
    
    allQuotes = /['\"]/ig; 

    return str.replace(allQuotes, "");
}


/**
 * Returns string with inital spaces removed up to the first character not in
 * the set.  Set defaults to " ".
 * Used by the cleanAttributes function to trim the operands of the 
 * attribute name-value pair.
 */
function ltrim(str, set)
{
    if (typeof str == "undefined" || str == null) return str;
    if (typeof set == "undefined"  || set == null || set == "") {
        set = " ";  // Default
    }
    str = "" + str;
    set = "" + set;

    while (str.length > 0 && set.indexOf(str.charAt(0)) != -1) {
        str = str.substring(1, str.length);
    }
    return str;
}

/**
 * Returns string with final characters removed up the last character not in
 * the set.  Set defaults to " ".
 * Used by the cleanAttributes function to trim the operands of the 
 * attribute name-value pair.
 */
function rtrim(str, set)
{
    if (typeof str == "undefined" || str == null) return str;
    if (typeof set == "undefined"  || set == null || set == "") {
        set = " ";  // Default
    }
    str = "" + str;
    set = "" + set;

    while(str.length > 0 && set.indexOf(str.charAt(str.length-1)) != -1) {
        str = str.substring(0, str.length-1);
    }
    return str;
}

/**
 * Returns true if the string contains only numberic characters, and false if
 * string contains at least one non-numeric character.
 * Used by singeQuote function to appropriately quote only non-numeric strings.
 */
function isNumeric(value)
{
    if (typeof value == "undefined" || value == null) return false;
    
    var v = 1.0 * value;

    if (isNaN(v)) {
        return false;
    } 
    else {
        return true;
    }
}

/*
 * Copyright 2004 Capital One Financial Corporation
 * All Rights Reserved.
 *
 * This software contains valuable trade secrets and proprietary
 * information of Capital One and is protected by law. It may not
 * be copied or distributed in any form or medium, disclosed to third
 * parties, reverse engineered or used in any manner without prior
 * written authorization from Capital One.
 */

