DelightedD0D
1/18/2018 - 12:51 PM

siteSpeedLogger.js

(function($) {
    'use strict';

    const HOME_PAGE                        = 'home';
    const PACKAGE_RESULTS_PAGE             = 'packages.php';
    const DETAILS_PAGE                     = 'details.php';
    const REVIEW_PAGE                      = 'review.php';
    const BOOK_PAGE                        = 'confirm.php';
    const HOTEL_ONLY_RESULTS_PAGE          = 'hotels.php';

    const HOME_PAGE_SELECTOR               = '.at-tp-submit:contains(Search)';                          // search button
    const PACKAGE_RESULTS_PAGE_SELECTOR    = '.result .js-fb-track.btn-bookit:contains(Select Hotel)';  // select hotel button
    const HOTEL_ONLY_RESULTS_PAGE_SELECTOR = '.result .js-fb-track.btn-bookit:contains(Select Hotel)';  // select hotel button
    const DETAILS_PAGE_SELECTOR            = '.room .select-button.at-details-select:contains(Select)'; // select room button
    const REVIEW_PAGE_SELECTOR             = '#trip-summary .continue-btn:contains(CONTINUE)';          // continue to book button
    const BOOK_PAGE_SELECTOR               = '.agree-terms-container .continue-btn:contains(BOOK IT)';  // book button
    const HOME_PAGE_PAGE_FLIGHT_SELECTOR   = '.trip-select:contains(Flight) > span.icon-checkbox';      // flight package check box

    const PAGE_SELECTORS = [
        HOME_PAGE_SELECTOR,
        PACKAGE_RESULTS_PAGE_SELECTOR,
        HOTEL_ONLY_RESULTS_PAGE_SELECTOR,
        DETAILS_PAGE_SELECTOR,
        REVIEW_PAGE_SELECTOR,
        BOOK_PAGE_SELECTOR
    ];


    var elementWatchTimer;

    /**
     * Poll the DOM for an element and call a callback with the time it was found
     *
     * @param {string}   selector
     * @param {function} callback
     */
    function waitForElement(selector, callback) {
        elementWatchTimer = setInterval(function(){
            if ($(selector).length > 0) {
                clearInterval(elementWatchTimer);
                callback(moment());
            }
        }, 100);
    }

    /**
     * Get the Selector whose presence on the page indicates that the given page is loaded
     *
     * @param  {string} page The name of the page
     * @return {string}
     */
    function getSelectorForPage(page) {
        var selector;
        switch (page) {
            case HOME_PAGE:
                selector = HOME_PAGE_SELECTOR;
                break;
            case PACKAGE_RESULTS_PAGE:
                selector = PACKAGE_RESULTS_PAGE_SELECTOR;
                break;
            case HOTEL_ONLY_RESULTS_PAGE:
                selector = HOTEL_ONLY_RESULTS_PAGE_SELECTOR;
                break;
            case DETAILS_PAGE:
                selector = DETAILS_PAGE_SELECTOR;
                break;
            case REVIEW_PAGE:
                selector = REVIEW_PAGE_SELECTOR;
                break;
            case BOOK_PAGE:
                selector = BOOK_PAGE_SELECTOR;
                break;
        }
        return selector;
    }

    /**
     * Get time it took to load in seconds with precision
     *
     * @param startedAt
     * @param loadedAt
     */
    function getLoadTime(startedAt, loadedAt) {
        var duration = moment.duration(loadedAt.diff(startedAt));
        return ((duration.asMilliseconds() % 10000) / 1000).toFixed(2);
    }

    /**
     * Set the time the page was "fully loaded"
     * ie, ready to click the button to go to the next page
     *
     * @param page
     * @param loadedTime
     */
    function setLoadTimeForPage(page, loadedTime) {
        var timeTest               = getTimeTest();
        timeTest[page].loadedAt= loadedTime;
        timeTest[page].loadTime = getLoadTime(timeTest[page].startedAt, loadedTime);
        setLocal('timeTest', timeTest);
    }

    /**
     * Get a value from our storage
     * like localStorage but shared across subdomains
     *
     * @param key
     */
    function getLocal(key) {
        return GM_getValue(key);
    }

    /**
     * Set a value to our storage
     * like localStorage but shared across subdomains
     *
     * @param key
     * @param data
     */
    function setLocal(key,data) {
        GM_setValue(key, data);
    }

    /**
     * Set the start loading time for the current page but
     * only if if wasn't already set elsewhere
     *
     * @param page
     * @param startTime
     */
    function setStartTimeForPage(page, startTime) {
        var timeTest = getTimeTest();
        if (!timeTest[page]) {
            timeTest[page] = {};
        }
        // set time when page started to load, if it wasnt already set elsewhere
        var message = 'Time test initiated on previous page...';
        if (!timeTest[page].startedAt) {
            message = 'Initiating time test';
            timeTest[page].startedAt = startTime;
            setLocal('timeTest', timeTest);
        }
        logTimeTestStage(message);
    }


    /**
     * Get the current time test data from storage
     */
    function getTimeTest() {
        return getLocal('timeTest') || {};
    }

    /**
     * Log a table to the console displaying all the time test data
     */
    function displayResultsTable() {
        var timeTest = getTimeTest();
        var displayData = [];
        var elapsedTime = 0;

        $.each(timeTest,function (page, data) {
            var row = {};

            row['Page Name']   = page;
            row['Load Start']  = moment(data.startedAt).tz('America/New_York').format('ddd, MMM Do YYYY, h:mm:ss a z');
            row['Load Finish'] = moment(data.loadedAt).tz('America/New_York').format('ddd, MMM Do YYYY, h:mm:ss a z');
            row['Load Time']   = data.loadTime + ' seconds';
            elapsedTime        = elapsedTime + parseFloat(data.loadTime);
            displayData.push(row);
        });
        var row = {};
        row['Page Name']   = 'Total All Pages';
        row['Load Start']  = '-';
        row['Load Finish'] = '-';
        row['Load Time']   = elapsedTime.toFixed(2)+' seconds';
        displayData.push(row);
        console.table(displayData);
    }

    /**
     * Get the current page file name
     * @return {string}
     */
    function getPage() {
        var path = window.location.pathname;
        return path === '/' ? HOME_PAGE : path.split("/").pop();
    }

    /**
     * Returns true if the current search is a package search, on the homepage
     * @return {boolean}
     */
    function isPackageSearch() {
        return $(HOME_PAGE_PAGE_FLIGHT_SELECTOR).length > 0;
    }

    /**
     * Log some info to the console to show each stage of the time test
     *
     * @param {string} message Message stating what stage we are logging
     */
    function logTimeTestStage(message) {
        console.log('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
        console.log(message+'----------------------------------------------------------------------------------------------------------------------------------------------------------');
        console.log(getTimeTest());
        console.log('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
        console.warn('*** Tables only print if the console is open. Use window.reprocessTimeTestTable() to reprocess the table of data, in case the console was closed when the page was loaded. ***');
        console.log('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');

    }

    /**
     * Get the next page in the progression of the funnel after the given page
     * @param currentPage
     * @return {string}
     */
    function getNextPage(currentPage) {
        var nextPage;
        switch (currentPage) {
            case HOME_PAGE:
                // if the flight box is checked it the TP, package results is next
                if (isPackageSearch()) {
                    nextPage = PACKAGE_RESULTS_PAGE;
                } else {
                    nextPage = HOTEL_ONLY_RESULTS_PAGE;
                }
                break;
            case HOTEL_ONLY_RESULTS_PAGE:
            case PACKAGE_RESULTS_PAGE:
                nextPage = DETAILS_PAGE;
                break;
            case DETAILS_PAGE:
                nextPage = REVIEW_PAGE;
                break;
            case REVIEW_PAGE:
                nextPage = BOOK_PAGE;
                break;
        }
        return nextPage;
    }

    /**
     * Start logging page time data
     */
    function beginLoggingPageTimeTestInfo() {
        var page     = getPage();
        var selector = getSelectorForPage(page);

        if (page === HOME_PAGE) {
            // if this is the home page, reset our metrics
            setLocal('timeTest', null);
        }

        setStartTimeForPage(page, moment());;

        // wait for the page to be fully "loaded", ie, we can move to the next page
        waitForElement(selector, function(loadedTime){
            setLoadTimeForPage(page, loadedTime);
            logTimeTestStage('Finished time test');
            displayResultsTable();
        });
    }

    /**
     * When user clicks the search button on homepage
     * start the timer for the results page
     */
    var pageButtonSelectors = PAGE_SELECTORS.join(', ');
    var userClickedButton   = true;
    $(document).on('mousedown', pageButtonSelectors, function (e) {
        // when the user clicks the button
        // stop the navigation event so we can log the start time
        if (userClickedButton) {
            e.preventDefault();
            userClickedButton = false;
            logTimeTestStage('Initiating time test for next page');
            var nextPage = getNextPage(getPage());
            setStartTimeForPage(nextPage, moment());
            // now re-click the button
            var $this=$(this);
            $this.click();
        }
    });

    // set this on the main window so users can display the table if the console
    // was closed when it ran originally since they cant access it directly
    unsafeWindow.reprocessTimeTestTable = displayResultsTable;


    // debugger;                   // un-comment for easier debugging
    // setLocal('timeTest',null);  // un-comment to clear data for testing
    beginLoggingPageTimeTestInfo(); // kick off the logging

})(jQuery);