<commit_before>var renderer = null;
var scene = null;
var camera = null;
var clock = new THREE.Clock();
var controls = null;
var projector = null;
var highlight = null;
var targetList = [];
var brickTexture = null;
var woodTexture = null;
var floorTexture = null;

function makeTextSprite( message, parameters )
{
	if ( parameters === undefined ) parameters = {};

	var fontface = parameters.hasOwnProperty("fontface") ?
		parameters["fontface"] : "Arial";

	var fontsize = parameters.hasOwnProperty("fontsize") ?
		parameters["fontsize"] : 18;

	var borderThickness = parameters.hasOwnProperty("borderThickness") ?
		parameters["borderThickness"] : 4;

	var borderColor = parameters.hasOwnProperty("borderColor") ?
		parameters["borderColor"] : { r:0, g:0, b:0, a:1.0 };

	var backgroundColor = parameters.hasOwnProperty("backgroundColor") ?
		parameters["backgroundColor"] : { r:255, g:255, b:255, a:1.0 };

	var canvas = document.createElement('canvas');
	var context = canvas.getContext('2d');
	context.font = "Bold " + fontsize + "px " + fontface;

	// get size data (height depends only on font size)
	var metrics = context.measureText( message );
	var textWidth = metrics.width;

	// background color
	context.fillStyle   = "rgba(" + backgroundColor.r + "," + backgroundColor.g + ","
								  + backgroundColor.b + "," + backgroundColor.a + ")";
	// border color
	context.strokeStyle = "rgba(" + borderColor.r + "," + borderColor.g + ","
								  + borderColor.b + "," + borderColor.a + ")";

	context.lineWidth = borderThickness;
	roundRect(context, borderThickness/2, borderThickness/2, textWidth + borderThickness, fontsize * 1.4 + borderThickness, 6);
	// 1.4 is extra height factor for text below baseline: g,j,p,q.

	// text color
	context.fillStyle = "rgba(0, 0, 0, 1.0)";

	context.fillText( message, borderThickness, fontsize + borderThickness);

	// canvas contents will be used for a texture
	var texture = new THREE.Texture(canvas)
	texture.needsUpdate = true;

	var spriteMaterial = new THREE.SpriteMaterial(
		{ map: texture } );
	var sprite = new THREE.Sprite( spriteMaterial );
	sprite.scale.set(2,2,2);
	return sprite;
}

// function for drawing rounded rectangles
function roundRect(ctx, x, y, w, h, r)
{
    ctx.beginPath();
    ctx.moveTo(x+r, y);
    ctx.lineTo(x+w-r, y);
    ctx.quadraticCurveTo(x+w, y, x+w, y+r);
    ctx.lineTo(x+w, y+h-r);
    ctx.quadraticCurveTo(x+w, y+h, x+w-r, y+h);
    ctx.lineTo(x+r, y+h);
    ctx.quadraticCurveTo(x, y+h, x, y+h-r);
    ctx.lineTo(x, y+r);
    ctx.quadraticCurveTo(x, y, x+r, y);
    ctx.closePath();
    ctx.fill();
	ctx.stroke();
}

// for a given app, create a model and add it to the scene
// visualize information about the app using sizes, colors, textures, and placement
function addAppModelToScene(inApp) {
	var scaledRatings = Math.min(4.0, 0.5 + inApp.ratings.count / 100.0);

	var material = new THREE.MeshBasicMaterial( {
		map: inApp.is_packaged? brickTexture : woodTexture,
		color: {r: 0.9, g: 0.9, b: 0.9} } );

	var geometry = new THREE.BoxGeometry(1, 1, 1);
	inApp.cube = new THREE.Mesh( geometry, material );
	inApp.cube.scale.x = scaledRatings;
	inApp.cube.scale.y = scaledRatings;
	inApp.cube.scale.z = scaledRatings;

	inApp.cube.position.x = Math.random() * 100 - 50;
	inApp.cube.position.y = scaledRatings / 2;
	inApp.cube.position.z = Math.random() * 100 - 50;
	inApp.cube.app = inApp;

	scene.add(inApp.cube);
	targetList.push(inApp.cube);

	// make different colors for privileged and not privileged apps
	inApp.label = makeTextSprite(
			inApp.slug,
			{
				fontsize: 24,
				borderColor: inApp.app_type == 'privileged' ? {r:0, g:255, b:0, a:1.0} : {r:255, g:0, b:0, a:1.0},
				backgroundColor: inApp.app_type == 'privileged' ? {r:100, g:255, b:100, a:0.8} : {r:255, g:100, b:100, a:0.8}
			});

	inApp.label.position.set(inApp.cube.position.x, inApp.cube.position.y + scaledRatings / 2, inApp.cube.position.z);
	inApp.label.visible = false;

	scene.add(inApp.label);
}

function createVRScene(inView) {
	console.log('createVRScene');
	var model = inView.get('model');

	scene = new THREE.Scene();
	camera = new THREE.PerspectiveCamera(75, 800 / 600, 0.1, 1000);
	renderer = new THREE.WebGLRenderer();
	renderer.setSize(800, 600);

	projector = new THREE.Projector();
	$('.vr')[0].appendChild(renderer.domElement);

	floorTexture = new THREE.ImageUtils.loadTexture( 'roadway.jpg' );
	floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;
	floorTexture.repeat.set( 20, 20 );

	// DoubleSide: render texture on both sides of mesh
	var floorMaterial = new THREE.MeshBasicMaterial( { map: floorTexture, side: THREE.DoubleSide } );
	var floorGeometry = new THREE.PlaneBufferGeometry(100, 100, 1, 1);
	var floor = new THREE.Mesh(floorGeometry, floorMaterial);
	floor.rotation.x = Math.PI / 2;
	scene.add(floor);

	brickTexture = new THREE.ImageUtils.loadTexture( 'brick-texture3.jpg' );
	brickTexture.wrapS = brickTexture.wrapT = THREE.RepeatWrapping;
	brickTexture.repeat.set( 2, 2 );

	woodTexture = new THREE.ImageUtils.loadTexture( 'wood-texture.jpg' );
	woodTexture.wrapS = woodTexture.wrapT = THREE.RepeatWrapping;
	woodTexture.repeat.set( 1, 1 );

	var materialArray = [];
	materialArray.push(new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'clouds4.jpg' ) }));
	materialArray.push(new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'clouds2.jpg' ) }));
	materialArray.push(new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'cloudstop.jpg' ) }));
	materialArray.push(new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'cloudsbot.jpg' ) }));
	materialArray.push(new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'clouds3.jpg' ) }));
	materialArray.push(new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'clouds1.jpg' ) }));
	for (var i = 0; i < 6; i++)
	   materialArray[i].side = THREE.BackSide;
	var skyboxMaterial = new THREE.MeshFaceMaterial( materialArray );

	var skyboxGeom = new THREE.BoxGeometry( 1000, 1000, 1000, 1, 1, 1 );

	var skybox = new THREE.Mesh( skyboxGeom, skyboxMaterial );

	scene.add( skybox );
	scene.fog = new THREE.FogExp2( 0x999999, 0.0002 );

	for (var index = 0; index < model.length; index++) {
		addAppModelToScene(model[index]);
	}

	controls = new THREE.FirstPersonControls(camera);
	controls.movementSpeed = 4;
	controls.lookSpeed = 0.08;
    controls.constrainVertical = true;
    controls.verticalMin = Math.PI * 0.25;
    controls.verticalMax = Math.PI * 0.55;

	camera.position.y = 1;

	render();
}

function handleSelection() {
	var vector = new THREE.Vector3( 0, 0, 1 );
	vector.unproject( camera );
	var ray = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );

	var intersects = ray.intersectObjects( targetList );

	if (intersects.length == 1) {
		if ((highlight == null) || (highlight != intersects[0].object.app)) {
			highlight = intersects[0].object.app;
			highlight.label.visible = true;
			intersects[0].object.material.color = {r: 0.9, g: 0.1, b: 0.1};
			// console.log(highlight.manifest_url);
		}

		if (intersects[0].distance < 5) {
			console.log('BOOM');
			// window.location='/#/app/' + highlight.id;
		}
	} else {
		if (highlight != null) {
			for (var resetIndex = 0; resetIndex < targetList.length; resetIndex++) {
				targetList[resetIndex].material.color = {r: 0.9, g: 0.9, b: 0.9};
				targetList[resetIndex].app.label.visible = false;
			}
			highlight = null;
		}
	}
}

function render() {
	controls.update( clock.getDelta() );

	// ensure the altitude (y) doesn't go too high or too low
	camera.position.y = THREE.Math.clamp( camera.position.y, 0.5, 5.0 );

	handleSelection();

	renderer.render(scene, camera);
	requestAnimationFrame(render);
}<commit_msg>refactor code into smaller pieces<commit_after>var renderer = null;
var scene = null;
var camera = null;
var clock = new THREE.Clock();
var controls = null;
var projector = null;
var highlight = null;
var targetList = [];
var brickTexture = null;
var woodTexture = null;
var floorTexture = null;

function makeTextSprite( message, parameters )
{
	if ( parameters === undefined ) parameters = {};

	var fontface = parameters.hasOwnProperty("fontface") ?
		parameters["fontface"] : "Arial";

	var fontsize = parameters.hasOwnProperty("fontsize") ?
		parameters["fontsize"] : 18;

	var borderThickness = parameters.hasOwnProperty("borderThickness") ?
		parameters["borderThickness"] : 4;

	var borderColor = parameters.hasOwnProperty("borderColor") ?
		parameters["borderColor"] : { r:0, g:0, b:0, a:1.0 };

	var backgroundColor = parameters.hasOwnProperty("backgroundColor") ?
		parameters["backgroundColor"] : { r:255, g:255, b:255, a:1.0 };

	var canvas = document.createElement('canvas');
	var context = canvas.getContext('2d');
	context.font = "Bold " + fontsize + "px " + fontface;

	// get size data (height depends only on font size)
	var metrics = context.measureText( message );
	var textWidth = metrics.width;

	// background color
	context.fillStyle   = "rgba(" + backgroundColor.r + "," + backgroundColor.g + ","
								  + backgroundColor.b + "," + backgroundColor.a + ")";
	// border color
	context.strokeStyle = "rgba(" + borderColor.r + "," + borderColor.g + ","
								  + borderColor.b + "," + borderColor.a + ")";

	context.lineWidth = borderThickness;
	roundRect(context, borderThickness/2, borderThickness/2, textWidth + borderThickness, fontsize * 1.4 + borderThickness, 6);
	// 1.4 is extra height factor for text below baseline: g,j,p,q.

	// text color
	context.fillStyle = "rgba(0, 0, 0, 1.0)";

	context.fillText( message, borderThickness, fontsize + borderThickness);

	// canvas contents will be used for a texture
	var texture = new THREE.Texture(canvas)
	texture.needsUpdate = true;

	var spriteMaterial = new THREE.SpriteMaterial(
		{ map: texture } );
	var sprite = new THREE.Sprite( spriteMaterial );
	sprite.scale.set(2,2,2);
	return sprite;
}

// function for drawing rounded rectangles
function roundRect(ctx, x, y, w, h, r)
{
    ctx.beginPath();
    ctx.moveTo(x+r, y);
    ctx.lineTo(x+w-r, y);
    ctx.quadraticCurveTo(x+w, y, x+w, y+r);
    ctx.lineTo(x+w, y+h-r);
    ctx.quadraticCurveTo(x+w, y+h, x+w-r, y+h);
    ctx.lineTo(x+r, y+h);
    ctx.quadraticCurveTo(x, y+h, x, y+h-r);
    ctx.lineTo(x, y+r);
    ctx.quadraticCurveTo(x, y, x+r, y);
    ctx.closePath();
    ctx.fill();
	ctx.stroke();
}

// for a given app, create a model and add it to the scene
// visualize information about the app using sizes, colors, textures, and placement
function addAppModelToScene(inApp) {
	var scaledRatings = Math.min(4.0, 0.5 + inApp.ratings.count / 100.0);

	var material = new THREE.MeshBasicMaterial( {
		map: inApp.is_packaged? brickTexture : woodTexture,
		color: {r: 0.9, g: 0.9, b: 0.9} } );

	var geometry = new THREE.BoxGeometry(1, 1, 1);
	inApp.cube = new THREE.Mesh( geometry, material );
	inApp.cube.scale.x = scaledRatings;
	inApp.cube.scale.y = scaledRatings;
	inApp.cube.scale.z = scaledRatings;

	inApp.cube.position.x = Math.random() * 100 - 50;
	inApp.cube.position.y = scaledRatings / 2;
	inApp.cube.position.z = Math.random() * 100 - 50;
	inApp.cube.app = inApp;

	scene.add(inApp.cube);
	targetList.push(inApp.cube);

	// make different colors for privileged and not privileged apps
	inApp.label = makeTextSprite(
			inApp.slug,
			{
				fontsize: 24,
				borderColor: inApp.app_type == 'privileged' ? {r:0, g:255, b:0, a:1.0} : {r:255, g:0, b:0, a:1.0},
				backgroundColor: inApp.app_type == 'privileged' ? {r:100, g:255, b:100, a:0.8} : {r:255, g:100, b:100, a:0.8}
			});

	inApp.label.position.set(inApp.cube.position.x, inApp.cube.position.y + scaledRatings / 2, inApp.cube.position.z);
	inApp.label.visible = false;

	scene.add(inApp.label);
}

function createFloor() {
	floorTexture = new THREE.ImageUtils.loadTexture( 'roadway.jpg' );
	floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;
	floorTexture.repeat.set( 20, 20 );

	// DoubleSide: render texture on both sides of mesh
	var floorMaterial = new THREE.MeshBasicMaterial( { map: floorTexture, side: THREE.DoubleSide } );
	var floorGeometry = new THREE.PlaneBufferGeometry(100, 100, 1, 1);
	var floor = new THREE.Mesh(floorGeometry, floorMaterial);
	floor.rotation.x = Math.PI / 2;

	return floor;
}

function createSkybox() {
	var materialArray = [];
	materialArray.push(new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'clouds4.jpg' ) }));
	materialArray.push(new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'clouds2.jpg' ) }));
	materialArray.push(new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'cloudstop.jpg' ) }));
	materialArray.push(new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'cloudsbot.jpg' ) }));
	materialArray.push(new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'clouds3.jpg' ) }));
	materialArray.push(new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'clouds1.jpg' ) }));
	for (var i = 0; i < 6; i++)
	   materialArray[i].side = THREE.BackSide;
	var skyboxMaterial = new THREE.MeshFaceMaterial( materialArray );

	var skyboxGeom = new THREE.BoxGeometry( 1000, 1000, 1000, 1, 1, 1 );

	var skybox = new THREE.Mesh( skyboxGeom, skyboxMaterial );

	return skybox;
}

function createVRScene(inView) {
	console.log('createVRScene');
	var model = inView.get('model');

	scene = new THREE.Scene();
	camera = new THREE.PerspectiveCamera(75, 800 / 600, 0.1, 1000);
	renderer = new THREE.WebGLRenderer();
	renderer.setSize(800, 600);

	projector = new THREE.Projector();
	$('.vr')[0].appendChild(renderer.domElement);

	scene.add(createFloor());

	brickTexture = new THREE.ImageUtils.loadTexture( 'brick-texture3.jpg' );
	brickTexture.wrapS = brickTexture.wrapT = THREE.RepeatWrapping;
	brickTexture.repeat.set( 2, 2 );

	woodTexture = new THREE.ImageUtils.loadTexture( 'wood-texture.jpg' );
	woodTexture.wrapS = woodTexture.wrapT = THREE.RepeatWrapping;
	woodTexture.repeat.set( 1, 1 );

	scene.add(createSkybox());

	scene.fog = new THREE.FogExp2( 0x999999, 0.0002 );

	for (var index = 0; index < model.length; index++) {
		addAppModelToScene(model[index]);
	}

	controls = new THREE.FirstPersonControls(camera);
	controls.movementSpeed = 4;
	controls.lookSpeed = 0.08;
    controls.constrainVertical = true;
    controls.verticalMin = Math.PI * 0.25;
    controls.verticalMax = Math.PI * 0.55;

	camera.position.y = 1;

	render();
}

function handleSelection() {
	var vector = new THREE.Vector3( 0, 0, 1 );
	vector.unproject( camera );
	var ray = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );

	var intersects = ray.intersectObjects( targetList );

	if (intersects.length == 1) {
		if ((highlight == null) || (highlight != intersects[0].object.app)) {
			highlight = intersects[0].object.app;
			highlight.label.visible = true;
			intersects[0].object.material.color = {r: 0.9, g: 0.1, b: 0.1};
			// console.log(highlight.manifest_url);
		}

		if (intersects[0].distance < 5) {
			console.log('BOOM');
			// window.location='/#/app/' + highlight.id;
		}
	} else {
		if (highlight != null) {
			for (var resetIndex = 0; resetIndex < targetList.length; resetIndex++) {
				targetList[resetIndex].material.color = {r: 0.9, g: 0.9, b: 0.9};
				targetList[resetIndex].app.label.visible = false;
			}
			highlight = null;
		}
	}
}

function render() {
	controls.update( clock.getDelta() );

	// ensure the altitude (y) doesn't go too high or too low
	camera.position.y = THREE.Math.clamp( camera.position.y, 0.5, 5.0 );

	handleSelection();

	renderer.render(scene, camera);
	requestAnimationFrame(render);
}<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013, David Farrell <shokku.ra@gmail.com>
 *  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * - Neither the name of iBot nor the names of its contributors may be used to
 *   endorse or promote products derived from this software without specific
 *   prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

exports.mod = function(context, server)
{

}
<commit_msg>Wrote basic relay module with no authentication system currently. Handle with care.<commit_after>/*
 * Copyright (c) 2013, David Farrell <shokku.ra@gmail.com>
 *  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * - Neither the name of iBot nor the names of its contributors may be used to
 *   endorse or promote products derived from this software without specific
 *   prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

var net = require('net');
var util = require('util');

exports.mod = function(context, server)
{
	this.server = undefined;
	this.clients = [];

	this._suspend = function()
	{
		if(this.timeout !== undefined) clearTimeout(this.timeout);
		this.destroy();
	}

	this._loaded = function()
	{
		this.timeout = setTimeout(function() { this.create(); }.bind(this), 2000);
	}

	this._unloaded = function()
	{
		if(this.timeout !== undefined) clearTimeout(this.timeout);
		this.destroy();
	}

	this.$recv = function(prefix, opcode, params)
	{
		for(var iClient in this.clients)
		{
			this.clients[iClient].write(prefix.mask + ' ' + opcode + ' ' + params.join(' ') + '\r\n');
		}
	}

	this.core$cmd = function(prefix, target, cmd, params, $core)
	{
		if(cmd === 'relay')
		{
			switch(params[0])
			{
				case '?':
					$core._privmsg(target, this.clients.length);
					break;
			}
		}
	}

	this.create = function()
	{
		this.server = net.createServer();

		this.server.on('connection', function(c)
		{
			this.clients.push(c);

			c.setEncoding('utf8');

			c.on('data', function(data)
			{
				server.fire('$log', data);
				server.send(data.trim());
			}.bind(this));

			c.on('end', function()
			{
				var i = this.clients.indexOf(c);
				if(i !== -1) this.clients.splice(i, 1);
			}.bind(this));

			c.on('error', function(err)
			{
				console.log(err);
			}.bind(this));
		}.bind(this));

		this.server.on('error', function(err)
		{
			console.log(err);
		});

		this.server.listen(18010);
	}

	this.destroy = function()
	{
		if(this.server !== undefined)
		{
			for(var iClient in this.clients)
			{
				this.clients[iClient].end();
				this.clients[iClient].destroy();
			}

			this.server.close();
		}
	}
}
<|endoftext|>


<commit_before>// TODO: it might be better to make a ContextProvider class, since you
// can always find the canvas through context.canvas. This saves code and
// speed by not having to do canvas.getContext('2d')
// TODO: Run through the canvas array to find a canvas with the requested
// width / height, so we don't need to resize it?
CanvasProvider = {
	canvases: [],
	getCanvas: function(size) {
		if (this.canvases.length) {
			var canvas = this.canvases.pop();
			// If they are not the same size, we don't need to clear them
			// using clearRect and visa versa.
			if ((canvas.width != size.width) ||  (canvas.height != size.height)) {
				canvas.width = size.width;
				canvas.height = size.height;
			} else {
				var context = canvas.getContext('2d');
				context.clearRect(0, 0, size.width + 1, size.height + 1);
			}
			return canvas;
		} else {
			var canvas = document.createElement('canvas');
			canvas.width = size.width;
			canvas.height = size.height;
			return canvas;
		}
	},

	returnCanvas: function(canvas) {
		this.canvases.push(canvas);
	}
};<commit_msg>Reformat code and add comment about clearRect dimensions.<commit_after>// TODO: it might be better to make a ContextProvider class, since you
// can always find the canvas through context.canvas. This saves code and
// speed by not having to do canvas.getContext('2d')
// TODO: Run through the canvas array to find a canvas with the requested
// width / height, so we don't need to resize it?
CanvasProvider = {
	canvases: [],
	getCanvas: function(size) {
		if (this.canvases.length) {
			var canvas = this.canvases.pop();
			// If they are not the same size, we don't need to clear them
			// using clearRect and visa versa.
			if ((canvas.width != size.width)
					|| (canvas.height != size.height)) {
				canvas.width = size.width;
				canvas.height = size.height;
			} else {
				var context = canvas.getContext('2d');
				// +1 is needed on some browsers to really clear the borders
				context.clearRect(0, 0, size.width + 1, size.height + 1);
			}
			return canvas;
		} else {
			var canvas = document.createElement('canvas');
			canvas.width = size.width;
			canvas.height = size.height;
			return canvas;
		}
	},

	returnCanvas: function(canvas) {
		this.canvases.push(canvas);
	}
};<|endoftext|>


<commit_before>var crypto = require('crypto');
var util = require('util');

exports.mod = function(context, server)
{
	this.help$register = function()
	{
		server.do('help$register', 'utils');
	}

	this._help = function(topic, params)
	{
		switch(topic)
		{
			case 'utils':
				return {
					text: 'Provides various utility commands.',
					sub: ['ping', 'digest', 'memusage']
				};
		}
	}

	this.core$cmd = function(prefix, target, cmd, params, $core)
	{
		switch(cmd)
		{
			case 'ping':
				$core._privmsg(target, 'pong' + (params[0] !== undefined ? (' ' + params[0]) : ''));
				break;
			case 'digest':
				if(params.length < 2)
				{
					$core._privmsg(target, 'Syntax: digest <type> <string>');
					break;
				}

				var h = crypto.createHash(params[0]);
				h.update(params[1]);
				$core._privmsg(target, 'Hash: ' + h.digest('hex'));
				break;
			case 'memusage':
				$core._privmsg(target, util.inspect(process.memoryUsage()));
				break;
		}
	}
}
<commit_msg>Finished adding help to mod utils.<commit_after>var crypto = require('crypto');
var util = require('util');

exports.mod = function(context, server)
{
	this.help$register = function()
	{
		server.do('help$register', 'utils');
	}

	this._help = function(topic, params)
	{
		switch(topic)
		{
			case 'utils':
				switch(params[0])
				{
					case 'ping':
						return "Send a ping to the bot (useful for connection checks).";
					case 'digest':
						return "Digest a string into a hash.";
					case 'memusage':
						return "Show current memory usage.";
					default:
						return {
							text: 'Provides various utility commands.',
							sub: ['ping', 'digest', 'memusage']
						};
				}
				break;
		}
	}

	this.core$cmd = function(prefix, target, cmd, params, $core)
	{
		switch(cmd)
		{
			case 'ping':
				$core._privmsg(target, 'pong' + (params[0] !== undefined ? (' ' + params[0]) : ''));
				break;
			case 'digest':
				if(params.length < 2)
				{
					$core._privmsg(target, 'Syntax: digest <type> <string>');
					break;
				}

				var h = crypto.createHash(params[0]);
				h.update(params[1]);
				$core._privmsg(target, 'Hash: ' + h.digest('hex'));
				break;
			case 'memusage':
				$core._privmsg(target, util.inspect(process.memoryUsage()));
				break;
		}
	}
}
<|endoftext|>


<commit_before>// These events are run when the DOM is ready
$( document ).ready(function() {

  if (Modernizr.localstorage) {
    // This browser supports HTML5 so we can store the players
    // in the browser localstorage.
    if (localStorage.length == 0)
    {
      initialiseLocalStorage();
    }
    readPlayers();
  }
  else
  {
    // no native support for HTML5 storage :(
    // maybe try dojox.storage or a third-party solution
    alert("HTML5 localtstorage not supported =(")
  }

  setPlayerDivDimensions();
  initialiseEventHandlers();

});

function initialiseLocalStorage()
{
  localStorage.setItem('webplayer.counter', 0);
}

function toggleTogglerAndForm()
{
  $("#playerForm").trigger("reset");
  $("#playerForm").slideToggle(200);
  $(".toggler").slideToggle(200);
}

function initialiseEventHandlers()
{

  // We use jQuery UI Dialog to show the add player form
  // and should be able to reuse most of this code for editing
  // existing players
  $( "#playerForm" ).dialog({
     autoOpen: false,
     modal: true,
     appendTo: "#player",
     width: "auto",
     dialogClass: "addPlayer",
     position: { my: "center", at: "center", of: window },
     resizable: false,
     buttons: {
       "Add player": function() {
           addNewPlayer($("#name").val(), $("#address").val());
           $( this ).dialog( "close" );
       },
       "Edit player": function() {
           editPlayer($("#name").val(), $("#address").val(), $("#id").val());
           readPlayers();
           $( this ).dialog( "close" );
       },
       Cancel: function() {
         $( this ).dialog( "close" );
       }
     },
     open: function() {
        $('.ui-widget-overlay').addClass('darker-overlay');
        alert(id);
        if (id != null)
        {
          $('.ui-button:contains(Edit player)').show()
          $('.ui-button:contains(Add player)').hide()
          $("#address").val(localStorage.getItem("webplayer.players."+id+".url"));
          $("#name").val(localStorage.getItem("webplayer.players."+id+".name"));
          $("#id").val(id);
        }
        else
        {
          $("#address").val("");
          $("#name").val("");
          $("#id").val("");
          $('.ui-button:contains(Add player)').show()
          $('.ui-button:contains(Edit player)').hide()
        }
     },
     close: function() {
       $('.ui-widget-overlay').removeClass('darker-overlay');
     }
   });

   $( "#new-player" )
     .button()
     .click(function() {
       $( "#playerForm" ).data("id",null).dialog( "open" );
     });
}

// Resize the player whenever the window is resized.
$(window).resize(function() {
  setPlayerDivDimensions();
});

function loadPlayer(sender) {
  // Load a player into the iframe
  // TODO: Fix focus so <space> can pause
  var iframe = document.getElementById("playerframe");
  var purl = sender.currentTarget.parentNode.childNodes.item(1).textContent;
  iframe.src = purl;
  if (purl.search("soundcloud.com") > 0)
  {
    // TODO: Autoplay on load
    iframe.src = "http://w.soundcloud.com/player/?url="+purl;
    soundcloudWidget = SC.Widget(iframe);
    soundcloudWidget.load(purl, {'auto_play': true});
  }
  document.title = "Webplayer - " + sender.currentTarget.parentNode.childNodes.item(0).textContent;
}

function setPlayerDivDimensions()
{
  var width = document.body.clientWidth;
  // playerList = 300px and has 2 x 10px paddings
  width = width - 320;

  $("#player").css("width", width);
  $("#playerframe").css("width", width);

  // TODO: Set div and frame height more correctly than this hack.
  $("#player").css("height", document.height);
  $("#playerFrame").css("height", document.height);
}

function resetPlaylistAttributes()
{
  $("li").hoverIntent({
    over: slideDownwards,
    interval: 1000,
    out: slideUpwards
  });

  $(".tools-delete").click(function(event) {
    deletePlayer(event);
  });

  $(".tools-edit").click(function(event) {
    $("#playerForm").data("id",event.currentTarget.parentNode.parentNode.parentNode.childNodes.item(0).childNodes.item(0).childNodes.item(2).textContent).dialog("open");
  });

  // This binds actions to the new
 elements that make them open the players
  $(".playerlink").on("click", function(event) {
    loadPlayer(event);
  });
}

function slideDownwards()
{
  $(this).find(".tools").slideDown(200);
}

function slideUpwards()
{
  $(this).find(".tools").slideUp(200);
}

function editPlayer(name, address, id)
{
  if (id != null)
  {
    localStorage.setItem("webplayer.players."+id+".name", name);
    localStorage.setItem("webplayer.players."+id+".url", address);
  }
}

function readPlayers()
{
  // Read the saved players from the localstorage and populate the
  // playerlist with the stored metadata
  // TODO: set

  var values = [];

  var counter = parseInt(localStorage["webplayer.counter"])
  for (var i = 0; i < counter; i++)
  {
    var exists = localStorage.getItem("webplayer.players."+i+".name");
    if (exists != null)
    {
      values.push({name: localStorage.getItem("webplayer.players."+i+".name"),
                   url: localStorage.getItem("webplayer.players."+i+".url"),
                   id: i});
    }
  }

  var options = {
    item: '

EditDelete

'
  };

  var playerList = new List('playerlist', options, values);
  resetPlaylistAttributes();
}

function addNewPlayer(name, url)
{
  // TODO: Scrape the Bandcamp  to extract a reference to the playlist ID
  //       so we can embedd HTML5 players instead of full pages inside the
  //       IFRAME
  // TODO: Make this modular so it can effectively cope with adding resources from
  //       different domains.
  if (Modernizr.localstorage)
  {
    if (url.length == 0)
    {
      return;
    }

    var counter = parseInt(localStorage.getItem("webplayer.counter"));
    if (name.length > 0)
    {
      localStorage.setItem("webplayer.players." + counter+".name", name);
    }
    else
    {
      localStorage.setItem("webplayer.players." + counter+".name", "No name");
    }
    localStorage.setItem("webplayer.players." + counter+".added", new Date());
    localStorage.setItem("webplayer.players." + counter+".url", url);
    localStorage.setItem("webplayer.counter", counter + 1);
  }
  readPlayers();
  return false;
}

function deletePlayer(sender)
{
  if (Modernizr.localstorage)
  {
    var key = sender.currentTarget.parentNode.parentNode.childNodes.item(0).childNodes.item(2).textContent;
    if (key != null)
    {
      localStorage.removeItem("webplayer.players."+key+".name");
      localStorage.removeItem("webplayer.players."+key+".added");
      localStorage.removeItem("webplayer.players."+key+".url");
      readPlayers();
     }
  }
}
<commit_msg>Fixed bug that causes the edit player contents to not populate.<commit_after>// These events are run when the DOM is ready
$( document ).ready(function() {

  if (Modernizr.localstorage) {
    // This browser supports HTML5 so we can store the players
    // in the browser localstorage.
    if (localStorage.length == 0)
    {
      initialiseLocalStorage();
    }
    readPlayers();
  }
  else
  {
    // no native support for HTML5 storage :(
    // maybe try dojox.storage or a third-party solution
    alert("HTML5 localtstorage not supported =(")
  }

  setPlayerDivDimensions();
  initialiseEventHandlers();

});

function initialiseLocalStorage()
{
  localStorage.setItem('webplayer.counter', 0);
}

function toggleTogglerAndForm()
{
  $("#playerForm").trigger("reset");
  $("#playerForm").slideToggle(200);
  $(".toggler").slideToggle(200);
}

function initialiseEventHandlers()
{

  // We use jQuery UI Dialog to show the add player form
  // and should be able to reuse most of this code for editing
  // existing players
  $( "#playerForm" ).dialog({
     autoOpen: false,
     modal: true,
     appendTo: "#player",
     width: "auto",
     dialogClass: "addPlayer",
     position: { my: "center", at: "center", of: window },
     resizable: false,
     buttons: {
       "Add player": function() {
           addNewPlayer($("#name").val(), $("#address").val());
           $( this ).dialog( "close" );
       },
       "Edit player": function() {
           editPlayer($("#name").val(), $("#address").val(), $("#id").val());
           readPlayers();
           $( this ).dialog( "close" );
       },
       Cancel: function() {
         $( this ).dialog( "close" );
       }
     },
     open: function() {
        $('.ui-widget-overlay').addClass('darker-overlay');
        var id = $(this).data("id");
        if (id == null)
        {
          $("#address").val("");
          $("#name").val("");
          $("#id").val("");
          $('.ui-button:contains(Add player)').show()
          $('.ui-button:contains(Edit player)').hide()
        }
        else
        {
          $('.ui-button:contains(Edit player)').show()
          $('.ui-button:contains(Add player)').hide()
          $("#address").val(localStorage.getItem("webplayer.players."+id+".url"));
          $("#name").val(localStorage.getItem("webplayer.players."+id+".name"));
          $("#id").val(id);
        }
     },
     close: function() {
       $('.ui-widget-overlay').removeClass('darker-overlay');
     }
   });

   $( "#new-player" )
     .button()
     .click(function() {
       $( "#playerForm" ).data("id",null).dialog( "open" );
     });
}

// Resize the player whenever the window is resized.
$(window).resize(function() {
  setPlayerDivDimensions();
});

function loadPlayer(sender) {
  // Load a player into the iframe
  // TODO: Fix focus so <space> can pause
  var iframe = document.getElementById("playerframe");
  var purl = sender.currentTarget.parentNode.childNodes.item(1).textContent;
  iframe.src = purl;
  if (purl.search("soundcloud.com") > 0)
  {
    // TODO: Autoplay on load
    iframe.src = "http://w.soundcloud.com/player/?url="+purl;
    soundcloudWidget = SC.Widget(iframe);
    soundcloudWidget.load(purl, {'auto_play': true});
  }
  document.title = "Webplayer - " + sender.currentTarget.parentNode.childNodes.item(0).textContent;
}

function setPlayerDivDimensions()
{
  var width = document.body.clientWidth;
  // playerList = 300px and has 2 x 10px paddings
  width = width - 320;

  $("#player").css("width", width);
  $("#playerframe").css("width", width);

  // TODO: Set div and frame height more correctly than this hack.
  $("#player").css("height", document.height);
  $("#playerFrame").css("height", document.height);
}

function resetPlaylistAttributes()
{
  $("li").hoverIntent({
    over: slideDownwards,
    interval: 1000,
    out: slideUpwards
  });

  $(".tools-delete").click(function(event) {
    deletePlayer(event);
  });

  $(".tools-edit").click(function(event) {
    $("#playerForm").data("id",event.currentTarget.parentNode.parentNode.parentNode.childNodes.item(0).childNodes.item(0).childNodes.item(2).textContent).dialog("open");
  });

  // This binds actions to the new
 elements that make them open the players
  $(".playerlink").on("click", function(event) {
    loadPlayer(event);
  });
}

function slideDownwards()
{
  $(this).find(".tools").slideDown(200);
}

function slideUpwards()
{
  $(this).find(".tools").slideUp(200);
}

function editPlayer(name, address, id)
{
  if (id != null)
  {
    localStorage.setItem("webplayer.players."+id+".name", name);
    localStorage.setItem("webplayer.players."+id+".url", address);
  }
}

function readPlayers()
{
  // Read the saved players from the localstorage and populate the
  // playerlist with the stored metadata
  // TODO: set

  var values = [];

  var counter = parseInt(localStorage["webplayer.counter"])
  for (var i = 0; i < counter; i++)
  {
    var exists = localStorage.getItem("webplayer.players."+i+".name");
    if (exists != null)
    {
      values.push({name: localStorage.getItem("webplayer.players."+i+".name"),
                   url: localStorage.getItem("webplayer.players."+i+".url"),
                   id: i});
    }
  }

  var options = {
    item: '

EditDelete

'
  };

  var playerList = new List('playerlist', options, values);
  resetPlaylistAttributes();
}

function addNewPlayer(name, url)
{
  // TODO: Scrape the Bandcamp  to extract a reference to the playlist ID
  //       so we can embedd HTML5 players instead of full pages inside the
  //       IFRAME
  // TODO: Make this modular so it can effectively cope with adding resources from
  //       different domains.
  if (Modernizr.localstorage)
  {
    if (url.length == 0)
    {
      return;
    }

    var counter = parseInt(localStorage.getItem("webplayer.counter"));
    if (name.length > 0)
    {
      localStorage.setItem("webplayer.players." + counter+".name", name);
    }
    else
    {
      localStorage.setItem("webplayer.players." + counter+".name", "No name");
    }
    localStorage.setItem("webplayer.players." + counter+".added", new Date());
    localStorage.setItem("webplayer.players." + counter+".url", url);
    localStorage.setItem("webplayer.counter", counter + 1);
  }
  readPlayers();
  return false;
}

function deletePlayer(sender)
{
  if (Modernizr.localstorage)
  {
    var key = sender.currentTarget.parentNode.parentNode.childNodes.item(0).childNodes.item(2).textContent;
    if (key != null)
    {
      localStorage.removeItem("webplayer.players."+key+".name");
      localStorage.removeItem("webplayer.players."+key+".added");
      localStorage.removeItem("webplayer.players."+key+".url");
      readPlayers();
     }
  }
}
<|endoftext|>


<commit_before>$('#song_content').on('mouseover', '.chord', function() {
    $(this).children('img').css('visibility', 'visible');
})

$('#song_content').on('mouseout', '.chord', function() {
    $(this).children('img').css('visibility', 'hidden');
})

$('#hide_chords').click(function() {
    $('.chordline').toggle();
    $('#hide_chords').text(
        $('#hide_chords').text() == 'Hide chords' ? 'Show chords' : 'Hide chords');
})

$('#hide_tabs').click(function() {
    $('.tabsline').toggle();
    $('#hide_tabs').text(
        $('#hide_tabs').text() == 'Hide tabs' ? 'Show tabs' : 'Hide tabs');
})

$('#semiton_change').change(function() {
    var semitonsMove = parseInt($(this).find(':selected').text(), 10);
    $('.chord').each(function() {
        $(this).text(changeSemiton($(this).attr('origchord'), semitonsMove));
    });
})

/**
 * Return the given chord, changed by semitonsMove semitons.
 *
 * @param {String} chord a string representing a chord, eg. "A#5"
 * @param {Number} semitonsMove an integer from -5 to 6
 * @return {String} the changed chord
 */
function changeSemiton(chord, semitonsMove) {
    if (semitonsMove === 0)
        return chord;

    var semitons = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];

    var regex = /([A-G][#b]?)((maj|m|aug|dim|sus)?[245679]?)/;

    var match = regex.exec(chord);
    var origSemiton = match[1];
    var restChord = match[2];

    if (origSemiton.charAt(1) === 'b') {
        var map = {'Ab' : 'G#', 'Bb' : 'A#', 'Cb' : 'B', 'Db' : 'C#',
                   'Eb' : 'F#', 'Fb' : 'E', 'Gb' : 'F#'};
        var origSemiton = map[origSemiton];
    }

    var index = semitons.indexOf(origSemiton) + semitonsMove;
    if (index >= semitons.length)
        index = index % semitons.length;
    else if (index < 0)
        index = semitons.length + index;

    return semitons[index] + restChord;
}

/**
 * Encloses each chord in a span tag of class "chord", and assigns an
 * appropriate image to it. Additionally it encloses each line containing
 * chords in a div tag of class "chordline" and each line containing tabs
 * in a div tag of class "tabsline".
 */
function parseSong() {
    var content = $("#song_content");

    // parse tabs
    var regex = /^[A-Ga-g]\|?[1-9-]{3,}/;
    var lines = content.html().split('\n');
    var newlines = [];
    for (i = 0; i < lines.length; i++) {
        if (regex.test(lines[i]))
            lines[i] = '
' + lines[i] + '
';
        else
            lines[i] += '\n'
        newlines.push(lines[i]);
    }

    // parse chords
    var lines = newlines;
    var finallines = "";

    for (i = 0; i < lines.length; i++) {
        if (lines[i].indexOf('
') != -1) {
            // don't parse tab lines for chords
            finallines += lines[i];
            continue;
        }

        lines[i] = lines[i].replace(
            /([A-G][#b]?(maj|m|aug|dim|sus)?[245679]?)/g,
            '$1' +
            ''
        );

        if (lines[i].indexOf('= -5; i--) {
        if (i != 0) {
            $('#semiton_change')
                .append($('<option></option>')
                .text((i>0 ? '+' : '') + i + ' (' + changeSemiton(songBase, i) + ')'));
        } else {
            $('#semiton_change')
                .append($('<option></option>')
                .attr('selected', 'selected')
                .text(i + ' (' + changeSemiton(songBase, i) + ')'));
        }
    }
}

$().ready(function() {
    parseSong();
    fillSemitonChange();
});
<commit_msg>semiton change bugfix<commit_after>$('#song_content').on('mouseover', '.chord', function() {
    $(this).children('img').css('visibility', 'visible');
})

$('#song_content').on('mouseout', '.chord', function() {
    $(this).children('img').css('visibility', 'hidden');
})

$('#hide_chords').click(function() {
    $('.chordline').toggle();
    $('#hide_chords').text(
        $('#hide_chords').text() == 'Hide chords' ? 'Show chords' : 'Hide chords');
})

$('#hide_tabs').click(function() {
    $('.tabsline').toggle();
    $('#hide_tabs').text(
        $('#hide_tabs').text() == 'Hide tabs' ? 'Show tabs' : 'Hide tabs');
})

$('#semiton_change').change(function() {
    var semitonsMove = parseInt($(this).find(':selected').text(), 10);
    $('.chord').each(function() {
        $(this).children('.chordname').text(
            changeSemiton($(this).attr('origchord'), semitonsMove));
    });
})

/**
 * Return the given chord, changed by semitonsMove semitons.
 *
 * @param {String} chord a string representing a chord, eg. "A#5"
 * @param {Number} semitonsMove an integer from -5 to 6
 * @return {String} the changed chord
 */
function changeSemiton(chord, semitonsMove) {
    if (semitonsMove === 0)
        return chord;

    var semitons = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];

    var regex = /([A-G][#b]?)((maj|m|aug|dim|sus)?[245679]?)/;

    var match = regex.exec(chord);
    var origSemiton = match[1];
    var restChord = match[2];

    if (origSemiton.charAt(1) === 'b') {
        var map = {'Ab' : 'G#', 'Bb' : 'A#', 'Cb' : 'B', 'Db' : 'C#',
                   'Eb' : 'F#', 'Fb' : 'E', 'Gb' : 'F#'};
        var origSemiton = map[origSemiton];
    }

    var index = semitons.indexOf(origSemiton) + semitonsMove;
    if (index >= semitons.length)
        index = index % semitons.length;
    else if (index < 0)
        index = semitons.length + index;

    return semitons[index] + restChord;
}

/**
 * Encloses each chord in a span tag of class "chord", and assigns an
 * appropriate image to it. Additionally it encloses each line containing
 * chords in a div tag of class "chordline" and each line containing tabs
 * in a div tag of class "tabsline".
 */
function parseSong() {
    var content = $("#song_content");

    // parse tabs
    var regex = /^[A-Ga-g]\|?[1-9-]{3,}/;
    var lines = content.html().split('\n');
    var newlines = [];
    for (i = 0; i < lines.length; i++) {
        if (regex.test(lines[i]))
            lines[i] = '
' + lines[i] + '
';
        else
            lines[i] += '\n'
        newlines.push(lines[i]);
    }

    // parse chords
    var lines = newlines;
    var finallines = "";

    for (i = 0; i < lines.length; i++) {
        if (lines[i].indexOf('
') != -1) {
            // don't parse tab lines for chords
            finallines += lines[i];
            continue;
        }

        lines[i] = lines[i].replace(
            /([A-G][#b]?(maj|m|aug|dim|sus)?[245679]?)/g,
            '$1' +
            ''
        );

        if (lines[i].indexOf('= -5; i--) {
        if (i != 0) {
            $('#semiton_change')
                .append($('<option></option>')
                .text((i>0 ? '+' : '') + i + ' (' + changeSemiton(songBase, i) + ')'));
        } else {
            $('#semiton_change')
                .append($('<option></option>')
                .attr('selected', 'selected')
                .text(i + ' (' + changeSemiton(songBase, i) + ')'));
        }
    }
}

$().ready(function() {
    parseSong();
    fillSemitonChange();
});
<|endoftext|>


<commit_before>/**
 * connection statistics service
 * record connection, login count and list
 */
var Service = function(app) {
  this.serverId = app.getServerId();
  this.connCount = 0;
  this.loginedCount = 0;
  this.logined = {};
};

module.exports = Service;

var pro = Service.prototype;

/**
 * Add logined user.
 *
 * @param uid {String} user id
 * @param info {Object} record for logined user
 */
pro.addLoginedUser = function(uid, info) {
  if(!this.logined[uid]) {
    this.loginedCount++;
  }
  info.uid = uid;
  this.logined[uid] = info;
};

/**
 * Increase connection count
 */
pro.increaseConnectionCount = function() {
  this.connCount++;
};

/**
 * Remote logined user
 *
 * @param uid {String} user id
 */
pro.removeLoginedUser = function(uid) {
  console.error('uid: ' + uid);
  if(!!this.logined[uid]) {
    this.loginedCount--;
  }
  delete this.logined[uid];
};

/**
 * Decrease connection count
 *
 * @param uid {String} uid
 */
pro.decreaseConnectionCount = function(uid) {
  if(this.connCount) {
    this.connCount--;
  }
  if(!!uid) {
    this.removeLoginedUser(uid);
  }
};

/**
 * Get statistics info
 *
 * @return {Object} statistics info
 */
pro.getStatisticsInfo = function() {
  var list = [];
  for(var uid in this.logined) {
    list.push(this.logined[uid]);
  }

  return {serverId: this.serverId, totalConnCount: this.connCount, loginedCount: this.loginedCount, loginedList: list};
};
<commit_msg>remove logger<commit_after>/**
 * connection statistics service
 * record connection, login count and list
 */
var Service = function(app) {
  this.serverId = app.getServerId();
  this.connCount = 0;
  this.loginedCount = 0;
  this.logined = {};
};

module.exports = Service;

var pro = Service.prototype;

/**
 * Add logined user.
 *
 * @param uid {String} user id
 * @param info {Object} record for logined user
 */
pro.addLoginedUser = function(uid, info) {
  if(!this.logined[uid]) {
    this.loginedCount++;
  }
  info.uid = uid;
  this.logined[uid] = info;
};

/**
 * Increase connection count
 */
pro.increaseConnectionCount = function() {
  this.connCount++;
};

/**
 * Remote logined user
 *
 * @param uid {String} user id
 */
pro.removeLoginedUser = function(uid) {
  if(!!this.logined[uid]) {
    this.loginedCount--;
  }
  delete this.logined[uid];
};

/**
 * Decrease connection count
 *
 * @param uid {String} uid
 */
pro.decreaseConnectionCount = function(uid) {
  if(this.connCount) {
    this.connCount--;
  }
  if(!!uid) {
    this.removeLoginedUser(uid);
  }
};

/**
 * Get statistics info
 *
 * @return {Object} statistics info
 */
pro.getStatisticsInfo = function() {
  var list = [];
  for(var uid in this.logined) {
    list.push(this.logined[uid]);
  }

  return {serverId: this.serverId, totalConnCount: this.connCount, loginedCount: this.loginedCount, loginedList: list};
};
<|endoftext|>


<commit_before>import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { Navbar } from 'react-bootstrap'
import FontAwesome from 'react-fontawesome'
import { connect } from 'react-redux'

import { setMobileScreen } from '../../actions/ui'
import AppMenu from '../app/app-menu'
import NavLoginButtonAuth0 from '../../components/user/nav-login-button-auth0'
import { accountLinks, getAuth0Config } from '../../util/auth'

class MobileNavigationBar extends Component {
  static propTypes = {
    backScreen: PropTypes.number,
    headerAction: PropTypes.element,
    headerText: PropTypes.string,
    showBackButton: PropTypes.bool,
    setMobileScreen: PropTypes.func,
    title: PropTypes.element
  }

  _backButtonPressed = () => {
    const { backScreen, onBackClicked } = this.props
    if (backScreen) this.props.setMobileScreen(this.props.backScreen)
    else if (typeof onBackClicked === 'function') onBackClicked()
  }

  render () {
    const {
      auth0Config,
      headerAction,
      headerText,
      showBackButton,
      title
    } = this.props

    return (
      <Navbar fluid fixedTop>

        <Navbar.Header>
          <Navbar.Brand>
            {showBackButton
              ?
<FontAwesome name='arrow-left' onClick={this._backButtonPressed} />

              : <AppMenu />
            }
          </Navbar.Brand>
        </Navbar.Header>

          {headerText
            ?
{headerText}

            :
{title}

          }

        {headerAction && (

{headerAction}

        )}

        {/**
          * HACK: Normally, NavLoginButtonAuth0 should be inside a  element,
          * however, in mobile mode, react-bootstrap's  causes the
          * submenus of this component to be displayed full-screen-width,
          * and that behavior is not desired here.
          */}
        {auth0Config && (
          <NavLoginButtonAuth0
            id='login-control'
            links={accountLinks}
          />
        )}
      </Navbar>
    )
  }
}

// connect to the redux store

const mapStateToProps = (state, ownProps) => {
  return {
    auth0Config: getAuth0Config(state.otp.config.persistence)
  }
}

const mapDispatchToProps = {
  setMobileScreen
}

export default connect(mapStateToProps, mapDispatchToProps)(MobileNavigationBar)
<commit_msg>refactor(mobile/NavigationBar): Replace FontAwesome with narrative/Icon<commit_after>import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { Navbar } from 'react-bootstrap'
import { connect } from 'react-redux'

import { setMobileScreen } from '../../actions/ui'
import AppMenu from '../app/app-menu'
import NavLoginButtonAuth0 from '../../components/user/nav-login-button-auth0'
import Icon from '../narrative/icon'
import { accountLinks, getAuth0Config } from '../../util/auth'

class MobileNavigationBar extends Component {
  static propTypes = {
    backScreen: PropTypes.number,
    headerAction: PropTypes.element,
    headerText: PropTypes.string,
    showBackButton: PropTypes.bool,
    setMobileScreen: PropTypes.func,
    title: PropTypes.element
  }

  _backButtonPressed = () => {
    const { backScreen, onBackClicked } = this.props
    if (backScreen) this.props.setMobileScreen(this.props.backScreen)
    else if (typeof onBackClicked === 'function') onBackClicked()
  }

  render () {
    const {
      auth0Config,
      headerAction,
      headerText,
      showBackButton,
      title
    } = this.props

    return (
      <Navbar fluid fixedTop>

        <Navbar.Header>
          <Navbar.Brand>
            {showBackButton
              ? (

                  <Icon
                    fixedWidth={false}
                    name='arrow-left'
                    onClick={this._backButtonPressed}
                  />

              )
              : <AppMenu />
            }
          </Navbar.Brand>
        </Navbar.Header>

          {headerText
            ?
{headerText}

            :
{title}

          }

        {headerAction && (

{headerAction}

        )}

        {/**
          * HACK: Normally, NavLoginButtonAuth0 should be inside a  element,
          * however, in mobile mode, react-bootstrap's  causes the
          * submenus of this component to be displayed full-screen-width,
          * and that behavior is not desired here.
          */}
        {auth0Config && (
          <NavLoginButtonAuth0
            id='login-control'
            links={accountLinks}
          />
        )}
      </Navbar>
    )
  }
}

// connect to the redux store

const mapStateToProps = (state, ownProps) => {
  return {
    auth0Config: getAuth0Config(state.otp.config.persistence)
  }
}

const mapDispatchToProps = {
  setMobileScreen
}

export default connect(mapStateToProps, mapDispatchToProps)(MobileNavigationBar)
<|endoftext|>


<commit_before>'use strict';

const models = require('../models'),
  restify = require('restify'),
  Honeybadger = require('../utils').Honeybadger,
  statistics = require('../statistics'),
  requestUtils = require('../requestUtils'),
  area = require('@turf/area'),
  millify = require('millify');

const { Box, Measurement } = models;
const { idwTransformer } = statistics;

/**
 * @api {get} /stats Get some statistics about the database
 * @apiDescription returns an array with three numbers which denominates the count of senseBoxes, the count of measurements and the count of measurements in the last minute.
 * @apiName getStatistics
 * @apiGroup Misc
 * @apiVersion 0.1.0
 * @apiParam {Boolean=true,false} [human=false] if true, make numbers easier human readable.
 * @apiSuccessExample {json} [human=false]
 * [318,118241889,393]
 * @apiSuccessExample {json} [human=true]
 * ["318","118M","393"]
 */
const getStatistics = function getStatistics (req, res) {
  const { human } = req._userParams;
  const qrys = [
    Box.count({}),
    Measurement.count({}),
    Measurement.count({
      createdAt: {
        '$gt': new Date(Date.now() - 60000),
        '$lt': new Date()
      }
    })
  ];
  Promise.all(qrys)
    .then(function (results) {
      if (human === 'true') {
        results = results.map(r => millify(r).toString());
      }
      res.send(200, results);
    });
};

/**
 * @api {get} /statistics/idw?bbox=7.6,51.2,7.8,51.4&phenomenon=Temperatur Get a Inverse Distance Weighting Interpolation as FeatureCollection
 * @apiDescription Retrieve a JSON object containing
 *  - `breaks`: an array containing equal distance breaks. use `numClasses` parameter to control how many breaks to return
 *  - `featureCollection`: a GeoJSON FeatureCollection with a computed Inverse Distance Interpolation for a certain region of interest and phenomenon.
 *
 * The properties of each feature in the featureCollection is an object with ISO8601 timestamps which are the timeSteps. The number of the timesteps can be controlled using the `numTimeSteps` parameter. Values falling inside each timestep are first averaged.
 * Please be aware that requests with (areaSquareKilometers / cellWidth) > 2500 will be rejected.
 * @apiVersion 0.0.1
 * @apiGroup Interpolation
 * @apiName calculateIdw
 * @apiParam {String} phenomenon the name of the phenomenon you want to download the data for.
 * @apiUse BBoxParam
 * @apiParam {ISO8601Date} [from-date] Beginning date of measurement data (default: 2 days ago from now)
 * @apiParam {ISO8601Date} [to-date] End date of measurement data (default: now)
 * @apiParam {String=indoor,outdoor} [exposure] only return sensors of boxes with the specified exposure. Can be indoor or outdoor. Default undecided.
 * @apiParam {String=hex,square,triangle} [gridType=hex] The type of the grid for IDW calculation
 * @apiParam {Number} [cellWidth=50] The width of the grid cells in kilometers. Must be positive
 * @apiParam {Number=1-9} [power=1] The power of the IDW calculation
 * @apiParam {Number=1-10} [numTimeSteps=6] Return this many timesteps between `from-date` and `to-date`
 * @apiParam {Number} [numClasses=6] Number of classes in the breaks array. Must be positive
 */

const idwColumns = ['sensorId', 'value', 'lat', 'lon'];

const idwHandler = function (req, res, next) {
  const { phenomenon, bbox, exposure, cellWidth, gridType, power, numTimeSteps, numClasses } = req._userParams,
    fromDate = req._userParams['from-date'],
    toDate = req._userParams['to-date'];

  // validate numTimeSteps
  if (numTimeSteps < 1 || numTimeSteps >= 10) {
    return next(new restify.BadRequestError('parameter numTimeSteps must be between 1 and 10'));
  }

  // validate power
  if (power < 1 || power >= 9) {
    return next(new restify.BadRequestError('parameter power must be between 1 and 9'));
  }

  // validate cellWidth
  if (cellWidth < 0) {
    return next(new restify.BadRequestError('parameter cellWidth must be positive'));
  }

  // validate numClasses
  if (numClasses < 0) {
    return next(new restify.BadRequestError('parameter numClasses must be positive'));
  }

  // validate bbox param, we don't want too much load on our server!
  const areaSqKm = area(bbox) / 10e6;

  if (areaSqKm / cellWidth > 2500) {
    return next(new restify.UnprocessableEntityError('planned computation too expensive ((area in square kilometers / cellWidth) > 2500)'));
  }

  // build query
  const queryParams = {
    'sensors.title': phenomenon,
    'loc.geometry': {
      '$geoWithin': {
        '$geometry': bbox
      }
    }
  };

  // exposure parameter
  if (exposure) {
    queryParams['exposure'] = exposure;
  }

  Box.findMeasurementsOfBoxesStream(
    queryParams,
    phenomenon,
    fromDate.toDate(),
    toDate.toDate(),
    idwColumns,
    { createdAt: 1 })
    .then(function (cursor) {
      res.header('Content-Type', 'application/json; charset=utf-8');

      cursor
        .pipe(idwTransformer({
          numTimeSteps,
          numClasses,
          toDate,
          fromDate,
          bbox,
          gridType,
          cellWidth,
          power
        }))
        .on('error', function (err) {
          res.end(`Error: ${err.message}`);
        })
        .pipe(res);

    })
    .catch(function (err) {
      if (err === 'no senseBoxes found') {
        return next(new restify.NotFoundError(err));
      }

      console.log(err);
      Honeybadger.notify(err);

      return next(new restify.InternalServerError(err.message));
    });

};

module.exports = {
  getStatistics: [
    requestUtils.retrieveParameter('human', 'String', 'false', ['true', 'false']),
    getStatistics
  ],
  getIdw: [
    requestUtils.retrieveParameter('bbox', 'Array,Number', true),
    requestUtils.retrieveParameter('phenomenon', 'String', true),
    requestUtils.retrieveParameter('exposure', 'String', false, ['indoor', 'outdoor']),
    requestUtils.retrieveParameter('gridType', 'String', 'hex', ['hex', 'square', 'triangle']),
    requestUtils.retrieveParameter('cellWidth', 'Number', 50),
    requestUtils.retrieveParameter('power', 'Number', 1),
    requestUtils.retrieveParameter('numTimeSteps', 'Number', 6),
    requestUtils.retrieveParameter('numClasses', 'Number', 6),
    requestUtils.validateBboxParam,
    requestUtils.parseAndValidateTimeParams,
    idwHandler]
};
<commit_msg>fix #62 allow idw power values between 1 and 9<commit_after>'use strict';

const models = require('../models'),
  restify = require('restify'),
  Honeybadger = require('../utils').Honeybadger,
  statistics = require('../statistics'),
  requestUtils = require('../requestUtils'),
  area = require('@turf/area'),
  millify = require('millify');

const { Box, Measurement } = models;
const { idwTransformer } = statistics;

/**
 * @api {get} /stats Get some statistics about the database
 * @apiDescription returns an array with three numbers which denominates the count of senseBoxes, the count of measurements and the count of measurements in the last minute.
 * @apiName getStatistics
 * @apiGroup Misc
 * @apiVersion 0.1.0
 * @apiParam {Boolean=true,false} [human=false] if true, make numbers easier human readable.
 * @apiSuccessExample {json} [human=false]
 * [318,118241889,393]
 * @apiSuccessExample {json} [human=true]
 * ["318","118M","393"]
 */
const getStatistics = function getStatistics (req, res) {
  const { human } = req._userParams;
  const qrys = [
    Box.count({}),
    Measurement.count({}),
    Measurement.count({
      createdAt: {
        '$gt': new Date(Date.now() - 60000),
        '$lt': new Date()
      }
    })
  ];
  Promise.all(qrys)
    .then(function (results) {
      if (human === 'true') {
        results = results.map(r => millify(r).toString());
      }
      res.send(200, results);
    });
};

/**
 * @api {get} /statistics/idw?bbox=7.6,51.2,7.8,51.4&phenomenon=Temperatur Get a Inverse Distance Weighting Interpolation as FeatureCollection
 * @apiDescription Retrieve a JSON object containing
 *  - `breaks`: an array containing equal distance breaks. use `numClasses` parameter to control how many breaks to return
 *  - `featureCollection`: a GeoJSON FeatureCollection with a computed Inverse Distance Interpolation for a certain region of interest and phenomenon.
 *
 * The properties of each feature in the featureCollection is an object with ISO8601 timestamps which are the timeSteps. The number of the timesteps can be controlled using the `numTimeSteps` parameter. Values falling inside each timestep are first averaged.
 * Please be aware that requests with (areaSquareKilometers / cellWidth) > 2500 will be rejected.
 * @apiVersion 0.0.1
 * @apiGroup Interpolation
 * @apiName calculateIdw
 * @apiParam {String} phenomenon the name of the phenomenon you want to download the data for.
 * @apiUse BBoxParam
 * @apiParam {ISO8601Date} [from-date] Beginning date of measurement data (default: 2 days ago from now)
 * @apiParam {ISO8601Date} [to-date] End date of measurement data (default: now)
 * @apiParam {String=indoor,outdoor} [exposure] only return sensors of boxes with the specified exposure. Can be indoor or outdoor. Default undecided.
 * @apiParam {String=hex,square,triangle} [gridType=hex] The type of the grid for IDW calculation
 * @apiParam {Number} [cellWidth=50] The width of the grid cells in kilometers. Must be positive
 * @apiParam {Number=1-9} [power=1] The power of the IDW calculation
 * @apiParam {Number=1-10} [numTimeSteps=6] Return this many timesteps between `from-date` and `to-date`
 * @apiParam {Number} [numClasses=6] Number of classes in the breaks array. Must be positive
 */

const idwColumns = ['sensorId', 'value', 'lat', 'lon'];

const idwHandler = function (req, res, next) {
  const { phenomenon, bbox, exposure, cellWidth, gridType, power, numTimeSteps, numClasses } = req._userParams,
    fromDate = req._userParams['from-date'],
    toDate = req._userParams['to-date'];

  // validate numTimeSteps
  if (numTimeSteps < 1 || numTimeSteps >= 10) {
    return next(new restify.BadRequestError('parameter numTimeSteps must be between 1 and 10'));
  }

  // validate power
  if (power < 1 || power > 9) {
    return next(new restify.BadRequestError('parameter power must be between 1 and 9'));
  }

  // validate cellWidth
  if (cellWidth < 0) {
    return next(new restify.BadRequestError('parameter cellWidth must be positive'));
  }

  // validate numClasses
  if (numClasses < 0) {
    return next(new restify.BadRequestError('parameter numClasses must be positive'));
  }

  // validate bbox param, we don't want too much load on our server!
  const areaSqKm = area(bbox) / 10e6;

  if (areaSqKm / cellWidth > 2500) {
    return next(new restify.UnprocessableEntityError('planned computation too expensive ((area in square kilometers / cellWidth) > 2500)'));
  }

  // build query
  const queryParams = {
    'sensors.title': phenomenon,
    'loc.geometry': {
      '$geoWithin': {
        '$geometry': bbox
      }
    }
  };

  // exposure parameter
  if (exposure) {
    queryParams['exposure'] = exposure;
  }

  Box.findMeasurementsOfBoxesStream(
    queryParams,
    phenomenon,
    fromDate.toDate(),
    toDate.toDate(),
    idwColumns,
    { createdAt: 1 })
    .then(function (cursor) {
      res.header('Content-Type', 'application/json; charset=utf-8');

      cursor
        .pipe(idwTransformer({
          numTimeSteps,
          numClasses,
          toDate,
          fromDate,
          bbox,
          gridType,
          cellWidth,
          power
        }))
        .on('error', function (err) {
          res.end(`Error: ${err.message}`);
        })
        .pipe(res);

    })
    .catch(function (err) {
      if (err === 'no senseBoxes found') {
        return next(new restify.NotFoundError(err));
      }

      console.log(err);
      Honeybadger.notify(err);

      return next(new restify.InternalServerError(err.message));
    });

};

module.exports = {
  getStatistics: [
    requestUtils.retrieveParameter('human', 'String', 'false', ['true', 'false']),
    getStatistics
  ],
  getIdw: [
    requestUtils.retrieveParameter('bbox', 'Array,Number', true),
    requestUtils.retrieveParameter('phenomenon', 'String', true),
    requestUtils.retrieveParameter('exposure', 'String', false, ['indoor', 'outdoor']),
    requestUtils.retrieveParameter('gridType', 'String', 'hex', ['hex', 'square', 'triangle']),
    requestUtils.retrieveParameter('cellWidth', 'Number', 50),
    requestUtils.retrieveParameter('power', 'Number', 1),
    requestUtils.retrieveParameter('numTimeSteps', 'Number', 6),
    requestUtils.retrieveParameter('numClasses', 'Number', 6),
    requestUtils.validateBboxParam,
    requestUtils.parseAndValidateTimeParams,
    idwHandler]
};
<|endoftext|>


<commit_before>var h = require('hyperscript')
var pull = require('pull-stream')
var plugs = require('../plugs')
var cat = require('pull-cat')

var sbot_links2 = plugs.first(exports.sbot_links2 = [])
var avatar_name = plugs.first(exports.avatar_name = [])
var blob_url = require('../plugs').first(exports.blob_url = [])

var defaultTheme = {
  id: '&JFa42U6HtPm9k+s+AmpDIAoTJJI/PzoRC/J/WCfduDY=.sha256',
  name: 'patchbay-minimal.css'
}

var next = 'undefined' === typeof setImmediate ? setTimeout : setImmediate

var link = document.head.appendChild(h('link', {rel: 'stylesheet'}))
var activeTheme

function useTheme(id) {
  activeTheme = id
  link.href = id ? blob_url(id) : ''
  var forms = [].slice.call(document.querySelectorAll('.themes__form'))
  forms.forEach(updateForm)

  var radios = [].slice.call(document.querySelectorAll('input[type=radio]'))
  radios.forEach(function (radio) {
    radio.checked = (radio.value === activeTheme)
  })
}

function useSavedTheme() {
  useTheme(localStorage.themeId || defaultTheme.id)
}

next(useSavedTheme)

function themes() {
  return cat([
    pull.values([
      {
        id: '',
        name: 'none',
        feed: ''
      },
      defaultTheme,
    ]),
    pull(
      sbot_links2({
        query: [
          {$filter: {rel: ['mentions', {$prefix: 'patchbay-'}, {$gt: null}]}},
          {$filter: {dest: {$prefix: '&'}}},
          {$map: {id: 'dest', feed: 'source', name: ['rel', 1]}}
        ],
        live: true,
        sync: false,
      }),
      pull.filter(function (link) {
        return /\.css$/.test(link.name)
      })
    )
  ])
}

function onRadioClick(e) {
  if (this.checked) useTheme(this.value)
}

function updateForm(form) {
  var same = localStorage.themeId === activeTheme
  form.querySelector('.themes__id').value = activeTheme
  form.querySelector('.themes__reset').disabled = same
  form.querySelector('.themes__submit').disabled = same
  return form
}

function renderTheme(link) {
  return h('div.theme',
    h('input', {type: 'radio', name: 'theme',
      value: link.id, onclick: onRadioClick,
      checked: link.id === activeTheme
    }),
    link.id ? h('a', {href: '#'+link.id}, link.name) : link.name, ' ',
    link.feed ? h('a', {href: '#'+link.feed}, avatar_name(link.feed)) : ''
  )
}

function insertAfter(parentNode, newNode, referenceNode) {
  var nextSibling = referenceNode && referenceNode.nextSibling
  if (nextSibling) parentNode.insertBefore(newNode, nextSibling)
  else parentNode.appendChild(newNode)
}

function theme_view() {
  var themeInput
  var themesList = h('form.themes__list')
  var themesByKey = {}

  pull(
    themes(),
    pull.unique('id'),
    pull.drain(function (theme) {
      // don't show old versions of theme
      var key = theme.feed + theme.name
      var newerTheme = themesByKey[key]
      theme.el = renderTheme(theme)
      themesByKey[key] = theme
      if (!newerTheme) {
        // show latest theme
        themesList.appendChild(theme.el)
      } else if (theme.id === localStorage.themeId
              || theme.id === activeTheme) {
        // show old version because the user is still using it
        theme.el.appendChild(document.createTextNode(' (old)'))
        insertAfter(themesList, theme.el, newerTheme.el)
      } else {
        // ignore old version of theme
      }
    }, function (err) {
      if (err) console.error(err)
    })
  )

  return h('div.column.scroll-y', h('div',
    updateForm(h('form.themes__form', {onsubmit: onsubmit, onreset: onreset},
      themeInput = h('input.themes__id', {placeholder: 'theme id',
        value: link.href}), ' ',
      h('input.themes__reset', {type: 'reset'}), ' ',
      h('input.themes__submit', {type: 'submit', value: 'Save'}))),
      themesList
  ))

  function onsubmit(e) {
    e.preventDefault()
    useTheme(localStorage.themeId = themeInput.value)
  }

  function onreset(e) {
    e.preventDefault()
    useSavedTheme()
  }
}

exports.menu_items = function () {
  return h('a', {href:'#/theme'}, '/theme')
}

exports.screen_view = function (path) {
  if(path === '/theme') return theme_view()
}
<commit_msg>Remove blob size filter from themes query<commit_after>var h = require('hyperscript')
var pull = require('pull-stream')
var plugs = require('../plugs')
var cat = require('pull-cat')

var sbot_links2 = plugs.first(exports.sbot_links2 = [])
var avatar_name = plugs.first(exports.avatar_name = [])
var blob_url = require('../plugs').first(exports.blob_url = [])

var defaultTheme = {
  id: '&JFa42U6HtPm9k+s+AmpDIAoTJJI/PzoRC/J/WCfduDY=.sha256',
  name: 'patchbay-minimal.css'
}

var next = 'undefined' === typeof setImmediate ? setTimeout : setImmediate

var link = document.head.appendChild(h('link', {rel: 'stylesheet'}))
var activeTheme

function useTheme(id) {
  activeTheme = id
  link.href = id ? blob_url(id) : ''
  var forms = [].slice.call(document.querySelectorAll('.themes__form'))
  forms.forEach(updateForm)

  var radios = [].slice.call(document.querySelectorAll('input[type=radio]'))
  radios.forEach(function (radio) {
    radio.checked = (radio.value === activeTheme)
  })
}

function useSavedTheme() {
  useTheme(localStorage.themeId || defaultTheme.id)
}

next(useSavedTheme)

function themes() {
  return cat([
    pull.values([
      {
        id: '',
        name: 'none',
        feed: ''
      },
      defaultTheme,
    ]),
    pull(
      sbot_links2({
        query: [
          {$filter: {rel: ['mentions', {$prefix: 'patchbay-'}]}},
          {$filter: {dest: {$prefix: '&'}}},
          {$map: {id: 'dest', feed: 'source', name: ['rel', 1]}}
        ],
        live: true,
        sync: false,
      }),
      pull.filter(function (link) {
        return /\.css$/.test(link.name)
      })
    )
  ])
}

function onRadioClick(e) {
  if (this.checked) useTheme(this.value)
}

function updateForm(form) {
  var same = localStorage.themeId === activeTheme
  form.querySelector('.themes__id').value = activeTheme
  form.querySelector('.themes__reset').disabled = same
  form.querySelector('.themes__submit').disabled = same
  return form
}

function renderTheme(link) {
  return h('div.theme',
    h('input', {type: 'radio', name: 'theme',
      value: link.id, onclick: onRadioClick,
      checked: link.id === activeTheme
    }),
    link.id ? h('a', {href: '#'+link.id}, link.name) : link.name, ' ',
    link.feed ? h('a', {href: '#'+link.feed}, avatar_name(link.feed)) : ''
  )
}

function insertAfter(parentNode, newNode, referenceNode) {
  var nextSibling = referenceNode && referenceNode.nextSibling
  if (nextSibling) parentNode.insertBefore(newNode, nextSibling)
  else parentNode.appendChild(newNode)
}

function theme_view() {
  var themeInput
  var themesList = h('form.themes__list')
  var themesByKey = {}

  pull(
    themes(),
    pull.unique('id'),
    pull.drain(function (theme) {
      // don't show old versions of theme
      var key = theme.feed + theme.name
      var newerTheme = themesByKey[key]
      theme.el = renderTheme(theme)
      themesByKey[key] = theme
      if (!newerTheme) {
        // show latest theme
        themesList.appendChild(theme.el)
      } else if (theme.id === localStorage.themeId
              || theme.id === activeTheme) {
        // show old version because the user is still using it
        theme.el.appendChild(document.createTextNode(' (old)'))
        insertAfter(themesList, theme.el, newerTheme.el)
      } else {
        // ignore old version of theme
      }
    }, function (err) {
      if (err) console.error(err)
    })
  )

  return h('div.column.scroll-y', h('div',
    updateForm(h('form.themes__form', {onsubmit: onsubmit, onreset: onreset},
      themeInput = h('input.themes__id', {placeholder: 'theme id',
        value: link.href}), ' ',
      h('input.themes__reset', {type: 'reset'}), ' ',
      h('input.themes__submit', {type: 'submit', value: 'Save'}))),
      themesList
  ))

  function onsubmit(e) {
    e.preventDefault()
    useTheme(localStorage.themeId = themeInput.value)
  }

  function onreset(e) {
    e.preventDefault()
    useSavedTheme()
  }
}

exports.menu_items = function () {
  return h('a', {href:'#/theme'}, '/theme')
}

exports.screen_view = function (path) {
  if(path === '/theme') return theme_view()
}
<|endoftext|>


<commit_before>/**
 * Boone Software Uptime HTTP Monitor
 * @author Wes King
 */
'use strict';

// Dependencies
const q         = require('Q');
const http      = require('q-io/http');
const _         = require('underscore');
const Endpoint  = require('../schema/endpoint');
const Result    = require('../schema/result');

class Monitor {

  getEndpoints() {
    let deferred = q.defer();

    Endpoint.find((error, results) => {
      if (error) {
        deferred.reject(error);
      }
      deferred.resolve(results);
    });

    return deferred.promise;
  }

  saveResult(result) {
    let deferred = q.defer();
    let entity = new Result(result);

    entity.save((error, entity) => {
      if (error) {
        deferred.reject(error);
      }
      deferred.resolve(entity);
    });

    return deferred.promise;
  }

  testEndpoint(endpoint) {
    let deferred = q.defer();

    http.request(options).then((res) => {
      deferred.resolve({
        code: res.code,
        body: res.body,
        headers: res.headers
      });
    });

    return deferred.promise;
  }

  main() {
    this.getEndpoints()
      .then((endpoints) => {
        return q.all(endpoints.map((endpoint) => {
          return this.testEndpoint(endpoint);
        }))
      })
      .then((results) => {
        return q.all(results.map((result) => {
          return this.saveResult(result);
        }))
      })
  }

  init() {
    setInterval(this.main, 5000);
  }

}
<commit_msg>ES2016 conformity<commit_after>/**
 * Boone Software Uptime HTTP Monitor
 * @author Wes King
 */
'use strict';

// Dependencies
const _         = require('underscore');
const http      = require('q-io/http');
const q         = require('q');
const Endpoint  = require('../schema/endpoint');
const Result    = require('../schema/result');

class Monitor {
  getEndpoints() {
    let deferred = q.defer();

    Endpoint.find((error, results) => {
      if (error) {
        deferred.reject(error);
      }

      deferred.resolve(results);
    });

    return deferred.promise;
  }

  saveResult(result) {
    let deferred = q.defer();
    let entity = new Result(result);

    entity.save((error, entity) => {
      if (error) {
        deferred.reject(error);
      }

      deferred.resolve(entity);
    });

    return deferred.promise;
  }

  testEndpoint(endpoint) {
    let deferred = q.defer();

    http.request(options).then((res) => {
      deferred.resolve({
        code: res.code,
        body: res.body,
        headers: res.headers,
      });
    });

    return deferred.promise;
  }

  main() {
    this.getEndpoints()
      .then((endpoints) => q.all(endpoints.map((endpoint) =>  this.testEndpoint(endpoint))))
      .then((results) => q.all(results.map((result) =>  this.saveResult(result))));
  }

  init() {
    setInterval(this.main, 5000);
  }
}
<|endoftext|>


<commit_before>/**
 * Proudly created by ohad on 14/02/2017.
 *
 * Send data to the server using http requests.
 */
const Client = require('../client'),
      _      = require('../util/wrapper'),
      Const           = require('../../../common/const'),
      Storage         = require('./storage');
/**
 * Sends a HTTP request with message.
 * @param {Object} message
 */
exports.save = function save(message) {
  message = _enrich(message);
  const backendUrl = message.backendUrl;
  delete message.backendUrl;
  _.http.ajax(process.env.BACKEND_HOST + '/' + backendUrl, message);
};

/**
 * Takes care of requirements for this storage.
 * @param {Object} [options]
 * @param {function} onReady
 */
exports.init = function (options, onReady) {
  // Waiting for google analytics, so that Client.id exists.
  // Client.init(() => {
    let userMessage = {
      client    : {
        agent         : Client.agent,
        cookiesEnabled: Client.cookiesEnabled,
        screen        : {
          height: window.innerHeight,
          width : window.innerWidth
        }
      }
    };
    if (window.screen) {
      userMessage.client.screen.availHeight = window.screen.availHeight;
      userMessage.client.screen.availWidth  = window.screen.availWidth;
    }
  //   Storage.save(userMessage);
  // // });
  // onReady();
  _.http.ajax(Const.BACKEND_URL.CLIENT, userMessage, onReady)
};

/**
 * Enriches message with additional properties.
 * @param {Object} message
 * @private
 */
function _enrich(message) {
  return _.deepExtend(message, {
    timestamp: new Date().getTime(),
    // client   : {
    //   id     : Client.id,
    //   created: Client.created
    // },
    // url      : window.location.href,
    tracker  : Client.tracker
  });
}
<commit_msg>ajax tokens<commit_after>/**
 * Proudly created by ohad on 14/02/2017.
 *
 * Send data to the server using http requests.
 */
const Client = require('../client'),
      _      = require('../util/wrapper'),
      Const           = require('../../../common/const'),
      Storage         = require('./storage');
/**
 * Sends a HTTP request with message.
 * @param {Object} message
 */
exports.save = function save(message) {
  message = _enrich(message);
  const url = process.env.BACKEND_URL + '/' + message.backendUrl;
  delete message.backendUrl;
  _.http.ajax(url, message);
};

/**
 * Takes care of requirements for this storage.
 * @param {Object} [options]
 * @param {function} onReady
 */
exports.init = function (options, onReady) {
  // Waiting for google analytics, so that Client.id exists.
  // Client.init(() => {
    let userMessage = {
      client    : {
        agent         : Client.agent,
        cookiesEnabled: Client.cookiesEnabled,
        screen        : {
          height: window.innerHeight,
          width : window.innerWidth
        }
      }
    };
    if (window.screen) {
      userMessage.client.screen.availHeight = window.screen.availHeight;
      userMessage.client.screen.availWidth  = window.screen.availWidth;
    }
  //   Storage.save(userMessage);
  // // });
  // onReady();
  _.http.ajax(Const.BACKEND_URL.CLIENT, userMessage, onReady)
};

/**
 * Enriches message with additional properties.
 * @param {Object} message
 * @private
 */
function _enrich(message) {
  return _.deepExtend(message, {
    timestamp: new Date().getTime(),
    // client   : {
    //   id     : Client.id,
    //   created: Client.created
    // },
    // url      : window.location.href,
    tracker  : Client.tracker
  });
}
<|endoftext|>