<commit_before>//  Sample refactored by David Rousset - Microsoft France - http://blogs.msdn.com/davrous
//  Using Hand.js to support all platforms

//  Based on https://github.com/sebleedelisle/JSTouchController/blob/master/Touches.html

//  Copyright (c)2010-2011, Seb Lee-Delisle, sebleedelisle.com. All rights reserved.

//  Redistribution and use in source and binary forms, with or without modification, are permitted provided
//  that the following conditions are met:

//    * Redistributions of source code must retain the above copyright notice,
//      this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above copyright notice,
//      this list of conditions and the following disclaimer in the documentation
//      and/or other materials provided with the distribution.

//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
//  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
//  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
//  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
//  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
//  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
//  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// shim layer with setTimeout fallback
window.requestAnimFrame = (function () {
    return window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.oRequestAnimationFrame ||
    window.msRequestAnimationFrame ||
    function (callback) {
        window.setTimeout(callback, 1000 / 60);
    };
})();

var control_canvas,
c,        // c is the fg_canvas' context 2D
halfWidth,
halfHeight,
leftPointerID = -1,
leftPointerPos = new Vector2(0, 0),
leftPointerStartPos = new Vector2(0, 0),
leftVector = new Vector2(0, 0);
arrow = new VectorLED(0, 0, 0, 0);

var touches; // collections of pointers

/////////////////////////////// ARDUINO SETUP
var IOBoard = BO.IOBoard;
var IOBoardEvent = BO.IOBoardEvent;
var Pin = BO.Pin;

//// Variables
// LED related
var led1; //-90
var led2; //0
var led3; //90
var led4; //180=-180
var LEDloopON = false;
var LEDcont = 1;
var max_val;

var frame = 0;
var lastUpdateTime = 0;
var acDelta = 0;
var msPerFrame = 10;

// Set to true to print debug messages to console
BO.enableDebugging = true;

var host = window.location.hostname;
// if the file is opened locally, set the host to "localhost"
if (window.location.protocol.indexOf("file:") === 0) {
        host = "171.65.102.132";
    }
var arduino = new IOBoard(host, 8887);
///////////////////////////// ARDUINO SETUP END

var username = "noname";
var socket;

document.addEventListener("DOMContentLoaded", init);

window.onorientationchange = resetCanvas;
window.onresize = resetCanvas;

function init() {
    setupCanvas();
    setupVidCanvas();
    setupMotionDetection();

    touches = new Collection();

    arduino.addEventListener(IOBoardEvent.READY, onReady);
}

function onReady(event) {
        // Remove the event listener because it is no longer needed
        arduino.removeEventListener(IOBoardEvent.READY, onReady);

        // Set Pin 5,6,9,10 to PWM
        arduino.setDigitalPinMode(5, Pin.PWM);
        arduino.setDigitalPinMode(6, Pin.PWM);
        arduino.setDigitalPinMode(9, Pin.PWM);
        arduino.setDigitalPinMode(10, Pin.PWM);

        // Create an LED object to interface with the LED wired
        // to the I/O board
        led1 = arduino.getDigitalPin(5);
        led2 = arduino.getDigitalPin(6);
        led3 = arduino.getDigitalPin(9);
        led4 = arduino.getDigitalPin(10);

        // jQuery part for the button
    $("#slider").slider({
        step: 0.05,
        min : 0.0,
        max : 1.0
    });

    $('#value').text("Value: 0");

    $( "#slider" ).bind( "slide", function(event, ui) {
        var value = ui.value;
        $('#value').text("Value: " + value.toFixed(2));
        brown_const = value;
        console.log(brown_const);
    });

        $('input[name=setUsername]').click(function(){
                        if($('input[name=usernameTxt]').val() != ""){
                                username = $('input[name=usernameTxt]').val();
                                var msg = {type:'setUsername', user:username};
                                socket.json.send(msg);
                        }
                        $('#username').slideUp("slow",function(){
                                $('#sendChat').slideDown("slow");
                                resetGame();
                        });
        });

        socket = new io.connect('http://171.65.102.132:8088');
        var chat = $('#chat');
        var board = $('#board');

        socket.on('connect', function() {
                console.log("Connected");
                //chat.html("Connected!");
                socket.emit('message', {channel:'realtime'});
        });

        socket.on('message', function(message){
                chat.append(message + '
');
        });

        socket.on('postscore', function(score){
                board.empty();
                for (var i=0;i<score.length;i++){
                        if(i==0){
                                board.append(''+ score[i][0]+'  :  '+score[i][1]+'
');
                        }else{
                                board.append(score[i][0]+'  :  '+score[i][1]+ '
');
                        }
                }
                board.fadeOut('fast');
                board.fadeIn('fast');
                board.fadeOut('fast');
                board.fadeIn('fast');
        });

        socket.on('disconnect', function() {
                console.log('disconnected');
                chat.html("Disconnect!");
        });

        $("input[name=sendBtn]").click(function(){
                var msg = {type:'chat',message:username + " : " + $("input[name=chatTxt]").val()}
                socket.json.send(msg);
                $("input[name=chatTxt]").val("");
        });

        $("input[name=recBtn]").click(function(){
        	var cc = new CanvasCapture({
				debug: true,
    			fps: 8,
    			inCanvasEl: document.getElementById('videoCanvas')
			});
        	cc.start();
        	setTimeout(function(){cc.stop();}, 3000);
        });

    control_canvas.addEventListener('pointerdown', onPointerDown, false);
    control_canvas.addEventListener('pointermove', onPointerMove, false);
    control_canvas.addEventListener('pointerup', onPointerUp, false);
    control_canvas.addEventListener('pointerout', onPointerUp, false);

    requestAnimFrame(draw);
}

function draw() {
    c.clearRect(0, 0, control_canvas.width, control_canvas.height);

    c.beginPath();
    c.moveTo(halfWidth, halfHeight-4);
    c.strokeStyle = "rgba(250, 102, 0, 1)";
    c.lineWidth = 2;
    c.lineTo(halfWidth, halfHeight+4);
    c.stroke();

    c.beginPath();
    c.moveTo(halfWidth+4, halfHeight);
    c.strokeStyle = "rgba(250, 102, 0, 1)";
    c.lineWidth = 2;
    c.lineTo(halfWidth-4, halfHeight);
    c.stroke();

    drawCircles(halfWidth, halfHeight);

        //// mouse event loop
    touches.forEach(function (touch) {
        if (touch.identifier == leftPointerID) {
            var alpha = arrow.trimArrow(leftVector, max_val);

            c.beginPath();
            c.fillStyle = "rgba(250, 102, 0, 1)";
            c.arc(leftPointerPos.x, leftPointerPos.y, 16, 0, Math.PI * 2, true);
            c.fill();

            c.beginPath();
            c.moveTo(leftPointerStartPos.x,leftPointerStartPos.y);
            c.strokeStyle = "rgba(250, 102, 0, 1)";
                        c.lineTo(leftPointerStartPos.x+max_val*(arrow.int2-arrow.int4),leftPointerStartPos.y+max_val*(arrow.int1-arrow.int3));
                        c.lineWidth = 3;
                        c.stroke();

                        c.beginPath();
                        c.fillStyle = "rgba(255, 255, 255, "+alpha*(LEDcont)+")";
                    c.arc(halfWidth, halfHeight, 16, 0, Math.PI * 2, true);
                    c.fill();

                        c.beginPath();
                c.fillStyle = "#fff";
                c.fillText(alpha,halfWidth-10, halfHeight-25);

                c.beginPath();
                c.fillStyle = "#dd6600";
                var theta = leftVector.angle();
                c.fillText(theta.toFixed(0),leftPointerPos.x+10, leftPointerPos.y-20);
        }
    });

    requestAnimFrame(draw);

    //// LED control loop
    var delta = Date.now() - lastUpdateTime;
    if (acDelta > msPerFrame)
    {
        acDelta = 0;
        if(LEDloopON)
        {
                //LEDon = frame%2;
                LEDcont = 1;
                changeLED(LEDcont);
        }
        frame++;
        if (frame >= 2) {frame = 0;}
    } else
    {
        acDelta += delta;
    }
    lastUpdateTime = Date.now();
}

function drawCircles(xCenter,yCenter)
{
        var needle = new Vector2(max_val, 0);
        for(var i=0;i<24*3;i++)
        {
                c.beginPath();
                c.fillStyle = "rgba(255, 255, 255, 0.5)";
                c.lineWidth = 3;
                c.arc(xCenter + needle.x, yCenter + needle.y, 1, 0, Math.PI * 2, true);
                c.fill();
                needle.rotate(5,0);
        }
}

function givePointerType(event) {
    switch (event.pointerType) {
        case event.POINTER_TYPE_MOUSE:
            return "MOUSE";
            break;
        case event.POINTER_TYPE_PEN:
            return "PEN";
            break;
        case event.POINTER_TYPE_TOUCH:
            return "TOUCH";
            break;
    }
}

function onPointerDown(e) {
    var newPointer = { identifier: e.pointerId, x: e.clientX, y: e.clientY, type: givePointerType(e) };
    leftPointerID = e.pointerId;
    //leftPointerStartPos.reset(e.clientX, e.clientY);
    leftPointerStartPos.reset(halfWidth, halfHeight);
    leftPointerPos.copyFrom(leftPointerStartPos);
    leftVector.reset(0, 0);
    arrow.reset(0, 0, 0, 0);
    touches.add(e.pointerId, newPointer);
}

function onPointerMove(e) {
    if (leftPointerID == e.pointerId) {
        //leftPointerPos.reset(e.clientX, e.clientY);
        leftPointerPos.reset(e.offsetX, e.offsetY);
        leftVector.copyFrom(leftPointerPos);
        //leftVector.minusEq(leftPointerStartPos);
        leftVector.minusEq(leftPointerStartPos);
        arrow.setArrow(leftVector, max_val);
        LEDloopON = true;
    }
    else {
        if (touches.item(e.pointerId)) {
            touches.item(e.pointerId).x = e.clientX;
            touches.item(e.pointerId).y = e.clientY;
        }
    }
}

function onPointerUp(e) {
    if (leftPointerID == e.pointerId) {
        leftPointerID = -1;
        leftVector.reset(0, 0);
    }
    leftVector.reset(0, 0);
    arrow.reset(0, 0, 0, 0);
    touches.remove(e.pointerId);
    LEDloopON = false;
    changeLED(0);
}

function setupCanvas() {
    control_canvas = document.getElementById('controlCanvas');
    c = control_canvas.getContext('2d');
    resetCanvas();
    c.strokeStyle = "#ffffff";
    c.lineWidth = 2;
}

// Arduino control functions
function changeLED(LEDon) {
    if(LEDon)
    {
            led1.value = arrow.int1;
            led2.value = arrow.int2;
            led3.value = arrow.int3;
            led4.value = arrow.int4;
    }
    else
    {
            led1.value = 0;
            led2.value = 0;
            led3.value = 0;
            led4.value = 0;
        }
}

function resetCanvas(e) {
    max_val = (document.getElementById("controlArea").offsetWidth-100)/2;

    // resize the canvas - but remember - this clears the canvas too.

    control_canvas.width = document.getElementById("controlArea").offsetWidth-20;
    control_canvas.height = control_canvas.width-20;

    halfWidth = (control_canvas.width)/2;
    halfHeight = (control_canvas.height)/2;

    // make sure we scroll to the top left.
    window.scrollTo(0, 0);
}<commit_msg>canvascapture<commit_after>//  Sample refactored by David Rousset - Microsoft France - http://blogs.msdn.com/davrous
//  Using Hand.js to support all platforms

//  Based on https://github.com/sebleedelisle/JSTouchController/blob/master/Touches.html

//  Copyright (c)2010-2011, Seb Lee-Delisle, sebleedelisle.com. All rights reserved.

//  Redistribution and use in source and binary forms, with or without modification, are permitted provided
//  that the following conditions are met:

//    * Redistributions of source code must retain the above copyright notice,
//      this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above copyright notice,
//      this list of conditions and the following disclaimer in the documentation
//      and/or other materials provided with the distribution.

//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
//  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
//  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
//  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
//  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
//  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
//  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// shim layer with setTimeout fallback
window.requestAnimFrame = (function () {
    return window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.oRequestAnimationFrame ||
    window.msRequestAnimationFrame ||
    function (callback) {
        window.setTimeout(callback, 1000 / 60);
    };
})();

var control_canvas,
c,        // c is the fg_canvas' context 2D
halfWidth,
halfHeight,
leftPointerID = -1,
leftPointerPos = new Vector2(0, 0),
leftPointerStartPos = new Vector2(0, 0),
leftVector = new Vector2(0, 0);
arrow = new VectorLED(0, 0, 0, 0);

var touches; // collections of pointers

/////////////////////////////// ARDUINO SETUP
var IOBoard = BO.IOBoard;
var IOBoardEvent = BO.IOBoardEvent;
var Pin = BO.Pin;

//// Variables
// LED related
var led1; //-90
var led2; //0
var led3; //90
var led4; //180=-180
var LEDloopON = false;
var LEDcont = 1;
var max_val;

var frame = 0;
var lastUpdateTime = 0;
var acDelta = 0;
var msPerFrame = 10;

// Set to true to print debug messages to console
BO.enableDebugging = true;

var host = window.location.hostname;
// if the file is opened locally, set the host to "localhost"
if (window.location.protocol.indexOf("file:") === 0) {
        host = "171.65.102.132";
    }
var arduino = new IOBoard(host, 8887);
///////////////////////////// ARDUINO SETUP END

var username = "noname";
var socket;

document.addEventListener("DOMContentLoaded", init);

window.onorientationchange = resetCanvas;
window.onresize = resetCanvas;

function init() {
    setupCanvas();
    setupVidCanvas();
    setupMotionDetection();

    touches = new Collection();

    arduino.addEventListener(IOBoardEvent.READY, onReady);
}

function onReady(event) {
        // Remove the event listener because it is no longer needed
        arduino.removeEventListener(IOBoardEvent.READY, onReady);

        // Set Pin 5,6,9,10 to PWM
        arduino.setDigitalPinMode(5, Pin.PWM);
        arduino.setDigitalPinMode(6, Pin.PWM);
        arduino.setDigitalPinMode(9, Pin.PWM);
        arduino.setDigitalPinMode(10, Pin.PWM);

        // Create an LED object to interface with the LED wired
        // to the I/O board
        led1 = arduino.getDigitalPin(5);
        led2 = arduino.getDigitalPin(6);
        led3 = arduino.getDigitalPin(9);
        led4 = arduino.getDigitalPin(10);

        // jQuery part for the button
    $("#slider").slider({
        step: 0.05,
        min : 0.0,
        max : 1.0
    });

    $('#value').text("Value: 0");

    $( "#slider" ).bind( "slide", function(event, ui) {
        var value = ui.value;
        $('#value').text("Value: " + value.toFixed(2));
        brown_const = value;
        console.log(brown_const);
    });

        $('input[name=setUsername]').click(function(){
                        if($('input[name=usernameTxt]').val() != ""){
                                username = $('input[name=usernameTxt]').val();
                                var msg = {type:'setUsername', user:username};
                                socket.json.send(msg);
                        }
                        $('#username').slideUp("slow",function(){
                                $('#sendChat').slideDown("slow");
                                resetGame();
                        });
        });

        socket = new io.connect('http://171.65.102.132:8088');
        var chat = $('#chat');
        var board = $('#board');

        socket.on('connect', function() {
                console.log("Connected");
                //chat.html("Connected!");
                socket.emit('message', {channel:'realtime'});
        });

        socket.on('message', function(message){
                chat.append(message + '
');
        });

        socket.on('postscore', function(score){
                board.empty();
                for (var i=0;i<score.length;i++){
                        if(i==0){
                                board.append(''+ score[i][0]+'  :  '+score[i][1]+'
');
                        }else{
                                board.append(score[i][0]+'  :  '+score[i][1]+ '
');
                        }
                }
                board.fadeOut('fast');
                board.fadeIn('fast');
                board.fadeOut('fast');
                board.fadeIn('fast');
        });

        socket.on('disconnect', function() {
                console.log('disconnected');
                chat.html("Disconnect!");
        });

        $("input[name=sendBtn]").click(function(){
                var msg = {type:'chat',message:username + " : " + $("input[name=chatTxt]").val()}
                socket.json.send(msg);
                $("input[name=chatTxt]").val("");
        });

        $("input[name=recBtn]").click(function(){
        	var cc = new CanvasCapture({
    			inCanvasEl: document.getElementById('videoCanvas')
			});
        	cc.start();
        	setTimeout(function(){cc.stop();}, 3000);
        });

    control_canvas.addEventListener('pointerdown', onPointerDown, false);
    control_canvas.addEventListener('pointermove', onPointerMove, false);
    control_canvas.addEventListener('pointerup', onPointerUp, false);
    control_canvas.addEventListener('pointerout', onPointerUp, false);

    requestAnimFrame(draw);
}

function draw() {
    c.clearRect(0, 0, control_canvas.width, control_canvas.height);

    c.beginPath();
    c.moveTo(halfWidth, halfHeight-4);
    c.strokeStyle = "rgba(250, 102, 0, 1)";
    c.lineWidth = 2;
    c.lineTo(halfWidth, halfHeight+4);
    c.stroke();

    c.beginPath();
    c.moveTo(halfWidth+4, halfHeight);
    c.strokeStyle = "rgba(250, 102, 0, 1)";
    c.lineWidth = 2;
    c.lineTo(halfWidth-4, halfHeight);
    c.stroke();

    drawCircles(halfWidth, halfHeight);

        //// mouse event loop
    touches.forEach(function (touch) {
        if (touch.identifier == leftPointerID) {
            var alpha = arrow.trimArrow(leftVector, max_val);

            c.beginPath();
            c.fillStyle = "rgba(250, 102, 0, 1)";
            c.arc(leftPointerPos.x, leftPointerPos.y, 16, 0, Math.PI * 2, true);
            c.fill();

            c.beginPath();
            c.moveTo(leftPointerStartPos.x,leftPointerStartPos.y);
            c.strokeStyle = "rgba(250, 102, 0, 1)";
                        c.lineTo(leftPointerStartPos.x+max_val*(arrow.int2-arrow.int4),leftPointerStartPos.y+max_val*(arrow.int1-arrow.int3));
                        c.lineWidth = 3;
                        c.stroke();

                        c.beginPath();
                        c.fillStyle = "rgba(255, 255, 255, "+alpha*(LEDcont)+")";
                    c.arc(halfWidth, halfHeight, 16, 0, Math.PI * 2, true);
                    c.fill();

                        c.beginPath();
                c.fillStyle = "#fff";
                c.fillText(alpha,halfWidth-10, halfHeight-25);

                c.beginPath();
                c.fillStyle = "#dd6600";
                var theta = leftVector.angle();
                c.fillText(theta.toFixed(0),leftPointerPos.x+10, leftPointerPos.y-20);
        }
    });

    requestAnimFrame(draw);

    //// LED control loop
    var delta = Date.now() - lastUpdateTime;
    if (acDelta > msPerFrame)
    {
        acDelta = 0;
        if(LEDloopON)
        {
                //LEDon = frame%2;
                LEDcont = 1;
                changeLED(LEDcont);
        }
        frame++;
        if (frame >= 2) {frame = 0;}
    } else
    {
        acDelta += delta;
    }
    lastUpdateTime = Date.now();
}

function drawCircles(xCenter,yCenter)
{
        var needle = new Vector2(max_val, 0);
        for(var i=0;i<24*3;i++)
        {
                c.beginPath();
                c.fillStyle = "rgba(255, 255, 255, 0.5)";
                c.lineWidth = 3;
                c.arc(xCenter + needle.x, yCenter + needle.y, 1, 0, Math.PI * 2, true);
                c.fill();
                needle.rotate(5,0);
        }
}

function givePointerType(event) {
    switch (event.pointerType) {
        case event.POINTER_TYPE_MOUSE:
            return "MOUSE";
            break;
        case event.POINTER_TYPE_PEN:
            return "PEN";
            break;
        case event.POINTER_TYPE_TOUCH:
            return "TOUCH";
            break;
    }
}

function onPointerDown(e) {
    var newPointer = { identifier: e.pointerId, x: e.clientX, y: e.clientY, type: givePointerType(e) };
    leftPointerID = e.pointerId;
    //leftPointerStartPos.reset(e.clientX, e.clientY);
    leftPointerStartPos.reset(halfWidth, halfHeight);
    leftPointerPos.copyFrom(leftPointerStartPos);
    leftVector.reset(0, 0);
    arrow.reset(0, 0, 0, 0);
    touches.add(e.pointerId, newPointer);
}

function onPointerMove(e) {
    if (leftPointerID == e.pointerId) {
        //leftPointerPos.reset(e.clientX, e.clientY);
        leftPointerPos.reset(e.offsetX, e.offsetY);
        leftVector.copyFrom(leftPointerPos);
        //leftVector.minusEq(leftPointerStartPos);
        leftVector.minusEq(leftPointerStartPos);
        arrow.setArrow(leftVector, max_val);
        LEDloopON = true;
    }
    else {
        if (touches.item(e.pointerId)) {
            touches.item(e.pointerId).x = e.clientX;
            touches.item(e.pointerId).y = e.clientY;
        }
    }
}

function onPointerUp(e) {
    if (leftPointerID == e.pointerId) {
        leftPointerID = -1;
        leftVector.reset(0, 0);
    }
    leftVector.reset(0, 0);
    arrow.reset(0, 0, 0, 0);
    touches.remove(e.pointerId);
    LEDloopON = false;
    changeLED(0);
}

function setupCanvas() {
    control_canvas = document.getElementById('controlCanvas');
    c = control_canvas.getContext('2d');
    resetCanvas();
    c.strokeStyle = "#ffffff";
    c.lineWidth = 2;
}

// Arduino control functions
function changeLED(LEDon) {
    if(LEDon)
    {
            led1.value = arrow.int1;
            led2.value = arrow.int2;
            led3.value = arrow.int3;
            led4.value = arrow.int4;
    }
    else
    {
            led1.value = 0;
            led2.value = 0;
            led3.value = 0;
            led4.value = 0;
        }
}

function resetCanvas(e) {
    max_val = (document.getElementById("controlArea").offsetWidth-100)/2;

    // resize the canvas - but remember - this clears the canvas too.

    control_canvas.width = document.getElementById("controlArea").offsetWidth-20;
    control_canvas.height = control_canvas.width-20;

    halfWidth = (control_canvas.width)/2;
    halfHeight = (control_canvas.height)/2;

    // make sure we scroll to the top left.
    window.scrollTo(0, 0);
}<|endoftext|>


<commit_before>const html = require('choo/html');

module.exports = (state, prev, send) => {
  return html`
     send('startup')}>

About 5 Calls

Why Calling Works

Calling members of Congress is the most effective way to have your voice heard. Calls are tallied by staffers and the count is given to your representatives, informing them how strongly their constituents feel about a current issue. The sooner your reach out, the more likely it is that your voice will influence their position.

Don’t just take it from us:

“Here’s Why You Should Call, Not Email, Your Legislators” NY Times

“Don’t just write to your representatives. Call them — and go to town halls.” Vox

“I can tell you the calls we’ve gotten in my district office and here in Washington surprised me, meaning the numbers of calls.”  Washington Post

“Most members tell me blizzard of angry constituent calls were most impt factor in getting the House to sideline the amdt” Robert Costa

5 Calls does the research for each issue, determining out which representatives are most influential for which topic, collecting phone numbers for those offices and writing scripts that clearly articulate a progressive position. You just have to call.

Are we not covering an issue we should be?  Please reach out.

Calling Tips

Calls should take less than a minute. You’ll be speaking to a staffer, so make your point clearly so they can tally your opinion correctly. The provided scripts are useful but you can add your own words.

Be respectful. The staffers that pick up the phone aren’t looking to challenge you and you should treat them with the same respect you expect from them, regardless of which party they work for.

Who made 5 Calls?

5 Calls is a volunteer effort. Founded and run by husband & wife team Nick and Rebecca, many dedicated volunteers contribute design, code and issues to the site including: @monteiro, @stewartsc, @liamdanger, @capndesign, @jameshome, @beau, @cdoremus, @ocderby, @mr0grog and more supporters.

Our iOS app is made by @subdigital, @mccarron, @chrisbrandow and more supporters

Our Android app is made by @dekthedek, gregliest and more supporters

Our issues, scripts and social team is @charmbtrippin, sara.n.g, amanda.l, @djpiebob, @theRati, @lhhargrave, amanda.n, @sagerke, eleanor and more contributors.

We want to make advocacy accessible. We hope 5 Calls will make it effortless for regular people to have a voice when it’s needed most.

Join us

This project is open source and volunteer made. If you’d like to join us in developing useful tools for citizens, please get in touch via Twitter or email.

  `;
}
<commit_msg>Updated a name<commit_after>const html = require('choo/html');

module.exports = (state, prev, send) => {
  return html`
     send('startup')}>

About 5 Calls

Why Calling Works

Calling members of Congress is the most effective way to have your voice heard. Calls are tallied by staffers and the count is given to your representatives, informing them how strongly their constituents feel about a current issue. The sooner your reach out, the more likely it is that your voice will influence their position.

Don’t just take it from us:

“Here’s Why You Should Call, Not Email, Your Legislators” NY Times

“Don’t just write to your representatives. Call them — and go to town halls.” Vox

“I can tell you the calls we’ve gotten in my district office and here in Washington surprised me, meaning the numbers of calls.”  Washington Post

“Most members tell me blizzard of angry constituent calls were most impt factor in getting the House to sideline the amdt” Robert Costa

5 Calls does the research for each issue, determining out which representatives are most influential for which topic, collecting phone numbers for those offices and writing scripts that clearly articulate a progressive position. You just have to call.

Are we not covering an issue we should be?  Please reach out.

Calling Tips

Calls should take less than a minute. You’ll be speaking to a staffer, so make your point clearly so they can tally your opinion correctly. The provided scripts are useful but you can add your own words.

Be respectful. The staffers that pick up the phone aren’t looking to challenge you and you should treat them with the same respect you expect from them, regardless of which party they work for.

Who made 5 Calls?

5 Calls is a volunteer effort. Founded and run by husband & wife team Nick and Rebecca, many dedicated volunteers contribute design, code and issues to the site including: @monteiro, @stewartsc, @liamdanger, @capndesign, @jameshome, @beau, @cdoremus, @ocderby, @mr0grog and more supporters.

Our iOS app is made by @subdigital, @mccarron, @chrisbrandow and more supporters

Our Android app is made by @dekthedek, gregliest and more supporters

Our issues, scripts and social team is @charmbtrippin, sara.n.g, amanda.l, @djpiebob, @theRati, @lhhargrave, amanda.n, @sagerke, Eleanor Wertman and more contributors.

We want to make advocacy accessible. We hope 5 Calls will make it effortless for regular people to have a voice when it’s needed most.

Join us

This project is open source and volunteer made. If you’d like to join us in developing useful tools for citizens, please get in touch via Twitter or email.

  `;
}
<|endoftext|>


<commit_before>'use strict';

function canvas_draw(){
    canvas_buffer.clearRect(
      0,
      0,
      canvas_width,
      canvas_height
    );

    draw_logic();

    if(core_menu_open){
        canvas_buffer.save();

        canvas_buffer.fillStyle = '#111';
        canvas_buffer.fillRect(
          canvas_x - 125,
          canvas_y - 50,
          250,
          100
        );

        canvas_buffer.fillStyle = '#fff';
        canvas_buffer.font = canvas_fonts['medium'];
        canvas_buffer.textAlign = 'center';
        canvas_buffer.textBaseline = 'middle';
        canvas_buffer.fillText(
          core_menu_resume,
          canvas_x,
          canvas_y - 25
        );
        canvas_buffer.fillText(
          core_menu_quit,
          canvas_x,
          canvas_y + 25
        );

        canvas_buffer.restore();
    }

    canvas_canvas.clearRect(
      0,
      0,
      canvas_width,
      canvas_height
    );
    canvas_canvas.drawImage(
      document.getElementById('buffer'),
      0,
      0
    );
}

function canvas_drawloop(){
    canvas_draw();
    canvas_animationFrame = window.requestAnimationFrame(canvas_drawloop);
}

// Required args: vertices
// Optional args: properties, style, type
function canvas_draw_path(args){
    args = core_args({
      'args': args,
      'defaults': {
        'properties': {},
        'style': canvas_style,
        'type': 'lineTo',
      },
    });

    canvas_buffer.beginPath();
    for(var vertex in args['vertices']){
        canvas_buffer[args['vertices'][vertex]['type'] || args['type']](
          args['vertices'][vertex]['x'],
          args['vertices'][vertex]['y'],
          args['vertices'][vertex]['radius'],
          args['vertices'][vertex]['startAngle'],
          args['vertices'][vertex]['endAngle'],
          args['vertices'][vertex]['antiClockwise']
        );
    }
    canvas_buffer.closePath();

    for(var property in args['properties']){
        canvas_buffer[property] = core_handle_defaults({
          'default': canvas_buffer,
          'var': args['properties'][property],
        });
    }

    canvas_buffer[args['style']]();
}

// Required args: stops
// Optional args: height, width, x, y
function canvas_gradient(args){
    args = core_args({
      'args': args,
      'defaults': {
        'height': 0,
        'width': 0,
        'x': 0,
        'y': 0,
      },
    });

    var gradient = canvas_buffer.createLinearGradient(
      args['x'],
      args['y'],
      args['width'],
      args['height']
    );
    for(var step in args['stops']){
        gradient.addColorStop(
          args['stops'][step]['offset'] || 0,
          args['stops'][step]['color'] || '#000'
        );
    }
    return gradient;
}

function canvas_init(){
    canvas_resize();
    canvas_setmode();
}

function canvas_menu_quit(){
    if(core_menu_open){
        canvas_setmode();
        core_menu_open = false;
    }
}

function canvas_resize(){
    if(canvas_mode <= 0){
        return;
    }

    canvas_height = window.innerHeight;
    document.getElementById('buffer').height = canvas_height;
    document.getElementById('canvas').height = canvas_height;
    canvas_y = canvas_height / 2;

    canvas_width = window.innerWidth;
    document.getElementById('buffer').width = canvas_width;
    document.getElementById('canvas').width = canvas_width;
    canvas_x = canvas_width / 2;

    canvas_buffer.font = canvas_fonts['medium'];

    core_call({
      'todo': 'resize_logic',
    });
}

// Optional args: mode, newgame
function canvas_setmode(args){
    args = core_args({
      'args': args,
      'defaults': {
        'mode': 0,
        'newgame': false,
      },
    });

    window.cancelAnimationFrame(canvas_animationFrame);
    window.clearInterval(canvas_interval);

    canvas_mode = args['mode'];

    if(core_type({
      'var': 'setmode_logic',
      'type': 'function',
    })){
        setmode_logic(args['newgame']);

    }else{
        canvas_mode = 1;
        args['newgame'] = true;
    }

    // Main menu mode.
    if(canvas_mode === 0){
        canvas_buffer = 0;
        canvas_canvas = 0;
        return;
    }

    // Simulation modes.
    if(args['newgame']){
        var properties = '';

        if(!canvas_oncontextmenu){
            properties = ' oncontextmenu="return false" ';
        }

        document.body.innerHTML =
          '<canvas id=canvas' + properties + '></canvas><canvas id=buffer></canvas>';

        canvas_buffer = document.getElementById('buffer').getContext('2d');
        canvas_canvas = document.getElementById('canvas').getContext('2d');

        canvas_resize();
    }

    core_call({
      'args': canvas_mode,
      'todo': 'load_level',
    });

    canvas_animationFrame = window.requestAnimationFrame(canvas_drawloop);

    if(core_type({
      'var': 'logic',
      'type': 'function',
    })){
        canvas_interval = window.setInterval(
          logic,
          canvas_interval_ms
        );
    }
}

var canvas_animationFrame = 0;
var canvas_buffer = 0;
var canvas_canvas = 0;
var canvas_fonts = {
  'big': '300% monospace',
  'medium': '200% monospace',
  'small': '100% monospace',
};
var canvas_height = 0;
var canvas_interval = 0;
var canvas_interval_ms = 25;
var canvas_mode = 0;
var canvas_oncontextmenu = true;
var canvas_style = 'fill';
var canvas_width = 0;
var canvas_x = 0;
var canvas_y = 0;

window.onresize = canvas_resize;
<commit_msg>moved core_menu_open check to canvas_logicloop()<commit_after>'use strict';

function canvas_draw(){
    canvas_buffer.clearRect(
      0,
      0,
      canvas_width,
      canvas_height
    );

    draw_logic();

    if(core_menu_open){
        canvas_buffer.save();

        canvas_buffer.fillStyle = '#111';
        canvas_buffer.fillRect(
          canvas_x - 125,
          canvas_y - 50,
          250,
          100
        );

        canvas_buffer.fillStyle = '#fff';
        canvas_buffer.font = canvas_fonts['medium'];
        canvas_buffer.textAlign = 'center';
        canvas_buffer.textBaseline = 'middle';
        canvas_buffer.fillText(
          core_menu_resume,
          canvas_x,
          canvas_y - 25
        );
        canvas_buffer.fillText(
          core_menu_quit,
          canvas_x,
          canvas_y + 25
        );

        canvas_buffer.restore();
    }

    canvas_canvas.clearRect(
      0,
      0,
      canvas_width,
      canvas_height
    );
    canvas_canvas.drawImage(
      document.getElementById('buffer'),
      0,
      0
    );
}

function canvas_drawloop(){
    canvas_draw();
    canvas_animationFrame = window.requestAnimationFrame(canvas_drawloop);
}

// Required args: vertices
// Optional args: properties, style, type
function canvas_draw_path(args){
    args = core_args({
      'args': args,
      'defaults': {
        'properties': {},
        'style': canvas_style,
        'type': 'lineTo',
      },
    });

    canvas_buffer.beginPath();
    for(var vertex in args['vertices']){
        canvas_buffer[args['vertices'][vertex]['type'] || args['type']](
          args['vertices'][vertex]['x'],
          args['vertices'][vertex]['y'],
          args['vertices'][vertex]['radius'],
          args['vertices'][vertex]['startAngle'],
          args['vertices'][vertex]['endAngle'],
          args['vertices'][vertex]['antiClockwise']
        );
    }
    canvas_buffer.closePath();

    for(var property in args['properties']){
        canvas_buffer[property] = core_handle_defaults({
          'default': canvas_buffer,
          'var': args['properties'][property],
        });
    }

    canvas_buffer[args['style']]();
}

// Required args: stops
// Optional args: height, width, x, y
function canvas_gradient(args){
    args = core_args({
      'args': args,
      'defaults': {
        'height': 0,
        'width': 0,
        'x': 0,
        'y': 0,
      },
    });

    var gradient = canvas_buffer.createLinearGradient(
      args['x'],
      args['y'],
      args['width'],
      args['height']
    );
    for(var step in args['stops']){
        gradient.addColorStop(
          args['stops'][step]['offset'] || 0,
          args['stops'][step]['color'] || '#000'
        );
    }
    return gradient;
}

function canvas_init(){
    canvas_resize();
    canvas_setmode();
}

function canvas_logicloop(){
    if(core_menu_open){
        return;
    }

    logic();
}

function canvas_menu_quit(){
    if(core_menu_open){
        canvas_setmode();
        core_menu_open = false;
    }
}

function canvas_resize(){
    if(canvas_mode <= 0){
        return;
    }

    canvas_height = window.innerHeight;
    document.getElementById('buffer').height = canvas_height;
    document.getElementById('canvas').height = canvas_height;
    canvas_y = canvas_height / 2;

    canvas_width = window.innerWidth;
    document.getElementById('buffer').width = canvas_width;
    document.getElementById('canvas').width = canvas_width;
    canvas_x = canvas_width / 2;

    canvas_buffer.font = canvas_fonts['medium'];

    core_call({
      'todo': 'resize_logic',
    });
}

// Optional args: mode, newgame
function canvas_setmode(args){
    args = core_args({
      'args': args,
      'defaults': {
        'mode': 0,
        'newgame': false,
      },
    });

    window.cancelAnimationFrame(canvas_animationFrame);
    window.clearInterval(canvas_interval);

    canvas_mode = args['mode'];

    if(core_type({
      'var': 'setmode_logic',
      'type': 'function',
    })){
        setmode_logic(args['newgame']);

    }else{
        canvas_mode = 1;
        args['newgame'] = true;
    }

    // Main menu mode.
    if(canvas_mode === 0){
        canvas_buffer = 0;
        canvas_canvas = 0;
        return;
    }

    // Simulation modes.
    if(args['newgame']){
        var properties = '';

        if(!canvas_oncontextmenu){
            properties = ' oncontextmenu="return false" ';
        }

        document.body.innerHTML =
          '<canvas id=canvas' + properties + '></canvas><canvas id=buffer></canvas>';

        canvas_buffer = document.getElementById('buffer').getContext('2d');
        canvas_canvas = document.getElementById('canvas').getContext('2d');

        canvas_resize();
    }

    core_call({
      'args': canvas_mode,
      'todo': 'load_level',
    });

    canvas_animationFrame = window.requestAnimationFrame(canvas_drawloop);

    if(core_type({
      'var': 'logic',
      'type': 'function',
    })){
        canvas_interval = window.setInterval(
          canvas_logicloop,
          canvas_interval_ms
        );
    }
}

var canvas_animationFrame = 0;
var canvas_buffer = 0;
var canvas_canvas = 0;
var canvas_fonts = {
  'big': '300% monospace',
  'medium': '200% monospace',
  'small': '100% monospace',
};
var canvas_height = 0;
var canvas_interval = 0;
var canvas_interval_ms = 25;
var canvas_mode = 0;
var canvas_oncontextmenu = true;
var canvas_style = 'fill';
var canvas_width = 0;
var canvas_x = 0;
var canvas_y = 0;

window.onresize = canvas_resize;
<|endoftext|>


<commit_before>var MongoClient = require('mongodb').MongoClient;
var cfg = require("./cfg.json");
var Q = require('q');
var extend = require('util')._extend;

// Note:
// using promises/Q for the first time in my life

var MAX_RATING_HISTORY_COUNT = 50;
var MOVING_AVERAGE_COUNT = 50;
var GAMETYPES_AVAILABLE = ["ad", "ctf", "tdm"];
var MATCH_RATING_CALC_METHODS = {
  'ad': { 'match_rating':
    { $multiply: [
      { $add: [
        { $divide: [ "$scoreboard.damage-dealt", 100 ] },
        "$scoreboard.kills",
      ]},
      { $divide: [ 1200, "$scoreboard.time" ] }
    ]}
  },
  'ctf': { 'match_rating':
    { $add: [
      { $multiply: [
        1/2.35,
        { $divide: [ "$scoreboard.damage-dealt", "$scoreboard.damage-taken" ] },
        { $add: [ "$scoreboard.score", { $divide: [ "$scoreboard.damage-dealt", 20 ] } ] },
        { $divide: [ 1200, "$scoreboard.time" ] }
      ]},
      { $multiply: [
        1/2.35,
        300,
        { $cond: ["$scoreboard.win", 1, 0] }
      ]}
    ]}
  },
  'tdm': { 'match_rating':
    { $add: [
      { $multiply: [ 0.5, { $subtract: ["$scoreboard.kills", "$scoreboard.deaths"] } ] },
      { $multiply: [ 0.004, { $subtract: [ "$scoreboard.damage-dealt", "$scoreboard.damage-taken" ] } ] },
      { $multiply: [ 0.003, "$scoreboard.damage-dealt" ] }
    ]}
  }
};

var RATING_FACTORS = {
  'ad': 1,
  'ctf': 1,
  'tdm': {
    $add: [
      1,
      { $multiply: [ 0.15, { $subtract: [
        { $divide: ["$w", "$n"] }, { $divide: ["$l", "$n"] }
      ] } ] },
    ]
  }
};

var connect = function(callback, fuck) {
  MongoClient.connect(cfg['db-url'], function(err, db) {
    if (err)
      fuck(err);
    else
      callback(db);
  });
};

var in_array = function(what, array) {
  return array.some(function(param) {return param==what});
};

var get_aggregate_options = function(gametype, after_unwind, after_project) {
  return [].concat([
    { $match: { "gametype": gametype } },
    { $sort: { "timestamp": 1 } },
    { $unwind: "$scoreboard" },
    { $match: { "scoreboard.time": { $gt: 300 } } },
    { $match: { "scoreboard.damage-taken": { $gt: 100 } } }
  ], after_unwind, [
    { $project: extend({"scoreboard.steam-id": 1, "scoreboard.win": 1, "timestamp": 1}, MATCH_RATING_CALC_METHODS[gametype]) },
    { $group: {
      _id: "$scoreboard.steam-id",
      n: { $sum: 1 },
      w: { $sum: { $cond: ["$scoreboard.win", 1, 0] } },
      l: { $sum: { $cond: ["$scoreboard.win", 0, 1] } },
      last_match_timestamp: { $max: "$timestamp" },
      match_ratings: { $addToSet: "$match_rating" }
    }},
    { $project: {
      _id: 1,
      n: 1,
      last_match_timestamp: 1,
      rating: { $multiply: [ { $avg: { $slice: [
        "$match_ratings",
        { $max: [ { $subtract: ["$n", MOVING_AVERAGE_COUNT] }, 0] }, // max(n-20,0)
        { $min: [ "$n", MOVING_AVERAGE_COUNT ] } // min(n,20)
      ] } }, RATING_FACTORS[gametype] ] }
    }}
  ], after_project);
};

var parse_stats_submission = function(body) {
  var len = function(obj) {
    return Object.keys(obj).length;
  };

  // storage vars for the request body
  var game_meta = {};
  var events = {};
  var players = [];
  var teams = [];

  // we're not in either stanza to start
  var in_P = false;
  var in_Q = false;
  var lines = body.split("\n");
  for(var k=0; k<lines.length; k++) {
    var line = lines[k];
    var key = line.substr(0, line.indexOf(' '));
    var value = line.substr(line.indexOf(' ')+1);

    if (in_array(key, ['P', 'Q', 'n', 'e', 't', 'i']) == false) {
      game_meta[key] = value;
    }

    if (in_array(key, ['Q', 'P'])) {
      //log.debug('Found a {0}'.format(key))
      //log.debug('in_Q: {0}'.format(in_Q))
      //log.debug('in_P: {0}'.format(in_P))
      //log.debug('events: {0}'.format(events))

      // check where we were before and append events accordingly
      if ( in_Q && (len(events) > 0) ) {
        // log.debug('creating a team (Q) entry')
        teams.push(events);
        events = {};
      } else if (in_P && (len(events) > 0) ) {
        // log.debug('creating a player (P) entry')
        players.push(events);
        events = {};
      }

      if (key == 'P') {
        //log.debug('key == P')
        in_P = true;
        in_Q = false;
      } else if (key == 'Q') {
        //log.debug('key == Q')
        in_P = false;
        in_Q = true;
      }

      events[key] = value;
    }

    if (key == 'e') {
      var subkey = value.split(' ')[0];
      var subvalue = value.split(' ')[1];
      events[subkey] = subvalue;
    }
    if (key == 'n') {
      events[key] = value;
    }
    if (key == 't') {
      events[key] = value;
    }
  }

  // add the last entity we were working on
  if (in_P && (len(events) > 0) ) {
    players.push(events);
  } else if (in_Q && (len(events) > 0) ) {
    teams.push(events);
  }

  return {game_meta: game_meta, players: players, teams: teams};
};

var updateRatings = function(db, docs, gametype, update_history) {
  return Q.all(docs.map(function(doc) {
    var result = {};
    result[gametype + ".n"] = doc.n;
    result[gametype + ".rating"] = parseFloat(doc.rating.toFixed(2));
    if (update_history) {
      var push_value = {};
      push_value[gametype + ".history"] = { $each: [{
        "timestamp": doc.last_match_timestamp,
        "rating": parseFloat(doc.rating.toFixed(2))
      }], $slice: -MAX_RATING_HISTORY_COUNT };
      return db.collection('players').update(
        {_id: doc._id},
        { $set: result, $push: push_value }
      );
    } else {
      return db.collection('players').update(
        {_id: doc._id },
        { $set: result }
      );
    }
  }));
};

var submitMatch = function(data, done) {

  if (typeof(data) == 'string') {
    data = parse_stats_submission(data);
  }

  if (in_array(data.game_meta["G"], GAMETYPES_AVAILABLE) == false ) {
    done({ok: false, message: "gametype is not accepted: " + data.game_meta["G"]});
    return;
  }

  connect(function(db) {

    var scoreboard = data.players.map(function(item) {
      return {
        "steam-id": item["P"],
        "score": parseInt(item["scoreboard-score"]),
        "kills": parseInt(item["scoreboard-kills"]),
        "deaths": parseInt(item["scoreboard-deaths"]),
        'damage-dealt': parseInt(item["scoreboard-pushes"]),
        'damage-taken': parseInt(item["scoreboard-destroyed"]),
        "time": parseInt(item["alivetime"]),
        "win": item["win"] ? true : false
      };
    });

    Q(db.collection('matches').insertOne({
      _id: data.game_meta["I"],
      map: data.game_meta["M"],
      gametype: data.game_meta["G"],
      factory: data.game_meta["O"],
      timestamp: parseInt(data.game_meta["1"]),
      scoreboard: scoreboard
    }))
    .then(function() {

      return Q.all(data.players.map(function(player) {
        return db.collection('players').update(
          {_id: player["P"]},
          { $set: {"name": player["n"]} },
          {upsert: true}
        );
      }));

    })
    .then(function() {

      var steamIds = data.players.map(function(item) {
        return item["P"];
      });

      return db.collection('matches').aggregate( get_aggregate_options(
        data.game_meta["G"], [ { $match: { "scoreboard.steam-id": { $in: steamIds } } } ], [ ]
      )).toArray();

    })
    .then(function(docs) {

      return updateRatings(db, docs, data.game_meta["G"], true);

    })
    .then(function() {

       done({ok: true});

    })
    .catch(function(err) {

      done({ok: false, message: err.toString()});

    })
    .finally(function() {

      db.close();

    });

  }, function(err) {

    done({ok: false, message: err.toString()});

  });

};

var getList = function(gametype, page, done) {

  connect(function(db) {

    var matching = {}; matching[gametype + ".n"] = { $gte: 10 };
    var sorting  = {};  sorting[gametype + ".rating"] = -1;

    var project  = {
      _id: "$_id",
      name: "$name",
    };
    project.n      = "$" + gametype + ".n";
    project.rating = "$" + gametype + ".rating";

    var docs = [];

    Q(db.collection('players').aggregate([
      { $match: matching },
      { $sort: sorting },
      { $skip: page * cfg['player-count-per-page'] },
      { $limit: cfg['player-count-per-page'] },
      { $project: project }
    ]).toArray())
    .then(function(result) {

      docs = result.map(function(item, i) {
        item.rank = page * cfg['player-count-per-page'] + 1 + i;
        return item;
      });
      return db.collection('players').find(matching).count();

    })
    .then(function(count) {

       done({ok: true, response: docs, page_count: Math.ceil(count / cfg['player-count-per-page'])});

    })
    .catch(function(err) {

      done({ok: false, message: err.toString()});

    })
    .finally(function() {

      db.close();

    });

  }, function(err) {

    done({ok: false, message: err.toString()});

  });

};

var getForBalancePlugin = function(steamIds, done) {

  var project = {};
  project._id = 0;
  project.steamid = "$_id";
  GAMETYPES_AVAILABLE.forEach(function(gametype) {
    project[gametype] = {
      games: "$" + gametype + ".n",
      elo: "$" + gametype + ".rating"
    }
  });

  connect(function(db) {

    Q.all(db.collection('players').aggregate([
        { $match: {"_id": { $in: steamIds } } },
        { $project: project }
    ]).toArray())
    .then(function(docs) {

      done({ok: true, players: docs, deactivated: []});

    }).catch(function(err) {

      done({ok: false, message: err.toString()});

    }).finally(function() {

      db.close();

    });

  }, function(err) {

    done({ok: false, message: err.toString()});

  });
};

var update = function(done) {

  if (typeof(done) == 'undefined') {
    done = function() {};
  }

  connect(function(db) {

    Q.all(GAMETYPES_AVAILABLE.map(function(gametype) {

      return db.collection('matches').aggregate( get_aggregate_options(
        gametype, [], [ { $sort: { "rating": -1 } } ]
      )).toArray();

    }))
    .then(function(docs_docs) {

      return Q.all(GAMETYPES_AVAILABLE.map(function(gametype, i) {
        var docs = docs_docs[i];
        return updateRatings(db, docs, gametype);
      }));

    })
    .then(function() {

      done({ok: true});

    }).catch(function(err) {

      done({ok: false, message: err.toString()});

    }).finally(function() {

      db.close();

    });

  }, function(err) {

    done({ok: false, message: err.toString()});

  });
};

module.exports.update = update;
module.exports.getList = getList;
module.exports.getForBalancePlugin = getForBalancePlugin;
module.exports.submitMatch = submitMatch;
<commit_msg>added history field in getForBalancePlugin result<commit_after>var MongoClient = require('mongodb').MongoClient;
var cfg = require("./cfg.json");
var Q = require('q');
var extend = require('util')._extend;

// Note:
// using promises/Q for the first time in my life

var MAX_RATING_HISTORY_COUNT = 50;
var MOVING_AVERAGE_COUNT = 50;
var GAMETYPES_AVAILABLE = ["ad", "ctf", "tdm"];
var MATCH_RATING_CALC_METHODS = {
  'ad': { 'match_rating':
    { $multiply: [
      { $add: [
        { $divide: [ "$scoreboard.damage-dealt", 100 ] },
        "$scoreboard.kills",
      ]},
      { $divide: [ 1200, "$scoreboard.time" ] }
    ]}
  },
  'ctf': { 'match_rating':
    { $add: [
      { $multiply: [
        1/2.35,
        { $divide: [ "$scoreboard.damage-dealt", "$scoreboard.damage-taken" ] },
        { $add: [ "$scoreboard.score", { $divide: [ "$scoreboard.damage-dealt", 20 ] } ] },
        { $divide: [ 1200, "$scoreboard.time" ] }
      ]},
      { $multiply: [
        1/2.35,
        300,
        { $cond: ["$scoreboard.win", 1, 0] }
      ]}
    ]}
  },
  'tdm': { 'match_rating':
    { $add: [
      { $multiply: [ 0.5, { $subtract: ["$scoreboard.kills", "$scoreboard.deaths"] } ] },
      { $multiply: [ 0.004, { $subtract: [ "$scoreboard.damage-dealt", "$scoreboard.damage-taken" ] } ] },
      { $multiply: [ 0.003, "$scoreboard.damage-dealt" ] }
    ]}
  }
};

var RATING_FACTORS = {
  'ad': 1,
  'ctf': 1,
  'tdm': {
    $add: [
      1,
      { $multiply: [ 0.15, { $subtract: [
        { $divide: ["$w", "$n"] }, { $divide: ["$l", "$n"] }
      ] } ] },
    ]
  }
};

var connect = function(callback, fuck) {
  MongoClient.connect(cfg['db-url'], function(err, db) {
    if (err)
      fuck(err);
    else
      callback(db);
  });
};

var in_array = function(what, array) {
  return array.some(function(param) {return param==what});
};

var get_aggregate_options = function(gametype, after_unwind, after_project) {
  return [].concat([
    { $match: { "gametype": gametype } },
    { $sort: { "timestamp": 1 } },
    { $unwind: "$scoreboard" },
    { $match: { "scoreboard.time": { $gt: 300 } } },
    { $match: { "scoreboard.damage-taken": { $gt: 100 } } }
  ], after_unwind, [
    { $project: extend({"scoreboard.steam-id": 1, "scoreboard.win": 1, "timestamp": 1}, MATCH_RATING_CALC_METHODS[gametype]) },
    { $group: {
      _id: "$scoreboard.steam-id",
      n: { $sum: 1 },
      w: { $sum: { $cond: ["$scoreboard.win", 1, 0] } },
      l: { $sum: { $cond: ["$scoreboard.win", 0, 1] } },
      last_match_timestamp: { $max: "$timestamp" },
      match_ratings: { $addToSet: "$match_rating" }
    }},
    { $project: {
      _id: 1,
      n: 1,
      last_match_timestamp: 1,
      rating: { $multiply: [ { $avg: { $slice: [
        "$match_ratings",
        { $max: [ { $subtract: ["$n", MOVING_AVERAGE_COUNT] }, 0] }, // max(n-20,0)
        { $min: [ "$n", MOVING_AVERAGE_COUNT ] } // min(n,20)
      ] } }, RATING_FACTORS[gametype] ] }
    }}
  ], after_project);
};

var parse_stats_submission = function(body) {
  var len = function(obj) {
    return Object.keys(obj).length;
  };

  // storage vars for the request body
  var game_meta = {};
  var events = {};
  var players = [];
  var teams = [];

  // we're not in either stanza to start
  var in_P = false;
  var in_Q = false;
  var lines = body.split("\n");
  for(var k=0; k<lines.length; k++) {
    var line = lines[k];
    var key = line.substr(0, line.indexOf(' '));
    var value = line.substr(line.indexOf(' ')+1);

    if (in_array(key, ['P', 'Q', 'n', 'e', 't', 'i']) == false) {
      game_meta[key] = value;
    }

    if (in_array(key, ['Q', 'P'])) {
      //log.debug('Found a {0}'.format(key))
      //log.debug('in_Q: {0}'.format(in_Q))
      //log.debug('in_P: {0}'.format(in_P))
      //log.debug('events: {0}'.format(events))

      // check where we were before and append events accordingly
      if ( in_Q && (len(events) > 0) ) {
        // log.debug('creating a team (Q) entry')
        teams.push(events);
        events = {};
      } else if (in_P && (len(events) > 0) ) {
        // log.debug('creating a player (P) entry')
        players.push(events);
        events = {};
      }

      if (key == 'P') {
        //log.debug('key == P')
        in_P = true;
        in_Q = false;
      } else if (key == 'Q') {
        //log.debug('key == Q')
        in_P = false;
        in_Q = true;
      }

      events[key] = value;
    }

    if (key == 'e') {
      var subkey = value.split(' ')[0];
      var subvalue = value.split(' ')[1];
      events[subkey] = subvalue;
    }
    if (key == 'n') {
      events[key] = value;
    }
    if (key == 't') {
      events[key] = value;
    }
  }

  // add the last entity we were working on
  if (in_P && (len(events) > 0) ) {
    players.push(events);
  } else if (in_Q && (len(events) > 0) ) {
    teams.push(events);
  }

  return {game_meta: game_meta, players: players, teams: teams};
};

var updateRatings = function(db, docs, gametype, update_history) {
  return Q.all(docs.map(function(doc) {
    var result = {};
    result[gametype + ".n"] = doc.n;
    result[gametype + ".rating"] = parseFloat(doc.rating.toFixed(2));
    if (update_history) {
      var push_value = {};
      push_value[gametype + ".history"] = { $each: [{
        "timestamp": doc.last_match_timestamp,
        "rating": parseFloat(doc.rating.toFixed(2))
      }], $slice: -MAX_RATING_HISTORY_COUNT };
      return db.collection('players').update(
        {_id: doc._id},
        { $set: result, $push: push_value }
      );
    } else {
      return db.collection('players').update(
        {_id: doc._id },
        { $set: result }
      );
    }
  }));
};

var submitMatch = function(data, done) {

  if (typeof(data) == 'string') {
    data = parse_stats_submission(data);
  }

  if (in_array(data.game_meta["G"], GAMETYPES_AVAILABLE) == false ) {
    done({ok: false, message: "gametype is not accepted: " + data.game_meta["G"]});
    return;
  }

  connect(function(db) {

    var scoreboard = data.players.map(function(item) {
      return {
        "steam-id": item["P"],
        "score": parseInt(item["scoreboard-score"]),
        "kills": parseInt(item["scoreboard-kills"]),
        "deaths": parseInt(item["scoreboard-deaths"]),
        'damage-dealt': parseInt(item["scoreboard-pushes"]),
        'damage-taken': parseInt(item["scoreboard-destroyed"]),
        "time": parseInt(item["alivetime"]),
        "win": item["win"] ? true : false
      };
    });

    Q(db.collection('matches').insertOne({
      _id: data.game_meta["I"],
      map: data.game_meta["M"],
      gametype: data.game_meta["G"],
      factory: data.game_meta["O"],
      timestamp: parseInt(data.game_meta["1"]),
      scoreboard: scoreboard
    }))
    .then(function() {

      return Q.all(data.players.map(function(player) {
        return db.collection('players').update(
          {_id: player["P"]},
          { $set: {"name": player["n"]} },
          {upsert: true}
        );
      }));

    })
    .then(function() {

      var steamIds = data.players.map(function(item) {
        return item["P"];
      });

      return db.collection('matches').aggregate( get_aggregate_options(
        data.game_meta["G"], [ { $match: { "scoreboard.steam-id": { $in: steamIds } } } ], [ ]
      )).toArray();

    })
    .then(function(docs) {

      return updateRatings(db, docs, data.game_meta["G"], true);

    })
    .then(function() {

       done({ok: true});

    })
    .catch(function(err) {

      done({ok: false, message: err.toString()});

    })
    .finally(function() {

      db.close();

    });

  }, function(err) {

    done({ok: false, message: err.toString()});

  });

};

var getList = function(gametype, page, done) {

  connect(function(db) {

    var matching = {}; matching[gametype + ".n"] = { $gte: 10 };
    var sorting  = {};  sorting[gametype + ".rating"] = -1;

    var project  = {
      _id: "$_id",
      name: "$name",
    };
    project.n      = "$" + gametype + ".n";
    project.rating = "$" + gametype + ".rating";

    var docs = [];

    Q(db.collection('players').aggregate([
      { $match: matching },
      { $sort: sorting },
      { $skip: page * cfg['player-count-per-page'] },
      { $limit: cfg['player-count-per-page'] },
      { $project: project }
    ]).toArray())
    .then(function(result) {

      docs = result.map(function(item, i) {
        item.rank = page * cfg['player-count-per-page'] + 1 + i;
        return item;
      });
      return db.collection('players').find(matching).count();

    })
    .then(function(count) {

       done({ok: true, response: docs, page_count: Math.ceil(count / cfg['player-count-per-page'])});

    })
    .catch(function(err) {

      done({ok: false, message: err.toString()});

    })
    .finally(function() {

      db.close();

    });

  }, function(err) {

    done({ok: false, message: err.toString()});

  });

};

var getForBalancePlugin = function(steamIds, done) {

  var project = {};
  var history_value = {};
  project._id = 0;
  project.steamid = "$_id";
  GAMETYPES_AVAILABLE.forEach(function(gametype) {
    project[gametype] = {
      games: "$" + gametype + ".n",
      elo: "$" + gametype + ".rating",
      history: {$slice: ["$" + gametype + ".history", -3]}
    };
  });

  connect(function(db) {

    Q.all(db.collection('players').aggregate([
        { $match: {"_id": { $in: steamIds } } },
        { $project: project }
    ]).toArray())
    .then(function(docs) {

      done({ok: true, players: docs, deactivated: []});

    }).catch(function(err) {

      done({ok: false, message: err.toString()});

    }).finally(function() {

      db.close();

    });

  }, function(err) {

    done({ok: false, message: err.toString()});

  });
};

var update = function(done) {

  if (typeof(done) == 'undefined') {
    done = function() {};
  }

  connect(function(db) {

    Q.all(GAMETYPES_AVAILABLE.map(function(gametype) {

      return db.collection('matches').aggregate( get_aggregate_options(
        gametype, [], [ { $sort: { "rating": -1 } } ]
      )).toArray();

    }))
    .then(function(docs_docs) {

      return Q.all(GAMETYPES_AVAILABLE.map(function(gametype, i) {
        var docs = docs_docs[i];
        return updateRatings(db, docs, gametype);
      }));

    })
    .then(function() {

      done({ok: true});

    }).catch(function(err) {

      done({ok: false, message: err.toString()});

    }).finally(function() {

      db.close();

    });

  }, function(err) {

    done({ok: false, message: err.toString()});

  });
};

module.exports.update = update;
module.exports.getList = getList;
module.exports.getForBalancePlugin = getForBalancePlugin;
module.exports.submitMatch = submitMatch;
<|endoftext|>


<commit_before>var fragmentVisualization = function (element, fragmentId, queryPlan) {
    $('#title-right-vis').html(templates.titleFragmentsVis({fragment: fragmentId}))

    element.selectAll("svg").remove();
    drawCharts(element, fragmentId, queryPlan);

    // return variables that are needed outside this scope
    return {};
}

function drawCharts(element, fragmentId, queryPlan) {
    var lanesChart = drawLanes(element, fragmentId, queryPlan.queryId);
    drawArea(element, fragmentId, queryPlan.queryId, lanesChart);
}

// Draw the area plot and the mini-brush and big-brush for it
function drawArea(element, fragmentId, queryId, lanesChart) {

    var margin = {top: 50, right: 10, bottom: 20, left:20 },
        margin2 = {top: 10, right:10, bottom: 170, left:20},
        width = parseInt(element.style('width'), 10) - margin.left - margin.right,
        height = 200 - margin.top - margin.bottom,
        height2 = 200 - margin2.top - margin2.bottom;

    var x = d3.scale.linear().range([0, width]),
        x2 = d3.scale.linear().range([0, width]),
        y = d3.scale.linear().range([height, 0]),
        y2 = d3.scale.linear().range([height2, 0]);

    var xAxis = d3.svg.axis()
        .scale(x)
        .tickFormat(customTimeFormat)
        .tickSize(-height)
        .orient("bottom");

    var xAxis2 = d3.svg.axis()
        .scale(x2)
        .tickFormat(customTimeFormat)
        .tickSize(-height2)
        .orient("bottom");

    var yAxis = d3.svg.axis()
        .scale(y)
        .tickFormat(d3.format("d"))
        .orient("left");

    var brush = d3.svg.brush()
        .x(x2)
        .on("brush", brushed)
        .on("brushend", brushEnd);

    var brush2 = d3.svg.brush()
        .x(x)
        .on("brushend", brushendWorkers);

    // Area 1 generator
    var area = d3.svg.area()
        .interpolate("step-after")
        .x(function(d) { return x(d.time); })
        .y0(height)
        .y1(function(d) { return y(d.value); });

    // Area 2 generator
    var area2 = d3.svg.area()
        .interpolate("step-after")
        .x(function(d) { return x2(d.time); })
        .y0(height2)
	.y1(function(d) { return y2(d.value); });

    // Svg element to draw the fragment utilization plot
    //var svg = element.append("svg")
    var svg = element.insert("svg", ":first-child")
                     .attr("width", width + margin.left + margin.right)
                     .attr("height", height + margin.top + margin.bottom)
                     .attr("class", "line-plot")
                     .attr("id", "fragment_utilization");

    svg.append("defs").append("clipPath")
       .attr("id", "clip")
      .append("rect")
       .attr("width", width)
       .attr("height", height);

    // Place the mini-brush
    var mini_brush = svg.append("g")
        .attr("class", "context")
        .attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");

    mini_brush.append("g")
	.attr("class", "x brush")
	.call(brush)
	.selectAll("rect")
	.attr("y", -6)
	.attr("height", height2 + 7);

    // Place the plot/big_brush
    var plot = svg.append("g")
	    .attr("class", "plot")
	    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    plot.append("g")
	.attr("class", "x brush")
	.call(brush2)
	.selectAll("rect")
	.attr("y", -6)
	.attr("height", height + 7);

    // Add ruler
    var tooltip = plot.append("g")
        .attr({"class": "rulerInfo"})
        .attr("transform", "translate(" + [0, height] + ")");

    tooltip.append("svg:rect");

    var tttext = tooltip.append("svg:text")
        .attr("text-anchor", "left");

    plot.on("mousemove", function (e) {
        ruler
            .style("display", "block")
            .style("left", d3.event.pageX - 1 + "px");

        plot
            .select(".rulerInfo")
            .style("opacity", 1)
            .attr("transform", "translate(" + [d3.mouse(this)[0] + 6, height + 14] + ")");

        var xValue = Math.round(x.invert(d3.mouse(this)[0]));
        tttext.text(templates.ganttTooltipTemplate({ time: customFullTimeFormat(xValue) }));

        var bbox = tttext.node().getBBox();
        tooltip.select("rect")
            .attr("width", bbox.width + 10)
            .attr("height", bbox.height + 6)
            .attr("x", bbox.x - 5)
            .attr("y", bbox.y - 3);
    });

    plot.on("mouseleave", function (e) {
        ruler.style("display", "none");
        plot
            .select(".rulerInfo")
            .style("opacity", 0);
    });

    var url = templates.urls.histogram({
        query: queryId,
        fragment: fragmentId
    });

    d3.csv(url, type, function(error, data) {
        x.domain(d3.extent(data, function(d) { return d.time; }));
        y.domain([0, d3.max(data, function(d) { return d.value; })]);
        x2.domain(x.domain());
        y2.domain(y.domain());

        // TODO: do before we have the data
        mini_brush.append("g")
            .attr("class", "x axis")
	    .attr("transform", "translate(0," + height2 + ")")
	    .call(xAxis2);

        mini_brush.append("path")
            .attr("clip-path", "url(#clip)")
            .datum(data)
	    .attr("class", "area")
	    .attr("d", area2);

        plot.append("g")
	    .attr("class", "x axis")
	    .attr("transform", "translate(0," + height + ")")
	    .call(xAxis);

        plot.append("g")
	    .attr("class", "y axis")
	    .call(yAxis);

        plot.append("path")
	    .attr("clip-path", "url(#clip)")
             .datum(data)
	    .attr("class", "area")
	    .attr("d", area);
    });

    function brushed() {
        x.domain(brush.empty() ? x2.domain() : brush.extent());
        plot.select(".area").attr("d", area);
        plot.select(".x.axis").call(xAxis);
    }

    function brushEnd() {
        lanesChart.redrawLanes(brush.extent());
    }

    function brushendWorkers() {
        //called brush; modify the lanes Chart ...
        //compute the visible workers

        lanesChart.redrawLanes(brush2.extent());

        x.domain(brush2.empty() ? x2.domain() : brush2.extent());
        plot.select(".area")
            .transition()
            .duration(animationDuration)
            .attr("d", area);
        plot.select(".x.axis")
            .transition()
            .duration(animationDuration)
            .call(xAxis);

        brush.extent(brush2.extent());
        d3.select(".context .x.brush")
            .transition()
            .duration(animationDuration)
            .call(brush);
        d3.select(".plot .x.brush")
            .call(brush2.clear());
    }

    function type(d) {
        d.time = +d.time;
        d.value = +d.value;
        return d;
    }
}

function drawLanes(element, fragmentId, queryId) {

    //var fullHeight =  _.keys(workersData).length * 50;
    var fullHeight = 400;

    var margin = {top: 10, right: 10, bottom: 20, left: 20},
        width = parseInt(element.style('width'), 10) - margin.left - margin.right,
        height = fullHeight - margin.top - margin.bottom;
    var x = d3.scale.linear().clamp(true).range([0, width]),
        y = d3.scale.ordinal().rangeRoundBands([height, 0], 0.2, 0.1);

    var xAxis = d3.svg.axis()
        .scale(x)
        .tickFormat(customTimeFormat)
        .tickSize(-height)
        .orient("bottom");

    var yAxis = d3.svg.axis()
        .scale(y)
        .orient("left");

    // Add lanes chart
    var svg = element.append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .attr("id", "fragment_workers");

    var chart = svg.append("g")
        .attr("class", "plot")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    // Place the xAxis
    chart.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

    // Place the lanes plot
    var lanes = chart.append("g")
        .attr("class", "lanes")
        //.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    /* Collect data for states at each worker */
    var url = templates.urls.profiling({
        myria: myriaConnection,
        query: queryId,
        fragment: fragmentId
    });

    var workersData = {};

    d3.csv(url, type2, function(error, data) {
        // copy the results into the workersData
        // don't allocate another object as we return the
        // reference to this one
        workersData = getWorkersStates(data);
        //redrawLanes(element, workersData, x, y, xAxis, yAxis, [0,0]);
    });

    function type2(d) {
        d.workerId = +d.workerId;
        d.nanoTime = parseFloat(d.nanoTime);
        d.numTuples = +d.numTuples;
        return d;
    }

    function getWorkersStates(data) {
	// TODO: this parsing function assumes the following about the data
	// received:
        //   - events are sorted at each worker

        // Create a structure:
        //  workers = [{ workerId : [{opName : [{}]}] }]
        //
        // TODO: use queryPlan to put the right lane numbers?

        var workersStates = {};
        var tmpStacks = {}; // a stack per worker keeps unfinished operator calls

        data.forEach(function(d) {
            stack = tmpStacks[d.workerId];
            if (stack == null || stack.length === 0) {
                tmpStacks[d.workerId] = [];
                tmpStacks[d.workerId].push(get_state(d));
                return;
            }

    	    // event on top of stack completed (we now know its endTime), add
    	    // to states

            if (!_.has(workersStates, d.workerId)) {
                workersStates[d.workerId] = {
                    workerId: d.workerId,
                    states: []
                }
            }

    	    states = workersStates[d.workerId].states;

            top_stack = stack[stack.length - 1];
            top_stack.end = d.nanoTime;
            states.push(top_stack);

            // check the event type and push unfinished event on the stack
            if (d.eventType === "call") {
                stack.push(get_state(d));
            } else if (d.eventType === "return") {
                // it's a return, update the link and replace top of stack
                // with a new, same opName event that starts from d.nanoTime
                stack.pop();
                if (stack.length > 0) {
                    top_stack = stack[stack.length - 1];
                    state = get_state(d);
                    state.name = top_stack.name; // the same call
                    top_stack.link = state; // belong together (still in the same call thread)
                    stack.pop();
                    stack.push(state);
               } //TODO: eos??
            }
        });

        function get_state(d) {
            return {
                link: null,           // if it belongs together with some previous event
                name: d.opName,
                begin: d.nanoTime,
                end: null,           // we don't know this yet ...
                numTuples: d.numTuples
            };
        }

        return workersStates;
    }

    function redrawLanes(xDomain) {
        var data = _.values(workersData);

        // Remove what was previously drawn
        y.domain(_.pluck(data, 'workerId'));
        x.domain(xDomain);

        var lane = lanes.selectAll(".worker").data(data, function(d) {return d.workerId});
        lane.enter().append("g").attr("class", "worker");
        lane.attr("transform", function(d) { return "translate(0," +  y(d.workerId) + ")"; });
        lane.exit().remove();

        var box = lane.selectAll("rect")
            .data(function(d) {
                return _.filter(d.states, function(s) {
                    // overlap
                    return overlap = xDomain[0] < s.end && xDomain[1] > s.begin;
                });
            }, function(d) {return d.begin;});

        box.enter().append("rect")
            .popover(function(d) {
                var content = '';
                if (d.numTuples >= 0) {
                    content += templates.numTuplesTemplate({numTuples: d.numTuples});
                } else {
                    content += templates.nullReturned();
                }
                var duration = d.end - d.begin;
                content += templates.boxTemplate({
                    duration: customFullTimeFormat(duration),
                    begin: customFullTimeFormat(d.begin),
                    end: customFullTimeFormat(d.end)
                });
                return {
                    title: d.name,
                    content: content
                };
            })
            //.attr("clip-path", "url(#clip)")
            .style("fill", function(d) { return opToColor[d.name]; })
            .attr("class", "box");

        box
            .transition()
            .duration(animationDuration)
            .attr("x", function(d) { return x(d.begin); })
            .style("opacity", 1)
            .attr("width", function(d) {
                return x(d.end) - x(d.begin);
            })
            .attr("height", function(d) { return y.rangeBand(); });

        box.exit().remove();

        chart.select(".x.axis")
            .transition()
            .duration(animationDuration)
            .call(xAxis);
    }

    return {
        redrawLanes: redrawLanes
    };
}

<commit_msg>Fixed bug with small brush<commit_after>var fragmentVisualization = function (element, fragmentId, queryPlan) {
    $('#title-right-vis').html(templates.titleFragmentsVis({fragment: fragmentId}))

    element.selectAll("svg").remove();
    drawCharts(element, fragmentId, queryPlan);

    // return variables that are needed outside this scope
    return {};
}

function drawCharts(element, fragmentId, queryPlan) {
    var lanesChart = drawLanes(element, fragmentId, queryPlan.queryId);
    drawArea(element, fragmentId, queryPlan.queryId, lanesChart);
}

// Draw the area plot and the mini-brush and big-brush for it
function drawArea(element, fragmentId, queryId, lanesChart) {

    var margin = {top: 50, right: 10, bottom: 20, left:20 },
        margin2 = {top: 10, right:10, bottom: 170, left:20},
        width = parseInt(element.style('width'), 10) - margin.left - margin.right,
        height = 200 - margin.top - margin.bottom,
        height2 = 200 - margin2.top - margin2.bottom;

    var x = d3.scale.linear().range([0, width]),
        x2 = d3.scale.linear().range([0, width]),
        y = d3.scale.linear().range([height, 0]),
        y2 = d3.scale.linear().range([height2, 0]);

    var xAxis = d3.svg.axis()
        .scale(x)
        .tickFormat(customTimeFormat)
        .tickSize(-height)
        .orient("bottom");

    var xAxis2 = d3.svg.axis()
        .scale(x2)
        .tickFormat(customTimeFormat)
        .tickSize(-height2)
        .orient("bottom");

    var yAxis = d3.svg.axis()
        .scale(y)
        .tickFormat(d3.format("d"))
        .orient("left");

    var brush = d3.svg.brush()
        .x(x2)
        .on("brush", brushed)
        .on("brushend", brushEnd);

    var brush2 = d3.svg.brush()
        .x(x)
        .on("brushend", brushendWorkers);

    // Area 1 generator
    var area = d3.svg.area()
        .interpolate("step-after")
        .x(function(d) { return x(d.time); })
        .y0(height)
        .y1(function(d) { return y(d.value); });

    // Area 2 generator
    var area2 = d3.svg.area()
        .interpolate("step-after")
        .x(function(d) { return x2(d.time); })
        .y0(height2)
	.y1(function(d) { return y2(d.value); });

    // Svg element to draw the fragment utilization plot
    //var svg = element.append("svg")
    var svg = element.insert("svg", ":first-child")
                     .attr("width", width + margin.left + margin.right)
                     .attr("height", height + margin.top + margin.bottom)
                     .attr("class", "line-plot")
                     .attr("id", "fragment_utilization");

    svg.append("defs").append("clipPath")
       .attr("id", "clip")
      .append("rect")
       .attr("width", width)
       .attr("height", height);

    // Place the mini-brush
    var mini_brush = svg.append("g")
        .attr("class", "context")
        .attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");

    mini_brush.append("g")
	.attr("class", "x brush")
	.call(brush)
	.selectAll("rect")
	.attr("y", -6)
	.attr("height", height2 + 7);

    // Place the plot/big_brush
    var plot = svg.append("g")
	    .attr("class", "plot")
	    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    plot.append("g")
	.attr("class", "x brush")
	.call(brush2)
	.selectAll("rect")
	.attr("y", -6)
	.attr("height", height + 7);

    // Add ruler
    var tooltip = plot.append("g")
        .attr({"class": "rulerInfo"})
        .attr("transform", "translate(" + [0, height] + ")");

    tooltip.append("svg:rect");

    var tttext = tooltip.append("svg:text")
        .attr("text-anchor", "left");

    plot.on("mousemove", function (e) {
        ruler
            .style("display", "block")
            .style("left", d3.event.pageX - 1 + "px");

        plot
            .select(".rulerInfo")
            .style("opacity", 1)
            .attr("transform", "translate(" + [d3.mouse(this)[0] + 6, height + 14] + ")");

        var xValue = Math.round(x.invert(d3.mouse(this)[0]));
        tttext.text(templates.ganttTooltipTemplate({ time: customFullTimeFormat(xValue) }));

        var bbox = tttext.node().getBBox();
        tooltip.select("rect")
            .attr("width", bbox.width + 10)
            .attr("height", bbox.height + 6)
            .attr("x", bbox.x - 5)
            .attr("y", bbox.y - 3);
    });

    plot.on("mouseleave", function (e) {
        ruler.style("display", "none");
        plot
            .select(".rulerInfo")
            .style("opacity", 0);
    });

    var url = templates.urls.histogram({
        query: queryId,
        fragment: fragmentId
    });

    d3.csv(url, type, function(error, data) {
        x.domain(d3.extent(data, function(d) { return d.time; }));
        y.domain([0, d3.max(data, function(d) { return d.value; })]);
        x2.domain(x.domain());
        y2.domain(y.domain());

        // TODO: do before we have the data
        mini_brush.append("g")
            .attr("class", "x axis")
	    .attr("transform", "translate(0," + height2 + ")")
	    .call(xAxis2);

        mini_brush.append("path")
            .attr("clip-path", "url(#clip)")
            .datum(data)
	    .attr("class", "area")
	    .attr("d", area2);

        plot.append("g")
	    .attr("class", "x axis")
	    .attr("transform", "translate(0," + height + ")")
	    .call(xAxis);

        plot.append("g")
	    .attr("class", "y axis")
	    .call(yAxis);

        plot.append("path")
	    .attr("clip-path", "url(#clip)")
             .datum(data)
	    .attr("class", "area")
	    .attr("d", area);
    });

    function brushed() {
        x.domain(brush.empty() ? x2.domain() : brush.extent());
        plot.select(".area").attr("d", area);
        plot.select(".x.axis").call(xAxis);
    }

    function brushEnd() {
        lanesChart.redrawLanes(brush.extent());
    }

    function brushendWorkers() {
        //called brush; modify the lanes Chart ...
        //compute the visible workers
        var brush_extent = brush2.extent();

        lanesChart.redrawLanes(brush2.extent());

        x.domain(brush2.empty() ? x2.domain() : brush_extent);
        plot.select(".area")
            .transition()
            .duration(animationDuration)
            .attr("d", area);
        plot.select(".x.axis")
            .transition()
            .duration(animationDuration)
            .call(xAxis);

        brush.extent(brush_extent);
        d3.select(".context .x.brush")
            .transition()
            .duration(animationDuration)
            .call(brush);
        d3.select(".plot .x.brush")
            .call(brush2.clear());
    }

    function type(d) {
        d.time = +d.time;
        d.value = +d.value;
        return d;
    }
}

function drawLanes(element, fragmentId, queryId) {

    //var fullHeight =  _.keys(workersData).length * 50;
    var fullHeight = 400;

    var margin = {top: 10, right: 10, bottom: 20, left: 20},
        width = parseInt(element.style('width'), 10) - margin.left - margin.right,
        height = fullHeight - margin.top - margin.bottom;
    var x = d3.scale.linear().clamp(true).range([0, width]),
        y = d3.scale.ordinal().rangeRoundBands([height, 0], 0.2, 0.1);

    var xAxis = d3.svg.axis()
        .scale(x)
        .tickFormat(customTimeFormat)
        .tickSize(-height)
        .orient("bottom");

    var yAxis = d3.svg.axis()
        .scale(y)
        .orient("left");

    // Add lanes chart
    var svg = element.append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .attr("id", "fragment_workers");

    var chart = svg.append("g")
        .attr("class", "plot")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    // Place the xAxis
    chart.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

    // Place the lanes plot
    var lanes = chart.append("g")
        .attr("class", "lanes")
        //.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    /* Collect data for states at each worker */
    var url = templates.urls.profiling({
        myria: myriaConnection,
        query: queryId,
        fragment: fragmentId
    });

    var workersData = {};

    d3.csv(url, type2, function(error, data) {
        // copy the results into the workersData
        // don't allocate another object as we return the
        // reference to this one
        workersData = getWorkersStates(data);
        //redrawLanes(element, workersData, x, y, xAxis, yAxis, [0,0]);
    });

    function type2(d) {
        d.workerId = +d.workerId;
        d.nanoTime = parseFloat(d.nanoTime);
        d.numTuples = +d.numTuples;
        return d;
    }

    function getWorkersStates(data) {
	// TODO: this parsing function assumes the following about the data
	// received:
        //   - events are sorted at each worker

        // Create a structure:
        //  workers = [{ workerId : [{opName : [{}]}] }]
        //
        // TODO: use queryPlan to put the right lane numbers?

        var workersStates = {};
        var tmpStacks = {}; // a stack per worker keeps unfinished operator calls

        data.forEach(function(d) {
            stack = tmpStacks[d.workerId];
            if (stack == null || stack.length === 0) {
                tmpStacks[d.workerId] = [];
                tmpStacks[d.workerId].push(get_state(d));
                return;
            }

    	    // event on top of stack completed (we now know its endTime), add
    	    // to states

            if (!_.has(workersStates, d.workerId)) {
                workersStates[d.workerId] = {
                    workerId: d.workerId,
                    states: []
                }
            }

    	    states = workersStates[d.workerId].states;

            top_stack = stack[stack.length - 1];
            top_stack.end = d.nanoTime;
            states.push(top_stack);

            // check the event type and push unfinished event on the stack
            if (d.eventType === "call") {
                stack.push(get_state(d));
            } else if (d.eventType === "return") {
                // it's a return, update the link and replace top of stack
                // with a new, same opName event that starts from d.nanoTime
                stack.pop();
                if (stack.length > 0) {
                    top_stack = stack[stack.length - 1];
                    state = get_state(d);
                    state.name = top_stack.name; // the same call
                    top_stack.link = state; // belong together (still in the same call thread)
                    stack.pop();
                    stack.push(state);
               } //TODO: eos??
            }
        });

        function get_state(d) {
            return {
                link: null,           // if it belongs together with some previous event
                name: d.opName,
                begin: d.nanoTime,
                end: null,           // we don't know this yet ...
                numTuples: d.numTuples
            };
        }

        return workersStates;
    }

    function redrawLanes(xDomain) {
        var data = _.values(workersData);

        // Remove what was previously drawn
        y.domain(_.pluck(data, 'workerId'));
        x.domain(xDomain);

        var lane = lanes.selectAll(".worker").data(data, function(d) {return d.workerId});
        lane.enter().append("g").attr("class", "worker");
        lane.attr("transform", function(d) { return "translate(0," +  y(d.workerId) + ")"; });
        lane.exit().remove();

        var box = lane.selectAll("rect")
            .data(function(d) {
                return _.filter(d.states, function(s) {
                    // overlap
                    return overlap = xDomain[0] < s.end && xDomain[1] > s.begin;
                });
            }, function(d) {return d.begin;});

        box.enter().append("rect")
            .popover(function(d) {
                var content = '';
                if (d.numTuples >= 0) {
                    content += templates.numTuplesTemplate({numTuples: d.numTuples});
                } else {
                    content += templates.nullReturned();
                }
                var duration = d.end - d.begin;
                content += templates.boxTemplate({
                    duration: customFullTimeFormat(duration),
                    begin: customFullTimeFormat(d.begin),
                    end: customFullTimeFormat(d.end)
                });
                return {
                    title: d.name,
                    content: content
                };
            })
            //.attr("clip-path", "url(#clip)")
            .style("fill", function(d) { return opToColor[d.name]; })
            .attr("class", "box");

        box
            .transition()
            .duration(animationDuration)
            .attr("x", function(d) { return x(d.begin); })
            .style("opacity", 1)
            .attr("width", function(d) {
                return x(d.end) - x(d.begin);
            })
            .attr("height", function(d) { return y.rangeBand(); });

        box.exit().remove();

        chart.select(".x.axis")
            .transition()
            .duration(animationDuration)
            .call(xAxis);
    }

    return {
        redrawLanes: redrawLanes
    };
}

<|endoftext|>


<commit_before>module.exports = (queryObj) => {
  return async function minifySvgAssetsWithSvgo(assetGraph) {
    let svgo;
    const svgAssets = assetGraph.findAssets({ type: 'Svg', ...queryObj });
    if (svgAssets.length > 0) {
      try {
        svgo = require('svgo');
      } catch (e) {
        assetGraph.info(
          new Error(
            `minifySvgAssetsWithSvgo: Found ${svgAssets.length} svg asset(s), but no svgo module is available. Please use npm to install svgo in your project so minifySvgAssetsWithSvgo can require it.`
          )
        );
        return;
      }
      if (typeof svgo.optimize !== 'function') {
        assetGraph.info(
          new Error(
            `minifySvgAssetsWithSvgo: The svgo module was found, but it does not export an optimize function. Ensure that you've installed version 2.0.0 or above.`
          )
        );
        return;
      }
    }
    await Promise.all(
      svgAssets.map(async (svgAsset) => {
        // The removeUnknownsAndDefaults SVGO plugin strips top-level attributes of the <svg> element
        // that could be important. Note down all the attributes so they can be re-attached after the optimization
        // https://github.com/svg/svgo/issues/301
        const originalTopLevelAttributes = {};
        if (svgAsset.isInline) {
          for (const attribute of Array.from(
            svgAsset.parseTree.documentElement.attributes
          )) {
            originalTopLevelAttributes[attribute.name] = attribute.value;
          }
        }

        let result;
        try {
          result = await svgo.optimize(svgAsset.text, {
            floatPrecision: 6, // The default of 2 is so low that it's visibly lossy in some cases
            plugins: [
              {
                name: 'preset-default',
                params: {
                  overrides: {
                    removeViewBox: false,
                  },
                },
              },
            ],
          });
        } catch (err) {
          assetGraph.warn(
            new Error(
              `SVG optimization error in ${svgAsset.urlOrDescription}\nOptimiztion skipped.\n${err}`
            )
          );
        }

        if (result) {
          svgAsset.text = result.data;
          if (svgAsset.isInline) {
            let dirty = false;
            for (const attributeName of Object.keys(
              originalTopLevelAttributes
            )) {
              if (
                !svgAsset.parseTree.documentElement.hasAttribute(attributeName)
              ) {
                svgAsset.parseTree.documentElement.setAttribute(
                  attributeName,
                  originalTopLevelAttributes[attributeName]
                );
                dirty = true;
              }
            }
            if (dirty) {
              svgAsset.markDirty();
            }
          }
        }
      })
    );
  };
};
<commit_msg>Looks like we don't need await anymore<commit_after>module.exports = (queryObj) => {
  return async function minifySvgAssetsWithSvgo(assetGraph) {
    let svgo;
    const svgAssets = assetGraph.findAssets({ type: 'Svg', ...queryObj });
    if (svgAssets.length > 0) {
      try {
        svgo = require('svgo');
      } catch (e) {
        assetGraph.info(
          new Error(
            `minifySvgAssetsWithSvgo: Found ${svgAssets.length} svg asset(s), but no svgo module is available. Please use npm to install svgo in your project so minifySvgAssetsWithSvgo can require it.`
          )
        );
        return;
      }
      if (typeof svgo.optimize !== 'function') {
        assetGraph.info(
          new Error(
            `minifySvgAssetsWithSvgo: The svgo module was found, but it does not export an optimize function. Ensure that you've installed version 2.0.0 or above.`
          )
        );
        return;
      }
    }
    await Promise.all(
      svgAssets.map(async (svgAsset) => {
        // The removeUnknownsAndDefaults SVGO plugin strips top-level attributes of the <svg> element
        // that could be important. Note down all the attributes so they can be re-attached after the optimization
        // https://github.com/svg/svgo/issues/301
        const originalTopLevelAttributes = {};
        if (svgAsset.isInline) {
          for (const attribute of Array.from(
            svgAsset.parseTree.documentElement.attributes
          )) {
            originalTopLevelAttributes[attribute.name] = attribute.value;
          }
        }

        let result;
        try {
          result = svgo.optimize(svgAsset.text, {
            floatPrecision: 6, // The default of 2 is so low that it's visibly lossy in some cases
            plugins: [
              {
                name: 'preset-default',
                params: {
                  overrides: {
                    removeViewBox: false,
                  },
                },
              },
            ],
          });
        } catch (err) {
          assetGraph.warn(
            new Error(
              `SVG optimization error in ${svgAsset.urlOrDescription}\nOptimiztion skipped.\n${err}`
            )
          );
        }

        if (result) {
          svgAsset.text = result.data;
          if (svgAsset.isInline) {
            let dirty = false;
            for (const attributeName of Object.keys(
              originalTopLevelAttributes
            )) {
              if (
                !svgAsset.parseTree.documentElement.hasAttribute(attributeName)
              ) {
                svgAsset.parseTree.documentElement.setAttribute(
                  attributeName,
                  originalTopLevelAttributes[attributeName]
                );
                dirty = true;
              }
            }
            if (dirty) {
              svgAsset.markDirty();
            }
          }
        }
      })
    );
  };
};
<|endoftext|>