// QL
if (typeof QL === 'undefined') {
    var QL = YAHOO;
}

// Dom
if (typeof $D === 'undefined') {
    var $D = QL.util.Dom;
}

// Event
if (typeof $E === 'undefined') {
    var $E = QL.util.Event;
}

// Selector
if (typeof $S === 'undefined') {
    var $S = QL.util.Selector;
}

// XMLHttpRequests (AJAX)
if (typeof $XHR === 'undefined') {
    var $XHR = QL.util.Connect;
}

// Cookie
if (typeof $C === 'undefined') {
    var $C = QL.util.Cookie;
}

// Create utilities namespace
QL.namespace('utilities');


// Prototypes
String.prototype.trim = function() {
    return this.replace(/^\s+|\s+$/g, '');
}

String.prototype.ltrim = function() {
    return this.replace(/^\s+/, '');
}

String.prototype.rtrim = function() {
    return this.replace(/\s+$/, '');
}

String.prototype.toTitleCase = function() {
    var pattern = /(\w)(\w*)/; // a letter, and then one, none or more letters

    var a = this.split(/\s+/g); // split the sentence into an array of words

    if ( a.length > 1 ) {
        for (i = 0 ; i < a.length ; i ++ ) {
            var parts = a[i].match(pattern); // just a temp variable to store the fragments in.
            if (parts != null) {
                var firstLetter = parts[1].toUpperCase();
                var restOfWord = parts[2].toLowerCase();
            }    

            a[i] = firstLetter + restOfWord; // re-assign it back to the array and move on
        }

        return a.join(' '); // join it back together
    } else {
        return a;
    }
}

String.prototype.formatCurrency = function(displayCents, dollarSign) {
 num = this.toString().replace(/\$|\,/g,'');
 numPrefix = (dollarSign == true) ? "" : "$";
 if(isNaN(num))
  num = "0";
  sign = (num == (num = Math.abs(num)));
  num = Math.floor(num*100+0.50000000001);
  cents = num%100;
  num = Math.floor(num/100).toString();
 if(cents<10)
  cents = "0" + cents;
 for (var i = 0; i < Math.floor((num.length-(1+i))/3); i++)
  num = num.substring(0,num.length-(4*i+3))+','+
  num.substring(num.length-(4*i+3));
 if (typeof displayCents != "undefined" && displayCents =="displayCents") {
  return (((sign)?'':'-') + numPrefix + num + '.' + cents);
 } else {
  return (((sign)?'':'-') + numPrefix + num); 
 } 
},


/**
 * Data Grid utilities.
 *
 * A collection of data grid utilities to make life easier.
 *
 * @namespace QL.utilities
 * @class DataGrid
 * @constructor
 */
QL.utilities.DataGrid = function()
{
    /**
     * Details for the decorative portion of the calculator
     *
     * @property decorativeDetails
     * @static
     * @type Object[]
     */
    var decorativeDetails = {
        // Decorative container
        decorativeContainer: document.createElement("TR")
    };

    return {

        /**
         * Decorates product collections when they are displayed in
         * order to keep from compromising markup
         *
         * @method productCollectionDecorator
         * @static
         * @param {Object} Table Element
         * @param {String} Class Name of Decorated Elements
         * @param {Array}  Array of Class Names of columns to be decorated
         */
        dataGridDecorator: function(datagrid, decorativeClassName, decorativeColumnClasses)
        {
            var decorativeContainer = decorativeDetails.decorativeContainer,
                decorativeElementClass = decorativeClassName,
                decorativeColumnClasses = decorativeColumnClasses;

            if(!datagrid) {
                return false;
            }

            // The element passed must be a table
            if (datagrid.nodeName.toUpperCase() != 'TABLE') {
                throw new Error("Specified parameter must be an HTML 'TABLE' element.");
            }

            // Store elements to be manipulated
            if (!datagrid.getElementsByTagName('THEAD')[0] || !datagrid.getElementsByTagName('TBODY')[0]) {
                throw new Error("Specified table must include THEAD and TBODY elements.");
            } else {
                var thead = datagrid.getElementsByTagName('THEAD')[0];
                var tbody = datagrid.getElementsByTagName('TBODY')[0];
            }

            // Insert our decorative TR container
            decorativeContainer.className = decorativeElementClass;

            var crown = thead.insertBefore(decorativeContainer.cloneNode(false), $D.getFirstChild(thead));
            var base = tbody.appendChild(decorativeContainer.cloneNode(false));

            var numberOfChildren = thead.getElementsByTagName("TH").length;

            // Grab each decorative column's target elements and decorate
            // Note that IE goes through some turmoil when innerHTML and THEAD meet up so
            // we're using standard node creation here
            for (var i = 0; i < numberOfChildren; i++) {

                // Grab last TD element in decorative column
                var decorativeElement = $D.getElementsByClassName(decorativeColumnClasses[i], 'TD', datagrid)[$D.getElementsByClassName(decorativeColumnClasses[i], 'TD', datagrid).length - 1];

                var TH = document.createElement('TH');
                var TD = document.createElement('TD');

                crown.appendChild(TH);
                base.appendChild(TD);

                // Add decorative markup if necessary
                if (decorativeElement) {
                    // Add class to decorative element
                    decorativeElement.className = decorativeColumnClasses[i]+'_bottom_cap';

                    // Insert additional markup in the thead and tbody of decorative columns
                    var SPAN = document.createElement('SPAN');
                    TH.className = decorativeColumnClasses[i];
                    TD.className = decorativeColumnClasses[i];
                    TH.appendChild(SPAN.cloneNode(false));
                    TD.appendChild(SPAN.cloneNode(false));
                }
            }
            return true;
        }
    };
}();


/**
 * Form utilities.
 *
 * A collection of form utilities to make life easier.
 *
 * @namespace QL.utilities
 * @class Form
 * @constructor
 */
QL.utilities.Form = function()
{

    return {

        /**
         * Ensures that specified element is a valid form element.
         *
         * @method  isValidFormElement
         * @param   {Object}    element
         * @return  {Boolean}
         */
         isValidFormElement: function(element)
        {
            if (typeof(element) !== 'object') {
                throw new TypeError('Specified element must be an object.');
            }

            var element = element.nodeName.toLowerCase();

            switch (element) {

                case 'input':
                case 'select':
                case 'textarea':
                    return true;

                default:
                    return false;
            }
        },

        /**
         * Finds all the child elements of a form.
         *
         * @method  getElements
         * @param   {Object}    form
         * @return  {Array}     validElements
         */
        getElements: function(form)
        {
            // Vars.
            var validElements = [];

            // Get form.
            var form = $D.get(form);

            if (!form || form.length === 0) return false;

            // Get all child elements.
            var elements = form.getElementsByTagName('*');

            if (elements.length === 0) return false;

            // Iterate elements. Add only valid form elements that are not
            // disabled or hidden.
            for (var i = 0; i < elements.length; i++) {
                if (this.isValidFormElement(elements[i])) {
                    if (!elements[i].getAttribute('disabled') && elements[i].getAttribute('type') !== 'hidden') {
                        validElements.push(elements[i]);
                    }
                }
            }

            return validElements;
        },

        /**
         * Gets the first element of a form.
         *
         * @param   {Object}    form
         * @return  {Array}     elements
         */
        getFirstElement: function(form)
        {
            // Get form elements.
            var elements = this.getElements(form);

            if (!elements || elements.length === 0) return false;

            // Return first element.
            return elements[0];
        },

        /**
         * Gives focus to the first element of a form.
         *
         * @param   {Object}    form
         */
        focusOnFirstElement: function(form)
        {
            // Get form elements.
            var element = this.getFirstElement(form);

            if (!element) return false;

            element.focus();
        },

        /**
         * Transforms a legend of a stated class into a newly specified
         * element with the same content as the legend and an optional
         * class.
         *
         * @param {String} targetClassName
         * @param {String} newElement
         * @param {String} newClassName
         * @static
         */
        transformLegends: function(targetClassName, newElement, newClassName)
        {
            // Grab the elements that we're looking for and place their
            // content into the new elements we're attaching to the document
            $D.batch($D.getElementsByClassName(targetClassName, 'LEGEND'), function(el) {
                var content = el.lastChild.nodeValue;
                var replacementElement = document.createElement(newElement);
                if(newClassName) {
                    $D.addClass(replacementElement, newClassName);
                }
                replacementElement.innerHTML = content;
                $D.insertAfter(replacementElement, el);
            });
        }

    };

}();

// Form
if (typeof $F === 'undefined') {
    var $F = QL.utilities.Form;
}

/**
 * Panel utilities.
 *
 * A collection of panel utilities to make life easier.
 *
 * @namespace QL.utilities
 * @class Panel
 * @constructor
 */
QL.utilities.Panel = function()
{
    /**
     * Details regarding tabview creation
     *
     * @property tabViewDetails
     * @static
     * @type Object[]
     */
    var tabViewDetails = {

        // Class of unordered list containing tabs
        tabContainerClass: 'yui-nav',

        // Class indicating an active tab
        activeTabClass: 'selected',

        // Prefix for panel elements
        panelPrefix: 'panel',

        // Prefix for tab elements
        tabPrefix: 'tab'

    };

    /**
     * Determines the default selected tab
     *
     * @method setActiveTab
     * @static
     */
    var setActiveTab = function(tabViewContainer)
    {
        var tabContainerClass = tabViewDetails.tabContainerClass,
            activeTabClass = tabViewDetails.activeTabClass,
            panelPrefix = tabViewDetails.panelPrefix,
            tabPrefix = tabViewDetails.tabPrefix;

        // Tab selection based on hash member of location object
        if(window.location.hash !== '') {
            if(window.location.hash.search(panelPrefix) != -1 && $D.get(window.location.hash.substring(1).replace(panelPrefix, tabPrefix))) {
                var activeTab = $D.get(window.location.hash.substring(1).replace(panelPrefix, tabPrefix));
                $D.addClass(activeTab, activeTabClass);

                // Scroll to top of page to give page reference
                scroll(0,0);

                return true;
            }
        }

        // Default selected tab if there is nothing/bad information in the hash member of the location object
        var activeTab = $D.getFirstChild($D.getElementsByClassName(tabContainerClass, 'UL', tabViewContainer)[0]);
        $D.addClass(activeTab, activeTabClass);
        return true;
    };

    return {
        /**
         * Enables tab view by wrapping the
         * YUI tabView constructor and automatically
         * setting the default active tab
         *
         * @method enableTabView
         * @static
         */
        enableTabView: function(tabViewContainer)
        {
            setActiveTab(tabViewContainer);
            var tabView = new QL.widget.TabView(tabViewContainer);
        }
    };
}();

/**
 * Container Object
 *
 * A collection of common container-related methods
 *
 * @namespace QL.utilities
 * @class Container
 * @constructor
 */
QL.utilities.Container = function()
{
    return {
        display: function(trigger, container)
        {
            var panel = new QL.widget.Panel( container,
                                              {
                                                  fixedcenter: true,
                                                  underlay: 'shadow',
                                                  close:false,
                                                  draggable:false,
                                                  modal:true,
                                                  visible:true
                                              } );

            panel.render( document.body );

            $D.setStyle(container + '_mask', 'background-color', '#000');
            $D.setStyle(container + '_mask', 'opacity', 0.6);
            $D.setStyle(container + '_mask', 'z-index', 301);
            $D.setStyle(container + '_c', 'z-index', 302);

            QL.utilities.Container.close(true, panel, container);
            
            return panel;
        },

        close: function(esc, panel, container)
        {
            // Close Video on ESC keypress
            $E.on(document, 'keypress', function(e) {
                var key = e.keyCode;

                if(key === 27 && esc) {
                    panel.hide();
                    esc = false;
                }
            });

            $E.on($D.get(container + '_mask'), 'click', function(ev) {
                panel.hide();
            });
        }
    }
}();


/**
 * Window Object
 *
 * A collection of common window-related methods
 *
 * @namespace QL.utilities
 * @class Window
 * @constructor
 */
QL.utilities.Window = function()
{
    var defaultWindowName = 'WINDOW';

    var nameLimit = 1000;

    return {

        /**
         * Opens a new window
         *
         * Valid options are:
         * name, toolbar, location, directories, status,
         * menubar, scrollbars, resizable, width
         * height, top, left, and center
         *
         * Use 1/0 or 'yes'/'no' for most params
         *
         * @param {String} href
         * @param {Object} options
         * @static
         */
         open: function(href, options)
          {
              // Make sure we have the required parameters for the method
              if (!QL.lang.isString(href)) throw new TypeError('Specified parameter \'href\' must be a string.');

              try {
                  // If the window name is in the options object use it
                  var windowName = options.name;
                  var windowOptions = '';

                  // Logic to center the window
                  if (options.center || options.center === 1 || options.center === 'yes') {
                      var width  = options.width + 32; // Accomodate browser chrome
                      var height = options.height + 96;
                      options.top = (screen.height - height) / 2;
                      options.left = (screen.width - width) / 2;
                  }

                  // Build options arguments
                  for (option in options) {
                      windowOptions += option + '=' + options[option] + ',';
                  }

                  // Trim trailing commma
                  windowOptions = windowOptions.substring(0, (windowOptions.length - 1));
              } catch (e) {
                  var windowOptions = '',
                      windowName = defaultWindowName + Math.ceil(Math.random() * nameLimit);
              }

              // Open the new window
              var newWindow = window.open(href, windowName, windowOptions);

              newWindow.focus();
          },

        /**
         * Returns the querystring
         *
         * @static
         */
        getQueryString : function()
        {
            return window.location.search.substring(1);
        },

        /**
         * Takes a querystring and returns an object.
         * The string parameter is optional as the method
         * will otherwise grab the querystring of the window
         * it is called in.
         *
         * @param {String} optional querystring
         * @static
         */
        queryStringToObject : function(queryString)
        {
            var stringToManipulate;

            // Determine what querystring to manipulate
            if(!queryString) {
                stringToManipulate = this.getQueryString();
            } else {
                stringToManipulate = queryString;
            }

            // Split the string at the '&' and '=' characters to create the object
            if(stringToManipulate.length) {
                stringToManipulate = stringToManipulate.split('&');
                var querystringObject = {};
                for(var i = 0; i < stringToManipulate.length; i++) {
                    var key = stringToManipulate[i].split('=')[0];
                    var value = stringToManipulate[i].split('=')[1];
                    querystringObject[key] = value;
                }
            }

            return querystringObject;
        }
    };
}();

/**
 * Video Object
 *
 * Common setting for enabling flash video using flowplayer
 *
 * @namespace QL.utilities
 * @class Video
 * @constructor
 */
QL.utilities.Video = function()
{
    /**
    *
    * @Object video settings
    *
    */
        var videoSettings = {
             flowplayerPath: "/resources/flowplayer/flowplayer.commercial-3.1.5.swf"
        }

        var videoPlayer = function() {
            
            if($S.query("a.video_container")) {

                if($("a.video_container").hasClass('autoPlayOff')) {
                    autoPlaySetting = false;
                } else {
                    autoPlaySetting = true;
                }
                            
                flowplayer("a.video_container", {src: videoSettings.flowplayerPath, wmode: 'transparent'}, {
    
                    key: '#@b7dfb1e63ee4ed29239',
                    
                    plugins: {
                        controls: {
                            sliderGradient: 'none',
                                  bufferGradient: 'none',
                                  timeColor: '#cdcccb',
                                  tooltipTextColor: '#ffffff',
                                  progressGradient: 'medium',
                                  tooltipColor: '#5F747C',
                                  volumeSliderColor: '#000000',
                                  buttonOverColor: '#312f2f',
                                  buttonColor: '#5e5e5e',
                                  volumeSliderGradient: 'none',
                                  backgroundColor: '#8c8c8c',
                                  progressColor: '#dd790e',
                                  borderRadius: '0',
                                  durationColor: '#f5840a',
                                  backgroundGradient: 'medium',
                                  timeBgColor: '#555555',
                                  bufferColor: '#48433c',
                                  sliderColor: '#000000',
                                  height: 24,
                                  opacity: 1.0
                        }
                    },
                                    
                    clip: {
                        autoPlay: autoPlaySetting,
                        autoBuffering: true,
                        baseUrl: '/'
                    }
                });
            }
        }
        return {
            init: function()
            {
                $E.onDOMReady(function() {
                    videoPlayer();
                });
            }
        };
}().init();

QL.utilities.Anchor = function()
{
    var defaultNodeName = 'A',
        fauxAnchorClass = 'faux_anchor';

    return {
        prepareFauxAnchors: function() {
            $D.getElementsByClassName( fauxAnchorClass, null, null, function( el ) {

                var anchor = $D.getElementBy( function( el ) { if ( el.nodeName == defaultNodeName ) return true; return false;  }, defaultNodeName, el ),
                    href   = anchor.getAttribute( 'href' ),
                    classes = anchor.className ? anchor.className.split( ' ' ) : null,
                    cm_sp = null,
                    cm_re = null;

                // Faux anchors should inherit Manual Tagging as intent is the same as anchor tagging
                if ( classes ) {
                    for ( var i = 0; i < classes.length; i++ ) {
                        if ( classes[i].match( 'cm_' ) && classes[i].indexOf( 'cm_' ) === 0 ) {
                            if ( ! $D.hasClass( el, classes[i] ) ) {
                                $D.addClass( el, classes[i] );
                                var tagPrefix = classes[i].split(':')[0];

                                switch( tagPrefix ) {
                                    case 'cm_re':
                                        QL.utilities.Coremetrics.cmPrepareRealEstateTags();
                                        cm_re = $D.getAttribute( el, 'manual_cm_re' );
                                    break
                                    case 'cm_sp':
                                        QL.utilities.Coremetrics.cmPrepareSitePromotionTags();
                                        cm_sp = $D.getAttribute( el, 'manual_cm_sp' );
                                    break
                                    default:
                                    break;
                                }
                                $D.removeClass( anchor, classes[i] );
                            }
                        }
                    }
                }

                $E.on( el, 'click', function( ev ) {
                    var target = $E.getTarget( ev );
                    $E.preventDefault( ev );

                    if ( target.nodeName != 'A' ) {
                        manufacturedhref = '/' + href;

                        if ( cm_sp ) {
                            var delimiter = ( href.indexOf( '?' ) == -1 ) ? '?' : '&';
                            manufacturedhref += delimiter + 'cm_sp=' + cm_sp;
                        }

                        if ( cm_re ) {
                            var delimiter = ( href.indexOf( '?' ) == -1 ) ? '?' : '&';
                            manufacturedhref += delimiter + 'cm_re=' + cm_re;
                        }

                        cmCreateManualLinkClickTag( manufacturedhref );

                    }

                    if ( href && ! href.match( location.host ) && ( href.match( 'http://' ) || href.match( 'https://' ) ) ) {
                        QL.utilities.Window.open( href );
                    } else {
                        location.href = href;
                    }
                } );
            } );
        }
    }
}();

/**
 * Coremetrics Object
 *
 * A collection of common Core-related methods
 *
 * @namespace QL.utilities
 * @class Coremetrics
 * @constructor
 */
QL.utilities.Coremetrics = function() 
{
    /**
     * Collection of all the parameter names necessary to run the various methods in the Coremetrics Library. 
     * Note that they directly correspond to meta tag name attributes. Further, not all indices in the array
     * are Core Metrics parameters - some act as triggers to lauch particular Core methods.
     *
     */
    var cmTagParameters = [ 'PageID', 'CategoryID', 'SearchTerm', 'NumSearchResults', 'ErrorNotice', 'ProductID', 'ProductName', 'City', 'State', 'Zip', 'Email', 'NewsletterName', 'Subscribed', 'AttributeString', 'EventID', 'ActionType', 'EventCategoryID', 'Points', 'ThankYou', 'RegistrationEvent', 'CustomerID', 'TransactionID' ];

    /**
     * JavaScript Object that will contain x    each Core Metrics parameter as a property.
     *
     */
    var cmTagParameterObject = new Object();

    /**
     * Constructs a JavaScript Object that represents the Extra Fields that will be delivered
     * in the Core Metrics custom export report.
     * 
     */
    var extraFieldObject = {
        pv1: ( $C.exists( 'metricsid' ) && $C.get( 'metricsid' ) ) ? $C.get( 'metricsid' ) : null, // Metrics ID
        pv2: ( $C.exists( 'PHPSESSID' ) && $C.get( 'PHPSESSID' ) ) ? $C.get( 'PHPSESSID' ) : null, // Session ID
        pv3: ( $C.exists( 'LUVProd' ) && $C.get( 'LUVProd' ) ) ? $C.get( 'LUVProd' ) : null, // LUV ID
        pv4: ( $C.exists( 'GLUV' ) && $C.get( 'GLUV' ) ) ? $C.get( 'GLUV' ) : null, // GLUV ID
        pv5: null, // TBD
        pv6: null, // TBD
        pv7: null, // TBD
        pv8: null, // TBD
        pv9: null, // TBD
        pv10: null, // TBD
        pv11: null, // TBD
        pv12: null, // TBD
        pv13: null, // TBD
        pv14: null, // TBD
        pv15: null // TBD
    };

    /**
     * Indicates parse point for class attributes of manually tagged elements
     *
     */    
    var notationDelimeter = ':';   
    
    /**
     * Indicates parse notation for manual tag attributes of manually tagged elements
     *
     */    
    var manualTagDelimeter = '-_-';
        
    /**
     * Object collecting details for the Product Selection Tag Process
     *
     */
    var productSelectionDetails = {
        activated: false,
        eventTrigger: 'focus',
        elementTriggers: $D.getElementsBy( function( el ) { 
                            if ( el.nodeName == 'INPUT' || el.nodeName == 'SELECT' ) return true; 
                            return false; 
                         }, null, $D.get( 'referral' ) )
    };

    /**
     * Object containing prefixes to trigger Core Metrics manual tagging
     *
     */
    var prefixObject = {
        elementTag: 'cm_el:',
        sitePromotionTag: 'cm_sp:',
        realEstateTag: 'cm_re:',
        conversionEventTag: 'cm_ce:'
    };

    /**
     * Object containing errors for manual tagging
     *
     */    
    var manualTagErrorObect = {
        elementTag: 'You have an improperly formatted class attribute triggering cmPrepareElementTags. It should be formatted as the following: ' + prefixObject.elementTag + 'Event:Category ID:Event ID',
        sitePromotionTag: 'You have an improperly formatted class attribute triggering cmPrepareSitePromotionTags. It should be formatted as the following: ' + prefixObject.sitePromotionTag + 'Promotion Type:Promotion:Link',
        realEstateTag: 'You have an improperly formatted class attribute triggering cmPrepareRealEstateTags. It should be formatted as the following: ' + prefixObject.realEstateTag + 'Version:Page Area:Link',
        conversionEventTag: 'You have an improperly formatted class attribute triggering cmPrepareConversionEventTags. It should be formatted as the following: ' + prefixObject.conversionEventTag + 'Event Category:Event ID:ActionType:Number of Points'
    };

    return {

        /**
         * Loop over the array of Core Metrics parameters and construct a JavaScript Object
         * that will set each property of the object to the content attribute of a meta tag
         * with a name attribute matching the index of the array.
         *
         */    
        cmCreateTagParameterObject: function()
        {
        
            // Make sure our parameter collection has not been inadvertantly decimated and 
            // that we have meta tags to play with
            if ( cmTagParameters.length === 0 || document.getElementsByTagName( 'META' ).length === 0 ) { return false; }
            
            // Collect all meta tags
            var metaInformation = document.getElementsByTagName( 'META' );
    
            // Prepopulate the object's properties with null values
            for ( tag in cmTagParameters ) {
                cmTagParameterObject[ cmTagParameters[tag] ] = null;
            }
                
            // Collect meta tags whose name attribute matches the parameter name in the 
            // cmTagParameters array and assign their name/value pairs to the cmTagParameterObject.
            $D.batch ( metaInformation, function( el ) {
                for ( tag in cmTagParameters ) {
                    if ( cmTagParameters[tag] == el.getAttribute( 'name' ) ) {
                        cmTagParameterObject[ cmTagParameters[tag] ] = el.getAttribute( 'content' );
                    }
                }
            } );
    
            return cmTagParameterObject;
        },

        /**
         * Method to wrap 'Page Level Tags.' These tags include the Pageview tag, Error tag and Productview tag  
         *
         */
        cmLaunchPageLevelTags: function()
        {
            if ( cmTagParameterObject.ActionType ) {
                QL.utilities.Coremetrics.launchConversionEvent();
            }
            
            if ( cmTagParameterObject.RegistrationEvent ) { 
                QL.utilities.Coremetrics.launchRegistration();
            }
    		
            if ( ! $C.get( 'TechPropsLaunch' ) ) {
                QL.utilities.Coremetrics.launchTechProps();
            }
    
            if ( cmTagParameterObject.ErrorNotice ) {
                QL.utilities.Coremetrics.launchError();
                return;
            }
    
            if ( cmTagParameterObject.ThankYou ) {
                QL.utilities.Coremetrics.cmLaunchThankYouTags();
                return;
            }
            
            if ( cmTagParameterObject.ProductID ) {
                QL.utilities.Coremetrics.launchProductview();
                return;
            }
            QL.utilities.Coremetrics.launchPageview();
            return;
        },

        /**
         * Method to wrap 'Thank You Tags.' These tags include the Productcompletion Tag, Transaction Tag, 
         * Registration Tag, and Conversion Event Tag
         *
         */
        cmLaunchThankYouTags: function()
        {
            QL.utilities.Coremetrics.launchPageview();
            QL.utilities.Coremetrics.launchProductcompletion();
            QL.utilities.Coremetrics.launchTransaction();
        },

        /**
         * Method to wrap 'Manual Tags.' These tags include the Element tag, Site Promotion 
         * tag and Real Estate tag  
         *
         */
        cmLaunchManualTags: function()
        {
            QL.utilities.Coremetrics.cmPrepareElementTags();
            QL.utilities.Coremetrics.cmPrepareSitePromotionTags();
            QL.utilities.Coremetrics.cmPrepareRealEstateTags();
            QL.utilities.Coremetrics.cmPrepareConversionEventTags();
        },

        /**
         * Collects all elements that contain the a class
         * attribute beginning with the specified prefix
         *
         * @param prefix string
         */
        cmCollectManualTagsByPrefix: function( prefix )
        {
            // Collect all elements specified by prefix
            var collection = $D.getElementsBy( function( el ) {        
                if ( ! el.className || ! el.className.match( prefix ) ) return false;            
                return true;
            } );
            
            return collection;                
        },

        /**
         * Breaks down the manual tagging notation and returns an array containing
         * each piece
         *
         * @param element HTML element
         * @param prefix string     
         * @param errorMessage string          
         */    
        cmProcessManualTagsByElementPrefixAndError: function ( element, prefix, errorMessage ) {
            var classes = element.className.split( ' ' ),
                trigger = '';
    
            for ( i = 0; i < classes.length; i++ ) {
                if ( classes[i].match( prefix ) ) {
                    if ( prefix != 'cm_el:' && prefix != 'cm_ce:' ) {
                        trigger = classes[i].replace( /-/g, '%20' );
                    } else {
                        trigger = classes[i].replace( /-/g, ' ' );
                    }
                }
            }
    
            var notationBreakDown = trigger.split( notationDelimeter );
            
            if ( notationBreakDown.length != 4 && prefix != 'cm_ce:' ) {
                throw new Error( errorMessage );
            }
            
            return notationBreakDown;
        },

        /**
         * Collects all elements specified for Element Tagging, assigns the appropriate
         * handlers, and waits...
         *
         */
        cmPrepareElementTags: function() {        
            $D.batch ( QL.utilities.Coremetrics.cmCollectManualTagsByPrefix( prefixObject.elementTag ), function ( el ) {
                var notationBreakDown = QL.utilities.Coremetrics.cmProcessManualTagsByElementPrefixAndError( el, prefixObject.elementTag, manualTagErrorObect.elementTag );
                
                // If the element tag is intended to happen in the event of a page load
                if ( notationBreakDown[1] == 'load' ) {
                    // QL.utilities.Coremetrics.launchElement( notationBreakDown[3], notationBreakDown[2] );
                    return;
                }
                
                $E.on( el, notationBreakDown[1], function ( ev ) {
                    QL.utilities.Coremetrics.launchElement( notationBreakDown[3], notationBreakDown[2] );
                } );
                
            } );
        },

        /**
         * Collects all elements specified for Real Estate Tagging and attaches invalid
         * attributes post page render
         *
         */
        cmPrepareRealEstateTags: function() {        
            $D.batch ( QL.utilities.Coremetrics.cmCollectManualTagsByPrefix( prefixObject.realEstateTag ), function ( el ) {
                var notationBreakDown = QL.utilities.Coremetrics.cmProcessManualTagsByElementPrefixAndError( el, prefixObject.realEstateTag, manualTagErrorObect.realEstateTag );
                $D.setAttribute( el, 'manual_cm_re', notationBreakDown[1] + manualTagDelimeter + notationBreakDown[2] + manualTagDelimeter + notationBreakDown[3] );
                QL.utilities.Coremetrics.preventDoubleCounting( el, 'manual_cm_re' );
            } );
        },

        /**
         * Collects all elements specified for Site Promotion Tagging and attaches invalid
         * attributes post render
         *
         */
        cmPrepareSitePromotionTags: function() {        
            // Loop through the collection...
            $D.batch ( QL.utilities.Coremetrics.cmCollectManualTagsByPrefix( prefixObject.sitePromotionTag ), function ( el ) {
                var notationBreakDown = QL.utilities.Coremetrics.cmProcessManualTagsByElementPrefixAndError( el, prefixObject.sitePromotionTag, manualTagErrorObect.sitePromotionTag );
                $D.setAttribute( el, 'manual_cm_sp', notationBreakDown[1] + manualTagDelimeter + notationBreakDown[2] + manualTagDelimeter + notationBreakDown[3] );
                QL.utilities.Coremetrics.preventDoubleCounting( el, 'manual_cm_sp' );
            } );
        },

        /**
         * Collects all elements specified for Conversion Event Tagging and attaches invalid
         * attributes post render
         *
         */
        cmPrepareConversionEventTags: function() {        
            // Loop through the collection...
            $D.batch ( QL.utilities.Coremetrics.cmCollectManualTagsByPrefix( prefixObject.conversionEventTag ), function ( el ) {
                var notationBreakDown = QL.utilities.Coremetrics.cmProcessManualTagsByElementPrefixAndError( el, prefixObject.conversionEventTag, manualTagErrorObect.conversionEventTag ),
                    clickCount = 0;
                cmCreateConversionEventTag( notationBreakDown[2], '1', notationBreakDown[1], notationBreakDown[3] );
                $E.on( el, 'click', function( ev ) {
                    if ( clickCount > 0 ) {
                        return;
                    }
                    cmCreateConversionEventTag( notationBreakDown[2], '2', notationBreakDown[1], notationBreakDown[3] );
                    clickCount++;
                } );
            } );
        },

        /**
         * Prevent Double(triple, quadruple) counting of site promotion
         * tags
         *
         */
        preventDoubleCounting: function( anchor, className ) {
            $E.on( anchor, 'click', function( ev ) {
                if ( $D.getAttribute( anchor, className ) ) {
                    $D.setAttribute( anchor, className, '' );
                }
            } );
        },

        /**
         * Method to launch Technical Properties Tag  
         *
         */
        launchTechProps: function() {
            // Check for required parameters, pass in optional as null
            if ( ! cmTagParameterObject.PageID ) { 
                throw new Error( 'The cmCreateTechPropsTag method requires a PageID parameter. Please review Core Metrics documentation for instruction.' );
            }
            cmCreateTechPropsTag( cmTagParameterObject.PageID, cmTagParameterObject.CategoryID, null );
            $C.set( 'TechPropsLaunch', true, { path: '/' } );

        },

        /**
         * Method to launch Pageview Tag  
         *
         */
        launchPageview: function() {
            // Check for required parameters, pass in optional as null
            if ( ! cmTagParameterObject.PageID ) { 
                //throw new Error( 'The cmCreatePageviewTag method requires a PageID parameter. Please review Core Metrics documentation for instruction.' );
                return;
            }
            cmCreatePageviewTag( cmTagParameterObject.PageID, cmTagParameterObject.CategoryID, cmTagParameterObject.SearchTerm, cmTagParameterObject.NumSearchResults );
        },

        /**
         * Method to launch Productview Tag 
         *
         */
        launchProductview: function( notPageView ) {
            // Check for required parameters, pass in optional as null
            if ( ! cmTagParameterObject.ProductID ) { 
                throw new Error( 'The cmCreateProductviewTag method requires both ProductID and ProductName parameters. Please review Core Metrics documentation for instruction.' );        
            }
            
            var pageView = ( ! notPageView ) ? 'Y' : 'N';
            
            cmCreateProductviewTag( cmTagParameterObject.ProductID, cmTagParameterObject.ProductName, cmTagParameterObject.CategoryID, cmTagParameterObject.PageID, pageView );
        },

        /**
         * Method to launch Productselection Tag  
         *
         */
        launchProductselection: function() {
            // Check for required parameters, pass in optional as null
            if ( ! cmTagParameterObject.ProductID ) { 
                throw new Error( 'The cmCreateProductselectionTag method requires both ProductID and ProductName parameters. Please review Core Metrics documentation for instruction.' );        
            }
            cmCreateProductselectionTag( cmTagParameterObject.ProductID, 'Lead Form ' + cmTagParameterObject.ProductID, cmTagParameterObject.CategoryID );
            cmDisplayProductselection();
        },

        /**
         * Method to launch Productcompletion Tag  
         *
         */
        launchProductcompletion: function() {
            // Check for required parameters, pass in optional as null
            if ( ! cmTagParameterObject.ProductID || ! cmTagParameterObject.ProductName || ! cmTagParameterObject.CustomerID || ! cmTagParameterObject.TransactionID ) { 
                throw new Error( 'The cmCreateProductcompletionTag method requires ProductID, ProductName, CustomerID and TransactionID parameters. Please review Core Metrics documentation for instruction.' );
            }
            cmCreateProductcompletionTag( cmTagParameterObject.ProductID, cmTagParameterObject.ProductName, cmTagParameterObject.CustomerID, cmTagParameterObject.TransactionID, cmTagParameterObject.CategoryID );
            cmDisplayProductcompletions();
        },

        /**
         * Method to launch Error Tag  
         *
         */
        launchError: function() {
            // Check for required parameters, pass in optional as null
            if ( ! cmTagParameterObject.PageID ) { 
                throw new Error( 'The cmCreateErrorTag method requires a PageID parameter. Please review Core Metrics documentation for instruction.' );        
            }
            cmCreateErrorTag( cmTagParameterObject.PageID, cmTagParameterObject.CategoryID );
        },

        /**
         * Method to launch Transaction Tag  
         *
         */
        launchTransaction: function() {
            // Check for required parameters, pass in optional as null
            if ( ! cmTagParameterObject.TransactionID || ! cmTagParameterObject.CustomerID ) { 
                throw new Error( 'The cmCreateTransactionTag method requires both CustomerID and TransactionID parameters. Please review Core Metrics documentation for instruction.' );
            }
            cmCreateTransactionTag( cmTagParameterObject.TransactionID, cmTagParameterObject.CustomerID, cmTagParameterObject.City, cmTagParameterObject.State, cmTagParameterObject.Zip );            
        },

        /**
         * Method to launch Conversion Event Tag  
         *
         */
        launchConversionEvent: function() {
            // Check for required parameters, pass in optional as null
            if ( ! cmTagParameterObject.EventID || ! cmTagParameterObject.ActionType ) { 
                throw new Error( 'The cmCreateConversionEventTag method requires both EventID and ActionType parameters. Please review Core Metrics documentation for instruction.' );        
            }
            cmCreateConversionEventTag( cmTagParameterObject.EventID, cmTagParameterObject.ActionType, cmTagParameterObject.EventCategoryID, cmTagParameterObject.Points );
        },

        /**
         * Method to launch Registration Tag  
         *
         */        
        launchRegistration: function() {
            // Check for required parameters, pass in optional as null
            if ( ! extraFieldObject.pv2 ) { 
                throw new Error( 'The cmCreateRegistrationTag method requires a CustomerID parameter. Please review Core Metrics documentation for instruction.' );        
            }
            cmCreateRegistrationTag( cmTagParameterObject.PageID, cmTagParameterObject.CategoryID, cmTagParameterObject.CustomerID, cmTagParameterObject.AttributeString );
        },
    
        /**
         * Method to launch Element Tag  
         *
         */    
        launchElement: function( ElementID, CategoryID ) {
            // Check for required parameters, pass in optional as null
            if ( ! ElementID || ! CategoryID ) { 
                throw new Error( 'The cmCreatePageElementTag method requires both ElementID and ElementCategoryID parameters. Please review Core Metrics documentation for instruction.' );        
            }
            cmCreatePageElementTag( ElementID, CategoryID );
        },

        launchCoremetrics: function() {
            QL.utilities.Coremetrics.cmCreateTagParameterObject();
            QL.utilities.Coremetrics.cmLaunchPageLevelTags();
            QL.utilities.Coremetrics.cmLaunchManualTags();
        }
    }
}();

