<commit_before>'use strict';

// Statistics controller
angular.module('statistics').controller('StatisticsController', ['$scope', '$stateParams', '$location', 'Authentication', 'Animeitems', 'Mangaitems', 'Characters', 'ListService', 'ItemService', 'CharacterService', 'StatisticsService', '$filter', 'spinnerService',
	function($scope, $stateParams, $location, Authentication, Animeitems, Mangaitems, Characters, ListService, ItemService, CharacterService, StatisticsService, $filter, spinnerService) {
		$scope.authentication = Authentication;

        // If user is not signed in then redirect back to signin.
		if (!$scope.authentication.user) $location.path('/signin');

        $scope.view = 'Anime';
        $scope.detail = {
            isVisible: false,
            year: '',
            division: '',
            divisionText: '',
            history: 'months',
            summary: {
                type: '',
                isVisible: false
            },
            isEpisodeRatings: false
        };
        $scope.commonArrays = ListService.getCommonArrays('statistics');
        $scope.filterConfig = {
            show: {
                tag: false,
                series: false,
                voice: false
            },
            sort: {
                tag: {
                    type: 'ratingWeighted',
                    reverse: true
                },
                tagDetail: {
                    type: 'count',
                    reverse: true
                },
                series: {
                    type: 'count',
                    reverse: true
                },
                voice: {
                    type: 'count',
                    reverse: true
                },
            },
            search: {
                tag: '',
                tagDetail: '',
                series: '',
                voice: ''
            }
        };
        $scope.overview = {}; //holds summary/overview details.
        $scope.gender = {}; //holds gender summary details.
        $scope.statTags = []; //for tag statistics;
        $scope.ratingsDistribution = []; //counts for each rating.
        $scope.statSeries = []; //for series statistics;
        $scope.voiceActors = []; //for voice actor list;
        $scope.historyDetails = {};
        $scope.areTagless = false; //are any items tagless
        $scope.taglessItem = false; //filter variable for showing tagless items.
        //handle getting view items and setting view specific defaults.
        function getItems(view) {
            if (view === 'Anime') {
                $scope.filterConfig.sort.tag.type = 'ratingWeighted'; //stat tag sort
                spinnerService.loading('items', Animeitems.query().$promise.then(function(result) {
                    $scope.items = result;
                }));
            } else if (view === 'Manga') {
                $scope.filterConfig.sort.tag.type = 'ratingWeighted'; //stat tag sort
                spinnerService.loading('items', Mangaitems.query().$promise.then(function(result) {
                    $scope.items = result;
                }));
            } else if (view === 'Character') {
                $scope.filterConfig.sort.tag.type = 'count'; //stat tag sort
                spinnerService.loading('character', Characters.query().$promise.then(function(result) {
                    $scope.items = result;
                }));
            }
        }
        $scope.find = function(view) {
            getItems(view);
        };
        //required for ctrl+v clicks.
        $scope.$watch('view', function(newValue) {
            if ($scope.view !== undefined) {
                getItems(newValue);
                //reset defaults that are shared between views.
                $scope.detail.history = 'months';
                $scope.filterConfig.search.tag = '';
                $scope.detail.isVisible = false;
                $scope.statTags = [];
                $scope.ratingsDistribution = [];
            }
        });
        //watch for items changes...occurs on view change.
        $scope.$watchCollection('items', function() {
            if ($scope.items !== undefined) {
                $scope.statTags = []; //clear to stop multiple views tags appearing.
                if ($scope.view !== 'Character') {
                    $scope.overview = ItemService.buildOverview($scope.items);
                    $scope.historyDetails.months = ItemService.completeByMonth($scope.items);
                    if ($scope.view === 'Anime') {
                        $scope.historyDetails.seasons = ItemService.completeBySeason($scope.items);
                    }
                    $scope.ratingValues = ItemService.getRatingValues($scope.items);
                    $scope.ratingsDistribution = ItemService.buildRatingsDistribution($scope.items);
                    $scope.statTags = ItemService.buildStatTags($scope.items, $scope.ratingValues.averageRating);
                } else if ($scope.view === 'Character') {
                    $scope.statTags = CharacterService.buildCharacterTags($scope.items);
                    $scope.statSeries = CharacterService.buildSeriesList($scope.items);
                    $scope.voiceActors = CharacterService.buildVoiceActors($scope.items);
                    CharacterService.buildGenderDistribution($scope.statTags, $scope.items.length).then(function(result) {
                        $scope.gender = result;
                        $scope.gender[0].colour = 'red';
                        $scope.gender[1].colour = 'green';
                        $scope.gender[2].colour = 'blue';
                    });
                }
            }
        });

        //Builds ratings aggregates.
        function getSummaryFunctions(array) {
            //Check whether to do ratings or episode ratings.
            if (!$scope.detail.isEpisodeRatings) {
                spinnerService.loading('detail', StatisticsService.buildSummaryFunctions(array).then(function(result) {
                    $scope.historyDetails.summaryFunctions = result;
                }));
                if ($scope.detail.summary.isVisible === true) {
                    spinnerService.loading('detail',
                        StatisticsService.buildYearSummary(array, $scope.detail.year, $scope.detail.summary.type).then(function(result) {
                            $scope.historyDetails.yearSummary = result;
                        })
                    );
                }
            } else {
                spinnerService.loading('detail', StatisticsService.buildEpisodeSummaries(array).then(function(result) {
                        console.log(array);
                }));
            }
//            console.log(array, $scope.historyDetails);
        }
        $scope.$watchGroup(['detail.history', 'detail.year', 'detail.division'], function(newValues) {
            if (newValues !== undefined) {
                var filtered = $filter('statisticsDetailFilter')($scope.items, newValues[0], newValues[1], newValues[2]);
                getSummaryFunctions(filtered);
            }
        });

        $scope.toggleEpisodeRatings = function(items) {
            getSummaryFunctions(items);
        };

        $scope.historyDetail = function(year, division, divisionText, summaryType) {
            if ($scope.detail.year === year && $scope.detail.divisionText === divisionText) {
                $scope.detail.isVisible = !$scope.detail.isVisible;
            } else {
                $scope.detail.isVisible = true;
                $scope.detail.year = year;
                $scope.detail.division = division;
                $scope.detail.divisionText = divisionText;
                $scope.detail.summary.type = summaryType;
                $scope.detail.summary.isVisible = (summaryType === undefined) ? false : true;
            }
        };

        $scope.tableDetail = function(type, name) {
            if ($scope.tableDetail[type] === name) {
                $scope.filterConfig.search[type] = '';
                $scope.filterConfig.show[type] = false;
                $scope.tableDetail[type] = '';
                if (type === 'tag') $scope.tableDetail.isEqual = false;
            } else {
                $scope.filterConfig.search[type] = name;
                $scope.tableDetail[type] = name;
                $scope.filterConfig.show[type] = true;
                if (type === 'tag') {
                    $scope.tagDetailResult = CharacterService.buildRelatedCharacterTags($scope.items, name);
                    $scope.tableDetail.isEqual = true;
                }
            }
        };

        /** Using the start date of confirmed 'in-season' shows
         *  to populate the new season attrs. that will work with the new
         *  filters in the hopes accuracy and speed will increase.
         */
        $scope.generateSeasons = function() {
            if ($scope.view === 'Anime') {
                var array = ItemService.setSeason($scope.items, $scope.detailSeasonYear, $scope.detailSeason);
                angular.forEach(array, function(item) {
                    item.$update(function() {
                        console.log(item);
                    }, function(errorResponse) {
                        $scope.error = errorResponse.data.message;
                    });
                });
            }
        };

    }
]);<commit_msg>checked and percentageBar colour tweeks.<commit_after>'use strict';

// Statistics controller
angular.module('statistics').controller('StatisticsController', ['$scope', '$stateParams', '$location', 'Authentication', 'Animeitems', 'Mangaitems', 'Characters', 'ListService', 'ItemService', 'CharacterService', 'StatisticsService', '$filter', 'spinnerService',
	function($scope, $stateParams, $location, Authentication, Animeitems, Mangaitems, Characters, ListService, ItemService, CharacterService, StatisticsService, $filter, spinnerService) {
		$scope.authentication = Authentication;

        // If user is not signed in then redirect back to signin.
		if (!$scope.authentication.user) $location.path('/signin');

        $scope.view = 'Anime';
        $scope.detail = {
            isVisible: false,
            year: '',
            division: '',
            divisionText: '',
            history: 'months',
            summary: {
                type: '',
                isVisible: false
            },
            isEpisodeRatings: false
        };
        $scope.commonArrays = ListService.getCommonArrays('statistics');
        $scope.filterConfig = {
            show: {
                tag: false,
                series: false,
                voice: false
            },
            sort: {
                tag: {
                    type: 'ratingWeighted',
                    reverse: true
                },
                tagDetail: {
                    type: 'count',
                    reverse: true
                },
                series: {
                    type: 'count',
                    reverse: true
                },
                voice: {
                    type: 'count',
                    reverse: true
                },
            },
            search: {
                tag: '',
                tagDetail: '',
                series: '',
                voice: ''
            }
        };
        $scope.overview = {}; //holds summary/overview details.
        $scope.gender = {}; //holds gender summary details.
        $scope.statTags = []; //for tag statistics;
        $scope.ratingsDistribution = []; //counts for each rating.
        $scope.statSeries = []; //for series statistics;
        $scope.voiceActors = []; //for voice actor list;
        $scope.historyDetails = {};
        $scope.areTagless = false; //are any items tagless
        $scope.taglessItem = false; //filter variable for showing tagless items.
        //handle getting view items and setting view specific defaults.
        function getItems(view) {
            if (view === 'Anime') {
                $scope.filterConfig.sort.tag.type = 'ratingWeighted'; //stat tag sort
                spinnerService.loading('items', Animeitems.query().$promise.then(function(result) {
                    $scope.items = result;
                }));
            } else if (view === 'Manga') {
                $scope.filterConfig.sort.tag.type = 'ratingWeighted'; //stat tag sort
                spinnerService.loading('items', Mangaitems.query().$promise.then(function(result) {
                    $scope.items = result;
                }));
            } else if (view === 'Character') {
                $scope.filterConfig.sort.tag.type = 'count'; //stat tag sort
                spinnerService.loading('character', Characters.query().$promise.then(function(result) {
                    $scope.items = result;
                }));
            }
        }
        $scope.find = function(view) {
            getItems(view);
        };
        //required for ctrl+v clicks.
        $scope.$watch('view', function(newValue) {
            if ($scope.view !== undefined) {
                getItems(newValue);
                //reset defaults that are shared between views.
                $scope.detail.history = 'months';
                $scope.filterConfig.search.tag = '';
                $scope.detail.isVisible = false;
                $scope.statTags = [];
                $scope.ratingsDistribution = [];
            }
        });
        //watch for items changes...occurs on view change.
        $scope.$watchCollection('items', function() {
            if ($scope.items !== undefined) {
                $scope.statTags = []; //clear to stop multiple views tags appearing.
                if ($scope.view !== 'Character') {
                    $scope.overview = ItemService.buildOverview($scope.items);
                    $scope.historyDetails.months = ItemService.completeByMonth($scope.items);
                    if ($scope.view === 'Anime') {
                        $scope.historyDetails.seasons = ItemService.completeBySeason($scope.items);
                    }
                    $scope.ratingValues = ItemService.getRatingValues($scope.items);
                    $scope.ratingsDistribution = ItemService.buildRatingsDistribution($scope.items);
                    $scope.statTags = ItemService.buildStatTags($scope.items, $scope.ratingValues.averageRating);
                } else if ($scope.view === 'Character') {
                    $scope.statTags = CharacterService.buildCharacterTags($scope.items);
                    $scope.statSeries = CharacterService.buildSeriesList($scope.items);
                    $scope.voiceActors = CharacterService.buildVoiceActors($scope.items);
                    CharacterService.buildGenderDistribution($scope.statTags, $scope.items.length).then(function(result) {
                        $scope.gender = result;
                        $scope.gender[0].colour = '#c9302c'; //'red'; '#d9534f'; //
                        $scope.gender[1].colour = '#449d44'; //'green';'#5cb85c'; //
                        $scope.gender[2].colour = '#31b0d5'; //'blue';'#5bc0de'; //
                    });
                }
            }
        });

        //Builds ratings aggregates.
        function getSummaryFunctions(array) {
            //Check whether to do ratings or episode ratings.
            if (!$scope.detail.isEpisodeRatings) {
                spinnerService.loading('detail', StatisticsService.buildSummaryFunctions(array).then(function(result) {
                    $scope.historyDetails.summaryFunctions = result;
                }));
                if ($scope.detail.summary.isVisible === true) {
                    spinnerService.loading('detail',
                        StatisticsService.buildYearSummary(array, $scope.detail.year, $scope.detail.summary.type).then(function(result) {
                            $scope.historyDetails.yearSummary = result;
                        })
                    );
                }
            } else {
                spinnerService.loading('detail', StatisticsService.buildEpisodeSummaries(array).then(function(result) {
                        console.log(array);
                }));
            }
//            console.log(array, $scope.historyDetails);
        }
        $scope.$watchGroup(['detail.history', 'detail.year', 'detail.division'], function(newValues) {
            if (newValues !== undefined) {
                var filtered = $filter('statisticsDetailFilter')($scope.items, newValues[0], newValues[1], newValues[2]);
                getSummaryFunctions(filtered);
            }
        });

        $scope.toggleEpisodeRatings = function(items) {
            getSummaryFunctions(items);
        };

        $scope.historyDetail = function(year, division, divisionText, summaryType) {
            if ($scope.detail.year === year && $scope.detail.divisionText === divisionText) {
                $scope.detail.isVisible = !$scope.detail.isVisible;
            } else {
                $scope.detail.isVisible = true;
                $scope.detail.year = year;
                $scope.detail.division = division;
                $scope.detail.divisionText = divisionText;
                $scope.detail.summary.type = summaryType;
                $scope.detail.summary.isVisible = (summaryType === undefined) ? false : true;
            }
        };

        $scope.tableDetail = function(type, name) {
            if ($scope.tableDetail[type] === name) {
                $scope.filterConfig.search[type] = '';
                $scope.filterConfig.show[type] = false;
                $scope.tableDetail[type] = '';
                if (type === 'tag') $scope.tableDetail.isEqual = false;
            } else {
                $scope.filterConfig.search[type] = name;
                $scope.tableDetail[type] = name;
                $scope.filterConfig.show[type] = true;
                if (type === 'tag') {
                    $scope.tagDetailResult = CharacterService.buildRelatedCharacterTags($scope.items, name);
                    $scope.tableDetail.isEqual = true;
                }
            }
        };

        /** Using the start date of confirmed 'in-season' shows
         *  to populate the new season attrs. that will work with the new
         *  filters in the hopes accuracy and speed will increase.
         */
        $scope.generateSeasons = function() {
            if ($scope.view === 'Anime') {
                var array = ItemService.setSeason($scope.items, $scope.detailSeasonYear, $scope.detailSeason);
                angular.forEach(array, function(item) {
                    item.$update(function() {
                        console.log(item);
                    }, function(errorResponse) {
                        $scope.error = errorResponse.data.message;
                    });
                });
            }
        };

    }
]);<|endoftext|>


<commit_before>/* global ImportedStrings */

/// <reference path="Table.js" />
/// <reference path="Strings.js" />
/// <reference path="Headers.js" />

// Builds array of values for each header in receivedHeaderNames.
// This algorithm should work regardless of the order of the headers, given:
//  - The date, if present, is always at the end, separated by a ";".
// Values not attached to a header will not be reflected in output.
var ReceivedRow = function (receivedHeader) {
    var receivedHeaderNames = ["from", "by", "with", "id", "for", "via"];

    // Build array of header locations
    var headerMatches = [];

    this.sourceHeader = receivedHeader;

    // Some bad dates don't wrap UTC in paren - fix that first
    receivedHeader = receivedHeader.replace(/UTC|\(UTC\)/g, "(UTC)");

    // Read out the date first, then clear it from the string
    var iDate = receivedHeader.lastIndexOf(";");

    // No semicolon means no date - or maybe there's one there?
    // Sendgrid is bad about this
    if (iDate === -1) {
        // First try to find a day of the week
        receivedHeader = receivedHeader.replace(/\s*(Mon|Tue|Wed|Thu|Fri|Sat|Sun)/g, "; $1");
        iDate = receivedHeader.lastIndexOf(";")
    }

    if (iDate === -1) {
        // Next we look for year-month-day
        // Swap to month-day-year because IE can't parse it otherwise
        receivedHeader = receivedHeader.replace(/\s*(\d{4})-(\d{1,2})-(\d{1,2})/g, "; $2-$3-$1");
        iDate = receivedHeader.lastIndexOf(";")
    }

    if (iDate !== -1) {
        this.date = receivedHeader.substring(iDate + 1);
        receivedHeader = receivedHeader.substring(0, iDate);
    }

    // Scan for malformed postFix headers
    // Received: by example.com (Postfix, from userid 1001)
    //   id 1234ABCD; Thu, 21 Aug 2014 12:12:48 +0200 (CEST)
    var postFix = receivedHeader.match(/(.*)by (.*? \(Postfix, from userid .*?\))(.*)/);
    if (postFix) {
        this["by"] = postFix[2];
        receivedHeader = postFix[1] + postFix[3];
        receivedHeaderNames = RemoveEntry(receivedHeaderNames, "by");
    }

    // Scan for malformed qmail headers
    // Received: (qmail 10876 invoked from network); 24 Aug 2014 16:13:38 -0000
    postFix = receivedHeader.match(/(.*)\((qmail .*? invoked from .*?)\)(.*)/);
    if (postFix) {
        this["by"] = postFix[2];
        receivedHeader = postFix[1] + postFix[3];
        receivedHeaderNames = RemoveEntry(receivedHeaderNames, "by");
    }

    // Split up the string now so we can look for our headers
    var tokens = receivedHeader.split(/\s+/);

    var iMatch = 0;
    receivedHeaderNames.forEach(function (receivedHeaderName, iHeader) {
        var iToken = -1;
        tokens.some(function (token, i) {
            if (receivedHeaderName === token) {
                iToken = i;
                return true;
            }
        });

        if (-1 !== iToken) {
            headerMatches[iMatch++] = { iHeader: iHeader, iToken: iToken };
        }
    });

    // Next bit assumes headerMatches[x,y] is increasing on y.
    // Sort it so it is.
    headerMatches.sort(function (a, b) { return a.iToken - b.iToken; });

    headerMatches.forEach(function (headerMatch, iMatch) {
        var iNextTokenHeader;
        if (iMatch + 1 < headerMatches.length) {
            iNextTokenHeader = headerMatches[iMatch + 1].iToken;
        } else {
            iNextTokenHeader = tokens.length;
        }

        var headerName = receivedHeaderNames[headerMatch.iHeader];
        if (this[headerName] !== "") { this[headerName] += "; "; }
        this[headerName] = tokens.slice(headerMatch.iToken + 1, iNextTokenHeader).join(" ").trim();
    }, this);

    if (this.date) {
        var milliseconds = this.date.match(/\d{1,2}:\d{2}:\d{2}.(\d+)/);
        var trimDate = this.date.replace(/(\d{1,2}:\d{2}:\d{2}).(\d+)/, "$1");
        this.dateNum = Date.parse(trimDate);
        if (milliseconds && milliseconds.length >= 2) {
            this.dateNum = this.dateNum + Math.floor(parseFloat("0." + milliseconds[1]) * 1000);
        }

        this.date = new Date(trimDate).toLocaleString().replace(/\u200E/g, "");
        this.dateSort = this.dateNum;
    }

    this.delaySort = -1; // Force the "no previous or current time" rows to sort before the 0 second rows
    this.percent = 0;
}

var Received = function () {
    this.receivedRows = [];
};

Received.prototype.tableName = "receivedHeaders";
Received.prototype.receivedRows = [];
Received.prototype.sortColumn = "hop";
Received.prototype.sortOrder = 1;

Received.prototype.exists = function () {
    return this.receivedRows.length > 0;
};

Received.prototype.doSort = function (col) {
    var that = this;
    if (this.sortColumn === col) {
        this.sortOrder *= -1;
    } else {
        this.sortColumn = col;
        this.sortOrder = 1;
    }

    if (this.sortColumn + "Sort" in this.receivedRows[0]) {
        col = col + "Sort";
    }

    this.receivedRows.sort(function (a, b) {
        return that.sortOrder * (a[col] < b[col] ? -1 : 1);
    });
};

function RemoveEntry(stringArray, entry) {
    var i = stringArray.indexOf(entry);
    if (i >= 0) {
        stringArray.splice(i, 1);
    }

    return stringArray;
}

Received.prototype.init = function (receivedHeader) {
    this.receivedRows.push(new ReceivedRow(receivedHeader));
};

Received.prototype.computeDeltas = function () {
    // Process received headers in reverse order
    this.receivedRows.reverse();

    // Parse rows and compute values needed for the "Delay" column
    var iStartTime = 0;
    var iEndTime = 0;
    var iLastTime = NaN;
    var iDelta = 0; // This will be the sum of our positive deltas

    this.receivedRows.forEach(function (row) {
        if (!isNaN(row.dateNum)) {
            if (!isNaN(iLastTime) && iLastTime < row.dateNum) {
                iDelta += row.dateNum - iLastTime;
            }

            iStartTime = iStartTime || row.dateNum;
            iEndTime = row.dateNum;
            iLastTime = row.dateNum;
        }
    });

    iLastTime = NaN;

    this.receivedRows.forEach(function (row, index) {
        row.hop = index + 1;
        row.delay = computeTime(row.dateNum, iLastTime);

        if (!isNaN(row.dateNum) && !isNaN(iLastTime) && iDelta !== 0) {
            row.delaySort = row.dateNum - iLastTime;

            // Only positive delays will get percentage bars. Negative delays will be color coded at render time.
            if (row.delaySort > 0) {
                row.percent = 100 * row.delaySort / iDelta;
            }
        }

        if (!isNaN(row.dateNum)) {
            iLastTime = row.dateNum;
        }
    });

    // Total time is still last minus first, even if negative.
    return iEndTime !== iStartTime ? computeTime(iEndTime, iStartTime) : "";
};

// Computes min/sec from the diff of current and last.
// Returns nothing if last or current is NaN.
function computeTime(current, last) {
    var time = [];

    if (isNaN(current) || isNaN(last)) { return ""; }
    var diff = current - last;
    var iDelay;
    var printedMinutes = false;

    if (Math.abs(diff) < 1000) {
        return "0 " + ImportedStrings.mha_seconds;
    }

    if (diff < 0) {
        time.push(ImportedStrings.mha_negative);
        diff = -diff;
    }

    if (diff >= 1000 * 60) {
        iDelay = Math.floor(diff / 1000 / 60);
        time.push(iDelay, " ");
        if (iDelay === 1) {
            time.push(ImportedStrings.mha_minute);
        } else {
            time.push(ImportedStrings.mha_minutes);
        }

        diff -= iDelay * 1000 * 60;
        printedMinutes = true;
    }

    if (printedMinutes && diff) {
        time.push(" ");
    }

    if (!printedMinutes || diff) {
        iDelay = Math.floor(diff / 1000);
        time.push(iDelay, " ");
        if (iDelay === 1) {
            time.push(ImportedStrings.mha_second);
        } else {
            time.push(ImportedStrings.mha_seconds);
        }
    }

    return time.join("");
}<commit_msg>Simplify some call<commit_after>/* global ImportedStrings */

/// <reference path="Table.js" />
/// <reference path="Strings.js" />
/// <reference path="Headers.js" />

// Builds array of values for each header in receivedHeaderNames.
// This algorithm should work regardless of the order of the headers, given:
//  - The date, if present, is always at the end, separated by a ";".
// Values not attached to a header will not be reflected in output.
var ReceivedRow = function (receivedHeader) {
    var receivedHeaderNames = ["from", "by", "with", "id", "for", "via"];

    // Build array of header locations
    var headerMatches = [];

    this.sourceHeader = receivedHeader;

    // Some bad dates don't wrap UTC in paren - fix that first
    receivedHeader = receivedHeader.replace(/UTC|\(UTC\)/g, "(UTC)");

    // Read out the date first, then clear it from the string
    var iDate = receivedHeader.lastIndexOf(";");

    // No semicolon means no date - or maybe there's one there?
    // Sendgrid is bad about this
    if (iDate === -1) {
        // First try to find a day of the week
        receivedHeader = receivedHeader.replace(/\s*(Mon|Tue|Wed|Thu|Fri|Sat|Sun)/g, "; $1");
        iDate = receivedHeader.lastIndexOf(";")
    }

    if (iDate === -1) {
        // Next we look for year-month-day
        // Swap to month-day-year because IE can't parse it otherwise
        receivedHeader = receivedHeader.replace(/\s*(\d{4})-(\d{1,2})-(\d{1,2})/g, "; $2-$3-$1");
        iDate = receivedHeader.lastIndexOf(";")
    }

    if (iDate !== -1) {
        this.date = receivedHeader.substring(iDate + 1);
        receivedHeader = receivedHeader.substring(0, iDate);
    }

    // Scan for malformed postFix headers
    // Received: by example.com (Postfix, from userid 1001)
    //   id 1234ABCD; Thu, 21 Aug 2014 12:12:48 +0200 (CEST)
    var postFix = receivedHeader.match(/(.*)by (.*? \(Postfix, from userid .*?\))(.*)/);
    if (postFix) {
        this["by"] = postFix[2];
        receivedHeader = postFix[1] + postFix[3];
        receivedHeaderNames = RemoveEntry(receivedHeaderNames, "by");
    }

    // Scan for malformed qmail headers
    // Received: (qmail 10876 invoked from network); 24 Aug 2014 16:13:38 -0000
    postFix = receivedHeader.match(/(.*)\((qmail .*? invoked from .*?)\)(.*)/);
    if (postFix) {
        this["by"] = postFix[2];
        receivedHeader = postFix[1] + postFix[3];
        receivedHeaderNames = RemoveEntry(receivedHeaderNames, "by");
    }

    // Split up the string now so we can look for our headers
    var tokens = receivedHeader.split(/\s+/);

    var iMatch = 0;
    receivedHeaderNames.forEach(function (receivedHeaderName, iHeader) {
        tokens.some(function (token, iToken) {
            if (receivedHeaderName === token) {
                headerMatches[iMatch++] = { iHeader: iHeader, iToken: iToken };
                return true;
            }
        });
    });

    // Next bit assumes headerMatches[x,y] is increasing on y.
    // Sort it so it is.
    headerMatches.sort(function (a, b) { return a.iToken - b.iToken; });

    headerMatches.forEach(function (headerMatch, iMatch) {
        var iNextTokenHeader;
        if (iMatch + 1 < headerMatches.length) {
            iNextTokenHeader = headerMatches[iMatch + 1].iToken;
        } else {
            iNextTokenHeader = tokens.length;
        }

        var headerName = receivedHeaderNames[headerMatch.iHeader];
        if (this[headerName] !== "") { this[headerName] += "; "; }
        this[headerName] = tokens.slice(headerMatch.iToken + 1, iNextTokenHeader).join(" ").trim();
    }, this);

    if (this.date) {
        var milliseconds = this.date.match(/\d{1,2}:\d{2}:\d{2}.(\d+)/);
        var trimDate = this.date.replace(/(\d{1,2}:\d{2}:\d{2}).(\d+)/, "$1");
        this.dateNum = Date.parse(trimDate);
        if (milliseconds && milliseconds.length >= 2) {
            this.dateNum = this.dateNum + Math.floor(parseFloat("0." + milliseconds[1]) * 1000);
        }

        this.date = new Date(trimDate).toLocaleString().replace(/\u200E/g, "");
        this.dateSort = this.dateNum;
    }

    this.delaySort = -1; // Force the "no previous or current time" rows to sort before the 0 second rows
    this.percent = 0;
}

var Received = function () {
    this.receivedRows = [];
};

Received.prototype.tableName = "receivedHeaders";
Received.prototype.receivedRows = [];
Received.prototype.sortColumn = "hop";
Received.prototype.sortOrder = 1;

Received.prototype.exists = function () {
    return this.receivedRows.length > 0;
};

Received.prototype.doSort = function (col) {
    var that = this;
    if (this.sortColumn === col) {
        this.sortOrder *= -1;
    } else {
        this.sortColumn = col;
        this.sortOrder = 1;
    }

    if (this.sortColumn + "Sort" in this.receivedRows[0]) {
        col = col + "Sort";
    }

    this.receivedRows.sort(function (a, b) {
        return that.sortOrder * (a[col] < b[col] ? -1 : 1);
    });
};

function RemoveEntry(stringArray, entry) {
    var i = stringArray.indexOf(entry);
    if (i >= 0) {
        stringArray.splice(i, 1);
    }

    return stringArray;
}

Received.prototype.init = function (receivedHeader) {
    this.receivedRows.push(new ReceivedRow(receivedHeader));
};

Received.prototype.computeDeltas = function () {
    // Process received headers in reverse order
    this.receivedRows.reverse();

    // Parse rows and compute values needed for the "Delay" column
    var iStartTime = 0;
    var iEndTime = 0;
    var iLastTime = NaN;
    var iDelta = 0; // This will be the sum of our positive deltas

    this.receivedRows.forEach(function (row) {
        if (!isNaN(row.dateNum)) {
            if (!isNaN(iLastTime) && iLastTime < row.dateNum) {
                iDelta += row.dateNum - iLastTime;
            }

            iStartTime = iStartTime || row.dateNum;
            iEndTime = row.dateNum;
            iLastTime = row.dateNum;
        }
    });

    iLastTime = NaN;

    this.receivedRows.forEach(function (row, index) {
        row.hop = index + 1;
        row.delay = computeTime(row.dateNum, iLastTime);

        if (!isNaN(row.dateNum) && !isNaN(iLastTime) && iDelta !== 0) {
            row.delaySort = row.dateNum - iLastTime;

            // Only positive delays will get percentage bars. Negative delays will be color coded at render time.
            if (row.delaySort > 0) {
                row.percent = 100 * row.delaySort / iDelta;
            }
        }

        if (!isNaN(row.dateNum)) {
            iLastTime = row.dateNum;
        }
    });

    // Total time is still last minus first, even if negative.
    return iEndTime !== iStartTime ? computeTime(iEndTime, iStartTime) : "";
};

// Computes min/sec from the diff of current and last.
// Returns nothing if last or current is NaN.
function computeTime(current, last) {
    var time = [];

    if (isNaN(current) || isNaN(last)) { return ""; }
    var diff = current - last;
    var iDelay;
    var printedMinutes = false;

    if (Math.abs(diff) < 1000) {
        return "0 " + ImportedStrings.mha_seconds;
    }

    if (diff < 0) {
        time.push(ImportedStrings.mha_negative);
        diff = -diff;
    }

    if (diff >= 1000 * 60) {
        iDelay = Math.floor(diff / 1000 / 60);
        time.push(iDelay, " ");
        if (iDelay === 1) {
            time.push(ImportedStrings.mha_minute);
        } else {
            time.push(ImportedStrings.mha_minutes);
        }

        diff -= iDelay * 1000 * 60;
        printedMinutes = true;
    }

    if (printedMinutes && diff) {
        time.push(" ");
    }

    if (!printedMinutes || diff) {
        iDelay = Math.floor(diff / 1000);
        time.push(iDelay, " ");
        if (iDelay === 1) {
            time.push(ImportedStrings.mha_second);
        } else {
            time.push(ImportedStrings.mha_seconds);
        }
    }

    return time.join("");
}<|endoftext|>


<commit_before>/**
* @license Apache-2.0
*
* Copyright (c) 2021 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

// MODULES //

var path = require( 'path' );
var logger = require( 'debug' );
var isNodeBuiltin = require( '@stdlib/assert/is-node-builtin' );
var readFileSync = require( '@stdlib/fs/read-file' ).sync;
var startsWith = require( '@stdlib/string/starts-with' );
var replace = require( '@stdlib/string/replace' );
var contains = require( '@stdlib/assert/contains' );
var instanceOf = require( '@stdlib/assert/instance-of' );
var pkgDeps = require( '@stdlib/_tools/pkgs/deps' ).sync;
var readJSON = require( '@stdlib/fs/read-json' ).sync;
var standalonePackage = require( './standalone_package.js' );
var prunePackage = require( './prune_package.js' );
var unique = require( './unique.js' );
var getRoot = require( './root.js' );

// VARIABLES //

var debug = logger( 'pkgs:namespace-deps' );
var STDLIB_TYPES = '/// <reference types="@stdlib/types"/>';

// MAIN //

/**
* Return a list of dependencies for the chosen namespace or package.
*
* @param {string} ns - namespace or package name
* @param {number} level - desired dependency level
* @param {boolean} dev - boolean indicating whether to return development dependencies instead
* @throws {Error} unable to read `index.d.ts` file when resolving Typescript definitions
* @returns {Array} dependencies array
*/
function namespaceDeps( ns, level, dev ) {
	var namespacePkgs;
	var fileDeps;
	var manifest;
	var entry;
	var conf;
	var deps;
	var name;
	var task;
	var def;
	var pkg;
	var i;
	var j;

	namespacePkgs = pkgDeps( [ ns ], {
		'dev': dev,
		'dir': getRoot( '' )
	})[ 0 ];
	namespacePkgs = ( dev ) ? namespacePkgs.devDeps : namespacePkgs.deps;
	deps = [];
	try {
		entry = path.dirname( require.resolve( ns ) );
		def = readFileSync( path.join( entry, '..', 'docs', 'types', 'index.d.ts' ), 'utf-8' );
		if ( contains( def, STDLIB_TYPES ) && !dev ) {
			deps.push( '@stdlib/types' );
		}
	} catch ( err ) {
		debug( 'Encountered an error while reading `index.d.ts` file: '+err.message );
	}
	manifest = readJSON( path.join( entry, '..', 'manifest.json' ) );
	if ( !instanceOf( manifest, Error ) ) {
		if ( !dev ) {
			deps.push( '@stdlib/utils/library-manifest' );
		}
		task = manifest.options.task;
		for ( i = 0; i < manifest.confs.length; i++ ) {
			conf = manifest.confs[ i ];
			if ( !dev && conf.task === task ) {
				for ( j = 0; j < conf.dependencies.length; j++ ) {
					if ( !startsWith( conf.dependencies[ j ], ns ) ) {
						deps.push( conf.dependencies[ j ] );
					}
				}
			} else if ( dev ) {
				for ( j = 0; j < conf.dependencies.length; j++ ) {
					if ( !startsWith( conf.dependencies[ j ], ns ) ) {
						deps.push( conf.dependencies[ j ] );
					}
				}
			}
		}
	}
	if ( !namespacePkgs || namespacePkgs.length === 0 ) {
		return unique( deps );
	}
	for ( i = 0; i < namespacePkgs.length; i++ ) {
		pkg = namespacePkgs[ i ];
		if ( startsWith( pkg, ns ) ) {
			fileDeps = namespaceDeps( pkg, level, dev );
			for ( j = 0; j < fileDeps.length; j++ ) {
				if ( !startsWith( replace( fileDeps[ j ], '/', '-' ), replace( ns, '/', '-' ) ) ) {
					deps.push( fileDeps[ j ] );
				}
			}
		} else {
			name = prunePackage( pkg, level );
			name = standalonePackage( name );
			if ( !isNodeBuiltin( name ) ) {
				deps.push( name );
			}
		}
	}
	return unique( deps );
}

// EXPORTS //

module.exports = namespaceDeps;
<commit_msg>Correctly prune deps extracted from manifest files<commit_after>/**
* @license Apache-2.0
*
* Copyright (c) 2021 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

// MODULES //

var path = require( 'path' );
var logger = require( 'debug' );
var isNodeBuiltin = require( '@stdlib/assert/is-node-builtin' );
var readFileSync = require( '@stdlib/fs/read-file' ).sync;
var startsWith = require( '@stdlib/string/starts-with' );
var replace = require( '@stdlib/string/replace' );
var contains = require( '@stdlib/assert/contains' );
var instanceOf = require( '@stdlib/assert/instance-of' );
var pkgDeps = require( '@stdlib/_tools/pkgs/deps' ).sync;
var readJSON = require( '@stdlib/fs/read-json' ).sync;
var standalonePackage = require( './standalone_package.js' );
var prunePackage = require( './prune_package.js' );
var unique = require( './unique.js' );
var getRoot = require( './root.js' );

// VARIABLES //

var debug = logger( 'pkgs:namespace-deps' );
var STDLIB_TYPES = '/// <reference types="@stdlib/types"/>';

// MAIN //

/**
* Return a list of dependencies for the chosen namespace or package.
*
* @param {string} ns - namespace or package name
* @param {number} level - desired dependency level
* @param {boolean} dev - boolean indicating whether to return development dependencies instead
* @throws {Error} unable to read `index.d.ts` file when resolving Typescript definitions
* @returns {Array} dependencies array
*/
function namespaceDeps( ns, level, dev ) {
	var namespacePkgs;
	var fileDeps;
	var manifest;
	var entry;
	var conf;
	var deps;
	var name;
	var task;
	var def;
	var pkg;
	var i;
	var j;

	namespacePkgs = pkgDeps( [ ns ], {
		'dev': dev,
		'dir': getRoot( '' )
	})[ 0 ];
	namespacePkgs = ( dev ) ? namespacePkgs.devDeps : namespacePkgs.deps;
	deps = [];
	try {
		entry = path.dirname( require.resolve( ns ) );
		def = readFileSync( path.join( entry, '..', 'docs', 'types', 'index.d.ts' ), 'utf-8' );
		if ( contains( def, STDLIB_TYPES ) && !dev ) {
			deps.push( '@stdlib/types' );
		}
	} catch ( err ) {
		debug( 'Encountered an error while reading `index.d.ts` file: '+err.message );
	}
	manifest = readJSON( path.join( entry, '..', 'manifest.json' ) );
	if ( !instanceOf( manifest, Error ) ) {
		if ( !dev ) {
			name = prunePackage( '@stdlib/utils/library-manifest', level );
			deps.push( standalonePackage( name ) );
		}
		task = manifest.options.task;
		for ( i = 0; i < manifest.confs.length; i++ ) {
			conf = manifest.confs[ i ];
			if ( !dev && conf.task === task ) {
				for ( j = 0; j < conf.dependencies.length; j++ ) {
					if ( !startsWith( conf.dependencies[ j ], ns ) ) {
						name = prunePackage( conf.dependencies[ j ], level );
						deps.push( standalonePackage( name ) );
					}
				}
			} else if ( dev ) {
				for ( j = 0; j < conf.dependencies.length; j++ ) {
					if ( !startsWith( conf.dependencies[ j ], ns ) ) {
						name = prunePackage( conf.dependencies[ j ], level );
						deps.push( standalonePackage( name ) );
					}
				}
			}
		}
	}
	if ( !namespacePkgs || namespacePkgs.length === 0 ) {
		return unique( deps );
	}
	for ( i = 0; i < namespacePkgs.length; i++ ) {
		pkg = namespacePkgs[ i ];
		if ( startsWith( pkg, ns ) ) {
			fileDeps = namespaceDeps( pkg, level, dev );
			for ( j = 0; j < fileDeps.length; j++ ) {
				if ( !startsWith( replace( fileDeps[ j ], '/', '-' ), replace( ns, '/', '-' ) ) ) {
					deps.push( fileDeps[ j ] );
				}
			}
		} else {
			name = prunePackage( pkg, level );
			name = standalonePackage( name );
			if ( !isNodeBuiltin( name ) ) {
				deps.push( name );
			}
		}
	}
	return unique( deps );
}

// EXPORTS //

module.exports = namespaceDeps;
<|endoftext|>


<commit_before>$(function() {

	/* change these to use a class selector since we added more than one of them */
	if ($('table#manageusers').length) {
		$('#manageusers').DataTable({
                    //dom: 'Vlfrtip',
                    dom: '<"top"li>frt<"bottom"p>V<"clear">',
                    iDisplayLength: 25,
                    lengthMenu: [[25, 50, 100], [25, 50, 100]]
  		  });
	}

	if ($('table#manageschools').length) {
		$('#manageschools').DataTable({
                    dom: '<"top"li>frt<"bottom"p>V<"clear">',
                    iDisplayLength: 25,
                    lengthMenu: [[25, 50, 100], [25, 50, 100]]
                });
	}

        $(".datatable-get-csv").detach().appendTo('#csv-button');
        $('.datatable-get-csv').addClass('btn btn-default').append('   ').removeClass('datatable-get-csv');

	if ($('table#manageschooldetails').length) {
		$('#manageschooldetails').DataTable();
	}

	if ($('table#analyzestudents').length) {
		$('#analyzestudents').DataTable();
	}

	$( "#filter-result" ).change(function() {
		$("#filter-form-result").val($( "#filter-result" ).val());
		$( "#dataGridFormFilter").submit();
	});

	$("[data-toggle='modal']").on('click', function(e) {
		e.preventDefault();
		var $that = $(this);

		var $loading = $(".loading", $that.attr('data-target')).fadeIn();

		if ($(this).attr('data-post-data')) {
			var $form = $($(this).attr('data-post-data'));
			$.ajax({
				url: $(this).attr('data-href'),
				type: 'post',
				data: $form.serialize(),
				dataType: 'json',
				success: function(response) {
					$loading.fadeOut();
					$(".modal-ajax-content", $that.attr('data-target')).html(response.html);
				}
			})
		}
	})

	$('#content-query').focus(function(){
		var query = $(this);
		if (query.val() == 'search text')
			query.val('');
	});

	/* Get the query string into an object for use in the defilter list */
	var urlParams;
	(window.onpopstate = function () {
		var match,
			pl     = /\+/g,  // Regex for replacing addition symbol with a space
			search = /([^&=]+)=?([^&]*)/g,
			decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
			query  = window.location.search.substring(1);

		urlParams = {};
		while (match = search.exec(query))
			urlParams[decode(match[1])] = decode(match[2]);
	})();

	$('.filter_active').click(function () {
		delete urlParams[this.value];
		var newQuery = $.param(urlParams);
		var baseUrl = [location.protocol, '//', location.host, location.pathname].join('');
		window.location = baseUrl+'?'+newQuery;
	})

	/* Sample code for browser extension dev when handling content links */
	var assignmentLink = $('a.extension'), target = 'chrome-extension://maifknjmjnafdaiohogiffkdaebomimn';

	/* Tell the extension what was the last active URL  */
	/*if(window.parent.location.origin === 'chrome-extension://maifknjmjnafdaiohogiffkdaebomimn'){*/
	window.parent.postMessage({operation: 'changeUrl', location: window.location.href}, target);
	/*  }*/

	if (assignmentLink.length) {
		// Hide links if a Google Classroom page is not open
		$(window).on('message', function (event) {
			if (event.originalEvent.origin === target && !event.originalEvent.data.isClassroomOpen) {
				assignmentLink.hide();
			}
		});
		window.parent.postMessage({operation: 'isClassroomOpen'}, target);

		// Click handler for entering fields into the new assignment form
		assignmentLink.click(function(event){
			/*  event.preventDefault();
			 var link   = $(this),
			 title      = link.attr('title'),
			 desc       = link.attr('description'),
			 href       = link.attr('href');*/

			event.preventDefault();
			var link = $(this);
			var message = {title: link.attr('title'), desc: link.attr('description'), href: link.attr('href')};
			window.parent.postMessage(message, target);
		});
	}

    $("#main-menu #management, #main-menu #learning-lab").click(function(event) {
    	event.preventDefault();
    	var submenu = $('ul', $(this).parent());
    	if (submenu.length && submenu.is(':visible')) {
    		submenu.slideUp( "slow", function() {
    		// Animation complete.
    		});
    	} else {
    		submenu.slideDown( "slow", function() {
    		// Animation complete.
    		});
    	}
    });

    if ($('.sublive').length) {
        $('.sublive').parent().show();
    }

	$( "#schoolFilter" ).change(function() {
		var schoolId = $(this).val();

		if (schoolId == 'all') {

			$('select#staffusi option').show();
			$('select#staffusi').val('');

		}else {

			$('select#staffusi option[school != "'+schoolId+'"]').hide();
			$('select#staffusi option[school = "'+schoolId+'"]').show();
			$('select#staffusi option[school = "reset"]').show();
			$('select#staffusi').val('');
		}

	});

	$('a.usermanagement-index-delete').click(function(event) {
		event.preventDefault();
		if (confirm('Are you sure you want to delete this user?')) {
			window.location = this.href;
		}

	})

	if ($('#usermanagement-addedit-authtype input:checkbox').length && $('#usermanagement-addedit-authtype input:checkbox').prop('checked')) {
		$('#usermanagement-addedit-password').hide();
	}

	$('#usermanagement-addedit-authtype input:checkbox').click(function(event) {
		$('#usermanagement-addedit-password').toggle(this.checked == false)
	});
	$('[data-toggle="tooltip"]').tooltip();

});

/* GOOGLE SIGN IN: https://developers.google.com/identity/sign-in/ */
function onSignIn(googleUser) {
	/*console.log(googleUser);*/
	// Useful data for your client-side scripts:
	var profile = googleUser.getBasicProfile();
	//console.log("ID: " + profile.getId()); // Don't send this directly to your server!
	//console.log("Name: " + profile.getName());
	//console.log("Image URL: " + profile.getImageUrl());
	//console.log("Email: " + profile.getEmail());
	// The ID token you need to pass to your backend:

	var id_token = googleUser.getAuthResponse().id_token;
	var uemail = profile.getEmail();
	var name = profile.getName();

	var localbe = 'http://localhost/easol/';
	var devbe = 'http://easol-dev.azurewebsites.net/';
	var staging = 'http://easol-stage.azurewebsites.net/';
	var livebe = '';
	var gloginPosturl = location.href;

	var xhr = new XMLHttpRequest();
	xhr.open('POST', gloginPosturl);
	xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
	xhr.onload = function() {
		if(xhr.responseText=="gloginValid") {
			/* GOOD SIGN IN */
			/*console.log('Signed in as: ' + profile.getName() );*/
			/* alert(profile.getName() + ' is now logged in!');  alert(xhr.responseText);*/
			window.location='student';
		} else {
			/* FAILED SIGN IN */
			console.log('Signed In Error: ' + xhr.responseText);
			signOut();
			/*
			 * we set the error message server side in the session for display on the redirect.
			 * we redirect to the current url but without resending the post data like window.location.reload() would do.
			 */
			window.location.href = window.location.href;
		}
	};
	xhr.send('idtoken=' + id_token + '&uemail=' + uemail);
}
//  END SIGN IN
/* GOOGLE LOGIN - WELL WE NEED TO LOGOUT */
/* Login button must be on same page as logout button - but hidden  - for this to work */
function signOut() { console.log('Attempting Google Logout');
	var auth2 = gapi.auth2.getAuthInstance();
	auth2.signOut().then(function () {
		console.log('User signed out.');
	});
	console.log('End Google Logout');
}

function loading($node) {

	var $box = $node.closest('.loading-box');
	if ($box.length == 0) $box = $node.css('position', 'relative');
	if ($box.find('.loading-img').length == 0) $box.append('

');

	$box.find('.loading-img').fadeIn();
}
function unloading($node) {
	var $box = $node.closest('.loading-box');
	$box.find('.loading-img').fadeOut();
}

<commit_msg>160-162 Reviewed codes about pagination of values #160 and "CSV" button placement #162 - Adjusted the script in custom.js for run every time that change the value of the element Records Per Page.<commit_after>$(function() {

	/* change these to use a class selector since we added more than one of them */
	if ($('table#manageusers').length) {
		$('#manageusers').DataTable({
                    //dom: 'Vlfrtip',
                    dom: '<"top"li>frt<"bottom"p>V<"clear">',
                    iDisplayLength: 25,
                    lengthMenu: [[25, 50, 100], [25, 50, 100]]
  		  });
	}

	if ($('table#manageschools').length) {
		$('#manageschools').DataTable({
                    dom: '<"top"li>frt<"bottom"p>V<"clear">',
                    iDisplayLength: 25,
                    lengthMenu: [[25, 50, 100], [25, 50, 100]]
                });
	}

        $(".datatable-get-csv").detach().appendTo('#csv-button');
        $('.datatable-get-csv').addClass('btn btn-default').append('   ').removeClass('datatable-get-csv');

	if ($('table#manageschooldetails').length) {
		$('#manageschooldetails').DataTable();
	}

	if ($('table#analyzestudents').length) {
		$('#analyzestudents').DataTable();
	}

	$( "#filter-result, #filter-form-result" ).change(function() {
		$("#filter-form-result").val($( "#filter-result , #filter-form-result" ).val());
		$( "#dataGridFormFilter").submit();
	});

	$("[data-toggle='modal']").on('click', function(e) {
		e.preventDefault();
		var $that = $(this);

		var $loading = $(".loading", $that.attr('data-target')).fadeIn();

		if ($(this).attr('data-post-data')) {
			var $form = $($(this).attr('data-post-data'));
			$.ajax({
				url: $(this).attr('data-href'),
				type: 'post',
				data: $form.serialize(),
				dataType: 'json',
				success: function(response) {
					$loading.fadeOut();
					$(".modal-ajax-content", $that.attr('data-target')).html(response.html);
				}
			})
		}
	})

	$('#content-query').focus(function(){
		var query = $(this);
		if (query.val() == 'search text')
			query.val('');
	});

	/* Get the query string into an object for use in the defilter list */
	var urlParams;
	(window.onpopstate = function () {
		var match,
			pl     = /\+/g,  // Regex for replacing addition symbol with a space
			search = /([^&=]+)=?([^&]*)/g,
			decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
			query  = window.location.search.substring(1);

		urlParams = {};
		while (match = search.exec(query))
			urlParams[decode(match[1])] = decode(match[2]);
	})();

	$('.filter_active').click(function () {
		delete urlParams[this.value];
		var newQuery = $.param(urlParams);
		var baseUrl = [location.protocol, '//', location.host, location.pathname].join('');
		window.location = baseUrl+'?'+newQuery;
	})

	/* Sample code for browser extension dev when handling content links */
	var assignmentLink = $('a.extension'), target = 'chrome-extension://maifknjmjnafdaiohogiffkdaebomimn';

	/* Tell the extension what was the last active URL  */
	/*if(window.parent.location.origin === 'chrome-extension://maifknjmjnafdaiohogiffkdaebomimn'){*/
	window.parent.postMessage({operation: 'changeUrl', location: window.location.href}, target);
	/*  }*/

	if (assignmentLink.length) {
		// Hide links if a Google Classroom page is not open
		$(window).on('message', function (event) {
			if (event.originalEvent.origin === target && !event.originalEvent.data.isClassroomOpen) {
				assignmentLink.hide();
			}
		});
		window.parent.postMessage({operation: 'isClassroomOpen'}, target);

		// Click handler for entering fields into the new assignment form
		assignmentLink.click(function(event){
			/*  event.preventDefault();
			 var link   = $(this),
			 title      = link.attr('title'),
			 desc       = link.attr('description'),
			 href       = link.attr('href');*/

			event.preventDefault();
			var link = $(this);
			var message = {title: link.attr('title'), desc: link.attr('description'), href: link.attr('href')};
			window.parent.postMessage(message, target);
		});
	}

    $("#main-menu #management, #main-menu #learning-lab").click(function(event) {
    	event.preventDefault();
    	var submenu = $('ul', $(this).parent());
    	if (submenu.length && submenu.is(':visible')) {
    		submenu.slideUp( "slow", function() {
    		// Animation complete.
    		});
    	} else {
    		submenu.slideDown( "slow", function() {
    		// Animation complete.
    		});
    	}
    });

    if ($('.sublive').length) {
        $('.sublive').parent().show();
    }

	$( "#schoolFilter" ).change(function() {
		var schoolId = $(this).val();

		if (schoolId == 'all') {

			$('select#staffusi option').show();
			$('select#staffusi').val('');

		}else {

			$('select#staffusi option[school != "'+schoolId+'"]').hide();
			$('select#staffusi option[school = "'+schoolId+'"]').show();
			$('select#staffusi option[school = "reset"]').show();
			$('select#staffusi').val('');
		}

	});

	$('a.usermanagement-index-delete').click(function(event) {
		event.preventDefault();
		if (confirm('Are you sure you want to delete this user?')) {
			window.location = this.href;
		}

	})

	if ($('#usermanagement-addedit-authtype input:checkbox').length && $('#usermanagement-addedit-authtype input:checkbox').prop('checked')) {
		$('#usermanagement-addedit-password').hide();
	}

	$('#usermanagement-addedit-authtype input:checkbox').click(function(event) {
		$('#usermanagement-addedit-password').toggle(this.checked == false)
	});
	$('[data-toggle="tooltip"]').tooltip();

});

/* GOOGLE SIGN IN: https://developers.google.com/identity/sign-in/ */
function onSignIn(googleUser) {
	/*console.log(googleUser);*/
	// Useful data for your client-side scripts:
	var profile = googleUser.getBasicProfile();
	//console.log("ID: " + profile.getId()); // Don't send this directly to your server!
	//console.log("Name: " + profile.getName());
	//console.log("Image URL: " + profile.getImageUrl());
	//console.log("Email: " + profile.getEmail());
	// The ID token you need to pass to your backend:

	var id_token = googleUser.getAuthResponse().id_token;
	var uemail = profile.getEmail();
	var name = profile.getName();

	var localbe = 'http://localhost/easol/';
	var devbe = 'http://easol-dev.azurewebsites.net/';
	var staging = 'http://easol-stage.azurewebsites.net/';
	var livebe = '';
	var gloginPosturl = location.href;

	var xhr = new XMLHttpRequest();
	xhr.open('POST', gloginPosturl);
	xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
	xhr.onload = function() {
		if(xhr.responseText=="gloginValid") {
			/* GOOD SIGN IN */
			/*console.log('Signed in as: ' + profile.getName() );*/
			/* alert(profile.getName() + ' is now logged in!');  alert(xhr.responseText);*/
			window.location='student';
		} else {
			/* FAILED SIGN IN */
			console.log('Signed In Error: ' + xhr.responseText);
			signOut();
			/*
			 * we set the error message server side in the session for display on the redirect.
			 * we redirect to the current url but without resending the post data like window.location.reload() would do.
			 */
			window.location.href = window.location.href;
		}
	};
	xhr.send('idtoken=' + id_token + '&uemail=' + uemail);
}
//  END SIGN IN
/* GOOGLE LOGIN - WELL WE NEED TO LOGOUT */
/* Login button must be on same page as logout button - but hidden  - for this to work */
function signOut() { console.log('Attempting Google Logout');
	var auth2 = gapi.auth2.getAuthInstance();
	auth2.signOut().then(function () {
		console.log('User signed out.');
	});
	console.log('End Google Logout');
}

function loading($node) {

	var $box = $node.closest('.loading-box');
	if ($box.length == 0) $box = $node.css('position', 'relative');
	if ($box.find('.loading-img').length == 0) $box.append('

');

	$box.find('.loading-img').fadeIn();
}
function unloading($node) {
	var $box = $node.closest('.loading-box');
	$box.find('.loading-img').fadeOut();
}

<|endoftext|>


<commit_before>var path = require('path')
    , fs = require('fs')
    , _ = require('underscore')
    , async = require('async')
    , dir = path.dirname(require.main.filename) + '/output/' // path to the root
    , barstool = require('./lib/barstool')
    , sheetData
    , wstream
    , speakers = {}
    ;

// =====
// GENERAL USE UTILITIES
// =====

// Helper function not called in the build process ...
// Processes each row, outputs it with double quotes escaped and characters
// correctly transformed.
function prepareRow(row){
  return _.map(row, function(item,key){
    // Escape double quotes inside and add double quotes around each item
    return '"' +
           item
           .replace(/"/g,'\'')
           .replace(/\n/g,'')
           .replace(/[\u2018\u2019]/g, '\'')
           .replace(/[\u201C\u201D]/g, '\'')
           + '"';
  })
  .join(',')+'\n';
}

// Create our writestream to @fileName (STRING)
function openStream(cb,fileName){
  // Clear the file
  fs.writeFile(dir + fileName, '', function(){})
  // Open a writestream
  wstream = fs.createWriteStream(dir + fileName);
  cb(null);
}

// Close the open write stream
function closeStream(cb){
  wstream.end();
  cb(null);
}

// Write our headers from @headers (STRING)
function writeHeaders(cb,headers){
  wstream.write(headers);
  cb(null);
};

// Get all worksheets, assign to global-scope `sheetData` variable
function getSheetData(cb){
  barstool.getAllWorksheets(function(err,data){
    if (err) throw err;
    sheetData = data;
    cb(err);
  });
}

// =====
// PARSE AND WRITE SPECIFIC DATA
// =====

// Write the mainstage/schedule block sessions
function writePrimarySessions(cb,sheetName){
  async.eachSeries(sheetData[sheetName], function(row,callback){
    var entry = [];

    // Name (required)
    entry.push(row['sessiontitle']);
    // Description
    entry.push(row['sessiondescription']);

    // ... do some data crunching to format date ...
    var time = row['timeblock'].split(' - ');
    // Start time (required): "3/13/2014 1:11:00 PM"
    entry.push(row['date'] + ' ' + time[0]);
    // End time (required): "3/13/2014 1:11:00 PM"
    entry.push(row['date'] + ' ' + time[1]);

    // Location
    // ... we aren't using these for now ...
    entry.push('');

    // Session Tracks
    entry.push(row['sessiontype']);

    // Filters
    // ... we aren't using these for now ...
    entry.push('');

    // Speaker IDs
    var speakers = [];
    if (row['speakersafe1'].length > 0) { speakers.push(row['speakersafe1']) }
    if (row['speakersafe2'].length > 0) { speakers.push(row['speakersafe2']) }
    if (row['speakersafe3'].length > 0) { speakers.push(row['speakersafe3']) }
    if (row['speakersafe4'].length > 0) { speakers.push(row['speakersafe4']) }
    if (row['speakersafe5'].length > 0) { speakers.push(row['speakersafe5']) }
    if (row['speakersafe6'].length > 0) { speakers.push(row['speakersafe6']) }
    entry.push(speakers.join(','));

    // Link URLs
    // ... we aren't using these for now ...
    entry.push('');

    // Session ID (unique)
    entry.push(row['sessionuniquekey']);

    // Write the prepared row
    wstream.write( prepareRow(entry) );
    callback(null);
  },function(err){ cb(err) });
}

// Write the breakout sessions
function writeBreakoutSessions(cb,sheetName){
  async.eachSeries(sheetData[sheetName], function(row,callback){
    // If the breakout isn't confirmed, pass on it
    if (row['status'] !== 'Confirmed') { callback(null); }
    else {
      // Otherwise, proceed
      var entry = [];

      // Name (required)
      entry.push(row['sessiontitle']);
      // Description
      entry.push(row['sessiondescription']);

      // ... do some data crunching to format date ...
      var time = row['timeblock'].split(' - ');
      // Start time (required): "3/13/2014 1:11:00 PM"
      entry.push(row['date'] + ' ' + time[0]);
      // End time (required): "3/13/2014 1:11:00 PM"
      entry.push(row['date'] + ' ' + time[1]);

      // Location
      // ... we aren't using these for now ...
      entry.push(row['roomlocation']);

      // Session Tracks
      entry.push(row['sessionaudience']);

      // Filters
      // ... we aren't using these for now ...
      entry.push('');

      // Speaker IDs
      var speakers = [];
      if (row['speakersafe1'].length > 0) { speakers.push(row['speakersafe1']) }
      if (row['speakersafe2'].length > 0) { speakers.push(row['speakersafe2']) }
      if (row['speakersafe3'].length > 0) { speakers.push(row['speakersafe3']) }
      if (row['speakersafe4'].length > 0) { speakers.push(row['speakersafe4']) }
      if (row['speakersafe5'].length > 0) { speakers.push(row['speakersafe5']) }
      if (row['speakersafe6'].length > 0) { speakers.push(row['speakersafe6']) }
      entry.push(speakers.join(','));

      // Link URLs
      // ... we aren't using these for now ...
      entry.push('');

      // Session ID (unique)
      entry.push(row['sessionuniquekey']);

      // Write the prepared row
      wstream.write( prepareRow(entry) );
      callback(null);
    }
  },function(err){ cb(err) });
}

function prepSpeakers(cb,sheetName){
  async.eachSeries(sheetData[sheetName], function(row,callback){
    // How many speakers do we support?
    var numSpeakers = 6,
        count = 0;

    async.until(
      function(){ return count == numSpeakers; },
      function(callllback){
        count++;
        var speaker_key = row['speakersafe'+count];

        // If we don't have speaker, skip
        if (row['speakername'+count].length === 0) {
          return callllback(null);
        }

        // Have we already seen this speaker?
        // console.log('trying ' + speaker_key + ' ... type: ' + typeof(speakers[speaker_key]));
        if (typeof(speakers[speaker_key]) === "object") {
          console.log('HIT! ' + speaker_key)
          // Then just add to it, don't make a new one
          speakers[speaker_key]['Session IDs'] = speakers[speaker_key]['Session IDs'] + ',' + row['sessiontitle'].substr(0,40).replace(/\s/g,'-').replace(/[^\w-]/g,'').toLowerCase();
          // We're done, move along
          return callllback(null);
        }

        // OK, we've got a speaker, so go ahead and get it ready...
        var speaker = {};

        // Grab the name and safe/key, this is easy(ish)
        var name = row['speakername'+count].replace('Dr. ','').replace('Mayor ','')

        speaker['First Name (required)'] = name.substr(0,name.indexOf(' '));
        speaker['Last Name (required)'] = name.substr(name.indexOf(' ')+1);
        speaker['Speaker ID'] = row['speakersafe'+count];

        // Get the title/org
        if ( row['speakertitleorg'+count].indexOf(',') > 0 ) {
          // If there's a comma, i.e. if there's a 'Title here, Org here', split it up
          speaker['Title'] = row['speakertitleorg'+count].substr(0,row['speakertitleorg'+count].indexOf(','));
          speaker['Company'] = row['speakertitleorg'+count].substr(row['speakertitleorg'+count].indexOf(',')+1).replace(' ','');
        } else {
          // No comma, so shove everything in the title and make org empty
          speaker['Title'] = row['speakertitleorg'+count];
          speaker['Company'] = "";
        }

        // Figure out if we can grab a link to a photo, if we can, give it
        if ( row['speakerphoto'+count] == 'STAFF' ) {
          speaker['Image URL'] = 'http://codeforamerica.org/media/images/people/' + row['speakersafe'+count] + '.jpg';
        } else if ( row['speakerphoto'+count] == 'SPEAKER' ) {
          speaker['Image URL'] = 'http://codeforamerica.org/media/images/summit/2015/speakers/thumbnails/' + row['speakersafe'+count] + '.jpg';
        } else {
          speaker['Image URL'] = "";
        }

        // Give a link to the session ID
        // speaker['Session IDs'] = row['sessionuniquekey'];
        // ...temporarily, since there are no unique keys, make one...
        // ...first 40 chars of session title, replace spaces with dashes, replace non-alpha chars with nothing (except those dashes), lowercase it
        speaker['Session IDs'] = row['sessiontitle'].substr(0,40).replace(/\s/g,'-').replace(/[^\w-]/g,'').toLowerCase();

        // ... we don't have these ...
        speaker['Description'] = "";
        speaker['Website'] = "";
        speaker['Twitter Handle'] = "";
        speaker['Facebook URL'] = "";
        speaker['LinkedIn URL'] = "";
        speaker['Attendee ID'] = "";

        // speakers.push(speaker);
        speakers[row['speakersafe'+count]] = speaker;
        callllback(null);

      },
      function(err){ callback(null); }
    );
  },function(err){
    cb(err);
  });
}

function writeSpeakers(cb){
  async.forEachOfSeries(speakers, function(value,key,callback){
    var entry = [];

    // "First Name (required)"
    entry.push(value["First Name (required)"]);
    // "Last Name (required)"
    entry.push(value["Last Name (required)"]);
    // "Title"
    entry.push(value["Title"]);
    // "Company"
    entry.push(value["Company"]);
    // "Description"
    entry.push(value["Description"]);
    // "Image URL"
    entry.push(value["Image URL"]);
    // "Website"
    entry.push(value["Website"]);
    // "Twitter Handle"
    entry.push(value["Twitter Handle"]);
    // "Facebook URL"
    entry.push(value["Facebook URL"]);
    // "LinkedIn URL"
    entry.push(value["LinkedIn URL"]);
    // "Session IDs"
    entry.push(value["Session IDs"]);
    // "Attendee ID"
    entry.push(value["Attendee ID"]);
    // "Speaker ID"
    entry.push(value["Speaker ID"]);

    // Write the prepared row
    wstream.write( prepareRow(entry) );
    callback(null);
  },
  function(err){
    cb(null);
  });
}

// =====
// PERFORM ALL THE TASKS
// =====

async.series([
  function(cb){ getSheetData(cb)
              },
  function(cb){ openStream(cb,'session-import.csv')
              },
  function(cb){ writeHeaders(cb,'"Name (required)","Description","Start Time (required)","End Time (required)","Location","Session Tracks","Filters","Speaker IDs","Link URLs","Session ID"\n')
              },
  function(cb){ writePrimarySessions(cb,'external_live_data')
              },
  function(cb){ writeBreakoutSessions(cb,'breakout_descriptions')
              },
  function(cb){ closeStream(cb)
              },
  function(cb){ openStream(cb,'speakers-import.csv')
              },
  function(cb){ writeHeaders(cb,'"First Name (required)","Last Name (required)","Title","Company","Description","Image URL","Website","Twitter Handle","Facebook URL","LinkedIn URL","Session IDs","Attendee ID","Speaker ID"\n')
              },
  function(cb){ prepSpeakers(cb,'external_live_data')
              },
  function(cb){ prepSpeakers(cb,'breakout_descriptions')
              },
  function(cb){ writeSpeakers(cb)
              },
  function(cb){ closeStream(cb)
              },
]);
<commit_msg>Now rely on unique keys<commit_after>var path = require('path')
    , fs = require('fs')
    , _ = require('underscore')
    , async = require('async')
    , dir = path.dirname(require.main.filename) + '/output/' // path to the root
    , barstool = require('./lib/barstool')
    , sheetData
    , wstream
    , speakers = {}
    ;

// =====
// GENERAL USE UTILITIES
// =====

// Helper function not called in the build process ...
// Processes each row, outputs it with double quotes escaped and characters
// correctly transformed.
function prepareRow(row){
  return _.map(row, function(item,key){
    // Escape double quotes inside and add double quotes around each item
    return '"' +
           item
           .replace(/"/g,'\'')
           .replace(/\n/g,'')
           .replace(/[\u2018\u2019]/g, '\'')
           .replace(/[\u201C\u201D]/g, '\'')
           + '"';
  })
  .join(',')+'\n';
}

// Create our writestream to @fileName (STRING)
function openStream(cb,fileName){
  // Clear the file
  fs.writeFile(dir + fileName, '', function(){})
  // Open a writestream
  wstream = fs.createWriteStream(dir + fileName);
  cb(null);
}

// Close the open write stream
function closeStream(cb){
  wstream.end();
  cb(null);
}

// Write our headers from @headers (STRING)
function writeHeaders(cb,headers){
  wstream.write(headers);
  cb(null);
};

// Get all worksheets, assign to global-scope `sheetData` variable
function getSheetData(cb){
  barstool.getAllWorksheets(function(err,data){
    if (err) throw err;
    sheetData = data;
    cb(err);
  });
}

// =====
// PARSE AND WRITE SPECIFIC DATA
// =====

// Write the mainstage/schedule block sessions
function writePrimarySessions(cb,sheetName){
  async.eachSeries(sheetData[sheetName], function(row,callback){
    var entry = [];

    // Name (required)
    entry.push(row['sessiontitle']);
    // Description
    entry.push(row['sessiondescription']);

    // ... do some data crunching to format date ...
    var time = row['timeblock'].split(' - ');
    // Start time (required): "3/13/2014 1:11:00 PM"
    entry.push(row['date'] + ' ' + time[0]);
    // End time (required): "3/13/2014 1:11:00 PM"
    entry.push(row['date'] + ' ' + time[1]);

    // Location
    // ... we aren't using these for now ...
    entry.push('');

    // Session Tracks
    entry.push(row['sessiontype']);

    // Filters
    // ... we aren't using these for now ...
    entry.push('');

    // Speaker IDs
    var speakers = [];
    if (row['speakersafe1'].length > 0) { speakers.push(row['speakersafe1']) }
    if (row['speakersafe2'].length > 0) { speakers.push(row['speakersafe2']) }
    if (row['speakersafe3'].length > 0) { speakers.push(row['speakersafe3']) }
    if (row['speakersafe4'].length > 0) { speakers.push(row['speakersafe4']) }
    if (row['speakersafe5'].length > 0) { speakers.push(row['speakersafe5']) }
    if (row['speakersafe6'].length > 0) { speakers.push(row['speakersafe6']) }
    entry.push(speakers.join(','));

    // Link URLs
    // ... we aren't using these for now ...
    entry.push('');

    // Session ID (unique)
    entry.push(row['sessionuniquekey']);

    // Write the prepared row
    wstream.write( prepareRow(entry) );
    callback(null);
  },function(err){ cb(err) });
}

// Write the breakout sessions
function writeBreakoutSessions(cb,sheetName){
  async.eachSeries(sheetData[sheetName], function(row,callback){
    // If the breakout isn't confirmed, pass on it
    if (row['status'] !== 'Confirmed') { callback(null); }
    else {
      // Otherwise, proceed
      var entry = [];

      // Name (required)
      entry.push(row['sessiontitle']);
      // Description
      entry.push(row['sessiondescription']);

      // ... do some data crunching to format date ...
      var time = row['timeblock'].split(' - ');
      // Start time (required): "3/13/2014 1:11:00 PM"
      entry.push(row['date'] + ' ' + time[0]);
      // End time (required): "3/13/2014 1:11:00 PM"
      entry.push(row['date'] + ' ' + time[1]);

      // Location
      // ... we aren't using these for now ...
      entry.push(row['roomlocation']);

      // Session Tracks
      entry.push(row['sessionaudience']);

      // Filters
      // ... we aren't using these for now ...
      entry.push('');

      // Speaker IDs
      var speakers = [];
      if (row['speakersafe1'].length > 0) { speakers.push(row['speakersafe1']) }
      if (row['speakersafe2'].length > 0) { speakers.push(row['speakersafe2']) }
      if (row['speakersafe3'].length > 0) { speakers.push(row['speakersafe3']) }
      if (row['speakersafe4'].length > 0) { speakers.push(row['speakersafe4']) }
      if (row['speakersafe5'].length > 0) { speakers.push(row['speakersafe5']) }
      if (row['speakersafe6'].length > 0) { speakers.push(row['speakersafe6']) }
      entry.push(speakers.join(','));

      // Link URLs
      // ... we aren't using these for now ...
      entry.push('');

      // Session ID (unique)
      entry.push(row['sessionuniquekey']);

      // Write the prepared row
      wstream.write( prepareRow(entry) );
      callback(null);
    }
  },function(err){ cb(err) });
}

function prepSpeakers(cb,sheetName){
  async.eachSeries(sheetData[sheetName], function(row,callback){
    // How many speakers do we support?
    var numSpeakers = 6,
        count = 0;

    async.until(
      function(){ return count == numSpeakers; },
      function(callllback){
        count++;
        var speaker_key = row['speakersafe'+count];

        // If we don't have speaker, skip
        if (row['speakername'+count].length === 0) {
          return callllback(null);
        }

        // Have we already seen this speaker?
        // console.log('trying ' + speaker_key + ' ... type: ' + typeof(speakers[speaker_key]));
        if (typeof(speakers[speaker_key]) === "object") {
          console.log('HIT! ' + speaker_key)
          // Then just add to it, don't make a new one
          speakers[speaker_key]['Session IDs'] = speakers[speaker_key]['Session IDs'] + ',' + row['sessionuniquekey'];
          // We're done, move along
          return callllback(null);
        }

        // OK, we've got a speaker, so go ahead and get it ready...
        var speaker = {};

        // Grab the name and safe/key, this is easy(ish)
        var name = row['speakername'+count].replace('Dr. ','').replace('Mayor ','')

        speaker['First Name (required)'] = name.substr(0,name.indexOf(' '));
        speaker['Last Name (required)'] = name.substr(name.indexOf(' ')+1);
        speaker['Speaker ID'] = row['speakersafe'+count];

        // Get the title/org
        if ( row['speakertitleorg'+count].indexOf(',') > 0 ) {
          // If there's a comma, i.e. if there's a 'Title here, Org here', split it up
          speaker['Title'] = row['speakertitleorg'+count].substr(0,row['speakertitleorg'+count].indexOf(','));
          speaker['Company'] = row['speakertitleorg'+count].substr(row['speakertitleorg'+count].indexOf(',')+1).replace(' ','');
        } else {
          // No comma, so shove everything in the title and make org empty
          speaker['Title'] = row['speakertitleorg'+count];
          speaker['Company'] = "";
        }

        // Figure out if we can grab a link to a photo, if we can, give it
        if ( row['speakerphoto'+count] == 'STAFF' ) {
          speaker['Image URL'] = 'http://codeforamerica.org/media/images/people/' + row['speakersafe'+count] + '.jpg';
        } else if ( row['speakerphoto'+count] == 'SPEAKER' ) {
          speaker['Image URL'] = 'http://codeforamerica.org/media/images/summit/2015/speakers/thumbnails/' + row['speakersafe'+count] + '.jpg';
        } else {
          speaker['Image URL'] = "";
        }

        // Give a link to the session ID
        speaker['Session IDs'] = row['sessionuniquekey'];

        // ... we don't have these ...
        speaker['Description'] = "";
        speaker['Website'] = "";
        speaker['Twitter Handle'] = "";
        speaker['Facebook URL'] = "";
        speaker['LinkedIn URL'] = "";
        speaker['Attendee ID'] = "";

        // speakers.push(speaker);
        speakers[row['speakersafe'+count]] = speaker;
        callllback(null);

      },
      function(err){ callback(null); }
    );
  },function(err){
    cb(err);
  });
}

function writeSpeakers(cb){
  async.forEachOfSeries(speakers, function(value,key,callback){
    var entry = [];

    // "First Name (required)"
    entry.push(value["First Name (required)"]);
    // "Last Name (required)"
    entry.push(value["Last Name (required)"]);
    // "Title"
    entry.push(value["Title"]);
    // "Company"
    entry.push(value["Company"]);
    // "Description"
    entry.push(value["Description"]);
    // "Image URL"
    entry.push(value["Image URL"]);
    // "Website"
    entry.push(value["Website"]);
    // "Twitter Handle"
    entry.push(value["Twitter Handle"]);
    // "Facebook URL"
    entry.push(value["Facebook URL"]);
    // "LinkedIn URL"
    entry.push(value["LinkedIn URL"]);
    // "Session IDs"
    entry.push(value["Session IDs"]);
    // "Attendee ID"
    entry.push(value["Attendee ID"]);
    // "Speaker ID"
    entry.push(value["Speaker ID"]);

    // Write the prepared row
    wstream.write( prepareRow(entry) );
    callback(null);
  },
  function(err){
    cb(null);
  });
}

// =====
// PERFORM ALL THE TASKS
// =====

async.series([
  function(cb){ getSheetData(cb)
              },
  function(cb){ openStream(cb,'session-import.csv')
              },
  function(cb){ writeHeaders(cb,'"Name (required)","Description","Start Time (required)","End Time (required)","Location","Session Tracks","Filters","Speaker IDs","Link URLs","Session ID"\n')
              },
  function(cb){ writePrimarySessions(cb,'external_live_data')
              },
  function(cb){ writeBreakoutSessions(cb,'breakout_descriptions')
              },
  function(cb){ closeStream(cb)
              },
  function(cb){ openStream(cb,'speakers-import.csv')
              },
  function(cb){ writeHeaders(cb,'"First Name (required)","Last Name (required)","Title","Company","Description","Image URL","Website","Twitter Handle","Facebook URL","LinkedIn URL","Session IDs","Attendee ID","Speaker ID"\n')
              },
  function(cb){ prepSpeakers(cb,'external_live_data')
              },
  function(cb){ prepSpeakers(cb,'breakout_descriptions')
              },
  function(cb){ writeSpeakers(cb)
              },
  function(cb){ closeStream(cb)
              },
]);
<|endoftext|>


<commit_before>jest.mock("../src/httpGet")

// Register init to DOMContentLoaded event
require("../src/index")

const resetEnv = () => {
  // Set up our document body
  document.body.innerHTML =
    "

" +
    "
Loading...
" +
    "

" +
    "" +
    "  Source: " +
    ""
}

describe("init", () => {
  beforeEach(resetEnv)

  test("displays an error if the gist request fails", () => {
    const httpGet = require("../src/httpGet")
    httpGet.default.mockImplementation(() => Promise.reject("Test error"))

    // Dispatch the DOMContentLoaded event
    document.dispatchEvent(new Event("DOMContentLoaded"))

    return new Promise(process.nextTick).then(() => {
      expect(document.getElementById("error").innerHTML).toEqual("Error: Test error")
      expect(document.getElementById("error").style.display).toEqual("block")
      expect(document.getElementById("loading").style.display).toEqual("none")
    })
  })

  test("displays the index scene content", () => {
    const gistContent = JSON.stringify({
      files: {
        "index.markdown": { raw_url: "http://gists/index.markdown" }
      }
    })
    const indexContent = "Once upon a time..."
    const httpGet = require("../src/httpGet")
    httpGet.default.mockImplementation((url) => {
      switch (url) {
        case "http://gists/index.markdown":
          return Promise.resolve(indexContent)
      }
      return Promise.resolve(gistContent)
    })

    // Dispatch the DOMContentLoaded event
    document.dispatchEvent(new Event("DOMContentLoaded"))

    return new Promise(process.nextTick).then(() => {
      expect(document.getElementById("error").style.display).toEqual("none")
      expect(document.getElementById("loading").style.display).toEqual("none")
      expect(document.getElementById("content").innerHTML).toContain("
Once upon a time...
")
    })
  })

  test("applies the default stylesheet when style.css is present", () => {
    const gistContent = JSON.stringify({
      files: {
        "index.markdown": { raw_url: "http://gists/index.markdown" },
        "style.css": { raw_url: "http://gists/style.css" }
      }
    })
    const indexContent = "Once upon a time..."
    const styleContent = "body { background-color: black; }"
    const httpGet = require("../src/httpGet")
    httpGet.default.mockImplementation((url) => {
      switch (url) {
        case "http://gists/index.markdown":
          return Promise.resolve(indexContent)
        case "http://gists/style.css":
          return Promise.resolve(styleContent)
      }
      return Promise.resolve(gistContent)
    })

    // Dispatch the DOMContentLoaded event
    document.dispatchEvent(new Event("DOMContentLoaded"))

    return new Promise(process.nextTick).then(() => {
      expect(document.getElementsByTagName("style")[0].innerHTML).toContain(styleContent)
    })
  })

  test("renders mustache tags", () => {
    const gistContent = JSON.stringify({
      files: {
        "index.markdown": { raw_url: "http://gists/index.markdown" }
      }
    })
    const indexContent =
      "---\n" +
      "state:\n" +
      "  hero: Bob\n" +
      "---\n\n" +
      "Once upon a time {{hero}}..."
    const httpGet = require("../src/httpGet")
    httpGet.default.mockImplementation((url) => {
      switch (url) {
        case "http://gists/index.markdown":
          return Promise.resolve(indexContent)
      }
      return Promise.resolve(gistContent)
    })

    // Dispatch the DOMContentLoaded event
    document.dispatchEvent(new Event("DOMContentLoaded"))

    return new Promise(process.nextTick).then(() => {
      expect(document.getElementById("content").innerHTML).toContain("
Once upon a time Bob...
")
    })
  })
})

describe("scene transition", () => {
  beforeEach(resetEnv)

  test("displays the scene content", () => {
    const gistContent = JSON.stringify({
      files: {
        "index.markdown": { raw_url: "http://gists/index.markdown" },
        "end.markdown": { raw_url: "http://gists/end.markdown" }
      }
    })
    const indexContent = "Once upon a time... [continue...](end)"
    const endContent = "The end"
    const httpGet = require("../src/httpGet")
    httpGet.default.mockImplementation((url) => {
      switch (url) {
        case "http://gists/index.markdown":
          return Promise.resolve(indexContent)
        case "http://gists/end.markdown":
          return Promise.resolve(endContent)
      }
      return Promise.resolve(gistContent)
    })

    // Dispatch the DOMContentLoaded event
    document.dispatchEvent(new Event("DOMContentLoaded"))

    return new Promise(process.nextTick).then(() => {
      expect(document.getElementById("content").innerHTML).toContain("Once upon a time...")

      // Dispatch scene transition
      document.querySelector("a[href=end]").click()
    }).then(() => {
      return new Promise(process.nextTick)
    }).then(() => {
      expect(document.getElementById("content").innerHTML).toContain("The end")
    })
  })
})
<commit_msg>Reset document.location.hash before each test<commit_after>jest.mock("../src/httpGet")

// Register init to DOMContentLoaded event
require("../src/index")

const resetEnv = () => {
  // Set up our document body
  document.body.innerHTML =
    "

" +
    "
Loading...
" +
    "

" +
    "" +
    "  Source: " +
    ""

  // Reset document.location.hash
  document.location.hash = ""
}

describe("init", () => {
  beforeEach(resetEnv)

  test("displays an error if the gist request fails", () => {
    const httpGet = require("../src/httpGet")
    httpGet.default.mockImplementation(() => Promise.reject("Test error"))

    // Dispatch the DOMContentLoaded event
    document.dispatchEvent(new Event("DOMContentLoaded"))

    return new Promise(process.nextTick).then(() => {
      expect(document.getElementById("error").innerHTML).toEqual("Error: Test error")
      expect(document.getElementById("error").style.display).toEqual("block")
      expect(document.getElementById("loading").style.display).toEqual("none")
    })
  })

  test("displays the index scene content", () => {
    const gistContent = JSON.stringify({
      files: {
        "index.markdown": { raw_url: "http://gists/index.markdown" }
      }
    })
    const indexContent = "Once upon a time..."
    const httpGet = require("../src/httpGet")
    httpGet.default.mockImplementation((url) => {
      switch (url) {
        case "http://gists/index.markdown":
          return Promise.resolve(indexContent)
      }
      return Promise.resolve(gistContent)
    })

    // Dispatch the DOMContentLoaded event
    document.dispatchEvent(new Event("DOMContentLoaded"))

    return new Promise(process.nextTick).then(() => {
      expect(document.getElementById("error").style.display).toEqual("none")
      expect(document.getElementById("loading").style.display).toEqual("none")
      expect(document.getElementById("content").innerHTML).toContain("
Once upon a time...
")
    })
  })

  test("applies the default stylesheet when style.css is present", () => {
    const gistContent = JSON.stringify({
      files: {
        "index.markdown": { raw_url: "http://gists/index.markdown" },
        "style.css": { raw_url: "http://gists/style.css" }
      }
    })
    const indexContent = "Once upon a time..."
    const styleContent = "body { background-color: black; }"
    const httpGet = require("../src/httpGet")
    httpGet.default.mockImplementation((url) => {
      switch (url) {
        case "http://gists/index.markdown":
          return Promise.resolve(indexContent)
        case "http://gists/style.css":
          return Promise.resolve(styleContent)
      }
      return Promise.resolve(gistContent)
    })

    // Dispatch the DOMContentLoaded event
    document.dispatchEvent(new Event("DOMContentLoaded"))

    return new Promise(process.nextTick).then(() => {
      expect(document.getElementsByTagName("style")[0].innerHTML).toContain(styleContent)
    })
  })

  test("renders mustache tags", () => {
    const gistContent = JSON.stringify({
      files: {
        "index.markdown": { raw_url: "http://gists/index.markdown" }
      }
    })
    const indexContent =
      "---\n" +
      "state:\n" +
      "  hero: Bob\n" +
      "---\n\n" +
      "Once upon a time {{hero}}..."
    const httpGet = require("../src/httpGet")
    httpGet.default.mockImplementation((url) => {
      switch (url) {
        case "http://gists/index.markdown":
          return Promise.resolve(indexContent)
      }
      return Promise.resolve(gistContent)
    })

    // Dispatch the DOMContentLoaded event
    document.dispatchEvent(new Event("DOMContentLoaded"))

    return new Promise(process.nextTick).then(() => {
      expect(document.getElementById("content").innerHTML).toContain("
Once upon a time Bob...
")
    })
  })
})

describe("scene transition", () => {
  beforeEach(resetEnv)

  test("displays the scene content", () => {
    const gistContent = JSON.stringify({
      files: {
        "index.markdown": { raw_url: "http://gists/index.markdown" },
        "end.markdown": { raw_url: "http://gists/end.markdown" }
      }
    })
    const indexContent = "Once upon a time... [continue...](end)"
    const endContent = "The end"
    const httpGet = require("../src/httpGet")
    httpGet.default.mockImplementation((url) => {
      switch (url) {
        case "http://gists/index.markdown":
          return Promise.resolve(indexContent)
        case "http://gists/end.markdown":
          return Promise.resolve(endContent)
      }
      return Promise.resolve(gistContent)
    })

    // Dispatch the DOMContentLoaded event
    document.dispatchEvent(new Event("DOMContentLoaded"))

    return new Promise(process.nextTick).then(() => {
      expect(document.getElementById("content").innerHTML).toContain("Once upon a time...")

      // Dispatch scene transition
      document.querySelector("a[href=end]").click()
    }).then(() => {
      return new Promise(process.nextTick)
    }).then(() => {
      expect(document.getElementById("content").innerHTML).toContain("The end")
    })
  })
})
<|endoftext|>