<commit_before>function Banner() {

    var keyword = "Interaction";
    var keyword2 = "Designer";
    var canvas;
	var context;

	var bgCanvas;
	var bgContext;

	var denseness = 10;

	//Each particle/icon
	var parts = [];

	var mouse = {x:-100,y:-100};
	var mouseOnScreen = false;

	var itercount = 0;
	var itertot = 40;

	var cwidth = $(document).width();
    var cheight = $(document).height();

	this.initialize = function(canvas_id){
		canvas = document.getElementById(canvas_id);
		context = canvas.getContext('2d');

		canvas.width = cwidth;
		canvas.height = cheight;

		bgCanvas = document.createElement('canvas');
		bgContext = bgCanvas.getContext('2d');

		bgCanvas.width = cwidth;
		bgCanvas.height = cheight;

		canvas.addEventListener('mousemove', MouseMove, false);
		canvas.addEventListener('mouseout', MouseOut, false);
//        window.addEventListener('resize', resizeCanvas, false);
		start();
	}

	var start = function(){
		bgContext.fillStyle = "#333";
		bgContext.font = '140px Arial';
        //fitTextOnCanvas("bgContext", keyword, "Arial", 50, 450);
        //fitTextOnCanvas("bgContext", keyword2, "Arial", 450, 400);
		bgContext.fillText(keyword, 500, 290);
        bgContext.fillText(keyword2, 500, 400);
		bgContext.beginPath();
        bgContext.lineWidth="5";
        bgContext.moveTo(960,450);
        bgContext.lineTo(960,650);
        bgContext.lineTo(350,650);
        bgContext.lineTo(350,6000);
        bgContext.stroke();

		clear();
		getCoords();
        //context.fillStyle = "#000000";
        //context.font = '100px Arial';
        //fitTextOnCanvas("context", keyword, "Quicksand", 85, 275);
        //context.fillText(keyword, 85, 275);
	}

	var getCoords = function(){
		var imageData, pixel, height, width;

		imageData = bgContext.getImageData(0, 0, canvas.width, canvas.height);

		// quickly iterate over all pixels - leaving density gaps
	    for(height = 0; height < bgCanvas.height; height += denseness){
            for(width = 0; width < bgCanvas.width; width += denseness){
               pixel = imageData.data[((width + (height * bgCanvas.width)) * 4) - 1];
                  //Pixel is black from being drawn on.
                  if(pixel == 255) {
                    drawCircle(width, height);
                  }
            }
        }

        setInterval( update, 40 );
	}

	var drawCircle = function(x, y){

		var startx = (Math.random() * canvas.width);
		var starty = (Math.random() * canvas.height);

		var velx = (x - startx) / itertot;
		var vely = (y - starty) / itertot;
        var mycolors = ['#26F0F1', '#FF1053', '#000099','#66C9FF'];

		parts.push(
			{
             //c: '#' + (Math.random() * 0x949494 + 0xaaaaaa | 0).toString(16),
             c: mycolors[Math.floor(Math.random() * mycolors.length)],
			 x: x, //goal position   0xaaaaaa 0x111111
			 y: y,
			 x2: startx, //start position
			 y2: starty,
			 r: true, //Released (to fly free!)
			 v:{x:velx , y: vely}
			}
		)
	}

	var update = function(){
		var i, dx, dy, sqrDist, scale;
		itercount++;
		clear();
        //context.fillStyle = "#000000";
        //context.fillText(keyword, 85, 275);
        //fitTextOnCanvas("context", keyword, "Quicksand", 85, 275);
		for (i = 0; i < parts.length; i++){

			//If the dot has been released
			if (parts[i].r == true){
				//Fly into infinity!!
				parts[i].x2 += parts[i].v.x;
		        parts[i].y2 += parts[i].v.y;
			//Perhaps I should check if they are out of screen... and kill them?
			}
			if (itercount == itertot){
				parts[i].v = {x:(Math.random() * 6) * 2 - 6 , y:(Math.random() * 6) * 2 - 6};
				parts[i].r = false;
			}

			//Look into using svg, so there is no mouse tracking.
			//Find distance from mouse/draw!
			dx = parts[i].x - mouse.x;
	        dy = parts[i].y - mouse.y;
	        sqrDist =  Math.sqrt(dx*dx + dy*dy);

			if (sqrDist < 20){
				parts[i].r = true;
			}

			//Draw the circle
			context.fillStyle = parts[i].c;
			context.beginPath();
			context.arc(parts[i].x2, parts[i].y2, 4 ,0 , Math.PI*2, true);
			context.closePath();
	    	context.fill();

		}
	}

	var MouseMove = function(e) {
	    if (e.layerX || e.layerX == 0) {
	    	//Reset particle positions
	    	mouseOnScreen = true;

                //use offsetX instead of layerX for Firefox
                //why was that working for Chrome?
	        mouse.x = e.offsetX - canvas.offsetLeft;
	        mouse.y = e.offsetY - canvas.offsetTop;
	    }
	}

	var MouseOut = function(e) {
		mouseOnScreen = false;
		mouse.x = -100;
		mouse.y = -100;
	}

	//Clear the on screen canvas
	var clear = function(){
        /*var my_gradient = context.createLinearGradient(0, 0, 0, 500);
        my_gradient.addColorStop(0, "black");
        my_gradient.addColorStop(1, "white");*/
		context.fillStyle = "#ffffff";
		context.beginPath();
  		context.rect(0, 0, canvas.width, canvas.height);
 		context.closePath();
 		context.fill();
	}

    /*function resizeCanvas(){
        cwidth = $(document).width();
        cheight = $(document).height();

        var tempContext = Utils.Canvas.Create2DContext(context.bgCanvas.width, context.bgCanvas.height);

        //Draw current canvas to temp canvas
        tempContext.drawImage(context.canvas, 0, 0);

        context.canvas.width = cwidth;
		context.canvas.height = cheight;
        context.bgCanvas.width = cwidth;
		context.bgCanvas.height = cheight;

        //Draw temp canvas back to the current canvas
        context.drawImage(tempContext.canvas, 0, 0);
    }*/

    function simulate(e) {
        var evt = document.createEvent("MouseEvents");
        evt.initMouseEvent("mousemove", true, true, window,
    0, e.screenX, e.screenY, e.clientX, e.clientY, false, false, false, false, 0, null);
        canvas.dispatchEvent(evt);
        console.log("Emulated.");
    }

    $("body > section, body > header").each(function(){
        this.addEventListener("mousemove", simulate);
    });

    function fitTextOnCanvas(contexttype, text, font, xPosition, yPosition) {

        // start with a large font size
        var fontsize = 200;

        /*if(contexttype=="context"){
            // lower the font size until the text fits the canvas
            do {
                fontsize--;
                context.font = fontsize+"px"+" "+font;
            } while (context.measureText(text).width > canvas.width*.9)

            // draw the text
            context.fillText(text, xPosition, yPosition);

        }else{*/
            // lower the font size until the text fits the canvas
            do {
                fontsize--;
                bgContext.font = fontsize+"px"+" "+font;
            } while (bgContext.measureText(text).width > canvas.width*.9)

            // draw the text
            bgContext.fillText(text, xPosition, yPosition);
       // }
    }

}

var banner = new Banner();
banner.initialize("canvas");<commit_msg>color update<commit_after>function Banner() {

    var keyword = "Interaction";
    var keyword2 = "Designer";
    var canvas;
	var context;

	var bgCanvas;
	var bgContext;

	var denseness = 10;

	//Each particle/icon
	var parts = [];

	var mouse = {x:-100,y:-100};
	var mouseOnScreen = false;

	var itercount = 0;
	var itertot = 40;

	var cwidth = $(document).width();
    var cheight = $(document).height();

	this.initialize = function(canvas_id){
		canvas = document.getElementById(canvas_id);
		context = canvas.getContext('2d');

		canvas.width = cwidth;
		canvas.height = cheight;

		bgCanvas = document.createElement('canvas');
		bgContext = bgCanvas.getContext('2d');

		bgCanvas.width = cwidth;
		bgCanvas.height = cheight;

		canvas.addEventListener('mousemove', MouseMove, false);
		canvas.addEventListener('mouseout', MouseOut, false);
//        window.addEventListener('resize', resizeCanvas, false);
		start();
	}

	var start = function(){
		bgContext.fillStyle = "#333";
		bgContext.font = '140px Arial';
        //fitTextOnCanvas("bgContext", keyword, "Arial", 50, 450);
        //fitTextOnCanvas("bgContext", keyword2, "Arial", 450, 400);
		bgContext.fillText(keyword, 500, 290);
        bgContext.fillText(keyword2, 500, 400);
		bgContext.beginPath();
        bgContext.lineWidth="5";
        bgContext.moveTo(960,450);
        bgContext.lineTo(960,650);
        bgContext.lineTo(350,650);
        bgContext.lineTo(350,6000);
        bgContext.stroke();

		clear();
		getCoords();
        //context.fillStyle = "#000000";
        //context.font = '100px Arial';
        //fitTextOnCanvas("context", keyword, "Quicksand", 85, 275);
        //context.fillText(keyword, 85, 275);
	}

	var getCoords = function(){
		var imageData, pixel, height, width;

		imageData = bgContext.getImageData(0, 0, canvas.width, canvas.height);

		// quickly iterate over all pixels - leaving density gaps
	    for(height = 0; height < bgCanvas.height; height += denseness){
            for(width = 0; width < bgCanvas.width; width += denseness){
               pixel = imageData.data[((width + (height * bgCanvas.width)) * 4) - 1];
                  //Pixel is black from being drawn on.
                  if(pixel == 255) {
                    drawCircle(width, height);
                  }
            }
        }

        setInterval( update, 40 );
	}

	var drawCircle = function(x, y){

		var startx = (Math.random() * canvas.width);
		var starty = (Math.random() * canvas.height);

		var velx = (x - startx) / itertot;
		var vely = (y - starty) / itertot;
        var mycolors = ['#0C6D6C', '#140152', '#000099','#FF1053', '#04E762'];

		parts.push(
			{
             //c: '#' + (Math.random() * 0x949494 + 0xaaaaaa | 0).toString(16),
             c: mycolors[Math.floor(Math.random() * mycolors.length)],
			 x: x, //goal position   0xaaaaaa 0x111111
			 y: y,
			 x2: startx, //start position
			 y2: starty,
			 r: true, //Released (to fly free!)
			 v:{x:velx , y: vely}
			}
		)
	}

	var update = function(){
		var i, dx, dy, sqrDist, scale;
		itercount++;
		clear();
        //context.fillStyle = "#000000";
        //context.fillText(keyword, 85, 275);
        //fitTextOnCanvas("context", keyword, "Quicksand", 85, 275);
		for (i = 0; i < parts.length; i++){

			//If the dot has been released
			if (parts[i].r == true){
				//Fly into infinity!!
				parts[i].x2 += parts[i].v.x;
		        parts[i].y2 += parts[i].v.y;
			//Perhaps I should check if they are out of screen... and kill them?
			}
			if (itercount == itertot){
				parts[i].v = {x:(Math.random() * 6) * 2 - 6 , y:(Math.random() * 6) * 2 - 6};
				parts[i].r = false;
			}

			//Look into using svg, so there is no mouse tracking.
			//Find distance from mouse/draw!
			dx = parts[i].x - mouse.x;
	        dy = parts[i].y - mouse.y;
	        sqrDist =  Math.sqrt(dx*dx + dy*dy);

			if (sqrDist < 20){
				parts[i].r = true;
			}

			//Draw the circle
			context.fillStyle = parts[i].c;
			context.beginPath();
			context.arc(parts[i].x2, parts[i].y2, 4 ,0 , Math.PI*2, true);
			context.closePath();
	    	context.fill();

		}
	}

	var MouseMove = function(e) {
	    if (e.layerX || e.layerX == 0) {
	    	//Reset particle positions
	    	mouseOnScreen = true;

                //use offsetX instead of layerX for Firefox
                //why was that working for Chrome?
	        mouse.x = e.offsetX - canvas.offsetLeft;
	        mouse.y = e.offsetY - canvas.offsetTop;
	    }
	}

	var MouseOut = function(e) {
		mouseOnScreen = false;
		mouse.x = -100;
		mouse.y = -100;
	}

	//Clear the on screen canvas
	var clear = function(){
        /*var my_gradient = context.createLinearGradient(0, 0, 0, 500);
        my_gradient.addColorStop(0, "black");
        my_gradient.addColorStop(1, "white");*/
		context.fillStyle = "#ffffff";
		context.beginPath();
  		context.rect(0, 0, canvas.width, canvas.height);
 		context.closePath();
 		context.fill();
	}

    /*function resizeCanvas(){
        cwidth = $(document).width();
        cheight = $(document).height();

        var tempContext = Utils.Canvas.Create2DContext(context.bgCanvas.width, context.bgCanvas.height);

        //Draw current canvas to temp canvas
        tempContext.drawImage(context.canvas, 0, 0);

        context.canvas.width = cwidth;
		context.canvas.height = cheight;
        context.bgCanvas.width = cwidth;
		context.bgCanvas.height = cheight;

        //Draw temp canvas back to the current canvas
        context.drawImage(tempContext.canvas, 0, 0);
    }*/

    function simulate(e) {
        var evt = document.createEvent("MouseEvents");
        evt.initMouseEvent("mousemove", true, true, window,
    0, e.screenX, e.screenY, e.clientX, e.clientY, false, false, false, false, 0, null);
        canvas.dispatchEvent(evt);
        console.log("Emulated.");
    }

    $("body > section, body > header").each(function(){
        this.addEventListener("mousemove", simulate);
    });

    function fitTextOnCanvas(contexttype, text, font, xPosition, yPosition) {

        // start with a large font size
        var fontsize = 200;

        /*if(contexttype=="context"){
            // lower the font size until the text fits the canvas
            do {
                fontsize--;
                context.font = fontsize+"px"+" "+font;
            } while (context.measureText(text).width > canvas.width*.9)

            // draw the text
            context.fillText(text, xPosition, yPosition);

        }else{*/
            // lower the font size until the text fits the canvas
            do {
                fontsize--;
                bgContext.font = fontsize+"px"+" "+font;
            } while (bgContext.measureText(text).width > canvas.width*.9)

            // draw the text
            bgContext.fillText(text, xPosition, yPosition);
       // }
    }

}

var banner = new Banner();
banner.initialize("canvas");<|endoftext|>


<commit_before>'use strict';

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

    draw_logic();

    if(canvas_menu){
        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(
          canvas_resume,
          canvas_x,
          canvas_y - 25
        );
        canvas_buffer.fillText(
          canvas_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
function canvas_draw_path(args){
    args['properties'] = args['properties'] || {};
    args['style'] = args['style'] || canvas_style;

    canvas_buffer.beginPath();
    for(var vertex in args['vertices']){
        canvas_buffer[args['vertices'][vertex]['type'] || 'lineTo'](
          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] = args['properties'][property];
    }

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

function canvas_init(){
    canvas_resize();
    canvas_setmode({
      'mode': 0,
    });
}

function canvas_menu_quit(){
    if(canvas_menu){
        canvas_setmode({
          'mode': 0,
        });
    }
}

function canvas_menu_toggle(){
    canvas_menu = !canvas_menu;
}

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'];

    if(typeof resize_logic === 'function'){
        resize_logic();
    }
}

// Required args: mode
// Optional args: newgame
function canvas_setmode(args){
    args['newgame'] = args['newgame'] || false;

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

    canvas_menu = false;
    canvas_mode = args['mode'];

    if(typeof setmode_logic === '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();
    }

    if(typeof load_level === 'function'){
        load_level(canvas_mode);
    }

    canvas_animationFrame = window.requestAnimationFrame(canvas_drawloop);

    if(typeof logic === '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_menu = false;
var canvas_mode = 0;
var canvas_oncontextmenu = true;
var canvas_quit = 'Q = Main Menu';
var canvas_resume = 'ESC = Resume';
var canvas_style = 'fill';
var canvas_width = 0;
var canvas_x = 0;
var canvas_y = 0;

window.onresize = canvas_resize;
<commit_msg>added common core.js usage to common canvas.js<commit_after>'use strict';

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

    draw_logic();

    if(canvas_menu){
        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(
          canvas_resume,
          canvas_x,
          canvas_y - 25
        );
        canvas_buffer.fillText(
          canvas_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
function canvas_draw_path(args){
    args['properties'] = args['properties'] || {};
    args['style'] = args['style'] || canvas_style;

    canvas_buffer.beginPath();
    for(var vertex in args['vertices']){
        canvas_buffer[args['vertices'][vertex]['type'] || 'lineTo'](
          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] = args['properties'][property];
    }

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

function canvas_init(){
    canvas_resize();
    canvas_setmode({
      'mode': 0,
    });
}

function canvas_menu_quit(){
    if(canvas_menu){
        canvas_setmode({
          'mode': 0,
        });
    }
}

function canvas_menu_toggle(){
    canvas_menu = !canvas_menu;
}

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',
    });
}

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

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

    canvas_menu = false;
    canvas_mode = args['mode'];

    if(core_type({
      'var': 'setmode_logic',
    })){
        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,
    })){
        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_menu = false;
var canvas_mode = 0;
var canvas_oncontextmenu = true;
var canvas_quit = 'Q = Main Menu';
var canvas_resume = 'ESC = Resume';
var canvas_style = 'fill';
var canvas_width = 0;
var canvas_x = 0;
var canvas_y = 0;

window.onresize = canvas_resize;
<|endoftext|>


<commit_before>/**
 * Make Raphael Set draggable
 * @param {Function} callback - Function to call when an object has been moved
 */
Raphael.st.draggable = function(callback)
{
	// Cache Set so elements can use it
	var parent = this;

	var canvas = canvas;

	// Transform location
	var tx = 0,
		ty = 0;

	// Drag origin
	var ox = 0,
		oy = 0;

	var onMove = function(dx, dy)
	{
		// Add delta to transform location
		tx = dx + ox;
		ty = dy + oy;
		parent.transform('t' + tx + ',' + ty);

		// Recalculate all of the Dataflows
		// TODO: Optimize, only affected Dataflows should be calculated
		callback();
	};

	var onStart = function()
	{
		parent.toFront();
	};

	var onEnd = function()
	{
		// Store for next drag origin
		ox = tx;
		oy = ty;
	};

	this.drag(onMove, onStart, onEnd);

	return this;
};

/**
 * Create a canvas
 * @constructor
 * @param {string} container - DOM element's ID for the parent element of the canvas
 * @param {number} width - The width of the canvas in pixels
 * @param {number} height - The height of the canvas in pixels
 */
function Canvas(container, width, height)
{
	// Create canvas with Raphael with given arguments
	var paper = Raphael(container, width, height);

	// Element and Dataflow arrays
	var elements = new Array();
	var dataflows = new Array();

	/**
	 * Set the background color of the canvas
	 * @param {string} color - Hex value of the color, i.e., '#A8A8A8'
	 */
	this.setBackground = function(color)
	{
		paper.canvas.style.backgroundColor = color;
	};

	/**
	 * Get number of Elements on the canvas
	 * @return {number} Number of Elements
	 */
	this.getNumberOfElements = function()
	{
		return elements.length;
	};

	/**
	 * Get number of Dataflows on the canvas
	 * @return {number} Number of Dataflows
	 */
	this.getNumberOfDataflows = function()
	{
		return dataflows.length;
	};

	/**
	 * Set the size of the canvas
	 * @param {number} width - New width of the canvas in pixels
	 * @param {number} height - New height of the canvas in pixels
	 */
	this.setSize = function(width,height)
	{
		paper.setSize(width,height);
	}

	/**
	 * Add a process element to the canvas at the given location
	 * @param {number} x - Coordinate in pixels
	 * @param {number} y - Coordinate in pixels
	 * @return {Process} Element on canvas
	 */
	this.addProcess = function(x,y)
	{
		var e = new Process(this,x,y);
		elements.push(e);

		return e;
	};

	/**
	 * Add a multi-process element to the canvas at the given location
	 * @param {number} x - Coordinate in pixels
	 * @param {number} y - Coordinate in pixels
	 * @return {MultiProcess} Element on canvas
	 */
	this.addMultiProcess = function(x,y)
	{
		var e = new MultiProcess(this,x,y);
		elements.push(e);

		return e;
	};

	/**
	 * Add a datastore element to the canvas at the given location
	 * @param {number} x - Coordinate in pixels
	 * @param {number} y - Coordinate in pixels
	 * @return {Datastore} Element on canvas
	 */
	this.addDatastore = function(x,y)
	{
		var e = new Datastore(this,x,y);
		elements.push(e);

		return e;
	};

	/**
	 * Add a external interactor element to the canvas at the given location
	 * @param {number} x - Coordinate in pixels
	 * @param {number} y - Coordinate in pixels
	 * @return {ExtInteractor} Element on canvas
	 */
	this.addExtInteractor = function(x,y)
	{
		var e = new ExtInteractor(this,x,y);
		elements.push(e);

		return e;
	};

	/**
	 * Add a dataflow to the cavas between the two elements
	 * @param {Element} source - Source of the Dataflow
	 * @param {Element} target - Target of the Dataflow
	 * @return {Dataflow} Dataflow on the canvas
	 */
	this.addDataflow = function(source, target)
	{
		var d = new Dataflow(source,target);
		dataflows.push(d);

		return d;
	};

	/**
	 * Remove an element from the canvas
	 * @param {Element} element - Element to remove from the canvas
	 * @return {boolean} TRUE if the element was removed, FALSE otherwise
	 */
	this.removeElement = function(element)
	{
		var index = elements.indexOf(element);
		if (index > -1)
		{
			elements.splice(index, 1);
			return true;
		}
		return false;
	};

	/**
	 * Remove a Dataflow from the canvas
	 * @param {Dataflow} dataflow - Dataflow to remove from the canvas
	 * @return {boolean} TRUE if the element was removed, FALSE otherwise
	 */
	this.removeDataflow = function(dataflow)
	{
		var index = dataflows.indexOf(dataflow);
		if (index > -1)
		{
			dataflows.splice(index, 1);
			return true;
		}
		return false;
	};

	/**
	 * Recalculate all Dataflows in the canvas
	 */
	this.calcDataflows = function()
	{
		for (var i = 0; i < dataflows.length; i++)
		{
			dataflows[i].calcPath();
		}
	};

	/**
	 * Base Class for all Elements
	 * @constructor
	 * @param {Cavas} canvas - Canvas Object
	 */
	var Element = function(canvas)
	{
		var set = paper.set(); // Raphael.Set for shapes
		var canvas = canvas; // Internal reference to canvas

		/**
		 * Add a shape to the Element
		 * @param {Raphael.Shape} shape - Shape to add
		 */
		this.push = function(shape)
		{
			set.push(shape);
		};

		/**
		 * Get Set of Shapes
		 * @returns {Raphael.Set} Set of Shapes
		 */
		this.getSet = function()
		{
			return set;
		};

		/**
		 * Make the Element draggable, any elements added after
		 * draggable is called will not function properly
		 */
		this.draggable = function()
		{
			set.draggable(canvas.calcDataflows);
		};

		/**
		 * Style all of the Shapes with the default styling
		 */
		this.applyDefaultStyle = function()
		{
			set.attr("fill", "#FFF");
			set.attr("stroke", "#000");
			set.attr("stroke-width", "1px");
		};

		/**
		 * Get the bounding box for the Element
		 * @returns {Raphael.BBox}
		 */
		this.getBBox = function()
		{
			return set.getBBox();
		};
	};

	/**
	 * Create a Process element
	 * @constructor
	 * @param {Cavas} canvas - Canvas Object
	 * @param {number} x - Coordinate in pixels
	 * @param {number} y - Coordinate in pixels
	 */
	var Process = function(canvas,x,y)
	{
		Element.call(this, canvas); // Call parent constructor

		this.push(paper.circle(x,y,25));
		this.applyDefaultStyle();
		this.draggable();
	};
	Process.prototype = new Element(); // Inherit Element
	Process.prototype.constructor = Process; // Fix the constructor pointer

	/**
	 * Create a MultiProcess element
	 * @constructor
	 * @param {Cavas} canvas - Canvas Object
	 * @param {number} x - Coordinate in pixels
	 * @param {number} y - Coordinate in pixels
	 */
	var MultiProcess = function(canvas,x,y)
	{
		Element.call(this, canvas);

		this.push(paper.circle(x,y,25),paper.circle(x,y,18));
		this.applyDefaultStyle();
		this.draggable();
	};
	MultiProcess.prototype = new Element();
	MultiProcess.prototype.constructor = MultiProcess;

	/**
	 * Create a Datastore element
	 * @constructor
	 * @param {Cavas} canvas - Canvas Object
	 * @param {number} x - Coordinate in pixels
	 * @param {number} y - Coordinate in pixels
	 */
	var Datastore = function(canvas,x,y)
	{
		Element.call(this, canvas);

		x = x - 25;
		y = y - 25;

		this.push(
			paper.path("M" + x + " " + y + " L" + (x+50) + " " + y + " Z"),
			paper.path("M" + x + " " + (y+50) + " L" + (x+50) + " " + (y+50) + " Z")
			);
		this.applyDefaultStyle();

		var rec = paper.rect((x), (y+1), 50, 48);
		rec.attr("stroke-width", 0);
		rec.attr("fill", "#FFF");
		this.push(rec);

		this.draggable();
	};
	Datastore.prototype = new Element();
	Datastore.prototype.constructor = Datastore;

	/**
	 * Create a External-interactor element
	 * @constructor
	 * @param {Cavas} canvas - Canvas Object
	 * @param {number} x - Coordinate in pixels
	 * @param {number} y - Coordinate in pixels
	 */
	var ExtInteractor = function(canvas,x,y)
	{
		Element.call(this, canvas);

		this.push(paper.rect(x - 25,y - 25,50,50));
		this.applyDefaultStyle();
		this.draggable();
	};
	ExtInteractor.prototype = new Element();
	ExtInteractor.prototype.constructor = ExtInteractor;

	/**
	 * Create a Dataflow from source to target
	 * @constructor
	 * @param {Element} source - Source of the Dataflow
	 * @param {Element} target - Target of the Dataflow
	 */
	var Dataflow = function(source,target)
	{
		var source = source;
		var target = target;
		var path;

		/**
		 * Get the attach points for an Element
		 * @param {Element} element - The element to get the attach points off
		 * @returns {Array} Array of four points (x,y)
		 */
		var getAttachPoints = function(element)
		{
			var bb = element.getBBox();
			var points = new Array();
			points.push({x: bb.x, y: bb.y + bb.height / 2}); // Left
			points.push({x: bb.x + bb.width / 2, y: bb.y}); // Top
			points.push({x: bb.x + bb.width, y: bb.y + bb.height / 2}); // Right
			points.push({x: bb.x + bb.width / 2, y: bb.y + bb.height}); // Bottom

			return points;
		};

		/**
		 * Calculate Dataflow's path as the minium between the two Elements
		 */
		this.calcPath = function() {
			var sP = getAttachPoints(source);
			var tP = getAttachPoints(target);
			var sIndex = 0; // Shortest point index for source
			var tIndex = 0; // Shortest point index for source
			var min; // Minimum length

			// Loop through all of the attach points for both Elements
			for (var i = 0; i < sP.length; i++)
			{
				for (var j = 0; j < tP.length; j++)
				{
					// Calculate vector's length (sqrt(a^2 + b^2))
					var dx = Math.abs(sP[i].x - tP[j].x);
					var dy = Math.abs(sP[i].y - tP[j].y);
					var length = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
					if (min)
					{
						// Check if new vector is minimum
						if (length < min)
						{
							sIndex = i;
							tIndex = j;
							min = length;
						}
					}
					else // No previous minimum existed
					{
						sIndex = i;
						tIndex = j;
						min = length;
					}
				}
			}
			var pathString = "M" + sP[sIndex].x + " " + sP[sIndex].y + " L" + tP[tIndex].x + " " + tP[tIndex].y + " Z";

			if (path)
			{
				// Path existed, update
				path.attr({path: pathString});
			}
			else
			{
				// Path did not exist, create
				path = paper.path(pathString);
			}
		};

		// Initially calculate the path
		this.calcPath();
	};
};<commit_msg>Fixed complex element dragging<commit_after>/**
 * Make Raphael Set draggable
 * @param {Function} callback - Function to call when an object has been moved
 */
Raphael.st.draggable = function(callback)
{
	// Cache Set so elements can use it
	var parent = this;

	var canvas = canvas;

	// Transform location
	var tx = 0,
		ty = 0;

	// Drag origin
	var ox = 0,
		oy = 0;

	var onMove = function(dx, dy)
	{
		// Add delta to transform location
		tx = dx + ox;
		ty = dy + oy;
		parent.transform('t' + tx + ',' + ty);

		// Recalculate all of the Dataflows
		// TODO: Optimize, only affected Dataflows should be calculated
		callback();
	};

	var onStart = function()
	{
		parent.toFront();
	};

	var onEnd = function()
	{
		// Store for next drag origin
		ox = tx;
		oy = ty;
	};

	this.drag(onMove, onStart, onEnd);

	return this;
};

/**
 * Create a canvas
 * @constructor
 * @param {string} container - DOM element's ID for the parent element of the canvas
 * @param {number} width - The width of the canvas in pixels
 * @param {number} height - The height of the canvas in pixels
 */
function Canvas(container, width, height)
{
	// Create canvas with Raphael with given arguments
	var paper = Raphael(container, width, height);

	// Element and Dataflow arrays
	var elements = new Array();
	var dataflows = new Array();

	/**
	 * Set the background color of the canvas
	 * @param {string} color - Hex value of the color, i.e., '#A8A8A8'
	 */
	this.setBackground = function(color)
	{
		paper.canvas.style.backgroundColor = color;
	};

	/**
	 * Get number of Elements on the canvas
	 * @return {number} Number of Elements
	 */
	this.getNumberOfElements = function()
	{
		return elements.length;
	};

	/**
	 * Get number of Dataflows on the canvas
	 * @return {number} Number of Dataflows
	 */
	this.getNumberOfDataflows = function()
	{
		return dataflows.length;
	};

	/**
	 * Set the size of the canvas
	 * @param {number} width - New width of the canvas in pixels
	 * @param {number} height - New height of the canvas in pixels
	 */
	this.setSize = function(width,height)
	{
		paper.setSize(width,height);
	}

	/**
	 * Add a process element to the canvas at the given location
	 * @param {number} x - Coordinate in pixels
	 * @param {number} y - Coordinate in pixels
	 * @return {Process} Element on canvas
	 */
	this.addProcess = function(x,y)
	{
		var e = new Process(this,x,y);
		elements.push(e);

		return e;
	};

	/**
	 * Add a multi-process element to the canvas at the given location
	 * @param {number} x - Coordinate in pixels
	 * @param {number} y - Coordinate in pixels
	 * @return {MultiProcess} Element on canvas
	 */
	this.addMultiProcess = function(x,y)
	{
		var e = new MultiProcess(this,x,y);
		elements.push(e);

		return e;
	};

	/**
	 * Add a datastore element to the canvas at the given location
	 * @param {number} x - Coordinate in pixels
	 * @param {number} y - Coordinate in pixels
	 * @return {Datastore} Element on canvas
	 */
	this.addDatastore = function(x,y)
	{
		var e = new Datastore(this,x,y);
		elements.push(e);

		return e;
	};

	/**
	 * Add a external interactor element to the canvas at the given location
	 * @param {number} x - Coordinate in pixels
	 * @param {number} y - Coordinate in pixels
	 * @return {ExtInteractor} Element on canvas
	 */
	this.addExtInteractor = function(x,y)
	{
		var e = new ExtInteractor(this,x,y);
		elements.push(e);

		return e;
	};

	/**
	 * Add a dataflow to the cavas between the two elements
	 * @param {Element} source - Source of the Dataflow
	 * @param {Element} target - Target of the Dataflow
	 * @return {Dataflow} Dataflow on the canvas
	 */
	this.addDataflow = function(source, target)
	{
		var d = new Dataflow(source,target);
		dataflows.push(d);

		return d;
	};

	/**
	 * Remove an element from the canvas
	 * @param {Element} element - Element to remove from the canvas
	 * @return {boolean} TRUE if the element was removed, FALSE otherwise
	 */
	this.removeElement = function(element)
	{
		var index = elements.indexOf(element);
		if (index > -1)
		{
			elements.splice(index, 1);
			return true;
		}
		return false;
	};

	/**
	 * Remove a Dataflow from the canvas
	 * @param {Dataflow} dataflow - Dataflow to remove from the canvas
	 * @return {boolean} TRUE if the element was removed, FALSE otherwise
	 */
	this.removeDataflow = function(dataflow)
	{
		var index = dataflows.indexOf(dataflow);
		if (index > -1)
		{
			dataflows.splice(index, 1);
			return true;
		}
		return false;
	};

	/**
	 * Recalculate all Dataflows in the canvas
	 */
	this.calcDataflows = function()
	{
		for (var i = 0; i < dataflows.length; i++)
		{
			dataflows[i].calcPath();
		}
	};

	/**
	 * Base Class for all Elements
	 * @constructor
	 * @param {Cavas} canvas - Canvas Object
	 */
	var Element = function(canvas)
	{
		var set = paper.set(); // Raphael.Set for shapes
		var canvas = canvas; // Internal reference to canvas

		/**
		 * Add a shape to the Element
		 * @param {Raphael.Shape} shape - Shape to add
		 */
		this.push = function(shape)
		{
			for (var i = 0; i < arguments.length; i++)
			{
				set.push(arguments[i]);
			}
		};

		/**
		 * Get Set of Shapes
		 * @returns {Raphael.Set} Set of Shapes
		 */
		this.getSet = function()
		{
			return set;
		};

		/**
		 * Make the Element draggable, any elements added after
		 * draggable is called will not function properly
		 */
		this.draggable = function()
		{
			set.draggable(canvas.calcDataflows);
		};

		/**
		 * Style all of the Shapes with the default styling
		 */
		this.applyDefaultStyle = function()
		{
			set.attr("fill", "#FFF");
			set.attr("stroke", "#000");
			set.attr("stroke-width", "1px");
		};

		/**
		 * Get the bounding box for the Element
		 * @returns {Raphael.BBox}
		 */
		this.getBBox = function()
		{
			return set.getBBox();
		};
	};

	/**
	 * Create a Process element
	 * @constructor
	 * @param {Cavas} canvas - Canvas Object
	 * @param {number} x - Coordinate in pixels
	 * @param {number} y - Coordinate in pixels
	 */
	var Process = function(canvas,x,y)
	{
		Element.call(this, canvas); // Call parent constructor

		this.push(paper.circle(x,y,25));
		this.applyDefaultStyle();
		this.draggable();
	};
	Process.prototype = new Element(); // Inherit Element
	Process.prototype.constructor = Process; // Fix the constructor pointer

	/**
	 * Create a MultiProcess element
	 * @constructor
	 * @param {Cavas} canvas - Canvas Object
	 * @param {number} x - Coordinate in pixels
	 * @param {number} y - Coordinate in pixels
	 */
	var MultiProcess = function(canvas,x,y)
	{
		Element.call(this, canvas);

		this.push(paper.circle(x,y,25),paper.circle(x,y,18));
		this.applyDefaultStyle();
		this.draggable();
	};
	MultiProcess.prototype = new Element();
	MultiProcess.prototype.constructor = MultiProcess;

	/**
	 * Create a Datastore element
	 * @constructor
	 * @param {Cavas} canvas - Canvas Object
	 * @param {number} x - Coordinate in pixels
	 * @param {number} y - Coordinate in pixels
	 */
	var Datastore = function(canvas,x,y)
	{
		Element.call(this, canvas);

		x = x - 25;
		y = y - 25;

		this.push(
			paper.path("M" + x + " " + y + " L" + (x+50) + " " + y + " Z"),
			paper.path("M" + x + " " + (y+50) + " L" + (x+50) + " " + (y+50) + " Z")
			);
		this.applyDefaultStyle();

		var rec = paper.rect((x), (y+1), 50, 48);
		rec.attr("stroke-width", 0);
		rec.attr("fill", "#FFF");
		this.push(rec);

		this.draggable();
	};
	Datastore.prototype = new Element();
	Datastore.prototype.constructor = Datastore;

	/**
	 * Create a External-interactor element
	 * @constructor
	 * @param {Cavas} canvas - Canvas Object
	 * @param {number} x - Coordinate in pixels
	 * @param {number} y - Coordinate in pixels
	 */
	var ExtInteractor = function(canvas,x,y)
	{
		Element.call(this, canvas);

		this.push(paper.rect(x - 25,y - 25,50,50));
		this.applyDefaultStyle();
		this.draggable();
	};
	ExtInteractor.prototype = new Element();
	ExtInteractor.prototype.constructor = ExtInteractor;

	/**
	 * Create a Dataflow from source to target
	 * @constructor
	 * @param {Element} source - Source of the Dataflow
	 * @param {Element} target - Target of the Dataflow
	 */
	var Dataflow = function(source,target)
	{
		var source = source;
		var target = target;
		var path;

		/**
		 * Get the attach points for an Element
		 * @param {Element} element - The element to get the attach points off
		 * @returns {Array} Array of four points (x,y)
		 */
		var getAttachPoints = function(element)
		{
			var bb = element.getBBox();
			var points = new Array();
			points.push({x: bb.x, y: bb.y + bb.height / 2}); // Left
			points.push({x: bb.x + bb.width / 2, y: bb.y}); // Top
			points.push({x: bb.x + bb.width, y: bb.y + bb.height / 2}); // Right
			points.push({x: bb.x + bb.width / 2, y: bb.y + bb.height}); // Bottom

			return points;
		};

		/**
		 * Calculate Dataflow's path as the minium between the two Elements
		 */
		this.calcPath = function() {
			var sP = getAttachPoints(source);
			var tP = getAttachPoints(target);
			var sIndex = 0; // Shortest point index for source
			var tIndex = 0; // Shortest point index for source
			var min; // Minimum length

			// Loop through all of the attach points for both Elements
			for (var i = 0; i < sP.length; i++)
			{
				for (var j = 0; j < tP.length; j++)
				{
					// Calculate vector's length (sqrt(a^2 + b^2))
					var dx = Math.abs(sP[i].x - tP[j].x);
					var dy = Math.abs(sP[i].y - tP[j].y);
					var length = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
					if (min)
					{
						// Check if new vector is minimum
						if (length < min)
						{
							sIndex = i;
							tIndex = j;
							min = length;
						}
					}
					else // No previous minimum existed
					{
						sIndex = i;
						tIndex = j;
						min = length;
					}
				}
			}
			var pathString = "M" + sP[sIndex].x + " " + sP[sIndex].y + " L" + tP[tIndex].x + " " + tP[tIndex].y + " Z";

			if (path)
			{
				// Path existed, update
				path.attr({path: pathString});
			}
			else
			{
				// Path did not exist, create
				path = paper.path(pathString);
			}
		};

		// Initially calculate the path
		this.calcPath();
	};
};<|endoftext|>


<commit_before>import {
  Scene,
  OrthographicCamera,
  WebGLRenderer,
  TextureLoader,
  MeshPhongMaterial,
  FlatShading,
  SpotLight,
  HemisphereLight,
  Matrix4,
  Mesh,
  Vector3,
} from "https://cdn.skypack.dev/three@0.138.2";
import { GLTFLoader } from "/js/GLTFLoader.js";

const ANIMATION_SPEED = 4;
const cameraX = -200;
const cameraY = 200;
const cameraZ = -200;

const isHoverSupport = matchMedia("(hover: hover").matches;
let catCenterXpos = window.innerWidth / 2;
let catCenterYpos = 280;
let mouseXpos = false;
let mouseYpos = false;
let isAnimating = false;
let startAngle = 0;
let currentAngle = 0;

const $container = document.getElementById("js-3dcat");

$container.classList.add("threeActive");
const w = $container.offsetWidth * 2;
const h = $container.offsetHeight * 2;
const scene = new Scene();
const camera = new OrthographicCamera(
  w / -2,
  w / 2,
  h / 2,
  h / -2,
  -1000,
  1000
);
const renderer = new WebGLRenderer({ alpha: true });
const textureLoader = new TextureLoader();
const loader = new GLTFLoader();

renderer.antialias = true;
renderer.setSize(w, h);
renderer.setClearColor(0x000000, 0);
$container.appendChild(renderer.domElement);

const textureBody = new MeshPhongMaterial({
  color: 0xcccccc,
  map: textureLoader.load("/images/baked-occlusion-body.jpg"),
  shininess: 1,
  shading: FlatShading,
});

const hemisphereLight = new HemisphereLight(0x404040, 0xfefefe, 2);
const spotLight = new SpotLight();
spotLight.intensity = 1.8;
spotLight.position.set(400, 2000, 40);

scene.add(hemisphereLight);
scene.add(spotLight);

const theBody = null;
const easeOutQuad = (t) => t * (2 - t);

const drawCatBody = (gltf) => {
  const bodyGeo = gltf.scene.children[0].geometry;
  bodyGeo.applyMatrix(new Matrix4().makeTranslation(0, 0, 1));
  const theBody = new Mesh(bodyGeo, textureBody);
  theBody.position.set(0, 0, 0);
  theBody.scale.set(140, 140, 140);
  theBody.rotation.set(0, (currentAngle * Math.PI) / 180, 0);
  scene.add(theBody);
};

loader.load("/geometry/3dcat-body.gltf", drawCatBody);

const updateCat = () => {
  if (!theBody || !mouseXpos) return;

  if (mouseYpos < catCenterYpos + 100) mouseYpos = catCenterYpos;
  const atan2 = Math.atan2(
    catCenterYpos - mouseYpos,
    catCenterXpos - mouseXpos
  );
  const targetAngle = (atan2 * -180) / Math.PI - 45;

  if (!isHoverSupport) {
    if (targetAngle === currentAngle) return;
    if (!isAnimating) startAngle = currentAngle;
    let updateAngle = currentAngle;

    if (isAnimating) {
      updateAngle =
        startAngle +
        easeOutQuad(
          Math.abs(currentAngle - startAngle) /
            Math.abs(targetAngle - startAngle)
        ) *
          (targetAngle - startAngle);
    }

    if (currentAngle < targetAngle) {
      currentAngle += ANIMATION_SPEED;
      isAnimating = true;
      if (targetAngle < currentAngle) {
        isAnimating = false;
        currentAngle = targetAngle;
      }
    }

    if (currentAngle > targetAngle) {
      currentAngle -= ANIMATION_SPEED;
      isAnimating = true;
      if (targetAngle > currentAngle) {
        isAnimating = false;
        currentAngle = targetAngle;
      }
    }
  }

  theBody.rotation.set(0, (targetAngle * Math.PI) / 180, 0);
};

document.addEventListener("mousemove", (e) => {
  mouseXpos = e.clientX;
  mouseYpos = e.clientY;
});

window.addEventListener("resize", () => {
  catCenterXpos = window.innerWidth / 2;
});

camera.position.set(cameraX + 1, cameraY + 1, cameraZ + 1);
camera.lookAt(new Vector3(cameraX, cameraY, cameraZ));
camera.updateProjectionMatrix();

function animate() {
  updateCat();
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}

animate();

// ---
// ---
//
// T = THREE
//
// unless new T.WebGLRenderer()
//   htmlEl = document.getElementsByTagName('html')[0]
//   htmlEl.classList.add 'is-no-webgl'
//   return
//

// # Lights
// hemisphereLight = new T.HemisphereLight 0x404040, 0xFEFEFE, 2
//
// spotLight = new T.SpotLight
// spotLight.intensity = 1.8
// spotLight.position.set 400, 2000, 40
//
// scene.add hemisphereLight
// scene.add spotLight
//
// # Geometry
// theBody = null
//
// easeOutQuad = (t) -> t * (2 - t)
//
// drawCatBody = (bodyGeo) ->
//   bodyGeo.applyMatrix new T.Matrix4().makeTranslation(0, 0, 1)
//   theBody = new T.Mesh bodyGeo, textureBody
//   theBody.position.set 0, 0, 0
//   theBody.scale.set 140, 140, 140
//   theBody.rotation.set 0, currentAngle * Math.PI / 180, 0
//   scene.add theBody
//
// JSONLoader.load '/geometry/3dcat-body.json', drawCatBody
//
// # Update cat on mouse move
// updateCat = ->
//   if theBody and mouseXpos isnt false
//     mouseYpos = catCenterYpos + 100 if mouseYpos < catCenterYpos + 100
//     atan2 = Math.atan2(catCenterYpos - mouseYpos, catCenterXpos - mouseXpos)
//     targetAngle =  (atan2 * -180 / Math.PI) - 45
//
//     # targetAngle = (180 * mouseXpos / window.innerWidth) - 45
//
//     if Modernizr.touchevents
//       return if targetAngle is currentAngle
//
//       startAngle = currentAngle unless isAnimating
//       updateAngle = currentAngle
//
//       if isAnimating
//         updateAngle = startAngle +
//         easeOutQuad(Math.abs(currentAngle - startAngle) /
//         Math.abs(targetAngle - startAngle)) *
//         (targetAngle - startAngle)
//
//       if currentAngle < targetAngle
//         currentAngle += ANIMATION_SPEED
//         isAnimating = yes
//         if targetAngle < currentAngle
//           isAnimating = no
//           currentAngle = targetAngle
//
//       if currentAngle > targetAngle
//         currentAngle -= ANIMATION_SPEED
//         isAnimating = yes
//         if targetAngle > currentAngle
//           isAnimating = no
//           currentAngle = targetAngle
//
//       theBody.rotation.set 0, updateAngle * Math.PI / 180, 0
//     else
//       theBody.rotation.set 0, targetAngle * Math.PI / 180, 0
//
// document.addEventListener 'mousemove', (e) ->
//   mouseXpos = e.clientX
//   mouseYpos = e.clientY
//
// window.addEventListener 'resize', ->
//   catCenterXpos = window.innerWidth / 2
//
// # Camera
// camera.position.set cameraX + 1, cameraY + 1, cameraZ + 1
// camera.lookAt new T.Vector3 cameraX, cameraY, cameraZ
// camera.updateProjectionMatrix()
//
// # Initate scene
// animate = ->
//   updateCat()
//   renderer.render scene, camera
//   requestAnimationFrame animate
//
// animate()
<commit_msg>`shading` has been deprecated, fix it<commit_after>import {
  Scene,
  OrthographicCamera,
  WebGLRenderer,
  TextureLoader,
  MeshPhongMaterial,
  SpotLight,
  HemisphereLight,
  Matrix4,
  Mesh,
  Vector3,
} from "https://cdn.skypack.dev/three@0.138.2";
import { GLTFLoader } from "/js/GLTFLoader.js";

const ANIMATION_SPEED = 4;
const cameraX = -200;
const cameraY = 200;
const cameraZ = -200;

const isHoverSupport = matchMedia("(hover: hover").matches;
let catCenterXpos = window.innerWidth / 2;
let catCenterYpos = 280;
let mouseXpos = false;
let mouseYpos = false;
let isAnimating = false;
let startAngle = 0;
let currentAngle = 0;

const $container = document.getElementById("js-3dcat");

$container.classList.add("threeActive");
const w = $container.offsetWidth * 2;
const h = $container.offsetHeight * 2;
const scene = new Scene();
const camera = new OrthographicCamera(
  w / -2,
  w / 2,
  h / 2,
  h / -2,
  -1000,
  1000
);
const renderer = new WebGLRenderer({ alpha: true });
const textureLoader = new TextureLoader();
const loader = new GLTFLoader();

renderer.antialias = true;
renderer.setSize(w, h);
renderer.setClearColor(0x000000, 0);
$container.appendChild(renderer.domElement);

const textureBody = new MeshPhongMaterial({
  color: 0xcccccc,
  map: textureLoader.load("/images/baked-occlusion-body.jpg"),
  shininess: 1,
  flatShading: true,
});

const hemisphereLight = new HemisphereLight(0x404040, 0xfefefe, 2);
const spotLight = new SpotLight();
spotLight.intensity = 1.8;
spotLight.position.set(400, 2000, 40);

scene.add(hemisphereLight);
scene.add(spotLight);

const theBody = null;
const easeOutQuad = (t) => t * (2 - t);

const drawCatBody = (gltf) => {
  const bodyGeo = gltf.scene.children[0].geometry;
  bodyGeo.applyMatrix4(new Matrix4().makeTranslation(0, 0, 1));
  theBody = new Mesh(bodyGeo, textureBody);
  theBody.position.set(0, 0, 0);
  theBody.scale.set(140, 140, 140);
  theBody.rotation.set(0, (currentAngle * Math.PI) / 180, 0);
  scene.add(theBody);
};

loader.load("/geometry/3dcat-body.gltf", drawCatBody);

const updateCat = () => {
  if (!theBody || !mouseXpos) return;

  if (mouseYpos < catCenterYpos + 100) mouseYpos = catCenterYpos;
  const atan2 = Math.atan2(
    catCenterYpos - mouseYpos,
    catCenterXpos - mouseXpos
  );
  const targetAngle = (atan2 * -180) / Math.PI - 45;

  if (!isHoverSupport) {
    if (targetAngle === currentAngle) return;
    if (!isAnimating) startAngle = currentAngle;
    let updateAngle = currentAngle;

    if (isAnimating) {
      updateAngle =
        startAngle +
        easeOutQuad(
          Math.abs(currentAngle - startAngle) /
            Math.abs(targetAngle - startAngle)
        ) *
          (targetAngle - startAngle);
    }

    if (currentAngle < targetAngle) {
      currentAngle += ANIMATION_SPEED;
      isAnimating = true;
      if (targetAngle < currentAngle) {
        isAnimating = false;
        currentAngle = targetAngle;
      }
    }

    if (currentAngle > targetAngle) {
      currentAngle -= ANIMATION_SPEED;
      isAnimating = true;
      if (targetAngle > currentAngle) {
        isAnimating = false;
        currentAngle = targetAngle;
      }
    }
  }

  theBody.rotation.set(0, (targetAngle * Math.PI) / 180, 0);
};

document.addEventListener("mousemove", (e) => {
  mouseXpos = e.clientX;
  mouseYpos = e.clientY;
});

window.addEventListener("resize", () => {
  catCenterXpos = window.innerWidth / 2;
});

camera.position.set(cameraX + 1, cameraY + 1, cameraZ + 1);
camera.lookAt(new Vector3(cameraX, cameraY, cameraZ));
camera.updateProjectionMatrix();

function animate() {
  updateCat();
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}

animate();

// ---
// ---
//
// T = THREE
//
// unless new T.WebGLRenderer()
//   htmlEl = document.getElementsByTagName('html')[0]
//   htmlEl.classList.add 'is-no-webgl'
//   return
//

// # Lights
// hemisphereLight = new T.HemisphereLight 0x404040, 0xFEFEFE, 2
//
// spotLight = new T.SpotLight
// spotLight.intensity = 1.8
// spotLight.position.set 400, 2000, 40
//
// scene.add hemisphereLight
// scene.add spotLight
//
// # Geometry
// theBody = null
//
// easeOutQuad = (t) -> t * (2 - t)
//
// drawCatBody = (bodyGeo) ->
//   bodyGeo.applyMatrix new T.Matrix4().makeTranslation(0, 0, 1)
//   theBody = new T.Mesh bodyGeo, textureBody
//   theBody.position.set 0, 0, 0
//   theBody.scale.set 140, 140, 140
//   theBody.rotation.set 0, currentAngle * Math.PI / 180, 0
//   scene.add theBody
//
// JSONLoader.load '/geometry/3dcat-body.json', drawCatBody
//
// # Update cat on mouse move
// updateCat = ->
//   if theBody and mouseXpos isnt false
//     mouseYpos = catCenterYpos + 100 if mouseYpos < catCenterYpos + 100
//     atan2 = Math.atan2(catCenterYpos - mouseYpos, catCenterXpos - mouseXpos)
//     targetAngle =  (atan2 * -180 / Math.PI) - 45
//
//     # targetAngle = (180 * mouseXpos / window.innerWidth) - 45
//
//     if Modernizr.touchevents
//       return if targetAngle is currentAngle
//
//       startAngle = currentAngle unless isAnimating
//       updateAngle = currentAngle
//
//       if isAnimating
//         updateAngle = startAngle +
//         easeOutQuad(Math.abs(currentAngle - startAngle) /
//         Math.abs(targetAngle - startAngle)) *
//         (targetAngle - startAngle)
//
//       if currentAngle < targetAngle
//         currentAngle += ANIMATION_SPEED
//         isAnimating = yes
//         if targetAngle < currentAngle
//           isAnimating = no
//           currentAngle = targetAngle
//
//       if currentAngle > targetAngle
//         currentAngle -= ANIMATION_SPEED
//         isAnimating = yes
//         if targetAngle > currentAngle
//           isAnimating = no
//           currentAngle = targetAngle
//
//       theBody.rotation.set 0, updateAngle * Math.PI / 180, 0
//     else
//       theBody.rotation.set 0, targetAngle * Math.PI / 180, 0
//
// document.addEventListener 'mousemove', (e) ->
//   mouseXpos = e.clientX
//   mouseYpos = e.clientY
//
// window.addEventListener 'resize', ->
//   catCenterXpos = window.innerWidth / 2
//
// # Camera
// camera.position.set cameraX + 1, cameraY + 1, cameraZ + 1
// camera.lookAt new T.Vector3 cameraX, cameraY, cameraZ
// camera.updateProjectionMatrix()
//
// # Initate scene
// animate = ->
//   updateCat()
//   renderer.render scene, camera
//   requestAnimationFrame animate
//
// animate()
<|endoftext|>


<commit_before>
var cpt = 1500 ;
var x ;
var pause=0;
var recommencer=0
function decompte()
{
     if(cpt>=0){
        if(pause==0)
        {
            if(cpt>60)
           {
        	var m=Math.trunc(cpt/60);
           	var s=(cpt % 60);
          	var min = " minutes et ";
            var sec = " secondes.";
            }
            else if(cpt==1){
                var m=0;
                var s=cpt%60;
                var min = " minute et ";
                var sec = " seconde.";
            }
            else {
            	var m=0;
            	var s=cpt%60;
            	var min = " minute et ";
                var sec = " secondes.";
            }
            document.getElementById("chrono").innerHTML = "
temps restant: " +m+min+s+sec +"
"
            document.getElementById("pause").innerHTML ="<button onclick='myFunction()'>Pause</button>";
            document.getElementById("recommencer").innerHTML = "<button onclick='myFunction1()'>Recommencer</button>";
            cpt-- ;
            x = setTimeout("decompte()",1000) ;
        }
		else if(recommencer==1){
			cpt=1500 ;
            x = setTimeout("decompte()",1000) ;
			document.getElementById("chrono").innerHTML = "
temps restant: " +m+min+s+sec +"
"
            document.getElementById("pause").innerHTML ="<button onclick='myFunction()'>Pause</button>";
            document.getElementById("recommencer").innerHTML = "<button onclick='myFunction1()'>Recommencer</button>";
			recommencer=0;
		}
		else{
            document.getElementById("pause").innerHTML = "";
            document.getElementById("continue").innerHTML = "<button onclick='myFunction()'>Continuez</button>";
            document.getElementById("recommencer").innerHTML = "<button onclick='myFunction1()'>Recommencer</button>";
            x = setTimeout("decompte()",1000) ;
        }

    }
    else
    {
        clearTimeout(x) ;
    }
}

function myFunction() {
    if(pause==0){
        pause=1;
    }else{
        pause=0;
    }
}
function myFunction1() {
    if(recommencer==0){
        recommencer=1;
    }else{
        recommencer=0;
    }
}
<commit_msg>Test 10<commit_after>
var cpt = 1500 ;
var x ;
var pause=0;

function decompte()
{
     if(cpt>=0){
        if(pause==0)
        {
            if(cpt>60)
           {
        	var m=Math.trunc(cpt/60);
           	var s=(cpt % 60);
          	var min = " minutes et ";
            var sec = " secondes.";
            }
            else if(cpt==1){
                var m=0;
                var s=cpt%60;
                var min = " minute et ";
                var sec = " seconde.";
            }
            else {
            	var m=0;
            	var s=cpt%60;
            	var min = " minute et ";
                var sec = " secondes.";
            }
            document.getElementById("chrono").innerHTML = "
temps restant: " +m+min+s+sec +"
"
            document.getElementById("pause").innerHTML ="<button onclick='myFunction()'>Pause</button>";
            document.getElementById("continue").innerHTML = "";
            cpt-- ;
            x = setTimeout("decompte()",1000) ;
        }else{
            document.getElementById("pause").innerHTML = "";
            document.getElementById("continue").innerHTML = "<button onclick='myFunction()'>continuez</button>";
            x = setTimeout("decompte()",1000) ;
        }

    }
    else
    {
        clearTimeout(x) ;
    }
}

function myFunction() {
    if(pause==0){
        pause=1;
    }else{
        pause=0;
    }
}
<|endoftext|>


<commit_before>$('#turn-crank').mousedown(function(event) {
  var clickval = parseInt(PLAYER_DATA['RESEARCHED']['powerful_cranks'][0] + 1);
  PLAYER_DATA['ENERGY'] += clickval;
  refreshInfo();

  var x = event.pageX;
  var y = event.pageY;
  var id = 'damagenum-' + (NUM_PARTICLES);
  var random_offset_x = getRandomInt(-5, 5);
  if (getRandomInt(0,200) == 77) {
    clickval = CLICK_PHRASES[getRandomInt(0, CLICK_PHRASES.length - 1)];
  }
  $("body").append('
+' + clickval + '
');
  NUM_PARTICLES += 1;
  PARTICLES.push(id);

  $('#'+id).fadeOut(1400);

  createParticle('bolt');
});

$('#request-order').click(function() {
  if (PLAYER_DATA['ENERGY'] > 500) {
    PLAYER_DATA['ENERGY'] -= 500;
    if (getRandomInt(0,1) == 0) {
      newOrder(true);
    } else {
      $.growl.warning({title: "Negotiations failed!", message: "You were unable to secure a new potion order from town."});
    }
  } else {
    $.growl.error({title: "Can't get new order.", message: "Not enough energy to perform that action."});
  }
  refreshInfo();
});

$('body').on('click', '.action-button', function() { //since the element is dynamic we can't listen to it directly.
    var action_name = $(this).data("action");
    performAction(action_name);
});
$('body').on('click', 'button.recipe-button', function() {
    var recipe = $(this).data("ingredients").split(',');
    combineIngredients(recipe[0], recipe[1]);
});
$('body').on('click', 'button.automate-recipe', function() {
    var recipe = $(this).data("ingredients")
    automateRecipe(recipe);
});
$('body').on('click', 'button.automate-action', function() {
    var action = $(this).data("action");
    automateAction(action);
});
$('body').on('click', 'div.researchable', function() {
    var action_name = $(this).data("type");
    doResearch(action_name);
});
$('body').on('click', '.removeaction', function() {
  removeActionAutomation($(this).data("action"));
});
$('body').on('click', '.removerecipe', function() {
  removeRecipeAutomation($(this).data("recipe"));
});
$("#enablegolems").change(function() {
    GOLEMS_ARE_ENABLED = this.checked;
});

$('#savegamebutton').click(function() {
  saveGame();
});
$('#exportsave').click(function() {
  saveGame();
  $('#save-export').val(localStorage.getItem("playerdata").replace(/(\r\n|\n|\r)/gm,""));
  $('#save-export').show();
  $.growl({title: "Save exported!", message: "Copy from the text area below."});
  $('#save-export').select();
});
$('#importsave').click(function() {
  var save_string = prompt('Save data?');
  if (save_string) {
    try {
      var decrypted_data = GibberishAES.dec(save_string, save_encryption_key);
      localStorage.setItem("playerdata", save_string);
      SHOULD_SAVE = false;
      location.reload();
    }
    catch(e) {
      alert('invalid save');
    }
  }
});

$('#deletesavedata').click(function() {
  if (confirm('Are you sure you want to delete all save data? This action cannot be undone.')) {
    deleteData();
  }
});

$('#closewelcomeback').click(function() {
  $('#welcomeback').fadeOut();
});
<commit_msg>never mind<commit_after>$('#turn-crank').mousedown(function(event) {
  var clickval = parseInt(PLAYER_DATA['RESEARCHED']['powerful_cranks'][0] + 1);
  PLAYER_DATA['ENERGY'] += clickval;
  refreshInfo();

  var x = event.pageX;
  var y = event.pageY;
  var id = 'damagenum-' + (NUM_PARTICLES);
  var random_offset_x = getRandomInt(-5, 5);
  if (getRandomInt(0,200) == 77) {
    clickval = CLICK_PHRASES[getRandomInt(0, CLICK_PHRASES.length - 1)];
  }
  $("body").append('
+' + clickval + '
');
  NUM_PARTICLES += 1;
  PARTICLES.push(id);

  $('#'+id).fadeOut(1400);

  createParticle('bolt');
});

$('#request-order').click(function() {
  if (PLAYER_DATA['ENERGY'] > 500) {
    PLAYER_DATA['ENERGY'] -= 500;
    if (getRandomInt(0,1) == 0) {
      newOrder(true);
    } else {
      $.growl.warning({title: "Negotiations failed!", message: "You were unable to secure a new potion order from town."});
    }
  } else {
    $.growl.error({title: "Can't get new order.", message: "Not enough energy to perform that action."});
  }
  refreshInfo();
});

$('body').on('click', '.action-button', function() { //since the element is dynamic we can't listen to it directly.
    var action_name = $(this).data("action");
    performAction(action_name);
});
$('body').on('click', 'button.recipe-button', function() {
    var recipe = $(this).data("ingredients").split(',');
    combineIngredients(recipe[0], recipe[1]);
});
$('body').on('click', 'button.automate-recipe', function() {
    var recipe = $(this).data("ingredients")
    automateRecipe(recipe);
});
$('body').on('click', 'button.automate-action', function() {
    var action = $(this).data("action");
    automateAction(action);
});
$('body').on('click', 'div.researchable', function() {
    var action_name = $(this).data("type");
    doResearch(action_name);
});
$('body').on('click', '.removeaction', function() {
  removeActionAutomation($(this).data("action"));
});
$('body').on('click', '.removerecipe', function() {
  removeRecipeAutomation($(this).data("recipe"));
});
$("#enablegolems").change(function() {
    GOLEMS_ARE_ENABLED = this.checked;
});

$('#savegamebutton').click(function() {
  saveGame();
});
$('#exportsave').click(function() {
  saveGame();
  $('#save-export').val(localStorage.getItem("playerdata").replace(/(\r\n|\n|\r)/gm,""));
  $('#save-export').show();
  $.growl({title: "Save exported!", message: "Copy from the text area below."});
  $('#save-export').select();
});
$('#importsave').click(function() {
  var save_string = prompt('Save data?');
  if (save_string) {
    try {
      var decrypted_data = GibberishAES.dec(save_string, save_encryption_key);
      localStorage.setItem("playerdata", save_string);
      SHOULD_SAVE = false;
      location.reload();
    }
    catch(e) {
      alert('invalid save');
    }
  }
});

$('#deletesavedata').click(function() {
  if (confirm('Are you sure you want to delete all save data? This action cannot be undone.')) {
    deleteData();
  }
});

$('#closewelcomeback').click(function() {
  $('#welcomeback').fadeOut();
});
<|endoftext|>


<commit_before>/* -*- mode: espresso; espresso-indent-level: 2; indent-tabs-mode: nil -*- */
/* vim: set softtabstop=2 shiftwidth=2 tabstop=2 expandtab: */

var ProjectStatistics = new function()
{
  var update_stats_fields = function(data) {
    $("#skeletons_created").text(data.skeletons_created);
    $("#treenodes_created").text(data.treenodes_created);
    $("#connectors_created").text(data.connectors_created);
  }

  var get_formated_entry = function(data) {
    var entry = '', points = 0;
    if( data.hasOwnProperty('new_treenodes') ) {
      entry += data['new_treenodes'] + ' /';
      points += data['new_treenodes'];
    } else {
      entry += '0 /';
    };
    if( data.hasOwnProperty('new_connectors') ) {
      entry += ' ' + data['new_connectors'] + ' /';
      points += data['new_connectors'];
    } else {
      entry += ' 0 /';
    };
    if( data.hasOwnProperty('new_reviewed_nodes') ) {
      entry += ' ' + data['new_reviewed_nodes'] + ' /';
      points += data['new_reviewed_nodes'];
    } else {
      entry += ' 0';
    };
    return {'entry': entry, 'points': points};
  }

  var update_user_history = function(data) {
    $('#project_stats_history_table').empty();
    var header = '';
    header += '
';
    header += 'username	';
    for(var i = 0; i < data['days'].length; i++ ) {
      header += ''+data['daysformatted'][i]+'	';
    }
    header += '
';
    $('#project_stats_history_table').append( header );
    for(var username in data['stats_table']) {
      var row = '', weekpointcount = 0;
      row += '
';
      if( data['stats_table'].hasOwnProperty( username ) ) {
        row += '' + username + '	';
        for(var i = 0; i < data['days'].length; i++ ) {
          var datekey = data['days'][i],
              formated = get_formated_entry( data['stats_table'][ username ][ datekey ] );
          row += ''+ formated['entry'] +'	';
          weekpointcount += formated['points'];
        }
      }
      row += '
';
      if( weekpointcount === 0 ) {
        continue;
      } else {
        $('#project_stats_history_table').append( row );
      }
    }
  }

  var update_piechart = function(data, chart_name) {
    $(chart_name).empty();
    var x = 90, y = 100, radius = 80, height = 200;
    // Create basic pie chart without any labels
    var rpie = Raphael(chart_name, '100%', height);
    var pie = rpie.piechart(x, y, radius, data.values, {
        colors: ['red', 'blue', 'green', 'yellow', 'orange', 'black', 'gray'],
    });

    /* Manually draw labels, because the legend can easily grow to large and
     * Raphael has no way of dealing with this properly. Therefore, name columns
     * are created manually if the list grows too long in Y. Of course, also the
     * X can be maxed out and this is the reason the SVG's width is set to 100%.
     */
    pie.labels = rpie.set();
    // Top left corner of the whole lefend
    var legend_x = x + radius + 16;
    var legend_y = y - radius;
    // The height of one legend line
    var line_height = 17;
    // Shifting is needed for making columns if the list grows to long
    var shift_x = 0, shift_y = 0;
    // The current maximum label lengths, can increase over time
    var max_label_width = 0;
    // Draw all labels, including a colored circle in front of it
    data.values.forEach(function(e, i, values) {
      var circ_r = 5;
      var text_indent = 2 * circ_r + 10;
      var l_x = legend_x + shift_x;
      var l_y = legend_y + i * line_height + shift_y;
      // Make name columns if names don't fit under below each other
      if (l_y + line_height > y + radius) {
        shift_x = shift_x + max_label_width + text_indent + 16;
        shift_y = shift_y - (i + 1) * line_height;
      }

      var color = pie.series[i].attrs.fill;
      // Draw leading color circle
      var circle = rpie.circle(l_x + circ_r, l_y + circ_r, circ_r)
          .attr({
              'stroke': color,
              'fill': color,
          });
      // Draw label
      var text = rpie.text(l_x + 2 * circ_r + 10, l_y + circ_r, data.users[i])
          .attr({
              'text-anchor': 'start',
              'font': '12px Arial, sans-serif',
          });
      // Find maximum text width
      var bb = text.getBBox();
      if (bb.width > max_label_width) {
          max_label_width = bb.width;
      }
      // Remember label
      pie.labels.push(rpie.set());
      pie.labels[i].push(circle);
      pie.labels[i].push(text);
    });

    // Add hover functionality
    var current_label = null;
    pie.hover(
      function () {
        // Scale everything up
        this.sector.stop();
        this.sector.scale(1.1, 1.1, this.cx, this.cy);
        if (this.label) {
          this.label[0].stop();
          this.label[0].attr({ r: 7.5 });
          this.label[1].attr({"font-weight": 800});
        }
      },
      function () {
        // Scale everything back to normal
        this.sector.animate({ transform: "s1 1 " + this.cx + " " + this.cy }, 500, "bounce");
        if (this.label) {
          this.label[0].animate({ r: 5 }, 500, "bounce");
          this.label[1].attr({"font-weight": 400});
        }
      });
  };

  var parseDate = function(d) {
    var d_s = d.toString();
    var year = d_s.substring(0, 4);
    var month = d_s.substring(4, 6);
    var day = d_s.substring(6, 8);
    return new Date(year + '-' + month + '-' + day);
  };

  Date.prototype.addDays = function(days){
    var msPerDay = 1000 * 60 * 60 * 24;
    var ms = this.getTime() + (msPerDay * days);
    var added = new Date(ms);
    return added;
  }

  var update_linegraph = function(data) {
    $("#linechart_treenode_holder").empty();

    var w = 600, h = 400, wPadding = 40, hPadding = 65;
    var legendW = 100;
    var endDate = new Date();
    var startDate = endDate.addDays(-30);
    var msPerDay = 1000 * 60 * 60 * 24;

    // Create zero-filled arrays for each user spanning the full date range.
    var dateRange = (endDate - startDate) / msPerDay;
    var counts = {};
    for (d in data) {
      date = parseDate(data[d].date);
      name = data[d].name;
      dayIndex = Math.round((date - startDate) / msPerDay);
      if (!(name in counts)) {
        counts[name] = [];
        for (var i = 0; i < dateRange; i++) {
          counts[name][i] = 0;
        }
      }
      counts[name][dayIndex] = data[d].count;
    }

    // Set up scales to map dates and counts to pixels.
    var dateScale = d3.time.scale()
      .domain([startDate, endDate])
      .range([wPadding, w - wPadding]);
    var countScale = d3.scale.linear()
      .domain([0, d3.max(data.map(function(x) { return x.count; }))])
      .range([h - hPadding, hPadding]);

    // Create a generator that will produce the x,y points defining each line given a count and a day index.
    var lineGen = d3.svg.line()
      .x(function(d, i) { return dateScale(startDate.addDays(i)); })
      .y(countScale);

    // Create the chart
    var chart = d3.select("div#linechart_treenode_holder").append("svg")
      .attr("class", "chart")
      .attr("width", w)
      .attr("height", h);

    // Add a line for each user.
    var lineGroup = chart.append("g")
      .attr("class", "linegroup");
    var colors = ['red', 'blue', 'green', 'yellow', 'orange', 'black', 'gray'];
    var i = 0;
    for (name in counts) {
      lineGroup.append("svg:path")
        .attr("d", lineGen(counts[name]))
        .attr("stroke", colors[i]);
      i = i + 1;
    }

    // Add the x axis.
    var xAxis = d3.svg.axis()
      .scale(dateScale)
      .orient("bottom");
    var xAxisGroup = chart.append("g")
      .attr("class", "axis")
        .attr("transform", "translate(0, " + (h - hPadding) + ")")
      .call(xAxis);
    xAxisGroup.selectAll("text")
      .attr("text-anchor", "start")
      .attr("transform", function(d) {return "rotate(90) translate(8, -" + this.getBBox().height + ")";});

    // Add the y axis.
    var yAxis = d3.svg.axis()
      .scale(countScale)
      .orient("right");
    chart.append("g")
      .attr("class", "axis")
        .attr("transform", "translate(" + (w - wPadding) + ", 0)")
      .call(yAxis);

    // Add the y-axis label.
    chart.append("text")
      .attr("class", "y-label")
      .attr("text-anchor", "middle")
      .attr("x", 0)
      .attr("y", 0)
      .attr("transform", "translate(" + (w - 12) + ", " + (h / 2) + ") rotate(90)")
      .text("Nodes edited");
  }

  var refresh_project_statistics = function() {
    // requestQueue.register(django_url + project.id + '/stats-summary', "GET", {
    // }, function (status, text, xml) {
    //   if (status == 200) {
    //     if (text && text != " ") {
    //       var jso = $.parseJSON(text);
    //       if (jso.error) {
    //         alert(jso.error);
    //       }
    //       else {
    //         update_stats_fields(jso);
    //       }
    //     }
    //   }
    //   return true;
    // });

    requestQueue.register(django_url + project.id + '/stats', "GET", {
    }, function (status, text, xml) {
      if (status == 200) {
        if (text && text != " ") {
          var jso = $.parseJSON(text);
          if (jso.error) {
            alert(jso.error);
          } else {
            update_piechart(jso, "piechart_treenode_holder");
          }
        }
      }
      return true;
    });

    // requestQueue.register(django_url + project.id + '/stats-editor', "GET",{
    // }, function (status, text, xml) {
    //   if (status == 200) {
    //     if (text && text != " ") {
    //       var jso = $.parseJSON(text);
    //       if (jso.error) {
    //         alert(jso.error);
    //       } else {
    //         update_piechart(jso, "piechart_editor_holder");
    //       }
    //     }
    //   }
    //   return true;
    // });

    requestQueue.register(django_url + project.id + '/stats-reviewer', "GET", {
    }, function (status, text, xml) {
      if (status == 200) {
        if (text && text != " ") {
          var jso = $.parseJSON(text);
          if (jso.error) {
            alert(jso.error);
          } else {
            update_piechart(jso, "piechart_reviewer_holder");
          }
        }
      }
      return true;
    });

    requestQueue.register(django_url + project.id + '/stats-user-history', "POST", {
      "pid": project.id
    }, function (status, text, xml) {
      if (status == 200) {
        if (text && text != " ") {
          var jso = $.parseJSON(text);
          if (jso.error) {
            alert(jso.error);
          } else {
            update_user_history(jso);
          }
        }
      }
      return true;
    });

    // d3.json(django_url + project.id + '/stats-history', update_linegraph);
  }

  this.init = function () {

    $('#project_stats_widget').load( django_url + project.id + '/statisticswidget' )

    refresh_project_statistics();

  };
};
<commit_msg>Tracing tool's project statistics: use TH tags for table header<commit_after>/* -*- mode: espresso; espresso-indent-level: 2; indent-tabs-mode: nil -*- */
/* vim: set softtabstop=2 shiftwidth=2 tabstop=2 expandtab: */

var ProjectStatistics = new function()
{
  var update_stats_fields = function(data) {
    $("#skeletons_created").text(data.skeletons_created);
    $("#treenodes_created").text(data.treenodes_created);
    $("#connectors_created").text(data.connectors_created);
  }

  var get_formated_entry = function(data) {
    var entry = '', points = 0;
    if( data.hasOwnProperty('new_treenodes') ) {
      entry += data['new_treenodes'] + ' /';
      points += data['new_treenodes'];
    } else {
      entry += '0 /';
    };
    if( data.hasOwnProperty('new_connectors') ) {
      entry += ' ' + data['new_connectors'] + ' /';
      points += data['new_connectors'];
    } else {
      entry += ' 0 /';
    };
    if( data.hasOwnProperty('new_reviewed_nodes') ) {
      entry += ' ' + data['new_reviewed_nodes'] + ' /';
      points += data['new_reviewed_nodes'];
    } else {
      entry += ' 0';
    };
    return {'entry': entry, 'points': points};
  }

  var update_user_history = function(data) {
    $('#project_stats_history_table').empty();
    var header = '';
    header += '
';
    header += 'username	';
    for(var i = 0; i < data['days'].length; i++ ) {
      header += ''+data['daysformatted'][i]+'	';
    }
    header += '
';
    $('#project_stats_history_table').append( header );
    for(var username in data['stats_table']) {
      var row = '', weekpointcount = 0;
      row += '
';
      if( data['stats_table'].hasOwnProperty( username ) ) {
        row += '' + username + '	';
        for(var i = 0; i < data['days'].length; i++ ) {
          var datekey = data['days'][i],
              formated = get_formated_entry( data['stats_table'][ username ][ datekey ] );
          row += ''+ formated['entry'] +'	';
          weekpointcount += formated['points'];
        }
      }
      row += '
';
      if( weekpointcount === 0 ) {
        continue;
      } else {
        $('#project_stats_history_table').append( row );
      }
    }
  }

  var update_piechart = function(data, chart_name) {
    $(chart_name).empty();
    var x = 90, y = 100, radius = 80, height = 200;
    // Create basic pie chart without any labels
    var rpie = Raphael(chart_name, '100%', height);
    var pie = rpie.piechart(x, y, radius, data.values, {
        colors: ['red', 'blue', 'green', 'yellow', 'orange', 'black', 'gray'],
    });

    /* Manually draw labels, because the legend can easily grow to large and
     * Raphael has no way of dealing with this properly. Therefore, name columns
     * are created manually if the list grows too long in Y. Of course, also the
     * X can be maxed out and this is the reason the SVG's width is set to 100%.
     */
    pie.labels = rpie.set();
    // Top left corner of the whole lefend
    var legend_x = x + radius + 16;
    var legend_y = y - radius;
    // The height of one legend line
    var line_height = 17;
    // Shifting is needed for making columns if the list grows to long
    var shift_x = 0, shift_y = 0;
    // The current maximum label lengths, can increase over time
    var max_label_width = 0;
    // Draw all labels, including a colored circle in front of it
    data.values.forEach(function(e, i, values) {
      var circ_r = 5;
      var text_indent = 2 * circ_r + 10;
      var l_x = legend_x + shift_x;
      var l_y = legend_y + i * line_height + shift_y;
      // Make name columns if names don't fit under below each other
      if (l_y + line_height > y + radius) {
        shift_x = shift_x + max_label_width + text_indent + 16;
        shift_y = shift_y - (i + 1) * line_height;
      }

      var color = pie.series[i].attrs.fill;
      // Draw leading color circle
      var circle = rpie.circle(l_x + circ_r, l_y + circ_r, circ_r)
          .attr({
              'stroke': color,
              'fill': color,
          });
      // Draw label
      var text = rpie.text(l_x + 2 * circ_r + 10, l_y + circ_r, data.users[i])
          .attr({
              'text-anchor': 'start',
              'font': '12px Arial, sans-serif',
          });
      // Find maximum text width
      var bb = text.getBBox();
      if (bb.width > max_label_width) {
          max_label_width = bb.width;
      }
      // Remember label
      pie.labels.push(rpie.set());
      pie.labels[i].push(circle);
      pie.labels[i].push(text);
    });

    // Add hover functionality
    var current_label = null;
    pie.hover(
      function () {
        // Scale everything up
        this.sector.stop();
        this.sector.scale(1.1, 1.1, this.cx, this.cy);
        if (this.label) {
          this.label[0].stop();
          this.label[0].attr({ r: 7.5 });
          this.label[1].attr({"font-weight": 800});
        }
      },
      function () {
        // Scale everything back to normal
        this.sector.animate({ transform: "s1 1 " + this.cx + " " + this.cy }, 500, "bounce");
        if (this.label) {
          this.label[0].animate({ r: 5 }, 500, "bounce");
          this.label[1].attr({"font-weight": 400});
        }
      });
  };

  var parseDate = function(d) {
    var d_s = d.toString();
    var year = d_s.substring(0, 4);
    var month = d_s.substring(4, 6);
    var day = d_s.substring(6, 8);
    return new Date(year + '-' + month + '-' + day);
  };

  Date.prototype.addDays = function(days){
    var msPerDay = 1000 * 60 * 60 * 24;
    var ms = this.getTime() + (msPerDay * days);
    var added = new Date(ms);
    return added;
  }

  var update_linegraph = function(data) {
    $("#linechart_treenode_holder").empty();

    var w = 600, h = 400, wPadding = 40, hPadding = 65;
    var legendW = 100;
    var endDate = new Date();
    var startDate = endDate.addDays(-30);
    var msPerDay = 1000 * 60 * 60 * 24;

    // Create zero-filled arrays for each user spanning the full date range.
    var dateRange = (endDate - startDate) / msPerDay;
    var counts = {};
    for (d in data) {
      date = parseDate(data[d].date);
      name = data[d].name;
      dayIndex = Math.round((date - startDate) / msPerDay);
      if (!(name in counts)) {
        counts[name] = [];
        for (var i = 0; i < dateRange; i++) {
          counts[name][i] = 0;
        }
      }
      counts[name][dayIndex] = data[d].count;
    }

    // Set up scales to map dates and counts to pixels.
    var dateScale = d3.time.scale()
      .domain([startDate, endDate])
      .range([wPadding, w - wPadding]);
    var countScale = d3.scale.linear()
      .domain([0, d3.max(data.map(function(x) { return x.count; }))])
      .range([h - hPadding, hPadding]);

    // Create a generator that will produce the x,y points defining each line given a count and a day index.
    var lineGen = d3.svg.line()
      .x(function(d, i) { return dateScale(startDate.addDays(i)); })
      .y(countScale);

    // Create the chart
    var chart = d3.select("div#linechart_treenode_holder").append("svg")
      .attr("class", "chart")
      .attr("width", w)
      .attr("height", h);

    // Add a line for each user.
    var lineGroup = chart.append("g")
      .attr("class", "linegroup");
    var colors = ['red', 'blue', 'green', 'yellow', 'orange', 'black', 'gray'];
    var i = 0;
    for (name in counts) {
      lineGroup.append("svg:path")
        .attr("d", lineGen(counts[name]))
        .attr("stroke", colors[i]);
      i = i + 1;
    }

    // Add the x axis.
    var xAxis = d3.svg.axis()
      .scale(dateScale)
      .orient("bottom");
    var xAxisGroup = chart.append("g")
      .attr("class", "axis")
        .attr("transform", "translate(0, " + (h - hPadding) + ")")
      .call(xAxis);
    xAxisGroup.selectAll("text")
      .attr("text-anchor", "start")
      .attr("transform", function(d) {return "rotate(90) translate(8, -" + this.getBBox().height + ")";});

    // Add the y axis.
    var yAxis = d3.svg.axis()
      .scale(countScale)
      .orient("right");
    chart.append("g")
      .attr("class", "axis")
        .attr("transform", "translate(" + (w - wPadding) + ", 0)")
      .call(yAxis);

    // Add the y-axis label.
    chart.append("text")
      .attr("class", "y-label")
      .attr("text-anchor", "middle")
      .attr("x", 0)
      .attr("y", 0)
      .attr("transform", "translate(" + (w - 12) + ", " + (h / 2) + ") rotate(90)")
      .text("Nodes edited");
  }

  var refresh_project_statistics = function() {
    // requestQueue.register(django_url + project.id + '/stats-summary', "GET", {
    // }, function (status, text, xml) {
    //   if (status == 200) {
    //     if (text && text != " ") {
    //       var jso = $.parseJSON(text);
    //       if (jso.error) {
    //         alert(jso.error);
    //       }
    //       else {
    //         update_stats_fields(jso);
    //       }
    //     }
    //   }
    //   return true;
    // });

    requestQueue.register(django_url + project.id + '/stats', "GET", {
    }, function (status, text, xml) {
      if (status == 200) {
        if (text && text != " ") {
          var jso = $.parseJSON(text);
          if (jso.error) {
            alert(jso.error);
          } else {
            update_piechart(jso, "piechart_treenode_holder");
          }
        }
      }
      return true;
    });

    // requestQueue.register(django_url + project.id + '/stats-editor', "GET",{
    // }, function (status, text, xml) {
    //   if (status == 200) {
    //     if (text && text != " ") {
    //       var jso = $.parseJSON(text);
    //       if (jso.error) {
    //         alert(jso.error);
    //       } else {
    //         update_piechart(jso, "piechart_editor_holder");
    //       }
    //     }
    //   }
    //   return true;
    // });

    requestQueue.register(django_url + project.id + '/stats-reviewer', "GET", {
    }, function (status, text, xml) {
      if (status == 200) {
        if (text && text != " ") {
          var jso = $.parseJSON(text);
          if (jso.error) {
            alert(jso.error);
          } else {
            update_piechart(jso, "piechart_reviewer_holder");
          }
        }
      }
      return true;
    });

    requestQueue.register(django_url + project.id + '/stats-user-history', "POST", {
      "pid": project.id
    }, function (status, text, xml) {
      if (status == 200) {
        if (text && text != " ") {
          var jso = $.parseJSON(text);
          if (jso.error) {
            alert(jso.error);
          } else {
            update_user_history(jso);
          }
        }
      }
      return true;
    });

    // d3.json(django_url + project.id + '/stats-history', update_linegraph);
  }

  this.init = function () {

    $('#project_stats_widget').load( django_url + project.id + '/statisticswidget' )

    refresh_project_statistics();

  };
};
<|endoftext|>


<commit_before>/******
 * This file is used to describe the interface that is used to interact with plugins for the exporter.
 * Included is information about what each method does and what callbacks are expected.
 *
 * Each method receives an option object where all configuration and stats (once set) are available.
 * Each method also receives a callback method that should be called whenever an operation is complete.
 * The only case when no callback should be called is when an error occurred and instead the program
 * should terminate.
 */

'use strict';

var Driver = require('../drivers/driver.interface');

class MockDriver extends Driver {
    constructor() {
        super();
        this.reset();
    }

    reset(env, callback) {
        this.info = {
            id: 'mock',
            name: 'Mock Driver',
            version: '1.0',
            desciption: 'A mock implementation for testing purposes'
        };

        this.options = {
            source: {},
            target: {}
        };

        this.targetStats = {
            version: "1.0.0",
            cluster_status: "Green"
        };

        this.sourceStats = {
            version: "1.0.0",
            cluster_status: "Green",
            docs: {
                total: 1
            }
        };

        this.metadata = {
            mappings: {},
            settings: {}
        };

        this.data = [];
        if (callback) {
            callback();
        }
    }

    getInfo(callback) {
        callback(null, this.info, this.options);
    }

    verifyOptions(opts, callback) {
        callback();
    }

    getTargetStats(env, callback) {
        callback(null, this.targetStats);
    }

    getSourceStats(env, callback) {
        callback(null, this.sourceStats);
    }

    getMeta(env, callback) {
        callback(null, this.metadata);
    }

    putMeta(env, metadata, callback) {
        this.metadata = metadata;
        callback();
    }

    getData(env, callback, from, size) {
        callback(null, this.data);
    }

    putData(env, data, callback) {
        this.data = data;
        callback();
    }

    /**
     *
     * @param {DriverInfo} info
     */
    setInfo(info) {
        this.info = info;
    }

    /**
     *
     * @param threadsafe
     * @returns {DriverInfo}
     */
    getInfoSync(threadsafe) {
        this.info.threadsafe = threadsafe;
        return this.info;
    }

    /**
     *
     * @param {Object.<string, OptionDef>} options
     */
    setOptions(options) {
        this.options = options;
    }

    /**
     *
     * @returns {Object.<string, OptionDef>}
     */
    getOptionsSync() {
        return this.options;
    }

    /**
     *
     * @param {TargetInfo} targetStats
     */
    setTargetStats(targetStats) {
        this.targetStats = targetStats;
    }

    /**
     *
     * @param {SourceInfo} sourceStats
     */
    setSourceStats(sourceStats) {
        this.sourceStats = sourceStats;
    }

    /**
     *
     * @param {Data} hit
     */
    addhit(hit) {
        this.data.push(hit);
    }

    /**
     *
     * @param {Data[]} hits
     */
    addhits(hits) {
        this.data.concat(hits);
    }
}

module.exports = Driver;
module.exports.getDriver = () => new Driver();
<commit_msg>Fixed MockDriver instance<commit_after>/******
 * This file is used to describe the interface that is used to interact with plugins for the exporter.
 * Included is information about what each method does and what callbacks are expected.
 *
 * Each method receives an option object where all configuration and stats (once set) are available.
 * Each method also receives a callback method that should be called whenever an operation is complete.
 * The only case when no callback should be called is when an error occurred and instead the program
 * should terminate.
 */

'use strict';

var Driver = require('../drivers/driver.interface');

class MockDriver extends Driver {
    constructor() {
        super();
        this.reset();
    }

    reset(env, callback) {
        this.info = {
            id: 'mock',
            name: 'Mock Driver',
            version: '1.0',
            desciption: 'A mock implementation for testing purposes'
        };

        this.options = {
            source: {},
            target: {}
        };

        this.targetStats = {
            version: "1.0.0",
            cluster_status: "Green"
        };

        this.sourceStats = {
            version: "1.0.0",
            cluster_status: "Green",
            docs: {
                total: 1
            }
        };

        this.metadata = {
            mappings: {},
            settings: {}
        };

        this.data = [];
        if (callback) {
            callback();
        }
    }

    getInfo(callback) {
        callback(null, this.info, this.options);
    }

    verifyOptions(opts, callback) {
        callback();
    }

    getTargetStats(env, callback) {
        callback(null, this.targetStats);
    }

    getSourceStats(env, callback) {
        callback(null, this.sourceStats);
    }

    getMeta(env, callback) {
        callback(null, this.metadata);
    }

    putMeta(env, metadata, callback) {
        this.metadata = metadata;
        callback();
    }

    getData(env, callback, from, size) {
        callback(null, this.data);
    }

    putData(env, data, callback) {
        this.data = data;
        callback();
    }

    /**
     *
     * @param {DriverInfo} info
     */
    setInfo(info) {
        this.info = info;
    }

    /**
     *
     * @param threadsafe
     * @returns {DriverInfo}
     */
    getInfoSync(threadsafe) {
        this.info.threadsafe = threadsafe;
        return this.info;
    }

    /**
     *
     * @param {Object.<string, OptionDef>} options
     */
    setOptions(options) {
        this.options = options;
    }

    /**
     *
     * @returns {Object.<string, OptionDef>}
     */
    getOptionsSync() {
        return this.options;
    }

    /**
     *
     * @param {TargetInfo} targetStats
     */
    setTargetStats(targetStats) {
        this.targetStats = targetStats;
    }

    /**
     *
     * @param {SourceInfo} sourceStats
     */
    setSourceStats(sourceStats) {
        this.sourceStats = sourceStats;
    }

    /**
     *
     * @param {Data} hit
     */
    addhit(hit) {
        this.data.push(hit);
    }

    /**
     *
     * @param {Data[]} hits
     */
    addhits(hits) {
        this.data.concat(hits);
    }
}

module.exports = MockDriver;
module.exports.getDriver = () => new MockDriver();
<|endoftext|>


<commit_before>import Base from '../Base/'
import { pluckNumber } from '../lib/'
import defs from '../defs/'
import Controls from '../Controls//'
import Renderer from '../Renderer/'
import Blocks from '../Blocks'

class BlocksGallery extends Base {
  /**
     * Constructor for BlocksGallery
     * @param {string} elementId Id of dom element
     * @param {Object} config Configuration object for BlocksGallery
     * @example
     * new BlocksGallery('my-gallery-2', {
     *   height: 500,
     *   width: 400,
     *   controls: true,
     *   src: [{
     *     image: 'path/to/image',
     *     title: 'title string',
     *     text: 'text string'
     *   }]
     * })
     */
  constructor(elementId, rawConfig) {
    // Making the environment variable
    // Every instance will have its own environment
    // object
    super({
      isEnvVariable: true,
    })
    /* Parse input configuration */
    const rootEl = document.getElementById(elementId)
    const config = Object.assign({}, rawConfig)
    const height = pluckNumber(config.height, defs.height)
    const width = pluckNumber(config.width, defs.width)
    config.height = height
    config.width = width

    /* Throw error if element doesnt exists */
    if (!rootEl) {
      throw Error(`Element with id ${elementId} not found`)
    }
    /* Applying height and width */
    rootEl.style.height = height
    rootEl.style.width = width

    /* Save the root element */
    this.addToEnv('root', rootEl)
    /* Saving config */
    this.addToEnv('config', config)
    /* Creating controllers for gallery */
    this.addToEnv('controls', new Controls(this))
    /* Creating handler to render the images */
    const renderer = new Renderer(this)
    this.addToEnv('renderer', renderer)
    /* Creating handler to manage the grids */
    this.addToEnv('blocks', new Blocks(this, renderer))
  }
  /**
   * Show next image in list
   */
  next() {
    this.getFromEnv('renderer').next()
  }
  /**
   * Show previous image in list
   */
  previous() {
    this.getFromEnv('renderer').previous()
  }
}

/* Export the BlocksGallery class */
export default BlocksGallery
<commit_msg>Using store for components in gallery<commit_after>import Base from '../Base/'
import { pluckNumber } from '../lib/'
import defs from '../defs/'
import Controls from '../Controls//'
import Renderer from '../Renderer/'
import Blocks from '../Blocks'

class BlocksGallery extends Base {
  /**
     * Constructor for BlocksGallery
     * @param {string} elementId Id of dom element
     * @param {Object} config Configuration object for BlocksGallery
     * @example
     * new BlocksGallery('my-gallery-2', {
     *   height: 500,
     *   width: 400,
     *   controls: true,
     *   src: [{
     *     image: 'path/to/image',
     *     title: 'title string',
     *     text: 'text string'
     *   }]
     * })
     */
  constructor(elementId, rawConfig) {
    // Making the environment variable
    // Every instance will have its own environment
    // object
    super({
      isEnvVariable: true,
    })
    /* Parse input configuration */
    const rootEl = document.getElementById(elementId)
    const config = Object.assign({}, rawConfig)
    const height = pluckNumber(config.height, defs.height)
    const width = pluckNumber(config.width, defs.width)
    config.height = height
    config.width = width

    /* Throw error if element doesnt exists */
    if (!rootEl) {
      throw Error(`Element with id ${elementId} not found`)
    }
    /* Applying height and width */
    rootEl.style.height = height
    rootEl.style.width = width

    /* Save the root element */
    this.addToEnv('root', rootEl)
    /* Saving config */
    this.addToEnv('config', config)
    /* Creating controllers for gallery */
    this.addToStore('controls', new Controls(this))
    /* Creating handler to render the images */
    const renderer = new Renderer(this)
    this.addToStore('renderer', renderer)
    /* Creating handler to manage the grids */
    this.addToStore('blocks', new Blocks(this, renderer))
  }
  /**
   * Show next image in list
   */
  next() {
    this.getFromStore('renderer').next()
  }
  /**
   * Show previous image in list
   */
  previous() {
    this.getFromStore('renderer').previous()
  }
}

/* Export the BlocksGallery class */
export default BlocksGallery
<|endoftext|>