<commit_before><html>
<head>
	<meta charset='UTF-8'>
	<title>Character Sheet - <NAME></title>
	<script type='text/javascript' src='js/paper-full.js'></script>
    <script src='http://www.parsecdn.com/js/parse-1.6.12.min.js'></script>
    <script src='http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js'></script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/mathjs/2.5.0/math.min.js'></script>
	<script type='text/paperscript' canvas='canvas'>
    
    'use strict';
    var SKILLS = 1;
    var PROJECTS = 2;
    var TIMELINE = 3;

    var layerRefDict;

    var createBarGraph = function(skillData, size)
    {
        console.dir("skillData: " + skillData);
        console.dir("size: " + size);

        var barChart = new Group();

        var generateFromData = function(size)
        {
            var barHeight = (size.height/skillData.length);
            var x = 0;
            var initialY = 0;
            var y = initialY;
            var maxSkillAmount = 2000; // dynamically calculate this in the future
            var totalBarLength = (size.width-(x*2));
            var lengthOfOneHour = totalBarLength/maxSkillAmount;

            // draw horizontal bars for skills
            for(var i = 0; i < skillData.length; i++)
            {
                var path = new Path();
                path.strokeColor = 'black';
                path.fillColor = 'black';
                var barLength = skillData[i].hours*lengthOfOneHour;
                var textOffset = 8;
                path.add(new Point(x, y));
                path.add(new Point(x+barLength, y)); 
                path.add(new Point(x+barLength, y+barHeight)); 
                path.add(new Point(x, y+barHeight));     
                path.closed = true;
                barChart.addChild(path);

                var skillText = new PointText(new Point((x+skillData[i].hours*lengthOfOneHour)+textOffset, y));
                skillText.fillColor = 'black';
                skillText.justification = 'left';
                skillText.fontSize = barHeight;
                skillText.content = skillData[i].skillName + ':';
                barChart.addChild(skillText);

                y+=barHeight;
            }

            var numVertBars = 10;

            for(var i = 0; i <= numVertBars; i++)
            {
                var vertPath = new Path();
                vertPath.strokeColor = 'black';
                var xPos = x+(totalBarLength/numVertBars)*i;
                vertPath.add(new Point(xPos, initialY-barHeight));
                vertPath.add(new Point(xPos, size.height));
                vertPath.opacity = 0.3;
                barChart.addChild(vertPath);

                var skillAmountLabel = new PointText(new Point(xPos-40, size.height/2));
                skillAmountLabel.fillColor = 'black';
                skillAmountLabel.content = ((maxSkillAmount/(numVertBars))*i) + ' hours';
                skillAmountLabel.rotate(90);
                skillAmountLabel.opacity = 0.3;
                barChart.addChild(skillAmountLabel);
            }
        }

        generateFromData(size);

        console.dir("group:" + barChart);
        return barChart;
    }

    var allSkillTypes = [];
    var allSkillCategories = [];


    // TODO: pull this in from backend
    var projectStatuses = ['Decommissioned', 'Completed', 'Future', 'Current', 'On Hold', 'Cancelled'];

    function drawProjectLabels()
    {
        var projectInitialGrouping = project.layers[PROJECTS].children['projectInitialGrouping'];
        
        var y = 50;
        var yStep = 70;

        for(var status in projectStatuses)
        {
            var currentProjectsText = new PointText(new Point(10, y));
            currentProjectsText.fillColor = 'black';
            currentProjectsText.content = projectStatuses[status] + ':';
            projectInitialGrouping.addChild(currentProjectsText);
            y+=yStep;
        }

        projectInitialGrouping.visible = true;
    }

    function getTotalHoursForProject(skillsUsed)
    {   
        var totalHours = 0;

        for(var j = 0; j < skillsUsed.skills.length; j++)
        {
            var skill = skillsUsed.skills[j];
            totalHours += skill.hours;
        }

        return totalHours;
    }

    function BarGraph(pos, size, data, group)
    {
        var maxSkillAmount = 0;

        //var yLen = ((monthCount+5) % 12) ? 2 : 5
        for(var i = 0; i < data.length; i++)
        {
            maxSkillAmount = (data[i].hours > maxSkillAmount) ? data[i].hours : maxSkillAmount;
        }

        console.dir(maxSkillAmount);

        var barHeight = (size.height/data.length) > 15 ? 15 : (size.height/data.length);

        var x = 0;
        var initialY = 0;
        var y = initialY;
        var totalBarLength = (size.width-(x*2));
        var lengthOfOneHour = totalBarLength/maxSkillAmount;

        // draw horizontal bars for skills
        console.dir("data: " + data.length);

        for(var i = 0; i < data.length; i++)
        {
            var path = new Path();
            path.strokeColor = 'white';
            path.fillColor = 'black';
            var barLength = data[i].hours*lengthOfOneHour;
            var textOffset = 8;
            path.add(new Point(x, y));
            path.add(new Point(x+barLength, y)); 
            path.add(new Point(x+barLength, y+barHeight)); 
            path.add(new Point(x, y+barHeight));     
            path.closed = true;
            group.addChild(path);
            path.position += pos;

            var skillText = new PointText(new Point((x+data[i].hours*lengthOfOneHour)+textOffset, y+barHeight-2));
            skillText.fillColor = 'black';
            skillText.justification = 'left';
            skillText.fontSize = barHeight;
            skillText.content = data[i].skillName + ':';
            group.addChild(skillText);
            skillText.position += pos;

            y+=barHeight;
        }

        var rightLimit = Math.ceil(maxSkillAmount/100)*100;
        var numVertBars = rightLimit/100;

        for(var i = 0; i <= numVertBars; i++)
        {
            var vertPath = new Path();
            vertPath.strokeColor = 'black';
            var xPos = x+(totalBarLength/numVertBars)*i;
            vertPath.add(new Point(xPos, initialY-barHeight));
            vertPath.add(new Point(xPos, size.height));
            vertPath.opacity = 0.3;
            group.addChild(vertPath);
            vertPath.position += pos;

            var skillAmountLabel = new PointText(new Point(xPos-40, size.height/2));
            skillAmountLabel.fillColor = 'black';
            skillAmountLabel.content = ((maxSkillAmount/(numVertBars))*i) + ' hours';
            skillAmountLabel.rotate(90);
            skillAmountLabel.opacity = 0.3;
            group.addChild(skillAmountLabel);
            skillAmountLabel.position += pos;
        }
    }

    function BoxText(text, pos, clickFunc, group)
    {
        var selected = false;

        var snd = new Audio("beep.mp3"); // buffers automatically when created

        var boxText = new PointText(pos);
        boxText.fillColor = 'black';
        boxText.justification = 'center';
        boxText.content = text;

        var boxTextBorder = new Path();
        boxTextBorder.name = text;
        boxTextBorder.strokeColor = 'white';
        boxTextBorder.fillColor = 'white';
        boxTextBorder.opacity = 0.3;
        boxTextBorder.add(pos.x-2, pos.y+3); 
        boxTextBorder.add(new Point(pos.x+boxText.bounds.width+2, pos.y+3)); 
        boxTextBorder.add(new Point(pos.x+boxText.bounds.width+2, pos.y-boxText.bounds.height));
        boxTextBorder.add(new Point(pos.x-2, pos.y-boxText.bounds.height));     
        boxTextBorder.closed = true;
        boxTextBorder.position.x -= boxText.bounds.width/2; // this is to offset for the text justification

        boxTextBorder.addChild(boxText);
        boxTextBorder.sendToBack();
    
        var test = "************** TEST *******************";

        this.overrideName = function(name)
        {
            boxText.name = name;
            boxTextBorder.name = name;
        };

        this.enter = function()
        {
            if(!selected)
            {
                console.dir("Enter");
                boxTextBorder.fillColor = 'grey';
                snd.play();    
            }
        };

        this.leave = function()
        {
            if(!selected)
            {
                console.dir("Leave");
                boxTextBorder.fillColor = 'white';    
            }
        };

        this.select = function()
        {
            selected = true;
            boxText.fillColor = 'red';
            boxTextBorder.fillColor = 'red';
        }

        this.unselect = function()
        {
            selected = false;
            boxText.fillColor = 'black';
            boxTextBorder.fillColor = 'white';
        }

        boxTextBorder.onMouseEnter = this.enter;
        boxTextBorder.onMouseLeave = this.leave;
        boxText.onMouseEnter = this.enter;
        boxText.onMouseLeave = this.leave;
        boxText.onMouseDown = clickFunc;
        boxTextBorder.onMouseDown = clickFunc;

        this.name = text;
        boxText.name = text;
        boxTextBorder.name = text;

        if(group)
        {
            group.addChild(boxTextBorder);
            group.addChild(boxText);
            console.dir("added to group " + group);
        }
    };


    

    function drawProjectsOfCategoryBoxes(projectData)
    {
        console.dir("project data: " + projectData);
        var projectsOfStatusGrouping = project.layers[PROJECTS].children['projectsOfStatusGrouping'];

        var initialX = 430;
        var x = initialX;
        var initialY = 90;
        var y = initialY;
        var yStep = 70;
        var xStep = 180;
        var count = 0;

        for(var i = 0; i < projectData.length; i++)
        {
            var badgePos = new Point(x, y);
            
            var xsize = 160;
            var ysize = 32;

            var boxClicked = function() 
            {
                console.dir("in boxClicked: " + this.name);

                Parse.Cloud.run('getDataOfType', { 'dataType': 'projectDetail', 'filter' : this.name}).then(function(response) 
                {
                    project.layers[PROJECTS].children['projectDetailGrouping'].removeChildren();
                    console.dir(response);  
                    drawProjectDetail(response);
                });
            };

            console.dir("***************:" + projectsOfStatusGrouping);
            var projectNameText = new BoxText(projectData[i].get("Name"), badgePos, boxClicked, projectsOfStatusGrouping);
            //console.dir("name:" + projectNameText.name);

            y+=yStep;

            if(y > canvas.height)
            {
                x+=xStep;
                y=initialY;
            }
        }

        //projectInitialGrouping.position.x += crossoverPoint.x;
        projectsOfStatusGrouping.visible = true;   
    }
    function drawProjectInitialBoxes(projectData)
    {
        var projectInitialGrouping = project.layers[PROJECTS].children['projectInitialGrouping'];

        var initialX = 30;
        var y = 90;
        var yStep = 70;
        var xStep = 96;

        var numProjects = 0;

        for(var i = 0; i < projectStatuses.length; i++)
        {
            var count = 0;

            _.each(projectData, function(project) 
            {
                if(project.get('Status') == projectStatuses[i])
                {
                    numProjects++;
                }
            });

            var textClicked = function() 
            {
                console.dir("name: " + this.name);

                Parse.Cloud.run('getDataOfType', { 'dataType': 'project', 'filter' : this.name }).then(function(response) 
                {
                    console.dir('response!' + response);

                    project.layers[PROJECTS].activate();

                    console.dir('children: ' + project.layers[PROJECTS].children['projectsOfStatusGrouping'].children.length);
                    

                    project.layers[PROJECTS].children['projectsOfStatusGrouping'].removeChildren();
                    project.layers[PROJECTS].children['projectDetailGrouping'].removeChildren();

                    console.dir('children: ' + project.layers[PROJECTS].children['projectsOfStatusGrouping'].children.length);

                    drawProjectsOfCategoryBoxes(response);

                    console.dir('children: ' + project.layers[PROJECTS].children['projectsOfStatusGrouping'].children.length);
                });
            };

            var badgePos = new Point(initialX+(xStep*count), y+(yStep*i));
            var projectStatusText = new BoxText(numProjects + ">>", badgePos, textClicked, projectInitialGrouping);
            projectStatusText.overrideName(projectStatuses[i]);  
            
            numProjects = 0;
        }

        //projectInitialGrouping.position.x += crossoverPoint.x;
        projectInitialGrouping.visible = true;
    }
    function drawProjectBoxes(projectData)
    {
        var initialX = 30;
        var y = 90;
        var yStep = 100;
        var xStep = 96;

        for(var i = 0; i < projectStatuses.length; i++)
        {
            var count = 0;

            _.each(projectData, function(project) 
            {
                if(project.get('Status') == projectStatuses[i])
                {
                    var projectName = new PointText(new Point(initialX+(xStep*count), y+(yStep*i)+20));
                    projectName.fillColor = 'black';
                    projectName.content = project.get('Name');
                    projectName.rotate(10);

                    var badgePos = new Point(initialX+(xStep*count), y+(yStep*i));
                    var badgePath = new Path();
                    var size = 32;

                    badgePath.strokeColor = 'black';
                    badgePath.fillColor = 'white';
                    badgePath.add(badgePos); 
                    badgePath.add(new Point(badgePos.x+size, badgePos.y)); 
                    badgePath.add(new Point(badgePos.x+size, badgePos.y-size));
                    badgePath.add(new Point(badgePos.x, badgePos.y-size));     
                    badgePath.closed = true;
                }
            });
        }
    }

    function onFrame(event)
    {
    }
    
    function initParse()
    {
        Parse.initialize('tpio94gHZkEKpbpxvjCA<KEY>', '<KEY>');
    }

    function drawSkillLabels(skillData)
    {
        var skillLabelGroup = project.layers[SKILLS].children['skillLabelGroup'];

        var x = 250;
        var y = 10;
        var yStep = 11;

        for(var skill in skillData)
        {
            var skillText = new PointText(new Point(x, y));
            skillText.fillColor = 'black';
            skillText.justification = 'right';
            skillText.content = skillData[skill].skillName + ':';
            skillLabelGroup.addChild(skillText);

            y+=yStep;
        }

        skillLabelGroup.visible = false;
        skillLabelGroup.position.x += crossoverPoint.x;
    }

    function drawSkillCategories(categoryData)
    {
        var skillCategoryGroup = project.layers[SKILLS].children['skillCategoryGroup'];

        var x = 10;
        var y = 18;
        var yStep = 18;

        var skillCategoryLabels = [];

        for(var skillCategory in categoryData)
        {
            var changeSkillCategory = function() 
            {
                console.dir("in changeSkillCategory");
                project.layers[SKILLS].children['skillBarGroup'].removeChildren();
                project.layers[SKILLS].children['skillLabelGroup'].removeChildren();

                //updateLayers(category.value);
                Parse.Cloud.run('getDataOfType', { 'dataType': 'skills', 'filter' : this.content }).then(function(response) 
                {
                    console.log('response! ' + response);
                    project.layers[SKILLS].activate();
                    drawSkillLabels(response);
                    drawSkillBars(response);
                    project.layers[SKILLS].children['skillBarGroup'].visible = true;
                    project.layers[SKILLS].children['skillLabelGroup'].visible = true;
                });

                /*for(var i = 1; i < skillCategoryGroup.children.length; i++)
                {
                    project.layers[i].visible = i === visibleIndex ? true : false;
                    i === visibleIndex ? categoryLabels[i-1].select() : categoryLabels[i-1].unselect();
                }*/

                // TODO: Refactor this into a "selection group" tyoe thing for a selection of BoxLabels
                for(var i = 0; i < skillCategoryLabels.length; i++)
                {
                    if(skillCategoryLabels[i].name === this.content)
                    {
                        console.dir("SELECT ***:" + this.content + " : " + skillCategoryLabels[i].name);
                        skillCategoryLabels[i].select();
                    }
                    else
                    {
                        console.dir("UNSELECT ***:" + this.content + " : " + skillCategoryLabels[i].name);
                        skillCategoryLabels[i].unselect();
                    }
                }
            };

            var skillCategoryText = new BoxText(categoryData[skillCategory].category, new Point(x,y), changeSkillCategory, skillCategoryGroup);
            skillCategoryText.overrideName("TEST");
            skillCategoryLabels.push(skillCategoryText);
            y+=yStep;
        }

        skillCategoryGroup.position.x += crossoverPoint.x;
    }
    function drawSkillBars(skillData)
    {
        var skillBarGroup = project.layers[SKILLS].children['skillBarGroup'];

        var x = 252;
        var initialY = 10
        var y = initialY;
        var yStep = 11;
        var barHeight = 10;
        var maxSkillAmount = 2000; // dynamically calculate this in the future
        var totalBarLength = canvas.width-crossoverPoint.x-x-50;
        var lengthOfOneHour = totalBarLength/maxSkillAmount;

        // draw horizontal bars for skills
        for(var i = 0; i < skillData.length; i++)
        {
            var barHeightDampener = 2;

            var path = new Path();
            path.strokeColor = 'black';
            path.fillColor = 'black';
            path.add(new Point(x, y));
            path.add(new Point(x+skillData[i].hours*lengthOfOneHour, y)); 
            path.add(new Point(x+skillData[i].hours*lengthOfOneHour, y-barHeight)); 
            path.add(new Point(x, y-barHeight));     
            path.closed = true;

            skillBarGroup.addChild(path);

            y+=yStep;
        }

        // draw vertical bars for progress
        /*var vertPath = new Path();
        vertPath.strokeColor = 'black';
        vertPath.add(new Point(maxSkillAmount/barHeightDampener, initialY-barHeight));
        vertPath.add(new Point(maxSkillAmount/barHeightDampener, y));
        vertPath.opacity = 0.3;

        skillBarGroup.addChild(vertPath);*/

        var numVertBars = 10;

        for(var i = 0; i <= numVertBars; i++)
        {
            var vertPath = new Path();
            vertPath.strokeColor = 'black';
            var xPos = x+(totalBarLength/numVertBars)*i;
            vertPath.add(new Point(xPos, initialY-barHeight));
            vertPath.add(new Point(xPos, y));
            vertPath.opacity = 0.3;
            skillBarGroup.addChild(vertPath);

            var skillMaxLabel = new PointText(new Point(xPos-20, y+40));
            skillMaxLabel.fillColor = 'black';
            skillMaxLabel.content = ((maxSkillAmount/(numVertBars))*i) + ' hours';
            skillMaxLabel.rotate(90);
            skillMaxLabel.opacity = 0.3;
            skillBarGroup.addChild(skillMaxLabel);
        }
        skillBarGroup.visible = false;

        skillBarGroup.position.x += crossoverPoint.x;
    }

    // move parse-side? or keep here?
    function getNumberOfMonths(from, to) 
    {
        from = convertDateToUTC(from);
        to = convertDateToUTC(to);

        var months;
        months = (to.getFullYear() - from.getFullYear()) * 12;
        months -= from.getMonth();
        months += to.getMonth();
        return months <= 0 ? 0 : months;
    }
    // port to moment.js?
    // times are occasionally coming out minus...

    function convertDateToUTC(date) 
    {
        return new Date(date.getUTCFullYear() + '-' + (date.getUTCMonth() + 1) + '-' + (date.getUTCDate()));
        //console.log(results[i].get("StartDate").getTimezoneOffset());
        //results[i].set("StartDate", e); 
    }

    function drawTimeLine(response)
    {
        //crossoverPoint
        // main line
        var xStart = 10;
        var xEnd = canvas.width-crossoverPoint.x-60;
        var y = canvas.height-100;
        var path = new Path();
        path.strokeColor = 'black';
        path.fillColor = 'black';
        path.add(new Point(xStart, y)); 
        path.add(new Point(xEnd,y));

        drawProjectsOnTimeLine(xEnd-xStart, response);
    }

    function drawProjectDetail(response)
    {
        var projectDetailGrouping = project.layers[PROJECTS].children['projectDetailGrouping'];

        var projectName = new PointText(new Point(0, 200));
        projectName.fillColor = 'black';
        projectName.justification = 'center';
        projectName.content = "Project Name: " + response.get("Name");
        projectDetailGrouping.addChild(projectName);

        var projectPlatform = new PointText(new Point(0, 300));
        projectPlatform.fillColor = 'black';
        projectPlatform.justification = 'center';
        projectPlatform.content = "Platform: " + response.get("Platform");
        projectDetailGrouping.addChild(projectPlatform)

        var projectStatus = new PointText(new Point(0, 400));
        projectStatus.fillColor = 'black';
        projectStatus.justification = 'center';
        projectStatus.content = "Status: " + response.get("Status");
        projectDetailGrouping.addChild(projectStatus)

        var projectDescription = new PointText(new Point(0, 500));
        projectDescription.fillColor = 'black';
        projectDescription.justification = 'center';
        projectDescription.content = "Description: " + response.get("Description");
        projectDetailGrouping.addChild(projectDescription)

        var projectImage = new Path();
        projectImage.fillColor = 'black';
        projectImage.strokeColor = 'black';
        projectImage.add(new Point(0,0));
        projectImage.add(new Point(100,0));
        projectImage.add(new Point(100,100));
        projectImage.add(new Point(0,100));
        projectImage.closed = true;
        projectDetailGrouping.addChild(projectImage);

        var image = new Raster(response.get("imageName"));
        image.scale(0.3);
        image.bounds = projectImage.bounds;
        var group = new Group([projectImage, image]);
        //group.clipMask = true;
        group.clipped = true;
        projectDetailGrouping.addChild(group);

        // draw skills used graph
        //var graph = createBarGraph(response.get("SkillsUsed"), new Size(200,100));
        console.dir("response:" + response);
        console.dir("skills used:" + response.get("SkillsUsed"));

        var graph = new BarGraph(new Point(-150,550), new Size(200,100), response.get("SkillsUsed").skills, projectDetailGrouping);
        graph.name = 'graph';
        //graph.position.x += 100;
        projectDetailGrouping.position.x += canvas.width - 200;
        projectDetailGrouping.visible = true;
    }
    function drawProjectsOnTimeLine(lineLength, response)
    {
        var initialY = canvas.height-100;
        var currentY = initialY;
        var xStart = 10;

        // first just sort by start time and sort in y

        var allProjects = _.sortBy(response, 'StartDate');
 
        var firstDate = _.first(allProjects).get('StartDate');
        var lastDate  = new Date();

        var totalNumMonths = getNumberOfMonths(firstDate, lastDate);
        var oneMonthLength = lineLength/totalNumMonths;

        // year lines
        var monthCount = 0;

        // calculate properly from 1st
        var year = 2004;

        while(monthCount <= totalNumMonths)
        {
            var path = new Path();
            path.strokeColor = 'black';
            path.fillColor = 'white';
            var xPos = xStart+(monthCount*oneMonthLength);
            // hack in time offset - calculate properly in the future!<commit_msg>tweak x<commit_after><html>
<head>
	<meta charset='UTF-8'>
	<title>Character Sheet - <NAME></title>
	<script type='text/javascript' src='js/paper-full.js'></script>
    <script src='http://www.parsecdn.com/js/parse-1.6.12.min.js'></script>
    <script src='http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js'></script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/mathjs/2.5.0/math.min.js'></script>
	<script type='text/paperscript' canvas='canvas'>
    
    'use strict';
    var SKILLS = 1;
    var PROJECTS = 2;
    var TIMELINE = 3;

    var layerRefDict;

    var createBarGraph = function(skillData, size)
    {
        console.dir("skillData: " + skillData);
        console.dir("size: " + size);

        var barChart = new Group();

        var generateFromData = function(size)
        {
            var barHeight = (size.height/skillData.length);
            var x = 0;
            var initialY = 0;
            var y = initialY;
            var maxSkillAmount = 2000; // dynamically calculate this in the future
            var totalBarLength = (size.width-(x*2));
            var lengthOfOneHour = totalBarLength/maxSkillAmount;

            // draw horizontal bars for skills
            for(var i = 0; i < skillData.length; i++)
            {
                var path = new Path();
                path.strokeColor = 'black';
                path.fillColor = 'black';
                var barLength = skillData[i].hours*lengthOfOneHour;
                var textOffset = 8;
                path.add(new Point(x, y));
                path.add(new Point(x+barLength, y)); 
                path.add(new Point(x+barLength, y+barHeight)); 
                path.add(new Point(x, y+barHeight));     
                path.closed = true;
                barChart.addChild(path);

                var skillText = new PointText(new Point((x+skillData[i].hours*lengthOfOneHour)+textOffset, y));
                skillText.fillColor = 'black';
                skillText.justification = 'left';
                skillText.fontSize = barHeight;
                skillText.content = skillData[i].skillName + ':';
                barChart.addChild(skillText);

                y+=barHeight;
            }

            var numVertBars = 10;

            for(var i = 0; i <= numVertBars; i++)
            {
                var vertPath = new Path();
                vertPath.strokeColor = 'black';
                var xPos = x+(totalBarLength/numVertBars)*i;
                vertPath.add(new Point(xPos, initialY-barHeight));
                vertPath.add(new Point(xPos, size.height));
                vertPath.opacity = 0.3;
                barChart.addChild(vertPath);

                var skillAmountLabel = new PointText(new Point(xPos-40, size.height/2));
                skillAmountLabel.fillColor = 'black';
                skillAmountLabel.content = ((maxSkillAmount/(numVertBars))*i) + ' hours';
                skillAmountLabel.rotate(90);
                skillAmountLabel.opacity = 0.3;
                barChart.addChild(skillAmountLabel);
            }
        }

        generateFromData(size);

        console.dir("group:" + barChart);
        return barChart;
    }

    var allSkillTypes = [];
    var allSkillCategories = [];


    // TODO: pull this in from backend
    var projectStatuses = ['Decommissioned', 'Completed', 'Future', 'Current', 'On Hold', 'Cancelled'];

    function drawProjectLabels()
    {
        var projectInitialGrouping = project.layers[PROJECTS].children['projectInitialGrouping'];
        
        var y = 50;
        var yStep = 70;

        for(var status in projectStatuses)
        {
            var currentProjectsText = new PointText(new Point(10, y));
            currentProjectsText.fillColor = 'black';
            currentProjectsText.content = projectStatuses[status] + ':';
            projectInitialGrouping.addChild(currentProjectsText);
            y+=yStep;
        }

        projectInitialGrouping.visible = true;
    }

    function getTotalHoursForProject(skillsUsed)
    {   
        var totalHours = 0;

        for(var j = 0; j < skillsUsed.skills.length; j++)
        {
            var skill = skillsUsed.skills[j];
            totalHours += skill.hours;
        }

        return totalHours;
    }

    function BarGraph(pos, size, data, group)
    {
        var maxSkillAmount = 0;

        //var yLen = ((monthCount+5) % 12) ? 2 : 5
        for(var i = 0; i < data.length; i++)
        {
            maxSkillAmount = (data[i].hours > maxSkillAmount) ? data[i].hours : maxSkillAmount;
        }

        console.dir(maxSkillAmount);

        var barHeight = (size.height/data.length) > 15 ? 15 : (size.height/data.length);

        var x = 0;
        var initialY = 0;
        var y = initialY;
        var totalBarLength = (size.width-(x*2));
        var lengthOfOneHour = totalBarLength/maxSkillAmount;

        // draw horizontal bars for skills
        console.dir("data: " + data.length);

        for(var i = 0; i < data.length; i++)
        {
            var path = new Path();
            path.strokeColor = 'white';
            path.fillColor = 'black';
            var barLength = data[i].hours*lengthOfOneHour;
            var textOffset = 8;
            path.add(new Point(x, y));
            path.add(new Point(x+barLength, y)); 
            path.add(new Point(x+barLength, y+barHeight)); 
            path.add(new Point(x, y+barHeight));     
            path.closed = true;
            group.addChild(path);
            path.position += pos;

            var skillText = new PointText(new Point((x+data[i].hours*lengthOfOneHour)+textOffset, y+barHeight-2));
            skillText.fillColor = 'black';
            skillText.justification = 'left';
            skillText.fontSize = barHeight;
            skillText.content = data[i].skillName + ':';
            group.addChild(skillText);
            skillText.position += pos;

            y+=barHeight;
        }

        var rightLimit = Math.ceil(maxSkillAmount/100)*100;
        var numVertBars = rightLimit/100;

        for(var i = 0; i <= numVertBars; i++)
        {
            var vertPath = new Path();
            vertPath.strokeColor = 'black';
            var xPos = x+(totalBarLength/numVertBars)*i;
            vertPath.add(new Point(xPos, initialY-barHeight));
            vertPath.add(new Point(xPos, size.height));
            vertPath.opacity = 0.3;
            group.addChild(vertPath);
            vertPath.position += pos;

            var skillAmountLabel = new PointText(new Point(xPos-40, size.height/2));
            skillAmountLabel.fillColor = 'black';
            skillAmountLabel.content = ((maxSkillAmount/(numVertBars))*i) + ' hours';
            skillAmountLabel.rotate(90);
            skillAmountLabel.opacity = 0.3;
            group.addChild(skillAmountLabel);
            skillAmountLabel.position += pos;
        }
    }

    function BoxText(text, pos, clickFunc, group)
    {
        var selected = false;

        var snd = new Audio("beep.mp3"); // buffers automatically when created

        var boxText = new PointText(pos);
        boxText.fillColor = 'black';
        boxText.justification = 'center';
        boxText.content = text;

        var boxTextBorder = new Path();
        boxTextBorder.name = text;
        boxTextBorder.strokeColor = 'white';
        boxTextBorder.fillColor = 'white';
        boxTextBorder.opacity = 0.3;
        boxTextBorder.add(pos.x-2, pos.y+3); 
        boxTextBorder.add(new Point(pos.x+boxText.bounds.width+2, pos.y+3)); 
        boxTextBorder.add(new Point(pos.x+boxText.bounds.width+2, pos.y-boxText.bounds.height));
        boxTextBorder.add(new Point(pos.x-2, pos.y-boxText.bounds.height));     
        boxTextBorder.closed = true;
        boxTextBorder.position.x -= boxText.bounds.width/2; // this is to offset for the text justification

        boxTextBorder.addChild(boxText);
        boxTextBorder.sendToBack();
    
        var test = "************** TEST *******************";

        this.overrideName = function(name)
        {
            boxText.name = name;
            boxTextBorder.name = name;
        };

        this.enter = function()
        {
            if(!selected)
            {
                console.dir("Enter");
                boxTextBorder.fillColor = 'grey';
                snd.play();    
            }
        };

        this.leave = function()
        {
            if(!selected)
            {
                console.dir("Leave");
                boxTextBorder.fillColor = 'white';    
            }
        };

        this.select = function()
        {
            selected = true;
            boxText.fillColor = 'red';
            boxTextBorder.fillColor = 'red';
        }

        this.unselect = function()
        {
            selected = false;
            boxText.fillColor = 'black';
            boxTextBorder.fillColor = 'white';
        }

        boxTextBorder.onMouseEnter = this.enter;
        boxTextBorder.onMouseLeave = this.leave;
        boxText.onMouseEnter = this.enter;
        boxText.onMouseLeave = this.leave;
        boxText.onMouseDown = clickFunc;
        boxTextBorder.onMouseDown = clickFunc;

        this.name = text;
        boxText.name = text;
        boxTextBorder.name = text;

        if(group)
        {
            group.addChild(boxTextBorder);
            group.addChild(boxText);
            console.dir("added to group " + group);
        }
    };


    

    function drawProjectsOfCategoryBoxes(projectData)
    {
        console.dir("project data: " + projectData);
        var projectsOfStatusGrouping = project.layers[PROJECTS].children['projectsOfStatusGrouping'];

        var initialX = 430;
        var x = initialX;
        var initialY = 90;
        var y = initialY;
        var yStep = 70;
        var xStep = 180;
        var count = 0;

        for(var i = 0; i < projectData.length; i++)
        {
            var badgePos = new Point(x, y);
            
            var xsize = 160;
            var ysize = 32;

            var boxClicked = function() 
            {
                console.dir("in boxClicked: " + this.name);

                Parse.Cloud.run('getDataOfType', { 'dataType': 'projectDetail', 'filter' : this.name}).then(function(response) 
                {
                    project.layers[PROJECTS].children['projectDetailGrouping'].removeChildren();
                    console.dir(response);  
                    drawProjectDetail(response);
                });
            };

            console.dir("***************:" + projectsOfStatusGrouping);
            var projectNameText = new BoxText(projectData[i].get("Name"), badgePos, boxClicked, projectsOfStatusGrouping);
            //console.dir("name:" + projectNameText.name);

            y+=yStep;

            if(y > canvas.height)
            {
                x+=xStep;
                y=initialY;
            }
        }

        //projectInitialGrouping.position.x += crossoverPoint.x;
        projectsOfStatusGrouping.visible = true;   
    }
    function drawProjectInitialBoxes(projectData)
    {
        var projectInitialGrouping = project.layers[PROJECTS].children['projectInitialGrouping'];

        var initialX = 30;
        var y = 90;
        var yStep = 70;
        var xStep = 96;

        var numProjects = 0;

        for(var i = 0; i < projectStatuses.length; i++)
        {
            var count = 0;

            _.each(projectData, function(project) 
            {
                if(project.get('Status') == projectStatuses[i])
                {
                    numProjects++;
                }
            });

            var textClicked = function() 
            {
                console.dir("name: " + this.name);

                Parse.Cloud.run('getDataOfType', { 'dataType': 'project', 'filter' : this.name }).then(function(response) 
                {
                    console.dir('response!' + response);

                    project.layers[PROJECTS].activate();

                    console.dir('children: ' + project.layers[PROJECTS].children['projectsOfStatusGrouping'].children.length);
                    

                    project.layers[PROJECTS].children['projectsOfStatusGrouping'].removeChildren();
                    project.layers[PROJECTS].children['projectDetailGrouping'].removeChildren();

                    console.dir('children: ' + project.layers[PROJECTS].children['projectsOfStatusGrouping'].children.length);

                    drawProjectsOfCategoryBoxes(response);

                    console.dir('children: ' + project.layers[PROJECTS].children['projectsOfStatusGrouping'].children.length);
                });
            };

            var badgePos = new Point(initialX+(xStep*count), y+(yStep*i));
            var projectStatusText = new BoxText(numProjects + ">>", badgePos, textClicked, projectInitialGrouping);
            projectStatusText.overrideName(projectStatuses[i]);  
            
            numProjects = 0;
        }

        //projectInitialGrouping.position.x += crossoverPoint.x;
        projectInitialGrouping.visible = true;
    }
    function drawProjectBoxes(projectData)
    {
        var initialX = 30;
        var y = 90;
        var yStep = 100;
        var xStep = 96;

        for(var i = 0; i < projectStatuses.length; i++)
        {
            var count = 0;

            _.each(projectData, function(project) 
            {
                if(project.get('Status') == projectStatuses[i])
                {
                    var projectName = new PointText(new Point(initialX+(xStep*count), y+(yStep*i)+20));
                    projectName.fillColor = 'black';
                    projectName.content = project.get('Name');
                    projectName.rotate(10);

                    var badgePos = new Point(initialX+(xStep*count), y+(yStep*i));
                    var badgePath = new Path();
                    var size = 32;

                    badgePath.strokeColor = 'black';
                    badgePath.fillColor = 'white';
                    badgePath.add(badgePos); 
                    badgePath.add(new Point(badgePos.x+size, badgePos.y)); 
                    badgePath.add(new Point(badgePos.x+size, badgePos.y-size));
                    badgePath.add(new Point(badgePos.x, badgePos.y-size));     
                    badgePath.closed = true;
                }
            });
        }
    }

    function onFrame(event)
    {
    }
    
    function initParse()
    {
        Parse.initialize('tpio94gHZkEKpbpxvjCAHKExanIx89VwpIsvtXt5', 'aQsoxqb0x8tk09TprScWDnEiZs9SSQFN8qsE3bP1');
    }

    function drawSkillLabels(skillData)
    {
        var skillLabelGroup = project.layers[SKILLS].children['skillLabelGroup'];

        var x = 250;
        var y = 10;
        var yStep = 11;

        for(var skill in skillData)
        {
            var skillText = new PointText(new Point(x, y));
            skillText.fillColor = 'black';
            skillText.justification = 'right';
            skillText.content = skillData[skill].skillName + ':';
            skillLabelGroup.addChild(skillText);

            y+=yStep;
        }

        skillLabelGroup.visible = false;
        skillLabelGroup.position.x += crossoverPoint.x;
    }

    function drawSkillCategories(categoryData)
    {
        var skillCategoryGroup = project.layers[SKILLS].children['skillCategoryGroup'];

        var x = 10;
        var y = 18;
        var yStep = 18;

        var skillCategoryLabels = [];

        for(var skillCategory in categoryData)
        {
            var changeSkillCategory = function() 
            {
                console.dir("in changeSkillCategory");
                project.layers[SKILLS].children['skillBarGroup'].removeChildren();
                project.layers[SKILLS].children['skillLabelGroup'].removeChildren();

                //updateLayers(category.value);
                Parse.Cloud.run('getDataOfType', { 'dataType': 'skills', 'filter' : this.content }).then(function(response) 
                {
                    console.log('response! ' + response);
                    project.layers[SKILLS].activate();
                    drawSkillLabels(response);
                    drawSkillBars(response);
                    project.layers[SKILLS].children['skillBarGroup'].visible = true;
                    project.layers[SKILLS].children['skillLabelGroup'].visible = true;
                });

                /*for(var i = 1; i < skillCategoryGroup.children.length; i++)
                {
                    project.layers[i].visible = i === visibleIndex ? true : false;
                    i === visibleIndex ? categoryLabels[i-1].select() : categoryLabels[i-1].unselect();
                }*/

                // TODO: Refactor this into a "selection group" tyoe thing for a selection of BoxLabels
                for(var i = 0; i < skillCategoryLabels.length; i++)
                {
                    if(skillCategoryLabels[i].name === this.content)
                    {
                        console.dir("SELECT ***:" + this.content + " : " + skillCategoryLabels[i].name);
                        skillCategoryLabels[i].select();
                    }
                    else
                    {
                        console.dir("UNSELECT ***:" + this.content + " : " + skillCategoryLabels[i].name);
                        skillCategoryLabels[i].unselect();
                    }
                }
            };

            var skillCategoryText = new BoxText(categoryData[skillCategory].category, new Point(x,y), changeSkillCategory, skillCategoryGroup);
            skillCategoryText.overrideName("TEST");
            skillCategoryLabels.push(skillCategoryText);
            y+=yStep;
        }

        skillCategoryGroup.position.x += crossoverPoint.x;
    }
    function drawSkillBars(skillData)
    {
        var skillBarGroup = project.layers[SKILLS].children['skillBarGroup'];

        var x = 252;
        var initialY = 10
        var y = initialY;
        var yStep = 11;
        var barHeight = 10;
        var maxSkillAmount = 2000; // dynamically calculate this in the future
        var totalBarLength = canvas.width-crossoverPoint.x-x-50;
        var lengthOfOneHour = totalBarLength/maxSkillAmount;

        // draw horizontal bars for skills
        for(var i = 0; i < skillData.length; i++)
        {
            var barHeightDampener = 2;

            var path = new Path();
            path.strokeColor = 'black';
            path.fillColor = 'black';
            path.add(new Point(x, y));
            path.add(new Point(x+skillData[i].hours*lengthOfOneHour, y)); 
            path.add(new Point(x+skillData[i].hours*lengthOfOneHour, y-barHeight)); 
            path.add(new Point(x, y-barHeight));     
            path.closed = true;

            skillBarGroup.addChild(path);

            y+=yStep;
        }

        // draw vertical bars for progress
        /*var vertPath = new Path();
        vertPath.strokeColor = 'black';
        vertPath.add(new Point(maxSkillAmount/barHeightDampener, initialY-barHeight));
        vertPath.add(new Point(maxSkillAmount/barHeightDampener, y));
        vertPath.opacity = 0.3;

        skillBarGroup.addChild(vertPath);*/

        var numVertBars = 10;

        for(var i = 0; i <= numVertBars; i++)
        {
            var vertPath = new Path();
            vertPath.strokeColor = 'black';
            var xPos = x+(totalBarLength/numVertBars)*i;
            vertPath.add(new Point(xPos, initialY-barHeight));
            vertPath.add(new Point(xPos, y));
            vertPath.opacity = 0.3;
            skillBarGroup.addChild(vertPath);

            var skillMaxLabel = new PointText(new Point(xPos-20, y+40));
            skillMaxLabel.fillColor = 'black';
            skillMaxLabel.content = ((maxSkillAmount/(numVertBars))*i) + ' hours';
            skillMaxLabel.rotate(90);
            skillMaxLabel.opacity = 0.3;
            skillBarGroup.addChild(skillMaxLabel);
        }
        skillBarGroup.visible = false;

        skillBarGroup.position.x += crossoverPoint.x;
    }

    // move parse-side? or keep here?
    function getNumberOfMonths(from, to) 
    {
        from = convertDateToUTC(from);
        to = convertDateToUTC(to);

        var months;
        months = (to.getFullYear() - from.getFullYear()) * 12;
        months -= from.getMonth();
        months += to.getMonth();
        return months <= 0 ? 0 : months;
    }
    // port to moment.js?
    // times are occasionally coming out minus...

    function convertDateToUTC(date) 
    {
        return new Date(date.getUTCFullYear() + '-' + (date.getUTCMonth() + 1) + '-' + (date.getUTCDate()));
        //console.log(results[i].get("StartDate").getTimezoneOffset());
        //results[i].set("StartDate", e); 
    }

    function drawTimeLine(response)
    {
        //crossoverPoint
        // main line
        var xStart = -20;
        var xEnd = canvas.width-crossoverPoint.x-60;
        var y = canvas.height-100;
        var path = new Path();
        path.strokeColor = 'black';
        path.fillColor = 'black';
        path.add(new Point(xStart, y)); 
        path.add(new Point(xEnd,y));

        drawProjectsOnTimeLine(xEnd-xStart, response);
    }

    function drawProjectDetail(response)
    {
        var projectDetailGrouping = project.layers[PROJECTS].children['projectDetailGrouping'];

        var projectName = new PointText(new Point(0, 200));
        projectName.fillColor = 'black';
        projectName.justification = 'center';
        projectName.content = "Project Name: " + response.get("Name");
        projectDetailGrouping.addChild(projectName);

        var projectPlatform = new PointText(new Point(0, 300));
        projectPlatform.fillColor = 'black';
        projectPlatform.justification = 'center';
        projectPlatform.content = "Platform: " + response.get("Platform");
        projectDetailGrouping.addChild(projectPlatform)

        var projectStatus = new PointText(new Point(0, 400));
        projectStatus.fillColor = 'black';
        projectStatus.justification = 'center';
        projectStatus.content = "Status: " + response.get("Status");
        projectDetailGrouping.addChild(projectStatus)

        var projectDescription = new PointText(new Point(0, 500));
        projectDescription.fillColor = 'black';
        projectDescription.justification = 'center';
        projectDescription.content = "Description: " + response.get("Description");
        projectDetailGrouping.addChild(projectDescription)

        var projectImage = new Path();
        projectImage.fillColor = 'black';
        projectImage.strokeColor = 'black';
        projectImage.add(new Point(0,0));
        projectImage.add(new Point(100,0));
        projectImage.add(new Point(100,100));
        projectImage.add(new Point(0,100));
        projectImage.closed = true;
        projectDetailGrouping.addChild(projectImage);

        var image = new Raster(response.get("imageName"));
        image.scale(0.3);
        image.bounds = projectImage.bounds;
        var group = new Group([projectImage, image]);
        //group.clipMask = true;
        group.clipped = true;
        projectDetailGrouping.addChild(group);

        // draw skills used graph
        //var graph = createBarGraph(response.get("SkillsUsed"), new Size(200,100));
        console.dir("response:" + response);
        console.dir("skills used:" + response.get("SkillsUsed"));

        var graph = new BarGraph(new Point(-150,550), new Size(200,100), response.get("SkillsUsed").skills, projectDetailGrouping);
        graph.name = 'graph';
        //graph.position.x += 100;
        projectDetailGrouping.position.x += canvas.width - 200;
        projectDetailGrouping.visible = true;
    }
    function drawProjectsOnTimeLine(lineLength, response)
    {
        var initialY = canvas.height-100;
        var currentY = initialY;
        var xStart = -20;

        // first just sort by start time and sort in y

        var allProjects = _.sortBy(response, 'StartDate');
 
        var firstDate = _.first(allProjects).get('StartDate');
        var lastDate  = new Date();

        var totalNumMonths = getNumberOfMonths(firstDate, lastDate);
        var oneMonthLength = lineLength/totalNumMonths;

        // year lines
        var monthCount = 0;

        // calculate properly from 1st
        var year = 2004;

        while(monthCount <= totalNumMonths)
        {
            var path = new Path();
            path.strokeColor = 'black';
            path.fillColor = 'white';
            var xPos = xStart+(monthCount*oneMonthLength);
            // hack in time offset - calculate properly in the future!
{'fraction_non_alphanumeric': 0.11897353000606183, 'fraction_numerical': 0.011012325722368155, 'mean_word_length': 1.8564585016737851, 'pattern_counts': {'":': 0, '<': 65, '<?xml version=': 0, '>': 45, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '54686', 'n_tokens_mistral': 14474, 'n_tokens_neox': 13599, 'n_words': 2624}